1use std::{
4 collections::HashMap,
5 fmt,
6 path::PathBuf,
7 sync::{self, Arc},
8};
9
10pub mod raw_device_events;
11pub mod raw_events;
12
13use crate::{
14 event::{event, event_args},
15 window::{MonitorId, WindowId},
16};
17
18use parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock};
19use zng_app_context::app_local;
20use zng_layout::unit::PxDensity2d;
21use zng_layout::unit::{DipPoint, DipRect, DipSideOffsets, DipSize, Factor, Px, PxPoint, PxRect, PxSize};
22use zng_task::{
23 SignalOnce,
24 channel::{self, ChannelError, IpcBytes, IpcReceiver},
25};
26use zng_txt::Txt;
27use zng_var::ResponderVar;
28use zng_view_api::{
29 self, DeviceEventsFilter, DragDropId, Event, FocusResult, ViewProcessGen,
30 api_extension::{ApiExtensionId, ApiExtensionName, ApiExtensionPayload, ApiExtensionRecvError, ApiExtensions},
31 dialog::{FileDialog, FileDialogResponse, MsgDialog, MsgDialogResponse},
32 drag_drop::{DragDropData, DragDropEffect, DragDropError},
33 font::{FontOptions, IpcFontBytes},
34 image::{ImageMaskMode, ImageRequest, ImageTextureId},
35 window::{
36 CursorIcon, FocusIndicator, FrameRequest, FrameUpdateRequest, HeadlessOpenData, HeadlessRequest, RenderMode, ResizeDirection,
37 VideoMode, WindowButton, WindowRequest, WindowStateAll,
38 },
39};
40
41pub(crate) use zng_view_api::{
42 Controller, raw_input::InputDeviceId as ApiDeviceId, window::MonitorId as ApiMonitorId, window::WindowId as ApiWindowId,
43};
44use zng_view_api::{
45 clipboard::{ClipboardData, ClipboardError, ClipboardType},
46 font::{FontFaceId, FontId, FontVariationName},
47 image::{ImageId, ImageLoadedData},
48};
49
50use self::raw_device_events::InputDeviceId;
51
52use super::{APP, AppId};
53
54#[expect(non_camel_case_types)]
56pub struct VIEW_PROCESS;
57struct ViewProcessService {
58 process: zng_view_api::Controller,
59 input_device_ids: HashMap<ApiDeviceId, InputDeviceId>,
60 monitor_ids: HashMap<ApiMonitorId, MonitorId>,
61
62 data_generation: ViewProcessGen,
63
64 extensions: ApiExtensions,
65
66 loading_images: Vec<sync::Weak<RwLock<ViewImageData>>>,
67 frame_images: Vec<sync::Weak<RwLock<ViewImageData>>>,
68 encoding_images: Vec<EncodeRequest>,
69
70 pending_frames: usize,
71
72 message_dialogs: Vec<(zng_view_api::dialog::DialogId, ResponderVar<MsgDialogResponse>)>,
73 file_dialogs: Vec<(zng_view_api::dialog::DialogId, ResponderVar<FileDialogResponse>)>,
74
75 ping_count: u16,
76}
77app_local! {
78 static VIEW_PROCESS_SV: Option<ViewProcessService> = None;
79}
80impl VIEW_PROCESS {
81 pub fn is_available(&self) -> bool {
84 APP.is_running() && VIEW_PROCESS_SV.read().is_some()
85 }
86
87 fn read(&self) -> MappedRwLockReadGuard<'_, ViewProcessService> {
88 VIEW_PROCESS_SV.read_map(|e| e.as_ref().expect("VIEW_PROCESS not available"))
89 }
90
91 fn write(&self) -> MappedRwLockWriteGuard<'_, ViewProcessService> {
92 VIEW_PROCESS_SV.write_map(|e| e.as_mut().expect("VIEW_PROCESS not available"))
93 }
94
95 fn try_write(&self) -> Result<MappedRwLockWriteGuard<'_, ViewProcessService>> {
96 let vp = VIEW_PROCESS_SV.write();
97 if let Some(w) = &*vp
98 && w.process.is_connected()
99 {
100 return Ok(MappedRwLockWriteGuard::map(vp, |w| w.as_mut().unwrap()));
101 }
102 Err(ChannelError::disconnected())
103 }
104
105 fn check_app(&self, id: AppId) {
106 let actual = APP.id();
107 if Some(id) != actual {
108 panic!("cannot use view handle from app `{id:?}` in app `{actual:?}`");
109 }
110 }
111
112 fn handle_write(&self, id: AppId) -> MappedRwLockWriteGuard<'_, ViewProcessService> {
113 self.check_app(id);
114 self.write()
115 }
116
117 pub fn is_connected(&self) -> bool {
119 self.read().process.is_connected()
120 }
121
122 pub fn is_headless_with_render(&self) -> bool {
124 self.read().process.headless()
125 }
126
127 pub fn is_same_process(&self) -> bool {
129 self.read().process.same_process()
130 }
131
132 pub fn generation(&self) -> ViewProcessGen {
134 self.read().process.generation()
135 }
136
137 pub fn set_device_events_filter(&self, filter: DeviceEventsFilter) -> Result<()> {
142 self.write().process.set_device_events_filter(filter)
143 }
144
145 pub fn open_window(&self, config: WindowRequest) -> Result<()> {
152 let _s = tracing::debug_span!("VIEW_PROCESS.open_window").entered();
153 self.write().process.open_window(config)
154 }
155
156 pub fn open_headless(&self, config: HeadlessRequest) -> Result<()> {
166 let _s = tracing::debug_span!("VIEW_PROCESS.open_headless").entered();
167 self.write().process.open_headless(config)
168 }
169
170 pub fn add_image(&self, request: ImageRequest<IpcBytes>) -> Result<ViewImage> {
179 let mut app = self.write();
180 let id = app.process.add_image(request)?;
181 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
182 id: Some(id),
183 app_id: APP.id(),
184 generation: app.process.generation(),
185 size: PxSize::zero(),
186 partial_size: PxSize::zero(),
187 density: None,
188 is_opaque: false,
189 partial_pixels: None,
190 pixels: None,
191 is_mask: false,
192 done_signal: SignalOnce::new(),
193 })));
194 app.loading_images.push(Arc::downgrade(&img.0));
195 Ok(img)
196 }
197
198 pub fn add_image_pro(&self, request: ImageRequest<IpcReceiver<IpcBytes>>) -> Result<ViewImage> {
209 let mut app = self.write();
210 let id = app.process.add_image_pro(request)?;
211 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
212 id: Some(id),
213 app_id: APP.id(),
214 generation: app.process.generation(),
215 size: PxSize::zero(),
216 partial_size: PxSize::zero(),
217 density: None,
218 is_opaque: false,
219 partial_pixels: None,
220 pixels: None,
221 is_mask: false,
222 done_signal: SignalOnce::new(),
223 })));
224 app.loading_images.push(Arc::downgrade(&img.0));
225 Ok(img)
226 }
227
228 pub fn clipboard(&self) -> Result<&ViewClipboard> {
230 if VIEW_PROCESS.is_connected() {
231 Ok(&ViewClipboard {})
232 } else {
233 Err(ChannelError::disconnected())
234 }
235 }
236
237 pub fn image_decoders(&self) -> Result<Vec<Txt>> {
241 self.write().process.image_decoders()
242 }
243
244 pub fn image_encoders(&self) -> Result<Vec<Txt>> {
248 self.write().process.image_encoders()
249 }
250
251 pub fn pending_frames(&self) -> usize {
255 self.write().pending_frames
256 }
257
258 pub fn respawn(&self) {
262 self.write().process.respawn()
263 }
264
265 pub fn extension_id(&self, extension_name: impl Into<ApiExtensionName>) -> Result<Option<ApiExtensionId>> {
271 let me = self.read();
272 if me.process.is_connected() {
273 Ok(me.extensions.id(&extension_name.into()))
274 } else {
275 Err(ChannelError::disconnected())
276 }
277 }
278
279 pub fn third_party_licenses(&self) -> Result<Vec<crate::third_party::LicenseUsed>> {
284 self.write().process.third_party_licenses()
285 }
286
287 pub fn app_extension_raw(&self, extension_id: ApiExtensionId, extension_request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
289 self.write().process.app_extension(extension_id, extension_request)
290 }
291
292 pub fn app_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
294 where
295 I: serde::Serialize,
296 O: serde::de::DeserializeOwned,
297 {
298 let payload = ApiExtensionPayload::serialize(&request).unwrap();
299 let response = self.write().process.app_extension(extension_id, payload)?;
300 Ok(response.deserialize::<O>())
301 }
302
303 pub fn handle_disconnect(&self, vp_gen: ViewProcessGen) {
309 self.write().process.handle_disconnect(vp_gen)
310 }
311
312 pub(super) fn start<F>(&self, view_process_exe: PathBuf, view_process_env: HashMap<Txt, Txt>, headless: bool, on_event: F)
314 where
315 F: FnMut(Event) + Send + 'static,
316 {
317 let _s = tracing::debug_span!("VIEW_PROCESS.start").entered();
318
319 let process = zng_view_api::Controller::start(view_process_exe, view_process_env, headless, on_event);
320 *VIEW_PROCESS_SV.write() = Some(ViewProcessService {
321 data_generation: process.generation(),
322 process,
323 input_device_ids: HashMap::default(),
324 monitor_ids: HashMap::default(),
325 loading_images: vec![],
326 encoding_images: vec![],
327 frame_images: vec![],
328 pending_frames: 0,
329 message_dialogs: vec![],
330 file_dialogs: vec![],
331 extensions: ApiExtensions::default(),
332 ping_count: 0,
333 });
334 }
335
336 pub(crate) fn on_window_opened(&self, window_id: WindowId, data: zng_view_api::window::WindowOpenData) -> (ViewWindow, WindowOpenData) {
337 let mut app = self.write();
338 let _ = app.check_generation();
339
340 let win = ViewWindow(Arc::new(ViewWindowData {
341 app_id: APP.id().unwrap(),
342 id: ApiWindowId::from_raw(window_id.get()),
343 generation: app.data_generation,
344 }));
345 drop(app);
346
347 let data = WindowOpenData::new(data, |id| self.monitor_id(id));
348
349 (win, data)
350 }
351 pub(super) fn input_device_id(&self, id: ApiDeviceId) -> InputDeviceId {
353 *self.write().input_device_ids.entry(id).or_insert_with(InputDeviceId::new_unique)
354 }
355
356 pub(super) fn monitor_id(&self, id: ApiMonitorId) -> MonitorId {
358 *self.write().monitor_ids.entry(id).or_insert_with(MonitorId::new_unique)
359 }
360
361 pub(super) fn handle_inited(&self, vp_gen: ViewProcessGen, extensions: ApiExtensions) {
367 let mut me = self.write();
368 me.extensions = extensions;
369 me.process.handle_inited(vp_gen);
370 }
371
372 pub(super) fn handle_suspended(&self) {
373 self.write().process.handle_suspended();
374 }
375
376 pub(crate) fn on_headless_opened(
377 &self,
378 id: WindowId,
379 data: zng_view_api::window::HeadlessOpenData,
380 ) -> (ViewHeadless, HeadlessOpenData) {
381 let mut app = self.write();
382 let _ = app.check_generation();
383
384 let surf = ViewHeadless(Arc::new(ViewWindowData {
385 app_id: APP.id().unwrap(),
386 id: ApiWindowId::from_raw(id.get()),
387 generation: app.data_generation,
388 }));
389
390 (surf, data)
391 }
392
393 fn loading_image_index(&self, id: ImageId) -> Option<usize> {
394 let mut app = self.write();
395
396 app.loading_images.retain(|i| i.strong_count() > 0);
398
399 app.loading_images.iter().position(|i| i.upgrade().unwrap().read().id == Some(id))
400 }
401
402 pub(super) fn on_image_metadata_loaded(
403 &self,
404 id: ImageId,
405 size: PxSize,
406 density: Option<PxDensity2d>,
407 is_mask: bool,
408 ) -> Option<ViewImage> {
409 if let Some(i) = self.loading_image_index(id) {
410 let img = self.read().loading_images[i].upgrade().unwrap();
411 {
412 let mut img = img.write();
413 img.size = size;
414 img.density = density;
415 img.is_mask = is_mask;
416 }
417 Some(ViewImage(img))
418 } else {
419 None
420 }
421 }
422
423 pub(super) fn on_image_partially_loaded(
424 &self,
425 id: ImageId,
426 partial_size: PxSize,
427 density: Option<PxDensity2d>,
428 is_opaque: bool,
429 is_mask: bool,
430 partial_pixels: IpcBytes,
431 ) -> Option<ViewImage> {
432 if let Some(i) = self.loading_image_index(id) {
433 let img = self.read().loading_images[i].upgrade().unwrap();
434 {
435 let mut img = img.write();
436 img.partial_size = partial_size;
437 img.density = density;
438 img.is_opaque = is_opaque;
439 img.partial_pixels = Some(partial_pixels);
440 img.is_mask = is_mask;
441 }
442 Some(ViewImage(img))
443 } else {
444 None
445 }
446 }
447
448 pub(super) fn on_image_loaded(&self, data: ImageLoadedData) -> Option<ViewImage> {
449 if let Some(i) = self.loading_image_index(data.id) {
450 let img = self.write().loading_images.swap_remove(i).upgrade().unwrap();
451 {
452 let mut img = img.write();
453 img.size = data.size;
454 img.partial_size = data.size;
455 img.density = data.density;
456 img.is_opaque = data.is_opaque;
457 img.pixels = Some(Ok(data.pixels));
458 img.partial_pixels = None;
459 img.is_mask = data.is_mask;
460 img.done_signal.set();
461 }
462 Some(ViewImage(img))
463 } else {
464 None
465 }
466 }
467
468 pub(super) fn on_image_error(&self, id: ImageId, error: Txt) -> Option<ViewImage> {
469 if let Some(i) = self.loading_image_index(id) {
470 let img = self.write().loading_images.swap_remove(i).upgrade().unwrap();
471 {
472 let mut img = img.write();
473 img.pixels = Some(Err(error));
474 img.done_signal.set();
475 }
476 Some(ViewImage(img))
477 } else {
478 None
479 }
480 }
481
482 pub(crate) fn on_frame_rendered(&self, _id: WindowId) {
483 let mut vp = self.write();
484 vp.pending_frames = vp.pending_frames.saturating_sub(1);
485 }
486
487 pub(crate) fn on_frame_image(&self, data: ImageLoadedData) -> ViewImage {
488 ViewImage(Arc::new(RwLock::new(ViewImageData {
489 app_id: APP.id(),
490 id: Some(data.id),
491 generation: self.generation(),
492 size: data.size,
493 partial_size: data.size,
494 density: data.density,
495 is_opaque: data.is_opaque,
496 partial_pixels: None,
497 pixels: Some(Ok(data.pixels)),
498 is_mask: data.is_mask,
499 done_signal: SignalOnce::new_set(),
500 })))
501 }
502
503 pub(super) fn on_frame_image_ready(&self, id: ImageId) -> Option<ViewImage> {
504 let mut app = self.write();
505
506 app.frame_images.retain(|i| i.strong_count() > 0);
508
509 let i = app.frame_images.iter().position(|i| i.upgrade().unwrap().read().id == Some(id));
510
511 i.map(|i| ViewImage(app.frame_images.swap_remove(i).upgrade().unwrap()))
512 }
513
514 pub(super) fn on_image_encoded(&self, id: ImageId, format: Txt, data: IpcBytes) {
515 self.on_image_encode_result(id, format, Ok(data));
516 }
517 pub(super) fn on_image_encode_error(&self, id: ImageId, format: Txt, error: Txt) {
518 self.on_image_encode_result(id, format, Err(EncodeError::Encode(error)));
519 }
520 fn on_image_encode_result(&self, id: ImageId, format: Txt, result: std::result::Result<IpcBytes, EncodeError>) {
521 let mut app = self.write();
522 app.encoding_images.retain(move |r| {
523 let done = r.image_id == id && r.format == format;
524 if done {
525 for sender in &r.listeners {
526 let _ = sender.send_blocking(result.clone());
527 }
528 }
529 !done
530 })
531 }
532
533 pub(crate) fn on_message_dlg_response(&self, id: zng_view_api::dialog::DialogId, response: MsgDialogResponse) {
534 let mut app = self.write();
535 if let Some(i) = app.message_dialogs.iter().position(|(i, _)| *i == id) {
536 let (_, r) = app.message_dialogs.swap_remove(i);
537 r.respond(response);
538 }
539 }
540
541 pub(crate) fn on_file_dlg_response(&self, id: zng_view_api::dialog::DialogId, response: FileDialogResponse) {
542 let mut app = self.write();
543 if let Some(i) = app.file_dialogs.iter().position(|(i, _)| *i == id) {
544 let (_, r) = app.file_dialogs.swap_remove(i);
545 r.respond(response);
546 }
547 }
548
549 pub(super) fn on_respawned(&self, _gen: ViewProcessGen) {
550 let mut app = self.write();
551 app.pending_frames = 0;
552 for (_, r) in app.message_dialogs.drain(..) {
553 r.respond(MsgDialogResponse::Error(Txt::from_static("respawn")));
554 }
555 }
556
557 pub(crate) fn exit(&self) {
558 *VIEW_PROCESS_SV.write() = None;
559 }
560
561 pub(crate) fn ping(&self) {
562 let mut app = self.write();
563 let count = app.ping_count.wrapping_add(1);
564 if let Ok(c) = app.process.ping(count)
565 && c != count
566 {
567 tracing::error!("incorrect ping response, expected {count}, was {c}");
568 }
569 app.ping_count = count;
570 }
571
572 pub(crate) fn on_pong(&self, count: u16) {
573 let expected = self.read().ping_count;
574 if expected != count {
575 tracing::warn!("unexpected pong event, expected {expected}, was {count}");
577 }
578 }
579}
580impl ViewProcessService {
581 #[must_use = "if `true` all current WinId, DevId and MonId are invalid"]
582 fn check_generation(&mut self) -> bool {
583 let vp_gen = self.process.generation();
584 let invalid = vp_gen != self.data_generation;
585 if invalid {
586 self.data_generation = vp_gen;
587 self.input_device_ids.clear();
588 self.monitor_ids.clear();
589 }
590 invalid
591 }
592}
593
594event_args! {
595 pub struct ViewProcessInitedArgs {
597 pub generation: ViewProcessGen,
599
600 pub is_respawn: bool,
605
606 pub extensions: ApiExtensions,
610
611 ..
612
613 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
615 list.search_all()
616 }
617 }
618
619 pub struct ViewProcessSuspendedArgs {
621
622 ..
623
624 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
626 list.search_all()
627 }
628 }
629}
630
631event! {
632 pub static VIEW_PROCESS_INITED_EVENT: ViewProcessInitedArgs;
634 pub static VIEW_PROCESS_SUSPENDED_EVENT: ViewProcessSuspendedArgs;
639}
640
641#[derive(Debug, Clone)]
643#[non_exhaustive]
644pub struct WindowOpenData {
645 pub state: WindowStateAll,
647
648 pub monitor: Option<MonitorId>,
650
651 pub position: (PxPoint, DipPoint),
655 pub size: DipSize,
657
658 pub scale_factor: Factor,
660
661 pub render_mode: RenderMode,
663
664 pub safe_padding: DipSideOffsets,
670}
671impl WindowOpenData {
672 pub(crate) fn new(data: zng_view_api::window::WindowOpenData, map_monitor: impl FnOnce(ApiMonitorId) -> MonitorId) -> Self {
673 WindowOpenData {
674 state: data.state,
675 monitor: data.monitor.map(map_monitor),
676 position: data.position,
677 size: data.size,
678 scale_factor: data.scale_factor,
679 render_mode: data.render_mode,
680 safe_padding: data.safe_padding,
681 }
682 }
683}
684
685#[derive(Debug, Clone)]
689#[must_use = "the window is closed when all clones of the handle are dropped"]
690pub struct ViewWindow(Arc<ViewWindowData>);
691impl PartialEq for ViewWindow {
692 fn eq(&self, other: &Self) -> bool {
693 Arc::ptr_eq(&self.0, &other.0)
694 }
695}
696impl Eq for ViewWindow {}
697
698impl ViewWindow {
699 pub fn generation(&self) -> ViewProcessGen {
701 self.0.generation
702 }
703
704 pub fn set_title(&self, title: Txt) -> Result<()> {
706 self.0.call(|id, p| p.set_title(id, title))
707 }
708
709 pub fn set_visible(&self, visible: bool) -> Result<()> {
711 self.0.call(|id, p| p.set_visible(id, visible))
712 }
713
714 pub fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
716 self.0.call(|id, p| p.set_always_on_top(id, always_on_top))
717 }
718
719 pub fn set_movable(&self, movable: bool) -> Result<()> {
721 self.0.call(|id, p| p.set_movable(id, movable))
722 }
723
724 pub fn set_resizable(&self, resizable: bool) -> Result<()> {
726 self.0.call(|id, p| p.set_resizable(id, resizable))
727 }
728
729 pub fn set_icon(&self, icon: Option<&ViewImage>) -> Result<()> {
731 self.0.call(|id, p| {
732 if let Some(icon) = icon {
733 let icon = icon.0.read();
734 if p.generation() == icon.generation {
735 p.set_icon(id, icon.id)
736 } else {
737 Err(ChannelError::disconnected())
738 }
739 } else {
740 p.set_icon(id, None)
741 }
742 })
743 }
744
745 pub fn set_cursor(&self, cursor: Option<CursorIcon>) -> Result<()> {
747 self.0.call(|id, p| p.set_cursor(id, cursor))
748 }
749
750 pub fn set_cursor_image(&self, cursor: Option<&ViewImage>, hotspot: PxPoint) -> Result<()> {
757 self.0.call(|id, p| {
758 if let Some(cur) = cursor {
759 let cur = cur.0.read();
760 if p.generation() == cur.generation {
761 p.set_cursor_image(id, cur.id.map(|img| zng_view_api::window::CursorImage::new(img, hotspot)))
762 } else {
763 Err(ChannelError::disconnected())
764 }
765 } else {
766 p.set_cursor_image(id, None)
767 }
768 })
769 }
770
771 pub fn set_taskbar_visible(&self, visible: bool) -> Result<()> {
773 self.0.call(|id, p| p.set_taskbar_visible(id, visible))
774 }
775
776 pub fn bring_to_top(&self) -> Result<()> {
778 self.0.call(|id, p| p.bring_to_top(id))
779 }
780
781 pub fn set_state(&self, state: WindowStateAll) -> Result<()> {
783 self.0.call(|id, p| p.set_state(id, state))
784 }
785
786 pub fn set_video_mode(&self, mode: VideoMode) -> Result<()> {
788 self.0.call(|id, p| p.set_video_mode(id, mode))
789 }
790
791 pub fn set_enabled_buttons(&self, buttons: WindowButton) -> Result<()> {
793 self.0.call(|id, p| p.set_enabled_buttons(id, buttons))
794 }
795
796 pub fn renderer(&self) -> ViewRenderer {
798 ViewRenderer(Arc::downgrade(&self.0))
799 }
800
801 pub fn set_capture_mode(&self, enabled: bool) -> Result<()> {
806 self.0.call(|id, p| p.set_capture_mode(id, enabled))
807 }
808
809 pub fn focus(&self) -> Result<FocusResult> {
813 self.0.call(|id, p| p.focus(id))
814 }
815
816 pub fn set_focus_indicator(&self, indicator: Option<FocusIndicator>) -> Result<()> {
819 self.0.call(|id, p| p.set_focus_indicator(id, indicator))
820 }
821
822 pub fn drag_move(&self) -> Result<()> {
826 self.0.call(|id, p| p.drag_move(id))
827 }
828
829 pub fn drag_resize(&self, direction: ResizeDirection) -> Result<()> {
833 self.0.call(|id, p| p.drag_resize(id, direction))
834 }
835
836 pub fn start_drag_drop(
842 &self,
843 data: Vec<DragDropData>,
844 allowed_effects: DragDropEffect,
845 ) -> Result<std::result::Result<DragDropId, DragDropError>> {
846 self.0.call(|id, p| p.start_drag_drop(id, data, allowed_effects))
847 }
848
849 pub fn drag_dropped(&self, drop_id: DragDropId, applied: DragDropEffect) -> Result<()> {
851 self.0.call(|id, p| p.drag_dropped(id, drop_id, applied))
852 }
853
854 pub fn open_title_bar_context_menu(&self, position: DipPoint) -> Result<()> {
856 self.0.call(|id, p| p.open_title_bar_context_menu(id, position))
857 }
858
859 pub fn message_dialog(&self, dlg: MsgDialog, responder: ResponderVar<MsgDialogResponse>) -> Result<()> {
864 let dlg_id = self.0.call(|id, p| p.message_dialog(id, dlg))?;
865 VIEW_PROCESS.handle_write(self.0.app_id).message_dialogs.push((dlg_id, responder));
866 Ok(())
867 }
868
869 pub fn file_dialog(&self, dlg: FileDialog, responder: ResponderVar<FileDialogResponse>) -> Result<()> {
874 let dlg_id = self.0.call(|id, p| p.file_dialog(id, dlg))?;
875 VIEW_PROCESS.handle_write(self.0.app_id).file_dialogs.push((dlg_id, responder));
876 Ok(())
877 }
878
879 pub fn access_update(&self, update: zng_view_api::access::AccessTreeUpdate) -> Result<()> {
881 self.0.call(|id, p| p.access_update(id, update))
882 }
883
884 pub fn set_ime_area(&self, area: Option<DipRect>) -> Result<()> {
888 self.0.call(|id, p| p.set_ime_area(id, area))
889 }
890
891 pub fn set_system_shutdown_warn(&self, reason: Txt) -> Result<()> {
902 self.0.call(move |id, p| p.set_system_shutdown_warn(id, reason))
903 }
904
905 pub fn close(self) {
907 drop(self)
908 }
909
910 pub fn window_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
912 self.0.call(|id, p| p.window_extension(id, extension_id, request))
913 }
914
915 pub fn window_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
917 where
918 I: serde::Serialize,
919 O: serde::de::DeserializeOwned,
920 {
921 let r = self.window_extension_raw(extension_id, ApiExtensionPayload::serialize(&request).unwrap())?;
922 Ok(r.deserialize())
923 }
924}
925
926#[derive(Clone, Debug)]
928pub enum ViewWindowOrHeadless {
929 Window(ViewWindow),
931 Headless(ViewHeadless),
933}
934impl ViewWindowOrHeadless {
935 pub fn renderer(&self) -> ViewRenderer {
937 match self {
938 ViewWindowOrHeadless::Window(w) => w.renderer(),
939 ViewWindowOrHeadless::Headless(h) => h.renderer(),
940 }
941 }
942
943 pub fn window_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
945 match self {
946 ViewWindowOrHeadless::Window(w) => w.window_extension_raw(extension_id, request),
947 ViewWindowOrHeadless::Headless(h) => h.window_extension_raw(extension_id, request),
948 }
949 }
950
951 pub fn window_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
953 where
954 I: serde::Serialize,
955 O: serde::de::DeserializeOwned,
956 {
957 match self {
958 ViewWindowOrHeadless::Window(w) => w.window_extension(extension_id, request),
959 ViewWindowOrHeadless::Headless(h) => h.window_extension(extension_id, request),
960 }
961 }
962}
963impl From<ViewWindow> for ViewWindowOrHeadless {
964 fn from(w: ViewWindow) -> Self {
965 ViewWindowOrHeadless::Window(w)
966 }
967}
968impl From<ViewHeadless> for ViewWindowOrHeadless {
969 fn from(w: ViewHeadless) -> Self {
970 ViewWindowOrHeadless::Headless(w)
971 }
972}
973
974#[derive(Debug)]
975struct ViewWindowData {
976 app_id: AppId,
977 id: ApiWindowId,
978 generation: ViewProcessGen,
979}
980impl ViewWindowData {
981 fn call<R>(&self, f: impl FnOnce(ApiWindowId, &mut Controller) -> Result<R>) -> Result<R> {
982 let mut app = VIEW_PROCESS.handle_write(self.app_id);
983 if app.check_generation() {
984 Err(ChannelError::disconnected())
985 } else {
986 f(self.id, &mut app.process)
987 }
988 }
989}
990impl Drop for ViewWindowData {
991 fn drop(&mut self) {
992 if VIEW_PROCESS.is_available() {
993 let mut app = VIEW_PROCESS.handle_write(self.app_id);
994 if self.generation == app.process.generation() {
995 let _ = app.process.close(self.id);
996 }
997 }
998 }
999}
1000type Result<T> = std::result::Result<T, ChannelError>;
1001
1002#[derive(Clone, Debug)]
1006#[must_use = "the view is disposed when all clones of the handle are dropped"]
1007pub struct ViewHeadless(Arc<ViewWindowData>);
1008impl PartialEq for ViewHeadless {
1009 fn eq(&self, other: &Self) -> bool {
1010 Arc::ptr_eq(&self.0, &other.0)
1011 }
1012}
1013impl Eq for ViewHeadless {}
1014impl ViewHeadless {
1015 pub fn set_size(&self, size: DipSize, scale_factor: Factor) -> Result<()> {
1017 self.0.call(|id, p| p.set_headless_size(id, size, scale_factor))
1018 }
1019
1020 pub fn renderer(&self) -> ViewRenderer {
1022 ViewRenderer(Arc::downgrade(&self.0))
1023 }
1024
1025 pub fn window_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
1027 self.0.call(|id, p| p.window_extension(id, extension_id, request))
1028 }
1029
1030 pub fn window_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
1032 where
1033 I: serde::Serialize,
1034 O: serde::de::DeserializeOwned,
1035 {
1036 let r = self.window_extension_raw(extension_id, ApiExtensionPayload::serialize(&request).unwrap())?;
1037 Ok(r.deserialize())
1038 }
1039}
1040
1041#[derive(Clone, Debug)]
1046pub struct ViewRenderer(sync::Weak<ViewWindowData>);
1047impl PartialEq for ViewRenderer {
1048 fn eq(&self, other: &Self) -> bool {
1049 if let Some(s) = self.0.upgrade()
1050 && let Some(o) = other.0.upgrade()
1051 {
1052 Arc::ptr_eq(&s, &o)
1053 } else {
1054 false
1055 }
1056 }
1057}
1058impl Eq for ViewRenderer {}
1059
1060impl ViewRenderer {
1061 fn call<R>(&self, f: impl FnOnce(ApiWindowId, &mut Controller) -> Result<R>) -> Result<R> {
1062 if let Some(c) = self.0.upgrade() {
1063 c.call(f)
1064 } else {
1065 Err(ChannelError::disconnected())
1066 }
1067 }
1068
1069 pub fn generation(&self) -> Result<ViewProcessGen> {
1071 self.0.upgrade().map(|c| c.generation).ok_or(ChannelError::disconnected())
1072 }
1073
1074 pub fn use_image(&self, image: &ViewImage) -> Result<ImageTextureId> {
1078 self.call(|id, p| {
1079 let image = image.0.read();
1080 if p.generation() == image.generation {
1081 p.use_image(id, image.id.unwrap_or(ImageId::INVALID))
1082 } else {
1083 Err(ChannelError::disconnected())
1084 }
1085 })
1086 }
1087
1088 pub fn update_image_use(&mut self, tex_id: ImageTextureId, image: &ViewImage, dirty_rect: Option<PxRect>) -> Result<bool> {
1095 self.call(|id, p| {
1096 let image = image.0.read();
1097 if p.generation() == image.generation {
1098 p.update_image_use(id, tex_id, image.id.unwrap_or(ImageId::INVALID), dirty_rect)
1099 } else {
1100 Err(ChannelError::disconnected())
1101 }
1102 })
1103 }
1104
1105 pub fn delete_image_use(&mut self, tex_id: ImageTextureId) -> Result<()> {
1107 self.call(|id, p| p.delete_image_use(id, tex_id))
1108 }
1109
1110 pub fn add_font_face(&self, bytes: IpcFontBytes, index: u32) -> Result<FontFaceId> {
1114 self.call(|id, p| p.add_font_face(id, bytes, index))
1115 }
1116
1117 pub fn delete_font_face(&self, font_face_id: FontFaceId) -> Result<()> {
1119 self.call(|id, p| p.delete_font_face(id, font_face_id))
1120 }
1121
1122 pub fn add_font(
1126 &self,
1127 font_face_id: FontFaceId,
1128 glyph_size: Px,
1129 options: FontOptions,
1130 variations: Vec<(FontVariationName, f32)>,
1131 ) -> Result<FontId> {
1132 self.call(|id, p| p.add_font(id, font_face_id, glyph_size, options, variations))
1133 }
1134
1135 pub fn delete_font(&self, font_id: FontId) -> Result<()> {
1137 self.call(|id, p| p.delete_font(id, font_id))
1138 }
1139
1140 pub fn frame_image(&self, mask: Option<ImageMaskMode>) -> Result<ViewImage> {
1142 if let Some(c) = self.0.upgrade() {
1143 let id = c.call(|id, p| p.frame_image(id, mask))?;
1144 Ok(Self::add_frame_image(c.app_id, id))
1145 } else {
1146 Err(ChannelError::disconnected())
1147 }
1148 }
1149
1150 pub fn frame_image_rect(&self, rect: PxRect, mask: Option<ImageMaskMode>) -> Result<ViewImage> {
1152 if let Some(c) = self.0.upgrade() {
1153 let id = c.call(|id, p| p.frame_image_rect(id, rect, mask))?;
1154 Ok(Self::add_frame_image(c.app_id, id))
1155 } else {
1156 Err(ChannelError::disconnected())
1157 }
1158 }
1159
1160 fn add_frame_image(app_id: AppId, id: ImageId) -> ViewImage {
1161 if id == ImageId::INVALID {
1162 ViewImage::dummy(None)
1163 } else {
1164 let mut app = VIEW_PROCESS.handle_write(app_id);
1165 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
1166 app_id: Some(app_id),
1167 id: Some(id),
1168 generation: app.process.generation(),
1169 size: PxSize::zero(),
1170 partial_size: PxSize::zero(),
1171 density: None,
1172 is_opaque: false,
1173 partial_pixels: None,
1174 pixels: None,
1175 is_mask: false,
1176 done_signal: SignalOnce::new(),
1177 })));
1178
1179 app.loading_images.push(Arc::downgrade(&img.0));
1180 app.frame_images.push(Arc::downgrade(&img.0));
1181
1182 img
1183 }
1184 }
1185
1186 pub fn render(&self, frame: FrameRequest) -> Result<()> {
1188 let _s = tracing::debug_span!("ViewRenderer.render").entered();
1189
1190 if let Some(w) = self.0.upgrade() {
1191 w.call(|id, p| p.render(id, frame))?;
1192 VIEW_PROCESS.handle_write(w.app_id).pending_frames += 1;
1193 Ok(())
1194 } else {
1195 Err(ChannelError::disconnected())
1196 }
1197 }
1198
1199 pub fn render_update(&self, frame: FrameUpdateRequest) -> Result<()> {
1201 let _s = tracing::debug_span!("ViewRenderer.render_update").entered();
1202
1203 if let Some(w) = self.0.upgrade() {
1204 w.call(|id, p| p.render_update(id, frame))?;
1205 VIEW_PROCESS.handle_write(w.app_id).pending_frames += 1;
1206 Ok(())
1207 } else {
1208 Err(ChannelError::disconnected())
1209 }
1210 }
1211
1212 pub fn render_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
1214 if let Some(w) = self.0.upgrade() {
1215 w.call(|id, p| p.render_extension(id, extension_id, request))
1216 } else {
1217 Err(ChannelError::disconnected())
1218 }
1219 }
1220
1221 pub fn render_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
1223 where
1224 I: serde::Serialize,
1225 O: serde::de::DeserializeOwned,
1226 {
1227 let r = self.render_extension_raw(extension_id, ApiExtensionPayload::serialize(&request).unwrap())?;
1228 Ok(r.deserialize())
1229 }
1230}
1231
1232#[must_use = "the image is disposed when all clones of the handle are dropped"]
1236#[derive(Clone)]
1237pub struct ViewImage(Arc<RwLock<ViewImageData>>);
1238impl PartialEq for ViewImage {
1239 fn eq(&self, other: &Self) -> bool {
1240 Arc::ptr_eq(&self.0, &other.0)
1241 }
1242}
1243impl Eq for ViewImage {}
1244impl std::hash::Hash for ViewImage {
1245 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1246 let ptr = Arc::as_ptr(&self.0) as usize;
1247 ptr.hash(state)
1248 }
1249}
1250impl fmt::Debug for ViewImage {
1251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1252 f.debug_struct("ViewImage")
1253 .field("loaded", &self.is_loaded())
1254 .field("error", &self.error())
1255 .field("size", &self.size())
1256 .field("density", &self.density())
1257 .field("is_opaque", &self.is_opaque())
1258 .field("is_mask", &self.is_mask())
1259 .field("generation", &self.generation())
1260 .finish_non_exhaustive()
1261 }
1262}
1263
1264struct ViewImageData {
1265 app_id: Option<AppId>,
1266 id: Option<ImageId>,
1267 generation: ViewProcessGen,
1268
1269 size: PxSize,
1270 partial_size: PxSize,
1271 density: Option<PxDensity2d>,
1272 is_opaque: bool,
1273
1274 partial_pixels: Option<IpcBytes>,
1275 pixels: Option<std::result::Result<IpcBytes, Txt>>,
1276 is_mask: bool,
1277
1278 done_signal: SignalOnce,
1279}
1280impl Drop for ViewImageData {
1281 fn drop(&mut self) {
1282 if let Some(id) = self.id {
1283 let app_id = self.app_id.unwrap();
1284 if let Some(app) = APP.id() {
1285 if app_id != app {
1286 tracing::error!("image from app `{:?}` dropped in app `{:?}`", app_id, app);
1287 }
1288
1289 if VIEW_PROCESS.is_available() && VIEW_PROCESS.generation() == self.generation {
1290 let _ = VIEW_PROCESS.write().process.forget_image(id);
1291 }
1292 }
1293 }
1294 }
1295}
1296
1297impl ViewImage {
1298 pub fn id(&self) -> Option<ImageId> {
1300 self.0.read().id
1301 }
1302
1303 pub fn is_dummy(&self) -> bool {
1305 self.0.read().id.is_none()
1306 }
1307
1308 pub fn is_loaded(&self) -> bool {
1310 self.0.read().pixels.as_ref().map(|r| r.is_ok()).unwrap_or(false)
1311 }
1312
1313 pub fn is_partially_loaded(&self) -> bool {
1315 self.0.read().partial_pixels.is_some()
1316 }
1317
1318 pub fn is_error(&self) -> bool {
1322 self.0.read().pixels.as_ref().map(|r| r.is_err()).unwrap_or(false)
1323 }
1324
1325 pub fn error(&self) -> Option<Txt> {
1327 self.0.read().pixels.as_ref().and_then(|s| s.as_ref().err().cloned())
1328 }
1329
1330 pub fn size(&self) -> PxSize {
1332 self.0.read().size
1333 }
1334
1335 pub fn partial_size(&self) -> PxSize {
1341 self.0.read().partial_size
1342 }
1343
1344 pub fn density(&self) -> Option<PxDensity2d> {
1347 self.0.read().density
1348 }
1349
1350 pub fn is_opaque(&self) -> bool {
1352 self.0.read().is_opaque
1353 }
1354
1355 pub fn is_mask(&self) -> bool {
1357 self.0.read().is_mask
1358 }
1359
1360 pub fn partial_pixels(&self) -> Option<Vec<u8>> {
1367 self.0.read().partial_pixels.as_ref().map(|r| r[..].to_vec())
1368 }
1369
1370 pub fn pixels(&self) -> Option<IpcBytes> {
1381 self.0.read().pixels.as_ref().and_then(|r| r.as_ref().ok()).cloned()
1382 }
1383
1384 pub fn app_id(&self) -> Option<AppId> {
1386 self.0.read().app_id
1387 }
1388
1389 pub fn generation(&self) -> ViewProcessGen {
1391 self.0.read().generation
1392 }
1393
1394 pub fn downgrade(&self) -> WeakViewImage {
1396 WeakViewImage(Arc::downgrade(&self.0))
1397 }
1398
1399 pub fn dummy(error: Option<Txt>) -> Self {
1401 ViewImage(Arc::new(RwLock::new(ViewImageData {
1402 app_id: None,
1403 id: None,
1404 generation: ViewProcessGen::INVALID,
1405 size: PxSize::zero(),
1406 partial_size: PxSize::zero(),
1407 density: None,
1408 is_opaque: true,
1409 partial_pixels: None,
1410 pixels: if let Some(e) = error {
1411 Some(Err(e))
1412 } else {
1413 Some(Ok(IpcBytes::default()))
1414 },
1415 is_mask: false,
1416 done_signal: SignalOnce::new_set(),
1417 })))
1418 }
1419
1420 pub fn awaiter(&self) -> SignalOnce {
1422 self.0.read().done_signal.clone()
1423 }
1424
1425 pub async fn encode(&self, format: Txt) -> std::result::Result<IpcBytes, EncodeError> {
1431 self.awaiter().await;
1432
1433 if let Some(e) = self.error() {
1434 return Err(EncodeError::Encode(e));
1435 }
1436
1437 let receiver = {
1438 let img = self.0.read();
1439 if let Some(id) = img.id {
1440 let mut app = VIEW_PROCESS.handle_write(img.app_id.unwrap());
1441
1442 app.process.encode_image(id, format.clone())?;
1443
1444 let (sender, receiver) = channel::bounded(1);
1445 if let Some(entry) = app.encoding_images.iter_mut().find(|r| r.image_id == id && r.format == format) {
1446 entry.listeners.push(sender);
1447 } else {
1448 app.encoding_images.push(EncodeRequest {
1449 image_id: id,
1450 format,
1451 listeners: vec![sender],
1452 });
1453 }
1454 receiver
1455 } else {
1456 return Err(EncodeError::Dummy);
1457 }
1458 };
1459
1460 receiver.recv().await?
1461 }
1462}
1463
1464#[derive(Debug, Clone, PartialEq, Eq)]
1466#[non_exhaustive]
1467pub enum EncodeError {
1468 Encode(Txt),
1470 Dummy,
1475 Disconnected,
1477}
1478impl From<Txt> for EncodeError {
1479 fn from(e: Txt) -> Self {
1480 EncodeError::Encode(e)
1481 }
1482}
1483impl From<ChannelError> for EncodeError {
1484 fn from(_: ChannelError) -> Self {
1485 EncodeError::Disconnected
1486 }
1487}
1488impl fmt::Display for EncodeError {
1489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1490 match self {
1491 EncodeError::Encode(e) => write!(f, "{e}"),
1492 EncodeError::Dummy => write!(f, "cannot encode dummy image"),
1493 EncodeError::Disconnected => write!(f, "{}", ChannelError::disconnected()),
1494 }
1495 }
1496}
1497impl std::error::Error for EncodeError {}
1498
1499#[derive(Clone)]
1505pub struct WeakViewImage(sync::Weak<RwLock<ViewImageData>>);
1506impl WeakViewImage {
1507 pub fn upgrade(&self) -> Option<ViewImage> {
1511 self.0.upgrade().map(ViewImage)
1512 }
1513}
1514
1515struct EncodeRequest {
1516 image_id: ImageId,
1517 format: Txt,
1518 listeners: Vec<channel::Sender<std::result::Result<IpcBytes, EncodeError>>>,
1519}
1520
1521type ClipboardResult<T> = std::result::Result<T, ClipboardError>;
1522
1523#[non_exhaustive]
1525pub struct ViewClipboard {}
1526impl ViewClipboard {
1527 pub fn read_text(&self) -> Result<ClipboardResult<Txt>> {
1531 match VIEW_PROCESS.try_write()?.process.read_clipboard(ClipboardType::Text)? {
1532 Ok(ClipboardData::Text(t)) => Ok(Ok(t)),
1533 Err(e) => Ok(Err(e)),
1534 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1535 }
1536 }
1537
1538 pub fn write_text(&self, txt: Txt) -> Result<ClipboardResult<()>> {
1542 VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::Text(txt))
1543 }
1544
1545 pub fn read_image(&self) -> Result<ClipboardResult<ViewImage>> {
1549 let mut app = VIEW_PROCESS.try_write()?;
1550 match app.process.read_clipboard(ClipboardType::Image)? {
1551 Ok(ClipboardData::Image(id)) => {
1552 if id == ImageId::INVALID {
1553 Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned invalid image"))))
1554 } else {
1555 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
1556 id: Some(id),
1557 app_id: APP.id(),
1558 generation: app.process.generation(),
1559 size: PxSize::zero(),
1560 partial_size: PxSize::zero(),
1561 density: None,
1562 is_opaque: false,
1563 partial_pixels: None,
1564 pixels: None,
1565 is_mask: false,
1566 done_signal: SignalOnce::new(),
1567 })));
1568 app.loading_images.push(Arc::downgrade(&img.0));
1569 Ok(Ok(img))
1570 }
1571 }
1572 Err(e) => Ok(Err(e)),
1573 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1574 }
1575 }
1576
1577 pub fn write_image(&self, img: &ViewImage) -> Result<ClipboardResult<()>> {
1581 if img.is_loaded()
1582 && let Some(id) = img.id()
1583 {
1584 return VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::Image(id));
1585 }
1586 Ok(Err(ClipboardError::Other(Txt::from_static("image not loaded"))))
1587 }
1588
1589 pub fn read_file_list(&self) -> Result<ClipboardResult<Vec<PathBuf>>> {
1593 match VIEW_PROCESS.try_write()?.process.read_clipboard(ClipboardType::FileList)? {
1594 Ok(ClipboardData::FileList(f)) => Ok(Ok(f)),
1595 Err(e) => Ok(Err(e)),
1596 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1597 }
1598 }
1599
1600 pub fn write_file_list(&self, list: Vec<PathBuf>) -> Result<ClipboardResult<()>> {
1604 VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::FileList(list))
1605 }
1606
1607 pub fn read_extension(&self, data_type: Txt) -> Result<ClipboardResult<IpcBytes>> {
1611 match VIEW_PROCESS
1612 .try_write()?
1613 .process
1614 .read_clipboard(ClipboardType::Extension(data_type.clone()))?
1615 {
1616 Ok(ClipboardData::Extension { data_type: rt, data }) if rt == data_type => Ok(Ok(data)),
1617 Err(e) => Ok(Err(e)),
1618 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1619 }
1620 }
1621
1622 pub fn write_extension(&self, data_type: Txt, data: IpcBytes) -> Result<ClipboardResult<()>> {
1626 VIEW_PROCESS
1627 .try_write()?
1628 .process
1629 .write_clipboard(ClipboardData::Extension { data_type, data })
1630 }
1631}