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()
250 }
251
252 pub fn pending_frames(&self) -> usize {
256 self.write().pending_frames
257 }
258
259 pub fn respawn(&self) {
263 self.write().process.respawn()
264 }
265
266 pub fn extension_id(&self, extension_name: impl Into<ApiExtensionName>) -> Result<Option<ApiExtensionId>> {
272 let me = self.read();
273 if me.process.is_connected() {
274 Ok(me.extensions.id(&extension_name.into()))
275 } else {
276 Err(ChannelError::disconnected())
277 }
278 }
279
280 pub fn third_party_licenses(&self) -> Result<Vec<crate::third_party::LicenseUsed>> {
285 self.write().process.third_party_licenses()
286 }
287
288 pub fn app_extension_raw(&self, extension_id: ApiExtensionId, extension_request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
290 self.write().process.app_extension(extension_id, extension_request)
291 }
292
293 pub fn app_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
295 where
296 I: serde::Serialize,
297 O: serde::de::DeserializeOwned,
298 {
299 let payload = ApiExtensionPayload::serialize(&request).unwrap();
300 let response = self.write().process.app_extension(extension_id, payload)?;
301 Ok(response.deserialize::<O>())
302 }
303
304 pub fn handle_disconnect(&self, vp_gen: ViewProcessGen) {
310 self.write().process.handle_disconnect(vp_gen)
311 }
312
313 pub(super) fn start<F>(&self, view_process_exe: PathBuf, view_process_env: HashMap<Txt, Txt>, headless: bool, on_event: F)
315 where
316 F: FnMut(Event) + Send + 'static,
317 {
318 let _s = tracing::debug_span!("VIEW_PROCESS.start").entered();
319
320 let process = zng_view_api::Controller::start(view_process_exe, view_process_env, headless, on_event);
321 *VIEW_PROCESS_SV.write() = Some(ViewProcessService {
322 data_generation: process.generation(),
323 process,
324 input_device_ids: HashMap::default(),
325 monitor_ids: HashMap::default(),
326 loading_images: vec![],
327 encoding_images: vec![],
328 frame_images: vec![],
329 pending_frames: 0,
330 message_dialogs: vec![],
331 file_dialogs: vec![],
332 extensions: ApiExtensions::default(),
333 ping_count: 0,
334 });
335 }
336
337 pub(crate) fn on_window_opened(&self, window_id: WindowId, data: zng_view_api::window::WindowOpenData) -> (ViewWindow, WindowOpenData) {
338 let mut app = self.write();
339 let _ = app.check_generation();
340
341 let win = ViewWindow(Arc::new(ViewWindowData {
342 app_id: APP.id().unwrap(),
343 id: ApiWindowId::from_raw(window_id.get()),
344 generation: app.data_generation,
345 }));
346 drop(app);
347
348 let data = WindowOpenData::new(data, |id| self.monitor_id(id));
349
350 (win, data)
351 }
352 pub(super) fn input_device_id(&self, id: ApiDeviceId) -> InputDeviceId {
354 *self.write().input_device_ids.entry(id).or_insert_with(InputDeviceId::new_unique)
355 }
356
357 pub(super) fn monitor_id(&self, id: ApiMonitorId) -> MonitorId {
359 *self.write().monitor_ids.entry(id).or_insert_with(MonitorId::new_unique)
360 }
361
362 pub(super) fn handle_inited(&self, vp_gen: ViewProcessGen, extensions: ApiExtensions) {
368 let mut me = self.write();
369 me.extensions = extensions;
370 me.process.handle_inited(vp_gen);
371 }
372
373 pub(super) fn handle_suspended(&self) {
374 self.write().process.handle_suspended();
375 }
376
377 pub(crate) fn on_headless_opened(
378 &self,
379 id: WindowId,
380 data: zng_view_api::window::HeadlessOpenData,
381 ) -> (ViewHeadless, HeadlessOpenData) {
382 let mut app = self.write();
383 let _ = app.check_generation();
384
385 let surf = ViewHeadless(Arc::new(ViewWindowData {
386 app_id: APP.id().unwrap(),
387 id: ApiWindowId::from_raw(id.get()),
388 generation: app.data_generation,
389 }));
390
391 (surf, data)
392 }
393
394 fn loading_image_index(&self, id: ImageId) -> Option<usize> {
395 let mut app = self.write();
396
397 app.loading_images.retain(|i| i.strong_count() > 0);
399
400 app.loading_images.iter().position(|i| i.upgrade().unwrap().read().id == Some(id))
401 }
402
403 pub(super) fn on_image_metadata_loaded(
404 &self,
405 id: ImageId,
406 size: PxSize,
407 density: Option<PxDensity2d>,
408 is_mask: bool,
409 ) -> Option<ViewImage> {
410 if let Some(i) = self.loading_image_index(id) {
411 let img = self.read().loading_images[i].upgrade().unwrap();
412 {
413 let mut img = img.write();
414 img.size = size;
415 img.density = density;
416 img.is_mask = is_mask;
417 }
418 Some(ViewImage(img))
419 } else {
420 None
421 }
422 }
423
424 pub(super) fn on_image_partially_loaded(
425 &self,
426 id: ImageId,
427 partial_size: PxSize,
428 density: Option<PxDensity2d>,
429 is_opaque: bool,
430 is_mask: bool,
431 partial_pixels: IpcBytes,
432 ) -> Option<ViewImage> {
433 if let Some(i) = self.loading_image_index(id) {
434 let img = self.read().loading_images[i].upgrade().unwrap();
435 {
436 let mut img = img.write();
437 img.partial_size = partial_size;
438 img.density = density;
439 img.is_opaque = is_opaque;
440 img.partial_pixels = Some(partial_pixels);
441 img.is_mask = is_mask;
442 }
443 Some(ViewImage(img))
444 } else {
445 None
446 }
447 }
448
449 pub(super) fn on_image_loaded(&self, data: ImageLoadedData) -> Option<ViewImage> {
450 if let Some(i) = self.loading_image_index(data.id) {
451 let img = self.write().loading_images.swap_remove(i).upgrade().unwrap();
452 {
453 let mut img = img.write();
454 img.size = data.size;
455 img.partial_size = data.size;
456 img.density = data.density;
457 img.is_opaque = data.is_opaque;
458 img.pixels = Some(Ok(data.pixels));
459 img.partial_pixels = None;
460 img.is_mask = data.is_mask;
461 img.done_signal.set();
462 }
463 Some(ViewImage(img))
464 } else {
465 None
466 }
467 }
468
469 pub(super) fn on_image_error(&self, id: ImageId, error: Txt) -> Option<ViewImage> {
470 if let Some(i) = self.loading_image_index(id) {
471 let img = self.write().loading_images.swap_remove(i).upgrade().unwrap();
472 {
473 let mut img = img.write();
474 img.pixels = Some(Err(error));
475 img.done_signal.set();
476 }
477 Some(ViewImage(img))
478 } else {
479 None
480 }
481 }
482
483 pub(crate) fn on_frame_rendered(&self, _id: WindowId) {
484 let mut vp = self.write();
485 vp.pending_frames = vp.pending_frames.saturating_sub(1);
486 }
487
488 pub(crate) fn on_frame_image(&self, data: ImageLoadedData) -> ViewImage {
489 ViewImage(Arc::new(RwLock::new(ViewImageData {
490 app_id: APP.id(),
491 id: Some(data.id),
492 generation: self.generation(),
493 size: data.size,
494 partial_size: data.size,
495 density: data.density,
496 is_opaque: data.is_opaque,
497 partial_pixels: None,
498 pixels: Some(Ok(data.pixels)),
499 is_mask: data.is_mask,
500 done_signal: SignalOnce::new_set(),
501 })))
502 }
503
504 pub(super) fn on_frame_image_ready(&self, id: ImageId) -> Option<ViewImage> {
505 let mut app = self.write();
506
507 app.frame_images.retain(|i| i.strong_count() > 0);
509
510 let i = app.frame_images.iter().position(|i| i.upgrade().unwrap().read().id == Some(id));
511
512 i.map(|i| ViewImage(app.frame_images.swap_remove(i).upgrade().unwrap()))
513 }
514
515 pub(super) fn on_image_encoded(&self, id: ImageId, format: Txt, data: IpcBytes) {
516 self.on_image_encode_result(id, format, Ok(data));
517 }
518 pub(super) fn on_image_encode_error(&self, id: ImageId, format: Txt, error: Txt) {
519 self.on_image_encode_result(id, format, Err(EncodeError::Encode(error)));
520 }
521 fn on_image_encode_result(&self, id: ImageId, format: Txt, result: std::result::Result<IpcBytes, EncodeError>) {
522 let mut app = self.write();
523 app.encoding_images.retain(move |r| {
524 let done = r.image_id == id && r.format == format;
525 if done {
526 for sender in &r.listeners {
527 let _ = sender.send_blocking(result.clone());
528 }
529 }
530 !done
531 })
532 }
533
534 pub(crate) fn on_message_dlg_response(&self, id: zng_view_api::dialog::DialogId, response: MsgDialogResponse) {
535 let mut app = self.write();
536 if let Some(i) = app.message_dialogs.iter().position(|(i, _)| *i == id) {
537 let (_, r) = app.message_dialogs.swap_remove(i);
538 r.respond(response);
539 }
540 }
541
542 pub(crate) fn on_file_dlg_response(&self, id: zng_view_api::dialog::DialogId, response: FileDialogResponse) {
543 let mut app = self.write();
544 if let Some(i) = app.file_dialogs.iter().position(|(i, _)| *i == id) {
545 let (_, r) = app.file_dialogs.swap_remove(i);
546 r.respond(response);
547 }
548 }
549
550 pub(super) fn on_respawned(&self, _gen: ViewProcessGen) {
551 let mut app = self.write();
552 app.pending_frames = 0;
553 for (_, r) in app.message_dialogs.drain(..) {
554 r.respond(MsgDialogResponse::Error(Txt::from_static("respawn")));
555 }
556 }
557
558 pub(crate) fn exit(&self) {
559 *VIEW_PROCESS_SV.write() = None;
560 }
561
562 pub(crate) fn ping(&self) {
563 let mut app = self.write();
564 let count = app.ping_count.wrapping_add(1);
565 if let Ok(c) = app.process.ping(count)
566 && c != count
567 {
568 tracing::error!("incorrect ping response, expected {count}, was {c}");
569 }
570 app.ping_count = count;
571 }
572
573 pub(crate) fn on_pong(&self, count: u16) {
574 let expected = self.read().ping_count;
575 if expected != count {
576 tracing::warn!("unexpected pong event, expected {expected}, was {count}");
578 }
579 }
580}
581impl ViewProcessService {
582 #[must_use = "if `true` all current WinId, DevId and MonId are invalid"]
583 fn check_generation(&mut self) -> bool {
584 let vp_gen = self.process.generation();
585 let invalid = vp_gen != self.data_generation;
586 if invalid {
587 self.data_generation = vp_gen;
588 self.input_device_ids.clear();
589 self.monitor_ids.clear();
590 }
591 invalid
592 }
593}
594
595event_args! {
596 pub struct ViewProcessInitedArgs {
598 pub generation: ViewProcessGen,
600
601 pub is_respawn: bool,
606
607 pub extensions: ApiExtensions,
611
612 ..
613
614 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
616 list.search_all()
617 }
618 }
619
620 pub struct ViewProcessSuspendedArgs {
622
623 ..
624
625 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
627 list.search_all()
628 }
629 }
630}
631
632event! {
633 pub static VIEW_PROCESS_INITED_EVENT: ViewProcessInitedArgs;
635 pub static VIEW_PROCESS_SUSPENDED_EVENT: ViewProcessSuspendedArgs;
640}
641
642#[derive(Debug, Clone)]
644#[non_exhaustive]
645pub struct WindowOpenData {
646 pub state: WindowStateAll,
648
649 pub monitor: Option<MonitorId>,
651
652 pub position: (PxPoint, DipPoint),
656 pub size: DipSize,
658
659 pub scale_factor: Factor,
661
662 pub render_mode: RenderMode,
664
665 pub safe_padding: DipSideOffsets,
671}
672impl WindowOpenData {
673 pub(crate) fn new(data: zng_view_api::window::WindowOpenData, map_monitor: impl FnOnce(ApiMonitorId) -> MonitorId) -> Self {
674 WindowOpenData {
675 state: data.state,
676 monitor: data.monitor.map(map_monitor),
677 position: data.position,
678 size: data.size,
679 scale_factor: data.scale_factor,
680 render_mode: data.render_mode,
681 safe_padding: data.safe_padding,
682 }
683 }
684}
685
686#[derive(Debug, Clone)]
690#[must_use = "the window is closed when all clones of the handle are dropped"]
691pub struct ViewWindow(Arc<ViewWindowData>);
692impl PartialEq for ViewWindow {
693 fn eq(&self, other: &Self) -> bool {
694 Arc::ptr_eq(&self.0, &other.0)
695 }
696}
697impl Eq for ViewWindow {}
698
699impl ViewWindow {
700 pub fn generation(&self) -> ViewProcessGen {
702 self.0.generation
703 }
704
705 pub fn set_title(&self, title: Txt) -> Result<()> {
707 self.0.call(|id, p| p.set_title(id, title))
708 }
709
710 pub fn set_visible(&self, visible: bool) -> Result<()> {
712 self.0.call(|id, p| p.set_visible(id, visible))
713 }
714
715 pub fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
717 self.0.call(|id, p| p.set_always_on_top(id, always_on_top))
718 }
719
720 pub fn set_movable(&self, movable: bool) -> Result<()> {
722 self.0.call(|id, p| p.set_movable(id, movable))
723 }
724
725 pub fn set_resizable(&self, resizable: bool) -> Result<()> {
727 self.0.call(|id, p| p.set_resizable(id, resizable))
728 }
729
730 pub fn set_icon(&self, icon: Option<&ViewImage>) -> Result<()> {
732 self.0.call(|id, p| {
733 if let Some(icon) = icon {
734 let icon = icon.0.read();
735 if p.generation() == icon.generation {
736 p.set_icon(id, icon.id)
737 } else {
738 Err(ChannelError::disconnected())
739 }
740 } else {
741 p.set_icon(id, None)
742 }
743 })
744 }
745
746 pub fn set_cursor(&self, cursor: Option<CursorIcon>) -> Result<()> {
748 self.0.call(|id, p| p.set_cursor(id, cursor))
749 }
750
751 pub fn set_cursor_image(&self, cursor: Option<&ViewImage>, hotspot: PxPoint) -> Result<()> {
758 self.0.call(|id, p| {
759 if let Some(cur) = cursor {
760 let cur = cur.0.read();
761 if p.generation() == cur.generation {
762 p.set_cursor_image(id, cur.id.map(|img| zng_view_api::window::CursorImage::new(img, hotspot)))
763 } else {
764 Err(ChannelError::disconnected())
765 }
766 } else {
767 p.set_cursor_image(id, None)
768 }
769 })
770 }
771
772 pub fn set_taskbar_visible(&self, visible: bool) -> Result<()> {
774 self.0.call(|id, p| p.set_taskbar_visible(id, visible))
775 }
776
777 pub fn bring_to_top(&self) -> Result<()> {
779 self.0.call(|id, p| p.bring_to_top(id))
780 }
781
782 pub fn set_state(&self, state: WindowStateAll) -> Result<()> {
784 self.0.call(|id, p| p.set_state(id, state))
785 }
786
787 pub fn set_video_mode(&self, mode: VideoMode) -> Result<()> {
789 self.0.call(|id, p| p.set_video_mode(id, mode))
790 }
791
792 pub fn set_enabled_buttons(&self, buttons: WindowButton) -> Result<()> {
794 self.0.call(|id, p| p.set_enabled_buttons(id, buttons))
795 }
796
797 pub fn renderer(&self) -> ViewRenderer {
799 ViewRenderer(Arc::downgrade(&self.0))
800 }
801
802 pub fn set_capture_mode(&self, enabled: bool) -> Result<()> {
807 self.0.call(|id, p| p.set_capture_mode(id, enabled))
808 }
809
810 pub fn focus(&self) -> Result<FocusResult> {
814 self.0.call(|id, p| p.focus(id))
815 }
816
817 pub fn set_focus_indicator(&self, indicator: Option<FocusIndicator>) -> Result<()> {
820 self.0.call(|id, p| p.set_focus_indicator(id, indicator))
821 }
822
823 pub fn drag_move(&self) -> Result<()> {
827 self.0.call(|id, p| p.drag_move(id))
828 }
829
830 pub fn drag_resize(&self, direction: ResizeDirection) -> Result<()> {
834 self.0.call(|id, p| p.drag_resize(id, direction))
835 }
836
837 pub fn start_drag_drop(
843 &self,
844 data: Vec<DragDropData>,
845 allowed_effects: DragDropEffect,
846 ) -> Result<std::result::Result<DragDropId, DragDropError>> {
847 self.0.call(|id, p| p.start_drag_drop(id, data, allowed_effects))
848 }
849
850 pub fn drag_dropped(&self, drop_id: DragDropId, applied: DragDropEffect) -> Result<()> {
852 self.0.call(|id, p| p.drag_dropped(id, drop_id, applied))
853 }
854
855 pub fn open_title_bar_context_menu(&self, position: DipPoint) -> Result<()> {
857 self.0.call(|id, p| p.open_title_bar_context_menu(id, position))
858 }
859
860 pub fn message_dialog(&self, dlg: MsgDialog, responder: ResponderVar<MsgDialogResponse>) -> Result<()> {
865 let dlg_id = self.0.call(|id, p| p.message_dialog(id, dlg))?;
866 VIEW_PROCESS.handle_write(self.0.app_id).message_dialogs.push((dlg_id, responder));
867 Ok(())
868 }
869
870 pub fn file_dialog(&self, dlg: FileDialog, responder: ResponderVar<FileDialogResponse>) -> Result<()> {
875 let dlg_id = self.0.call(|id, p| p.file_dialog(id, dlg))?;
876 VIEW_PROCESS.handle_write(self.0.app_id).file_dialogs.push((dlg_id, responder));
877 Ok(())
878 }
879
880 pub fn access_update(&self, update: zng_view_api::access::AccessTreeUpdate) -> Result<()> {
882 self.0.call(|id, p| p.access_update(id, update))
883 }
884
885 pub fn set_ime_area(&self, area: Option<DipRect>) -> Result<()> {
889 self.0.call(|id, p| p.set_ime_area(id, area))
890 }
891
892 pub fn set_system_shutdown_warn(&self, reason: Txt) -> Result<()> {
903 self.0.call(move |id, p| p.set_system_shutdown_warn(id, reason))
904 }
905
906 pub fn close(self) {
908 drop(self)
909 }
910
911 pub fn window_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
913 self.0.call(|id, p| p.window_extension(id, extension_id, request))
914 }
915
916 pub fn window_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
918 where
919 I: serde::Serialize,
920 O: serde::de::DeserializeOwned,
921 {
922 let r = self.window_extension_raw(extension_id, ApiExtensionPayload::serialize(&request).unwrap())?;
923 Ok(r.deserialize())
924 }
925}
926
927#[derive(Clone, Debug)]
929pub enum ViewWindowOrHeadless {
930 Window(ViewWindow),
932 Headless(ViewHeadless),
934}
935impl ViewWindowOrHeadless {
936 pub fn renderer(&self) -> ViewRenderer {
938 match self {
939 ViewWindowOrHeadless::Window(w) => w.renderer(),
940 ViewWindowOrHeadless::Headless(h) => h.renderer(),
941 }
942 }
943
944 pub fn window_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
946 match self {
947 ViewWindowOrHeadless::Window(w) => w.window_extension_raw(extension_id, request),
948 ViewWindowOrHeadless::Headless(h) => h.window_extension_raw(extension_id, request),
949 }
950 }
951
952 pub fn window_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
954 where
955 I: serde::Serialize,
956 O: serde::de::DeserializeOwned,
957 {
958 match self {
959 ViewWindowOrHeadless::Window(w) => w.window_extension(extension_id, request),
960 ViewWindowOrHeadless::Headless(h) => h.window_extension(extension_id, request),
961 }
962 }
963}
964impl From<ViewWindow> for ViewWindowOrHeadless {
965 fn from(w: ViewWindow) -> Self {
966 ViewWindowOrHeadless::Window(w)
967 }
968}
969impl From<ViewHeadless> for ViewWindowOrHeadless {
970 fn from(w: ViewHeadless) -> Self {
971 ViewWindowOrHeadless::Headless(w)
972 }
973}
974
975#[derive(Debug)]
976struct ViewWindowData {
977 app_id: AppId,
978 id: ApiWindowId,
979 generation: ViewProcessGen,
980}
981impl ViewWindowData {
982 fn call<R>(&self, f: impl FnOnce(ApiWindowId, &mut Controller) -> Result<R>) -> Result<R> {
983 let mut app = VIEW_PROCESS.handle_write(self.app_id);
984 if app.check_generation() {
985 Err(ChannelError::disconnected())
986 } else {
987 f(self.id, &mut app.process)
988 }
989 }
990}
991impl Drop for ViewWindowData {
992 fn drop(&mut self) {
993 if VIEW_PROCESS.is_available() {
994 let mut app = VIEW_PROCESS.handle_write(self.app_id);
995 if self.generation == app.process.generation() {
996 let _ = app.process.close(self.id);
997 }
998 }
999 }
1000}
1001type Result<T> = std::result::Result<T, ChannelError>;
1002
1003#[derive(Clone, Debug)]
1007#[must_use = "the view is disposed when all clones of the handle are dropped"]
1008pub struct ViewHeadless(Arc<ViewWindowData>);
1009impl PartialEq for ViewHeadless {
1010 fn eq(&self, other: &Self) -> bool {
1011 Arc::ptr_eq(&self.0, &other.0)
1012 }
1013}
1014impl Eq for ViewHeadless {}
1015impl ViewHeadless {
1016 pub fn set_size(&self, size: DipSize, scale_factor: Factor) -> Result<()> {
1018 self.0.call(|id, p| p.set_headless_size(id, size, scale_factor))
1019 }
1020
1021 pub fn renderer(&self) -> ViewRenderer {
1023 ViewRenderer(Arc::downgrade(&self.0))
1024 }
1025
1026 pub fn window_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
1028 self.0.call(|id, p| p.window_extension(id, extension_id, request))
1029 }
1030
1031 pub fn window_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
1033 where
1034 I: serde::Serialize,
1035 O: serde::de::DeserializeOwned,
1036 {
1037 let r = self.window_extension_raw(extension_id, ApiExtensionPayload::serialize(&request).unwrap())?;
1038 Ok(r.deserialize())
1039 }
1040}
1041
1042#[derive(Clone, Debug)]
1047pub struct ViewRenderer(sync::Weak<ViewWindowData>);
1048impl PartialEq for ViewRenderer {
1049 fn eq(&self, other: &Self) -> bool {
1050 if let Some(s) = self.0.upgrade()
1051 && let Some(o) = other.0.upgrade()
1052 {
1053 Arc::ptr_eq(&s, &o)
1054 } else {
1055 false
1056 }
1057 }
1058}
1059impl Eq for ViewRenderer {}
1060
1061impl ViewRenderer {
1062 fn call<R>(&self, f: impl FnOnce(ApiWindowId, &mut Controller) -> Result<R>) -> Result<R> {
1063 if let Some(c) = self.0.upgrade() {
1064 c.call(f)
1065 } else {
1066 Err(ChannelError::disconnected())
1067 }
1068 }
1069
1070 pub fn generation(&self) -> Result<ViewProcessGen> {
1072 self.0.upgrade().map(|c| c.generation).ok_or(ChannelError::disconnected())
1073 }
1074
1075 pub fn use_image(&self, image: &ViewImage) -> Result<ImageTextureId> {
1079 self.call(|id, p| {
1080 let image = image.0.read();
1081 if p.generation() == image.generation {
1082 p.use_image(id, image.id.unwrap_or(ImageId::INVALID))
1083 } else {
1084 Err(ChannelError::disconnected())
1085 }
1086 })
1087 }
1088
1089 pub fn update_image_use(&mut self, tex_id: ImageTextureId, image: &ViewImage, dirty_rect: Option<PxRect>) -> Result<bool> {
1096 self.call(|id, p| {
1097 let image = image.0.read();
1098 if p.generation() == image.generation {
1099 p.update_image_use(id, tex_id, image.id.unwrap_or(ImageId::INVALID), dirty_rect)
1100 } else {
1101 Err(ChannelError::disconnected())
1102 }
1103 })
1104 }
1105
1106 pub fn delete_image_use(&mut self, tex_id: ImageTextureId) -> Result<()> {
1108 self.call(|id, p| p.delete_image_use(id, tex_id))
1109 }
1110
1111 pub fn add_font_face(&self, bytes: IpcFontBytes, index: u32) -> Result<FontFaceId> {
1115 self.call(|id, p| p.add_font_face(id, bytes, index))
1116 }
1117
1118 pub fn delete_font_face(&self, font_face_id: FontFaceId) -> Result<()> {
1120 self.call(|id, p| p.delete_font_face(id, font_face_id))
1121 }
1122
1123 pub fn add_font(
1127 &self,
1128 font_face_id: FontFaceId,
1129 glyph_size: Px,
1130 options: FontOptions,
1131 variations: Vec<(FontVariationName, f32)>,
1132 ) -> Result<FontId> {
1133 self.call(|id, p| p.add_font(id, font_face_id, glyph_size, options, variations))
1134 }
1135
1136 pub fn delete_font(&self, font_id: FontId) -> Result<()> {
1138 self.call(|id, p| p.delete_font(id, font_id))
1139 }
1140
1141 pub fn frame_image(&self, mask: Option<ImageMaskMode>) -> Result<ViewImage> {
1143 if let Some(c) = self.0.upgrade() {
1144 let id = c.call(|id, p| p.frame_image(id, mask))?;
1145 Ok(Self::add_frame_image(c.app_id, id))
1146 } else {
1147 Err(ChannelError::disconnected())
1148 }
1149 }
1150
1151 pub fn frame_image_rect(&self, rect: PxRect, mask: Option<ImageMaskMode>) -> Result<ViewImage> {
1153 if let Some(c) = self.0.upgrade() {
1154 let id = c.call(|id, p| p.frame_image_rect(id, rect, mask))?;
1155 Ok(Self::add_frame_image(c.app_id, id))
1156 } else {
1157 Err(ChannelError::disconnected())
1158 }
1159 }
1160
1161 fn add_frame_image(app_id: AppId, id: ImageId) -> ViewImage {
1162 if id == ImageId::INVALID {
1163 ViewImage::dummy(None)
1164 } else {
1165 let mut app = VIEW_PROCESS.handle_write(app_id);
1166 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
1167 app_id: Some(app_id),
1168 id: Some(id),
1169 generation: app.process.generation(),
1170 size: PxSize::zero(),
1171 partial_size: PxSize::zero(),
1172 density: None,
1173 is_opaque: false,
1174 partial_pixels: None,
1175 pixels: None,
1176 is_mask: false,
1177 done_signal: SignalOnce::new(),
1178 })));
1179
1180 app.loading_images.push(Arc::downgrade(&img.0));
1181 app.frame_images.push(Arc::downgrade(&img.0));
1182
1183 img
1184 }
1185 }
1186
1187 pub fn render(&self, frame: FrameRequest) -> Result<()> {
1189 let _s = tracing::debug_span!("ViewRenderer.render").entered();
1190
1191 if let Some(w) = self.0.upgrade() {
1192 w.call(|id, p| p.render(id, frame))?;
1193 VIEW_PROCESS.handle_write(w.app_id).pending_frames += 1;
1194 Ok(())
1195 } else {
1196 Err(ChannelError::disconnected())
1197 }
1198 }
1199
1200 pub fn render_update(&self, frame: FrameUpdateRequest) -> Result<()> {
1202 let _s = tracing::debug_span!("ViewRenderer.render_update").entered();
1203
1204 if let Some(w) = self.0.upgrade() {
1205 w.call(|id, p| p.render_update(id, frame))?;
1206 VIEW_PROCESS.handle_write(w.app_id).pending_frames += 1;
1207 Ok(())
1208 } else {
1209 Err(ChannelError::disconnected())
1210 }
1211 }
1212
1213 pub fn render_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
1215 if let Some(w) = self.0.upgrade() {
1216 w.call(|id, p| p.render_extension(id, extension_id, request))
1217 } else {
1218 Err(ChannelError::disconnected())
1219 }
1220 }
1221
1222 pub fn render_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
1224 where
1225 I: serde::Serialize,
1226 O: serde::de::DeserializeOwned,
1227 {
1228 let r = self.render_extension_raw(extension_id, ApiExtensionPayload::serialize(&request).unwrap())?;
1229 Ok(r.deserialize())
1230 }
1231}
1232
1233#[must_use = "the image is disposed when all clones of the handle are dropped"]
1237#[derive(Clone)]
1238pub struct ViewImage(Arc<RwLock<ViewImageData>>);
1239impl PartialEq for ViewImage {
1240 fn eq(&self, other: &Self) -> bool {
1241 Arc::ptr_eq(&self.0, &other.0)
1242 }
1243}
1244impl Eq for ViewImage {}
1245impl std::hash::Hash for ViewImage {
1246 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1247 let ptr = Arc::as_ptr(&self.0) as usize;
1248 ptr.hash(state)
1249 }
1250}
1251impl fmt::Debug for ViewImage {
1252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1253 f.debug_struct("ViewImage")
1254 .field("loaded", &self.is_loaded())
1255 .field("error", &self.error())
1256 .field("size", &self.size())
1257 .field("density", &self.density())
1258 .field("is_opaque", &self.is_opaque())
1259 .field("is_mask", &self.is_mask())
1260 .field("generation", &self.generation())
1261 .finish_non_exhaustive()
1262 }
1263}
1264
1265struct ViewImageData {
1266 app_id: Option<AppId>,
1267 id: Option<ImageId>,
1268 generation: ViewProcessGen,
1269
1270 size: PxSize,
1271 partial_size: PxSize,
1272 density: Option<PxDensity2d>,
1273 is_opaque: bool,
1274
1275 partial_pixels: Option<IpcBytes>,
1276 pixels: Option<std::result::Result<IpcBytes, Txt>>,
1277 is_mask: bool,
1278
1279 done_signal: SignalOnce,
1280}
1281impl Drop for ViewImageData {
1282 fn drop(&mut self) {
1283 if let Some(id) = self.id {
1284 let app_id = self.app_id.unwrap();
1285 if let Some(app) = APP.id() {
1286 if app_id != app {
1287 tracing::error!("image from app `{:?}` dropped in app `{:?}`", app_id, app);
1288 }
1289
1290 if VIEW_PROCESS.is_available() && VIEW_PROCESS.generation() == self.generation {
1291 let _ = VIEW_PROCESS.write().process.forget_image(id);
1292 }
1293 }
1294 }
1295 }
1296}
1297
1298impl ViewImage {
1299 pub fn id(&self) -> Option<ImageId> {
1301 self.0.read().id
1302 }
1303
1304 pub fn is_dummy(&self) -> bool {
1306 self.0.read().id.is_none()
1307 }
1308
1309 pub fn is_loaded(&self) -> bool {
1311 self.0.read().pixels.as_ref().map(|r| r.is_ok()).unwrap_or(false)
1312 }
1313
1314 pub fn is_partially_loaded(&self) -> bool {
1316 self.0.read().partial_pixels.is_some()
1317 }
1318
1319 pub fn is_error(&self) -> bool {
1323 self.0.read().pixels.as_ref().map(|r| r.is_err()).unwrap_or(false)
1324 }
1325
1326 pub fn error(&self) -> Option<Txt> {
1328 self.0.read().pixels.as_ref().and_then(|s| s.as_ref().err().cloned())
1329 }
1330
1331 pub fn size(&self) -> PxSize {
1333 self.0.read().size
1334 }
1335
1336 pub fn partial_size(&self) -> PxSize {
1342 self.0.read().partial_size
1343 }
1344
1345 pub fn density(&self) -> Option<PxDensity2d> {
1348 self.0.read().density
1349 }
1350
1351 pub fn is_opaque(&self) -> bool {
1353 self.0.read().is_opaque
1354 }
1355
1356 pub fn is_mask(&self) -> bool {
1358 self.0.read().is_mask
1359 }
1360
1361 pub fn partial_pixels(&self) -> Option<Vec<u8>> {
1368 self.0.read().partial_pixels.as_ref().map(|r| r[..].to_vec())
1369 }
1370
1371 pub fn pixels(&self) -> Option<IpcBytes> {
1382 self.0.read().pixels.as_ref().and_then(|r| r.as_ref().ok()).cloned()
1383 }
1384
1385 pub fn app_id(&self) -> Option<AppId> {
1387 self.0.read().app_id
1388 }
1389
1390 pub fn generation(&self) -> ViewProcessGen {
1392 self.0.read().generation
1393 }
1394
1395 pub fn downgrade(&self) -> WeakViewImage {
1397 WeakViewImage(Arc::downgrade(&self.0))
1398 }
1399
1400 pub fn dummy(error: Option<Txt>) -> Self {
1402 ViewImage(Arc::new(RwLock::new(ViewImageData {
1403 app_id: None,
1404 id: None,
1405 generation: ViewProcessGen::INVALID,
1406 size: PxSize::zero(),
1407 partial_size: PxSize::zero(),
1408 density: None,
1409 is_opaque: true,
1410 partial_pixels: None,
1411 pixels: if let Some(e) = error {
1412 Some(Err(e))
1413 } else {
1414 Some(Ok(IpcBytes::default()))
1415 },
1416 is_mask: false,
1417 done_signal: SignalOnce::new_set(),
1418 })))
1419 }
1420
1421 pub fn awaiter(&self) -> SignalOnce {
1423 self.0.read().done_signal.clone()
1424 }
1425
1426 pub async fn encode(&self, format: Txt) -> std::result::Result<IpcBytes, EncodeError> {
1432 self.awaiter().await;
1433
1434 if let Some(e) = self.error() {
1435 return Err(EncodeError::Encode(e));
1436 }
1437
1438 let receiver = {
1439 let img = self.0.read();
1440 if let Some(id) = img.id {
1441 let mut app = VIEW_PROCESS.handle_write(img.app_id.unwrap());
1442
1443 app.process.encode_image(id, format.clone())?;
1444
1445 let (sender, receiver) = channel::bounded(1);
1446 if let Some(entry) = app.encoding_images.iter_mut().find(|r| r.image_id == id && r.format == format) {
1447 entry.listeners.push(sender);
1448 } else {
1449 app.encoding_images.push(EncodeRequest {
1450 image_id: id,
1451 format,
1452 listeners: vec![sender],
1453 });
1454 }
1455 receiver
1456 } else {
1457 return Err(EncodeError::Dummy);
1458 }
1459 };
1460
1461 receiver.recv().await?
1462 }
1463}
1464
1465#[derive(Debug, Clone, PartialEq, Eq)]
1467#[non_exhaustive]
1468pub enum EncodeError {
1469 Encode(Txt),
1471 Dummy,
1476 Disconnected,
1478}
1479impl From<Txt> for EncodeError {
1480 fn from(e: Txt) -> Self {
1481 EncodeError::Encode(e)
1482 }
1483}
1484impl From<ChannelError> for EncodeError {
1485 fn from(_: ChannelError) -> Self {
1486 EncodeError::Disconnected
1487 }
1488}
1489impl fmt::Display for EncodeError {
1490 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1491 match self {
1492 EncodeError::Encode(e) => write!(f, "{e}"),
1493 EncodeError::Dummy => write!(f, "cannot encode dummy image"),
1494 EncodeError::Disconnected => write!(f, "{}", ChannelError::disconnected()),
1495 }
1496 }
1497}
1498impl std::error::Error for EncodeError {}
1499
1500#[derive(Clone)]
1506pub struct WeakViewImage(sync::Weak<RwLock<ViewImageData>>);
1507impl WeakViewImage {
1508 pub fn upgrade(&self) -> Option<ViewImage> {
1512 self.0.upgrade().map(ViewImage)
1513 }
1514}
1515
1516struct EncodeRequest {
1517 image_id: ImageId,
1518 format: Txt,
1519 listeners: Vec<channel::Sender<std::result::Result<IpcBytes, EncodeError>>>,
1520}
1521
1522type ClipboardResult<T> = std::result::Result<T, ClipboardError>;
1523
1524#[non_exhaustive]
1526pub struct ViewClipboard {}
1527impl ViewClipboard {
1528 pub fn read_text(&self) -> Result<ClipboardResult<Txt>> {
1532 match VIEW_PROCESS.try_write()?.process.read_clipboard(ClipboardType::Text)? {
1533 Ok(ClipboardData::Text(t)) => Ok(Ok(t)),
1534 Err(e) => Ok(Err(e)),
1535 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1536 }
1537 }
1538
1539 pub fn write_text(&self, txt: Txt) -> Result<ClipboardResult<()>> {
1543 VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::Text(txt))
1544 }
1545
1546 pub fn read_image(&self) -> Result<ClipboardResult<ViewImage>> {
1550 let mut app = VIEW_PROCESS.try_write()?;
1551 match app.process.read_clipboard(ClipboardType::Image)? {
1552 Ok(ClipboardData::Image(id)) => {
1553 if id == ImageId::INVALID {
1554 Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned invalid image"))))
1555 } else {
1556 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
1557 id: Some(id),
1558 app_id: APP.id(),
1559 generation: app.process.generation(),
1560 size: PxSize::zero(),
1561 partial_size: PxSize::zero(),
1562 density: None,
1563 is_opaque: false,
1564 partial_pixels: None,
1565 pixels: None,
1566 is_mask: false,
1567 done_signal: SignalOnce::new(),
1568 })));
1569 app.loading_images.push(Arc::downgrade(&img.0));
1570 Ok(Ok(img))
1571 }
1572 }
1573 Err(e) => Ok(Err(e)),
1574 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1575 }
1576 }
1577
1578 pub fn write_image(&self, img: &ViewImage) -> Result<ClipboardResult<()>> {
1582 if img.is_loaded()
1583 && let Some(id) = img.id()
1584 {
1585 return VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::Image(id));
1586 }
1587 Ok(Err(ClipboardError::Other(Txt::from_static("image not loaded"))))
1588 }
1589
1590 pub fn read_file_list(&self) -> Result<ClipboardResult<Vec<PathBuf>>> {
1594 match VIEW_PROCESS.try_write()?.process.read_clipboard(ClipboardType::FileList)? {
1595 Ok(ClipboardData::FileList(f)) => Ok(Ok(f)),
1596 Err(e) => Ok(Err(e)),
1597 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1598 }
1599 }
1600
1601 pub fn write_file_list(&self, list: Vec<PathBuf>) -> Result<ClipboardResult<()>> {
1605 VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::FileList(list))
1606 }
1607
1608 pub fn read_extension(&self, data_type: Txt) -> Result<ClipboardResult<IpcBytes>> {
1612 match VIEW_PROCESS
1613 .try_write()?
1614 .process
1615 .read_clipboard(ClipboardType::Extension(data_type.clone()))?
1616 {
1617 Ok(ClipboardData::Extension { data_type: rt, data }) if rt == data_type => Ok(Ok(data)),
1618 Err(e) => Ok(Err(e)),
1619 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1620 }
1621 }
1622
1623 pub fn write_extension(&self, data_type: Txt, data: IpcBytes) -> Result<ClipboardResult<()>> {
1627 VIEW_PROCESS
1628 .try_write()?
1629 .process
1630 .write_clipboard(ClipboardData::Extension { data_type, data })
1631 }
1632}