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::{DipPoint, DipRect, DipSideOffsets, DipSize, Factor, Px, PxPoint, PxRect, PxSize};
21use zng_task::SignalOnce;
22use zng_txt::Txt;
23use zng_var::ResponderVar;
24use zng_view_api::{
25 self, DeviceEventsFilter, DragDropId, Event, FocusResult, ViewProcessGen,
26 api_extension::{ApiExtensionId, ApiExtensionName, ApiExtensionPayload, ApiExtensionRecvError, ApiExtensions},
27 dialog::{FileDialog, FileDialogResponse, MsgDialog, MsgDialogResponse},
28 drag_drop::{DragDropData, DragDropEffect, DragDropError},
29 font::FontOptions,
30 image::{ImageMaskMode, ImagePpi, ImageRequest, ImageTextureId},
31 ipc::{IpcBytes, IpcBytesReceiver, ViewChannelError},
32 window::{
33 CursorIcon, FocusIndicator, FrameRequest, FrameUpdateRequest, HeadlessOpenData, HeadlessRequest, RenderMode, ResizeDirection,
34 VideoMode, WindowButton, WindowRequest, WindowStateAll,
35 },
36};
37
38pub(crate) use zng_view_api::{
39 Controller, raw_input::InputDeviceId as ApiDeviceId, window::MonitorId as ApiMonitorId, window::WindowId as ApiWindowId,
40};
41use zng_view_api::{
42 clipboard::{ClipboardData, ClipboardError, ClipboardType},
43 font::{FontFaceId, FontId, FontVariationName},
44 image::{ImageId, ImageLoadedData},
45};
46
47use self::raw_device_events::InputDeviceId;
48
49use super::{APP, AppId};
50
51#[expect(non_camel_case_types)]
53pub struct VIEW_PROCESS;
54struct ViewProcessService {
55 process: zng_view_api::Controller,
56 input_device_ids: HashMap<ApiDeviceId, InputDeviceId>,
57 monitor_ids: HashMap<ApiMonitorId, MonitorId>,
58
59 data_generation: ViewProcessGen,
60
61 extensions: ApiExtensions,
62
63 loading_images: Vec<sync::Weak<RwLock<ViewImageData>>>,
64 frame_images: Vec<sync::Weak<RwLock<ViewImageData>>>,
65 encoding_images: Vec<EncodeRequest>,
66
67 pending_frames: usize,
68
69 message_dialogs: Vec<(zng_view_api::dialog::DialogId, ResponderVar<MsgDialogResponse>)>,
70 file_dialogs: Vec<(zng_view_api::dialog::DialogId, ResponderVar<FileDialogResponse>)>,
71}
72app_local! {
73 static VIEW_PROCESS_SV: Option<ViewProcessService> = None;
74}
75impl VIEW_PROCESS {
76 pub fn is_available(&self) -> bool {
79 APP.is_running() && VIEW_PROCESS_SV.read().is_some()
80 }
81
82 fn read(&self) -> MappedRwLockReadGuard<'_, ViewProcessService> {
83 VIEW_PROCESS_SV.read_map(|e| e.as_ref().expect("VIEW_PROCESS not available"))
84 }
85
86 fn write(&self) -> MappedRwLockWriteGuard<'_, ViewProcessService> {
87 VIEW_PROCESS_SV.write_map(|e| e.as_mut().expect("VIEW_PROCESS not available"))
88 }
89
90 fn try_write(&self) -> Result<MappedRwLockWriteGuard<'_, ViewProcessService>> {
91 let vp = VIEW_PROCESS_SV.write();
92 if let Some(w) = &*vp
93 && w.process.is_connected()
94 {
95 return Ok(MappedRwLockWriteGuard::map(vp, |w| w.as_mut().unwrap()));
96 }
97 Err(ViewChannelError::Disconnected)
98 }
99
100 fn check_app(&self, id: AppId) {
101 let actual = APP.id();
102 if Some(id) != actual {
103 panic!("cannot use view handle from app `{id:?}` in app `{actual:?}`");
104 }
105 }
106
107 fn handle_write(&self, id: AppId) -> MappedRwLockWriteGuard<'_, ViewProcessService> {
108 self.check_app(id);
109 self.write()
110 }
111
112 pub fn is_connected(&self) -> bool {
114 self.read().process.is_connected()
115 }
116
117 pub fn is_headless_with_render(&self) -> bool {
119 self.read().process.headless()
120 }
121
122 pub fn is_same_process(&self) -> bool {
124 self.read().process.same_process()
125 }
126
127 pub fn generation(&self) -> ViewProcessGen {
129 self.read().process.generation()
130 }
131
132 pub fn set_device_events_filter(&self, filter: DeviceEventsFilter) -> Result<()> {
137 self.write().process.set_device_events_filter(filter)
138 }
139
140 pub fn open_window(&self, config: WindowRequest) -> Result<()> {
147 let _s = tracing::debug_span!("VIEW_PROCESS.open_window").entered();
148 self.write().process.open_window(config)
149 }
150
151 pub fn open_headless(&self, config: HeadlessRequest) -> Result<()> {
161 let _s = tracing::debug_span!("VIEW_PROCESS.open_headless").entered();
162 self.write().process.open_headless(config)
163 }
164
165 pub fn add_image(&self, request: ImageRequest<IpcBytes>) -> Result<ViewImage> {
174 let mut app = self.write();
175 let id = app.process.add_image(request)?;
176 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
177 id: Some(id),
178 app_id: APP.id(),
179 generation: app.process.generation(),
180 size: PxSize::zero(),
181 partial_size: PxSize::zero(),
182 ppi: None,
183 is_opaque: false,
184 partial_pixels: None,
185 pixels: None,
186 is_mask: false,
187 done_signal: SignalOnce::new(),
188 })));
189 app.loading_images.push(Arc::downgrade(&img.0));
190 Ok(img)
191 }
192
193 pub fn add_image_pro(&self, request: ImageRequest<IpcBytesReceiver>) -> Result<ViewImage> {
204 let mut app = self.write();
205 let id = app.process.add_image_pro(request)?;
206 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
207 id: Some(id),
208 app_id: APP.id(),
209 generation: app.process.generation(),
210 size: PxSize::zero(),
211 partial_size: PxSize::zero(),
212 ppi: None,
213 is_opaque: false,
214 partial_pixels: None,
215 pixels: None,
216 is_mask: false,
217 done_signal: SignalOnce::new(),
218 })));
219 app.loading_images.push(Arc::downgrade(&img.0));
220 Ok(img)
221 }
222
223 pub fn clipboard(&self) -> Result<&ViewClipboard> {
225 if VIEW_PROCESS.is_connected() {
226 Ok(&ViewClipboard {})
227 } else {
228 Err(ViewChannelError::Disconnected)
229 }
230 }
231
232 pub fn image_decoders(&self) -> Result<Vec<Txt>> {
236 self.write().process.image_decoders()
237 }
238
239 pub fn image_encoders(&self) -> Result<Vec<Txt>> {
243 self.write().process.image_encoders()
244 }
245
246 pub fn pending_frames(&self) -> usize {
250 self.write().pending_frames
251 }
252
253 pub fn respawn(&self) {
257 self.write().process.respawn()
258 }
259
260 pub fn extension_id(&self, extension_name: impl Into<ApiExtensionName>) -> Result<Option<ApiExtensionId>> {
266 let me = self.read();
267 if me.process.is_connected() {
268 Ok(me.extensions.id(&extension_name.into()))
269 } else {
270 Err(ViewChannelError::Disconnected)
271 }
272 }
273
274 pub fn third_party_licenses(&self) -> Result<Vec<crate::third_party::LicenseUsed>> {
279 self.write().process.third_party_licenses()
280 }
281
282 pub fn app_extension_raw(&self, extension_id: ApiExtensionId, extension_request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
284 self.write().process.app_extension(extension_id, extension_request)
285 }
286
287 pub fn app_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
289 where
290 I: serde::Serialize,
291 O: serde::de::DeserializeOwned,
292 {
293 let payload = ApiExtensionPayload::serialize(&request).unwrap();
294 let response = self.write().process.app_extension(extension_id, payload)?;
295 Ok(response.deserialize::<O>())
296 }
297
298 pub fn handle_disconnect(&self, vp_gen: ViewProcessGen) {
304 self.write().process.handle_disconnect(vp_gen)
305 }
306
307 pub(super) fn start<F>(&self, view_process_exe: PathBuf, view_process_env: HashMap<Txt, Txt>, headless: bool, on_event: F)
309 where
310 F: FnMut(Event) + Send + 'static,
311 {
312 let _s = tracing::debug_span!("VIEW_PROCESS.start").entered();
313
314 let process = zng_view_api::Controller::start(view_process_exe, view_process_env, headless, on_event);
315 *VIEW_PROCESS_SV.write() = Some(ViewProcessService {
316 data_generation: process.generation(),
317 process,
318 input_device_ids: HashMap::default(),
319 monitor_ids: HashMap::default(),
320 loading_images: vec![],
321 encoding_images: vec![],
322 frame_images: vec![],
323 pending_frames: 0,
324 message_dialogs: vec![],
325 file_dialogs: vec![],
326 extensions: ApiExtensions::default(),
327 });
328 }
329
330 pub(crate) fn on_window_opened(&self, window_id: WindowId, data: zng_view_api::window::WindowOpenData) -> (ViewWindow, WindowOpenData) {
331 let mut app = self.write();
332 let _ = app.check_generation();
333
334 let win = ViewWindow(Arc::new(ViewWindowData {
335 app_id: APP.id().unwrap(),
336 id: ApiWindowId::from_raw(window_id.get()),
337 generation: app.data_generation,
338 }));
339 drop(app);
340
341 let data = WindowOpenData::new(data, |id| self.monitor_id(id));
342
343 (win, data)
344 }
345 pub(super) fn input_device_id(&self, id: ApiDeviceId) -> InputDeviceId {
347 *self.write().input_device_ids.entry(id).or_insert_with(InputDeviceId::new_unique)
348 }
349
350 pub(super) fn monitor_id(&self, id: ApiMonitorId) -> MonitorId {
352 *self.write().monitor_ids.entry(id).or_insert_with(MonitorId::new_unique)
353 }
354
355 pub(super) fn handle_inited(&self, vp_gen: ViewProcessGen, extensions: ApiExtensions) {
361 let mut me = self.write();
362 me.extensions = extensions;
363 me.process.handle_inited(vp_gen);
364 }
365
366 pub(super) fn handle_suspended(&self) {
367 self.write().process.handle_suspended();
368 }
369
370 pub(crate) fn on_headless_opened(
371 &self,
372 id: WindowId,
373 data: zng_view_api::window::HeadlessOpenData,
374 ) -> (ViewHeadless, HeadlessOpenData) {
375 let mut app = self.write();
376 let _ = app.check_generation();
377
378 let surf = ViewHeadless(Arc::new(ViewWindowData {
379 app_id: APP.id().unwrap(),
380 id: ApiWindowId::from_raw(id.get()),
381 generation: app.data_generation,
382 }));
383
384 (surf, data)
385 }
386
387 fn loading_image_index(&self, id: ImageId) -> Option<usize> {
388 let mut app = self.write();
389
390 app.loading_images.retain(|i| i.strong_count() > 0);
392
393 app.loading_images.iter().position(|i| i.upgrade().unwrap().read().id == Some(id))
394 }
395
396 pub(super) fn on_image_metadata_loaded(&self, id: ImageId, size: PxSize, ppi: Option<ImagePpi>, is_mask: bool) -> Option<ViewImage> {
397 if let Some(i) = self.loading_image_index(id) {
398 let img = self.read().loading_images[i].upgrade().unwrap();
399 {
400 let mut img = img.write();
401 img.size = size;
402 img.ppi = ppi;
403 img.is_mask = is_mask;
404 }
405 Some(ViewImage(img))
406 } else {
407 None
408 }
409 }
410
411 pub(super) fn on_image_partially_loaded(
412 &self,
413 id: ImageId,
414 partial_size: PxSize,
415 ppi: Option<ImagePpi>,
416 is_opaque: bool,
417 is_mask: bool,
418 partial_pixels: IpcBytes,
419 ) -> Option<ViewImage> {
420 if let Some(i) = self.loading_image_index(id) {
421 let img = self.read().loading_images[i].upgrade().unwrap();
422 {
423 let mut img = img.write();
424 img.partial_size = partial_size;
425 img.ppi = ppi;
426 img.is_opaque = is_opaque;
427 img.partial_pixels = Some(partial_pixels);
428 img.is_mask = is_mask;
429 }
430 Some(ViewImage(img))
431 } else {
432 None
433 }
434 }
435
436 pub(super) fn on_image_loaded(&self, data: ImageLoadedData) -> Option<ViewImage> {
437 if let Some(i) = self.loading_image_index(data.id) {
438 let img = self.write().loading_images.swap_remove(i).upgrade().unwrap();
439 {
440 let mut img = img.write();
441 img.size = data.size;
442 img.partial_size = data.size;
443 img.ppi = data.ppi;
444 img.is_opaque = data.is_opaque;
445 img.pixels = Some(Ok(data.pixels));
446 img.partial_pixels = None;
447 img.is_mask = data.is_mask;
448 img.done_signal.set();
449 }
450 Some(ViewImage(img))
451 } else {
452 None
453 }
454 }
455
456 pub(super) fn on_image_error(&self, id: ImageId, error: Txt) -> Option<ViewImage> {
457 if let Some(i) = self.loading_image_index(id) {
458 let img = self.write().loading_images.swap_remove(i).upgrade().unwrap();
459 {
460 let mut img = img.write();
461 img.pixels = Some(Err(error));
462 img.done_signal.set();
463 }
464 Some(ViewImage(img))
465 } else {
466 None
467 }
468 }
469
470 pub(crate) fn on_frame_rendered(&self, _id: WindowId) {
471 let mut vp = self.write();
472 vp.pending_frames = vp.pending_frames.saturating_sub(1);
473 }
474
475 pub(crate) fn on_frame_image(&self, data: ImageLoadedData) -> ViewImage {
476 ViewImage(Arc::new(RwLock::new(ViewImageData {
477 app_id: APP.id(),
478 id: Some(data.id),
479 generation: self.generation(),
480 size: data.size,
481 partial_size: data.size,
482 ppi: data.ppi,
483 is_opaque: data.is_opaque,
484 partial_pixels: None,
485 pixels: Some(Ok(data.pixels)),
486 is_mask: data.is_mask,
487 done_signal: SignalOnce::new_set(),
488 })))
489 }
490
491 pub(super) fn on_frame_image_ready(&self, id: ImageId) -> Option<ViewImage> {
492 let mut app = self.write();
493
494 app.frame_images.retain(|i| i.strong_count() > 0);
496
497 let i = app.frame_images.iter().position(|i| i.upgrade().unwrap().read().id == Some(id));
498
499 i.map(|i| ViewImage(app.frame_images.swap_remove(i).upgrade().unwrap()))
500 }
501
502 pub(super) fn on_image_encoded(&self, id: ImageId, format: Txt, data: IpcBytes) {
503 self.on_image_encode_result(id, format, Ok(data));
504 }
505 pub(super) fn on_image_encode_error(&self, id: ImageId, format: Txt, error: Txt) {
506 self.on_image_encode_result(id, format, Err(EncodeError::Encode(error)));
507 }
508 fn on_image_encode_result(&self, id: ImageId, format: Txt, result: std::result::Result<IpcBytes, EncodeError>) {
509 let mut app = self.write();
510 app.encoding_images.retain(move |r| {
511 let done = r.image_id == id && r.format == format;
512 if done {
513 for sender in &r.listeners {
514 let _ = sender.send(result.clone());
515 }
516 }
517 !done
518 })
519 }
520
521 pub(crate) fn on_message_dlg_response(&self, id: zng_view_api::dialog::DialogId, response: MsgDialogResponse) {
522 let mut app = self.write();
523 if let Some(i) = app.message_dialogs.iter().position(|(i, _)| *i == id) {
524 let (_, r) = app.message_dialogs.swap_remove(i);
525 r.respond(response);
526 }
527 }
528
529 pub(crate) fn on_file_dlg_response(&self, id: zng_view_api::dialog::DialogId, response: FileDialogResponse) {
530 let mut app = self.write();
531 if let Some(i) = app.file_dialogs.iter().position(|(i, _)| *i == id) {
532 let (_, r) = app.file_dialogs.swap_remove(i);
533 r.respond(response);
534 }
535 }
536
537 pub(super) fn on_respawned(&self, _gen: ViewProcessGen) {
538 let mut app = self.write();
539 app.pending_frames = 0;
540 for (_, r) in app.message_dialogs.drain(..) {
541 r.respond(MsgDialogResponse::Error(Txt::from_static("respawn")));
542 }
543 }
544
545 pub(crate) fn exit(&self) {
546 *VIEW_PROCESS_SV.write() = None;
547 }
548}
549impl ViewProcessService {
550 #[must_use = "if `true` all current WinId, DevId and MonId are invalid"]
551 fn check_generation(&mut self) -> bool {
552 let vp_gen = self.process.generation();
553 let invalid = vp_gen != self.data_generation;
554 if invalid {
555 self.data_generation = vp_gen;
556 self.input_device_ids.clear();
557 self.monitor_ids.clear();
558 }
559 invalid
560 }
561}
562
563event_args! {
564 pub struct ViewProcessInitedArgs {
566 pub generation: ViewProcessGen,
568
569 pub is_respawn: bool,
574
575 pub extensions: ApiExtensions,
579
580 ..
581
582 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
584 list.search_all()
585 }
586 }
587
588 pub struct ViewProcessSuspendedArgs {
590
591 ..
592
593 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
595 list.search_all()
596 }
597 }
598}
599
600event! {
601 pub static VIEW_PROCESS_INITED_EVENT: ViewProcessInitedArgs;
603 pub static VIEW_PROCESS_SUSPENDED_EVENT: ViewProcessSuspendedArgs;
608}
609
610#[derive(Debug, Clone)]
612#[non_exhaustive]
613pub struct WindowOpenData {
614 pub state: WindowStateAll,
616
617 pub monitor: Option<MonitorId>,
619
620 pub position: (PxPoint, DipPoint),
624 pub size: DipSize,
626
627 pub scale_factor: Factor,
629
630 pub render_mode: RenderMode,
632
633 pub safe_padding: DipSideOffsets,
639}
640impl WindowOpenData {
641 pub(crate) fn new(data: zng_view_api::window::WindowOpenData, map_monitor: impl FnOnce(ApiMonitorId) -> MonitorId) -> Self {
642 WindowOpenData {
643 state: data.state,
644 monitor: data.monitor.map(map_monitor),
645 position: data.position,
646 size: data.size,
647 scale_factor: data.scale_factor,
648 render_mode: data.render_mode,
649 safe_padding: data.safe_padding,
650 }
651 }
652}
653
654#[derive(Debug, Clone)]
658#[must_use = "the window is closed when all clones of the handle are dropped"]
659pub struct ViewWindow(Arc<ViewWindowData>);
660impl PartialEq for ViewWindow {
661 fn eq(&self, other: &Self) -> bool {
662 Arc::ptr_eq(&self.0, &other.0)
663 }
664}
665impl Eq for ViewWindow {}
666
667impl ViewWindow {
668 pub fn generation(&self) -> ViewProcessGen {
670 self.0.generation
671 }
672
673 pub fn set_title(&self, title: Txt) -> Result<()> {
675 self.0.call(|id, p| p.set_title(id, title))
676 }
677
678 pub fn set_visible(&self, visible: bool) -> Result<()> {
680 self.0.call(|id, p| p.set_visible(id, visible))
681 }
682
683 pub fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
685 self.0.call(|id, p| p.set_always_on_top(id, always_on_top))
686 }
687
688 pub fn set_movable(&self, movable: bool) -> Result<()> {
690 self.0.call(|id, p| p.set_movable(id, movable))
691 }
692
693 pub fn set_resizable(&self, resizable: bool) -> Result<()> {
695 self.0.call(|id, p| p.set_resizable(id, resizable))
696 }
697
698 pub fn set_icon(&self, icon: Option<&ViewImage>) -> Result<()> {
700 self.0.call(|id, p| {
701 if let Some(icon) = icon {
702 let icon = icon.0.read();
703 if p.generation() == icon.generation {
704 p.set_icon(id, icon.id)
705 } else {
706 Err(ViewChannelError::Disconnected)
707 }
708 } else {
709 p.set_icon(id, None)
710 }
711 })
712 }
713
714 pub fn set_cursor(&self, cursor: Option<CursorIcon>) -> Result<()> {
716 self.0.call(|id, p| p.set_cursor(id, cursor))
717 }
718
719 pub fn set_cursor_image(&self, cursor: Option<&ViewImage>, hotspot: PxPoint) -> Result<()> {
726 self.0.call(|id, p| {
727 if let Some(cur) = cursor {
728 let cur = cur.0.read();
729 if p.generation() == cur.generation {
730 p.set_cursor_image(id, cur.id.map(|img| zng_view_api::window::CursorImage::new(img, hotspot)))
731 } else {
732 Err(ViewChannelError::Disconnected)
733 }
734 } else {
735 p.set_cursor_image(id, None)
736 }
737 })
738 }
739
740 pub fn set_taskbar_visible(&self, visible: bool) -> Result<()> {
742 self.0.call(|id, p| p.set_taskbar_visible(id, visible))
743 }
744
745 pub fn bring_to_top(&self) -> Result<()> {
747 self.0.call(|id, p| p.bring_to_top(id))
748 }
749
750 pub fn set_state(&self, state: WindowStateAll) -> Result<()> {
752 self.0.call(|id, p| p.set_state(id, state))
753 }
754
755 pub fn set_video_mode(&self, mode: VideoMode) -> Result<()> {
757 self.0.call(|id, p| p.set_video_mode(id, mode))
758 }
759
760 pub fn set_enabled_buttons(&self, buttons: WindowButton) -> Result<()> {
762 self.0.call(|id, p| p.set_enabled_buttons(id, buttons))
763 }
764
765 pub fn renderer(&self) -> ViewRenderer {
767 ViewRenderer(Arc::downgrade(&self.0))
768 }
769
770 pub fn set_capture_mode(&self, enabled: bool) -> Result<()> {
775 self.0.call(|id, p| p.set_capture_mode(id, enabled))
776 }
777
778 pub fn focus(&self) -> Result<FocusResult> {
782 self.0.call(|id, p| p.focus(id))
783 }
784
785 pub fn set_focus_indicator(&self, indicator: Option<FocusIndicator>) -> Result<()> {
788 self.0.call(|id, p| p.set_focus_indicator(id, indicator))
789 }
790
791 pub fn drag_move(&self) -> Result<()> {
795 self.0.call(|id, p| p.drag_move(id))
796 }
797
798 pub fn drag_resize(&self, direction: ResizeDirection) -> Result<()> {
802 self.0.call(|id, p| p.drag_resize(id, direction))
803 }
804
805 pub fn start_drag_drop(
811 &self,
812 data: Vec<DragDropData>,
813 allowed_effects: DragDropEffect,
814 ) -> Result<std::result::Result<DragDropId, DragDropError>> {
815 self.0.call(|id, p| p.start_drag_drop(id, data, allowed_effects))
816 }
817
818 pub fn drag_dropped(&self, drop_id: DragDropId, applied: DragDropEffect) -> Result<()> {
820 self.0.call(|id, p| p.drag_dropped(id, drop_id, applied))
821 }
822
823 pub fn open_title_bar_context_menu(&self, position: DipPoint) -> Result<()> {
825 self.0.call(|id, p| p.open_title_bar_context_menu(id, position))
826 }
827
828 pub fn message_dialog(&self, dlg: MsgDialog, responder: ResponderVar<MsgDialogResponse>) -> Result<()> {
833 let dlg_id = self.0.call(|id, p| p.message_dialog(id, dlg))?;
834 VIEW_PROCESS.handle_write(self.0.app_id).message_dialogs.push((dlg_id, responder));
835 Ok(())
836 }
837
838 pub fn file_dialog(&self, dlg: FileDialog, responder: ResponderVar<FileDialogResponse>) -> Result<()> {
843 let dlg_id = self.0.call(|id, p| p.file_dialog(id, dlg))?;
844 VIEW_PROCESS.handle_write(self.0.app_id).file_dialogs.push((dlg_id, responder));
845 Ok(())
846 }
847
848 pub fn access_update(&self, update: zng_view_api::access::AccessTreeUpdate) -> Result<()> {
850 self.0.call(|id, p| p.access_update(id, update))
851 }
852
853 pub fn set_ime_area(&self, area: Option<DipRect>) -> Result<()> {
857 self.0.call(|id, p| p.set_ime_area(id, area))
858 }
859
860 pub fn set_system_shutdown_warn(&self, reason: Txt) -> Result<()> {
871 self.0.call(move |id, p| p.set_system_shutdown_warn(id, reason))
872 }
873
874 pub fn close(self) {
876 drop(self)
877 }
878
879 pub fn window_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
881 self.0.call(|id, p| p.window_extension(id, extension_id, request))
882 }
883
884 pub fn window_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
886 where
887 I: serde::Serialize,
888 O: serde::de::DeserializeOwned,
889 {
890 let r = self.window_extension_raw(extension_id, ApiExtensionPayload::serialize(&request).unwrap())?;
891 Ok(r.deserialize())
892 }
893}
894
895#[derive(Clone, Debug)]
897pub enum ViewWindowOrHeadless {
898 Window(ViewWindow),
900 Headless(ViewHeadless),
902}
903impl ViewWindowOrHeadless {
904 pub fn renderer(&self) -> ViewRenderer {
906 match self {
907 ViewWindowOrHeadless::Window(w) => w.renderer(),
908 ViewWindowOrHeadless::Headless(h) => h.renderer(),
909 }
910 }
911
912 pub fn window_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
914 match self {
915 ViewWindowOrHeadless::Window(w) => w.window_extension_raw(extension_id, request),
916 ViewWindowOrHeadless::Headless(h) => h.window_extension_raw(extension_id, request),
917 }
918 }
919
920 pub fn window_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
922 where
923 I: serde::Serialize,
924 O: serde::de::DeserializeOwned,
925 {
926 match self {
927 ViewWindowOrHeadless::Window(w) => w.window_extension(extension_id, request),
928 ViewWindowOrHeadless::Headless(h) => h.window_extension(extension_id, request),
929 }
930 }
931}
932impl From<ViewWindow> for ViewWindowOrHeadless {
933 fn from(w: ViewWindow) -> Self {
934 ViewWindowOrHeadless::Window(w)
935 }
936}
937impl From<ViewHeadless> for ViewWindowOrHeadless {
938 fn from(w: ViewHeadless) -> Self {
939 ViewWindowOrHeadless::Headless(w)
940 }
941}
942
943#[derive(Debug)]
944struct ViewWindowData {
945 app_id: AppId,
946 id: ApiWindowId,
947 generation: ViewProcessGen,
948}
949impl ViewWindowData {
950 fn call<R>(&self, f: impl FnOnce(ApiWindowId, &mut Controller) -> Result<R>) -> Result<R> {
951 let mut app = VIEW_PROCESS.handle_write(self.app_id);
952 if app.check_generation() {
953 Err(ViewChannelError::Disconnected)
954 } else {
955 f(self.id, &mut app.process)
956 }
957 }
958}
959impl Drop for ViewWindowData {
960 fn drop(&mut self) {
961 if VIEW_PROCESS.is_available() {
962 let mut app = VIEW_PROCESS.handle_write(self.app_id);
963 if self.generation == app.process.generation() {
964 let _ = app.process.close(self.id);
965 }
966 }
967 }
968}
969type Result<T> = std::result::Result<T, ViewChannelError>;
970
971#[derive(Clone, Debug)]
975#[must_use = "the view is disposed when all clones of the handle are dropped"]
976pub struct ViewHeadless(Arc<ViewWindowData>);
977impl PartialEq for ViewHeadless {
978 fn eq(&self, other: &Self) -> bool {
979 Arc::ptr_eq(&self.0, &other.0)
980 }
981}
982impl Eq for ViewHeadless {}
983impl ViewHeadless {
984 pub fn set_size(&self, size: DipSize, scale_factor: Factor) -> Result<()> {
986 self.0.call(|id, p| p.set_headless_size(id, size, scale_factor))
987 }
988
989 pub fn renderer(&self) -> ViewRenderer {
991 ViewRenderer(Arc::downgrade(&self.0))
992 }
993
994 pub fn window_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
996 self.0.call(|id, p| p.window_extension(id, extension_id, request))
997 }
998
999 pub fn window_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
1001 where
1002 I: serde::Serialize,
1003 O: serde::de::DeserializeOwned,
1004 {
1005 let r = self.window_extension_raw(extension_id, ApiExtensionPayload::serialize(&request).unwrap())?;
1006 Ok(r.deserialize())
1007 }
1008}
1009
1010#[derive(Clone, Debug)]
1015pub struct ViewRenderer(sync::Weak<ViewWindowData>);
1016impl PartialEq for ViewRenderer {
1017 fn eq(&self, other: &Self) -> bool {
1018 if let Some(s) = self.0.upgrade()
1019 && let Some(o) = other.0.upgrade()
1020 {
1021 Arc::ptr_eq(&s, &o)
1022 } else {
1023 false
1024 }
1025 }
1026}
1027impl Eq for ViewRenderer {}
1028
1029impl ViewRenderer {
1030 fn call<R>(&self, f: impl FnOnce(ApiWindowId, &mut Controller) -> Result<R>) -> Result<R> {
1031 if let Some(c) = self.0.upgrade() {
1032 c.call(f)
1033 } else {
1034 Err(ViewChannelError::Disconnected)
1035 }
1036 }
1037
1038 pub fn generation(&self) -> Result<ViewProcessGen> {
1040 self.0.upgrade().map(|c| c.generation).ok_or(ViewChannelError::Disconnected)
1041 }
1042
1043 pub fn use_image(&self, image: &ViewImage) -> Result<ImageTextureId> {
1047 self.call(|id, p| {
1048 let image = image.0.read();
1049 if p.generation() == image.generation {
1050 p.use_image(id, image.id.unwrap_or(ImageId::INVALID))
1051 } else {
1052 Err(ViewChannelError::Disconnected)
1053 }
1054 })
1055 }
1056
1057 pub fn update_image_use(&mut self, tex_id: ImageTextureId, image: &ViewImage) -> Result<()> {
1059 self.call(|id, p| {
1060 let image = image.0.read();
1061 if p.generation() == image.generation {
1062 p.update_image_use(id, tex_id, image.id.unwrap_or(ImageId::INVALID))
1063 } else {
1064 Err(ViewChannelError::Disconnected)
1065 }
1066 })
1067 }
1068
1069 pub fn delete_image_use(&mut self, tex_id: ImageTextureId) -> Result<()> {
1071 self.call(|id, p| p.delete_image_use(id, tex_id))
1072 }
1073
1074 pub fn add_font_face(&self, bytes: Vec<u8>, index: u32) -> Result<FontFaceId> {
1078 self.call(|id, p| p.add_font_face(id, IpcBytes::from_vec(bytes), index))
1079 }
1080
1081 pub fn delete_font_face(&self, font_face_id: FontFaceId) -> Result<()> {
1083 self.call(|id, p| p.delete_font_face(id, font_face_id))
1084 }
1085
1086 pub fn add_font(
1090 &self,
1091 font_face_id: FontFaceId,
1092 glyph_size: Px,
1093 options: FontOptions,
1094 variations: Vec<(FontVariationName, f32)>,
1095 ) -> Result<FontId> {
1096 self.call(|id, p| p.add_font(id, font_face_id, glyph_size, options, variations))
1097 }
1098
1099 pub fn delete_font(&self, font_id: FontId) -> Result<()> {
1101 self.call(|id, p| p.delete_font(id, font_id))
1102 }
1103
1104 pub fn frame_image(&self, mask: Option<ImageMaskMode>) -> Result<ViewImage> {
1106 if let Some(c) = self.0.upgrade() {
1107 let id = c.call(|id, p| p.frame_image(id, mask))?;
1108 Ok(Self::add_frame_image(c.app_id, id))
1109 } else {
1110 Err(ViewChannelError::Disconnected)
1111 }
1112 }
1113
1114 pub fn frame_image_rect(&self, rect: PxRect, mask: Option<ImageMaskMode>) -> Result<ViewImage> {
1116 if let Some(c) = self.0.upgrade() {
1117 let id = c.call(|id, p| p.frame_image_rect(id, rect, mask))?;
1118 Ok(Self::add_frame_image(c.app_id, id))
1119 } else {
1120 Err(ViewChannelError::Disconnected)
1121 }
1122 }
1123
1124 fn add_frame_image(app_id: AppId, id: ImageId) -> ViewImage {
1125 if id == ImageId::INVALID {
1126 ViewImage::dummy(None)
1127 } else {
1128 let mut app = VIEW_PROCESS.handle_write(app_id);
1129 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
1130 app_id: Some(app_id),
1131 id: Some(id),
1132 generation: app.process.generation(),
1133 size: PxSize::zero(),
1134 partial_size: PxSize::zero(),
1135 ppi: None,
1136 is_opaque: false,
1137 partial_pixels: None,
1138 pixels: None,
1139 is_mask: false,
1140 done_signal: SignalOnce::new(),
1141 })));
1142
1143 app.loading_images.push(Arc::downgrade(&img.0));
1144 app.frame_images.push(Arc::downgrade(&img.0));
1145
1146 img
1147 }
1148 }
1149
1150 pub fn render(&self, frame: FrameRequest) -> Result<()> {
1152 let _s = tracing::debug_span!("ViewRenderer.render").entered();
1153
1154 if let Some(w) = self.0.upgrade() {
1155 w.call(|id, p| p.render(id, frame))?;
1156 VIEW_PROCESS.handle_write(w.app_id).pending_frames += 1;
1157 Ok(())
1158 } else {
1159 Err(ViewChannelError::Disconnected)
1160 }
1161 }
1162
1163 pub fn render_update(&self, frame: FrameUpdateRequest) -> Result<()> {
1165 let _s = tracing::debug_span!("ViewRenderer.render_update").entered();
1166
1167 if let Some(w) = self.0.upgrade() {
1168 w.call(|id, p| p.render_update(id, frame))?;
1169 VIEW_PROCESS.handle_write(w.app_id).pending_frames += 1;
1170 Ok(())
1171 } else {
1172 Err(ViewChannelError::Disconnected)
1173 }
1174 }
1175
1176 pub fn render_extension_raw(&self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> Result<ApiExtensionPayload> {
1178 if let Some(w) = self.0.upgrade() {
1179 w.call(|id, p| p.render_extension(id, extension_id, request))
1180 } else {
1181 Err(ViewChannelError::Disconnected)
1182 }
1183 }
1184
1185 pub fn render_extension<I, O>(&self, extension_id: ApiExtensionId, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
1187 where
1188 I: serde::Serialize,
1189 O: serde::de::DeserializeOwned,
1190 {
1191 let r = self.render_extension_raw(extension_id, ApiExtensionPayload::serialize(&request).unwrap())?;
1192 Ok(r.deserialize())
1193 }
1194}
1195
1196#[must_use = "the image is disposed when all clones of the handle are dropped"]
1200#[derive(Clone)]
1201pub struct ViewImage(Arc<RwLock<ViewImageData>>);
1202impl PartialEq for ViewImage {
1203 fn eq(&self, other: &Self) -> bool {
1204 Arc::ptr_eq(&self.0, &other.0)
1205 }
1206}
1207impl Eq for ViewImage {}
1208impl std::hash::Hash for ViewImage {
1209 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1210 let ptr = Arc::as_ptr(&self.0) as usize;
1211 ptr.hash(state)
1212 }
1213}
1214impl fmt::Debug for ViewImage {
1215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1216 f.debug_struct("ViewImage")
1217 .field("loaded", &self.is_loaded())
1218 .field("error", &self.error())
1219 .field("size", &self.size())
1220 .field("dpi", &self.ppi())
1221 .field("is_opaque", &self.is_opaque())
1222 .field("is_mask", &self.is_mask())
1223 .field("generation", &self.generation())
1224 .finish_non_exhaustive()
1225 }
1226}
1227
1228struct ViewImageData {
1229 app_id: Option<AppId>,
1230 id: Option<ImageId>,
1231 generation: ViewProcessGen,
1232
1233 size: PxSize,
1234 partial_size: PxSize,
1235 ppi: Option<ImagePpi>,
1236 is_opaque: bool,
1237
1238 partial_pixels: Option<IpcBytes>,
1239 pixels: Option<std::result::Result<IpcBytes, Txt>>,
1240 is_mask: bool,
1241
1242 done_signal: SignalOnce,
1243}
1244impl Drop for ViewImageData {
1245 fn drop(&mut self) {
1246 if let Some(id) = self.id {
1247 let app_id = self.app_id.unwrap();
1248 if let Some(app) = APP.id() {
1249 if app_id != app {
1250 tracing::error!("image from app `{:?}` dropped in app `{:?}`", app_id, app);
1251 }
1252
1253 if VIEW_PROCESS.is_available() && VIEW_PROCESS.generation() == self.generation {
1254 let _ = VIEW_PROCESS.write().process.forget_image(id);
1255 }
1256 }
1257 }
1258 }
1259}
1260
1261impl ViewImage {
1262 pub fn id(&self) -> Option<ImageId> {
1264 self.0.read().id
1265 }
1266
1267 pub fn is_dummy(&self) -> bool {
1269 self.0.read().id.is_none()
1270 }
1271
1272 pub fn is_loaded(&self) -> bool {
1274 self.0.read().pixels.as_ref().map(|r| r.is_ok()).unwrap_or(false)
1275 }
1276
1277 pub fn is_partially_loaded(&self) -> bool {
1279 self.0.read().partial_pixels.is_some()
1280 }
1281
1282 pub fn is_error(&self) -> bool {
1286 self.0.read().pixels.as_ref().map(|r| r.is_err()).unwrap_or(false)
1287 }
1288
1289 pub fn error(&self) -> Option<Txt> {
1291 self.0.read().pixels.as_ref().and_then(|s| s.as_ref().err().cloned())
1292 }
1293
1294 pub fn size(&self) -> PxSize {
1296 self.0.read().size
1297 }
1298
1299 pub fn partial_size(&self) -> PxSize {
1305 self.0.read().partial_size
1306 }
1307
1308 pub fn ppi(&self) -> Option<ImagePpi> {
1311 self.0.read().ppi
1312 }
1313
1314 pub fn is_opaque(&self) -> bool {
1316 self.0.read().is_opaque
1317 }
1318
1319 pub fn is_mask(&self) -> bool {
1321 self.0.read().is_mask
1322 }
1323
1324 pub fn partial_pixels(&self) -> Option<Vec<u8>> {
1331 self.0.read().partial_pixels.as_ref().map(|r| r[..].to_vec())
1332 }
1333
1334 pub fn pixels(&self) -> Option<IpcBytes> {
1345 self.0.read().pixels.as_ref().and_then(|r| r.as_ref().ok()).cloned()
1346 }
1347
1348 pub fn app_id(&self) -> Option<AppId> {
1350 self.0.read().app_id
1351 }
1352
1353 pub fn generation(&self) -> ViewProcessGen {
1355 self.0.read().generation
1356 }
1357
1358 pub fn downgrade(&self) -> WeakViewImage {
1360 WeakViewImage(Arc::downgrade(&self.0))
1361 }
1362
1363 pub fn dummy(error: Option<Txt>) -> Self {
1365 ViewImage(Arc::new(RwLock::new(ViewImageData {
1366 app_id: None,
1367 id: None,
1368 generation: ViewProcessGen::INVALID,
1369 size: PxSize::zero(),
1370 partial_size: PxSize::zero(),
1371 ppi: None,
1372 is_opaque: true,
1373 partial_pixels: None,
1374 pixels: if let Some(e) = error {
1375 Some(Err(e))
1376 } else {
1377 Some(Ok(IpcBytes::from_slice(&[])))
1378 },
1379 is_mask: false,
1380 done_signal: SignalOnce::new_set(),
1381 })))
1382 }
1383
1384 pub fn awaiter(&self) -> SignalOnce {
1386 self.0.read().done_signal.clone()
1387 }
1388
1389 pub async fn encode(&self, format: Txt) -> std::result::Result<IpcBytes, EncodeError> {
1395 self.awaiter().await;
1396
1397 if let Some(e) = self.error() {
1398 return Err(EncodeError::Encode(e));
1399 }
1400
1401 let receiver = {
1402 let img = self.0.read();
1403 if let Some(id) = img.id {
1404 let mut app = VIEW_PROCESS.handle_write(img.app_id.unwrap());
1405
1406 app.process.encode_image(id, format.clone())?;
1407
1408 let (sender, receiver) = flume::bounded(1);
1409 if let Some(entry) = app.encoding_images.iter_mut().find(|r| r.image_id == id && r.format == format) {
1410 entry.listeners.push(sender);
1411 } else {
1412 app.encoding_images.push(EncodeRequest {
1413 image_id: id,
1414 format,
1415 listeners: vec![sender],
1416 });
1417 }
1418 receiver
1419 } else {
1420 return Err(EncodeError::Dummy);
1421 }
1422 };
1423
1424 receiver.recv_async().await?
1425 }
1426}
1427
1428#[derive(Debug, Clone, PartialEq, Eq)]
1430#[non_exhaustive]
1431pub enum EncodeError {
1432 Encode(Txt),
1434 Dummy,
1439 Disconnected,
1441}
1442impl From<Txt> for EncodeError {
1443 fn from(e: Txt) -> Self {
1444 EncodeError::Encode(e)
1445 }
1446}
1447impl From<ViewChannelError> for EncodeError {
1448 fn from(_: ViewChannelError) -> Self {
1449 EncodeError::Disconnected
1450 }
1451}
1452impl From<flume::RecvError> for EncodeError {
1453 fn from(_: flume::RecvError) -> Self {
1454 EncodeError::Disconnected
1455 }
1456}
1457impl fmt::Display for EncodeError {
1458 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1459 match self {
1460 EncodeError::Encode(e) => write!(f, "{e}"),
1461 EncodeError::Dummy => write!(f, "cannot encode dummy image"),
1462 EncodeError::Disconnected => write!(f, "{}", ViewChannelError::Disconnected),
1463 }
1464 }
1465}
1466impl std::error::Error for EncodeError {}
1467
1468#[derive(Clone)]
1474pub struct WeakViewImage(sync::Weak<RwLock<ViewImageData>>);
1475impl WeakViewImage {
1476 pub fn upgrade(&self) -> Option<ViewImage> {
1480 self.0.upgrade().map(ViewImage)
1481 }
1482}
1483
1484struct EncodeRequest {
1485 image_id: ImageId,
1486 format: Txt,
1487 listeners: Vec<flume::Sender<std::result::Result<IpcBytes, EncodeError>>>,
1488}
1489
1490type ClipboardResult<T> = std::result::Result<T, ClipboardError>;
1491
1492#[non_exhaustive]
1494pub struct ViewClipboard {}
1495impl ViewClipboard {
1496 pub fn read_text(&self) -> Result<ClipboardResult<Txt>> {
1500 match VIEW_PROCESS.try_write()?.process.read_clipboard(ClipboardType::Text)? {
1501 Ok(ClipboardData::Text(t)) => Ok(Ok(t)),
1502 Err(e) => Ok(Err(e)),
1503 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1504 }
1505 }
1506
1507 pub fn write_text(&self, txt: Txt) -> Result<ClipboardResult<()>> {
1511 VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::Text(txt))
1512 }
1513
1514 pub fn read_image(&self) -> Result<ClipboardResult<ViewImage>> {
1518 let mut app = VIEW_PROCESS.try_write()?;
1519 match app.process.read_clipboard(ClipboardType::Image)? {
1520 Ok(ClipboardData::Image(id)) => {
1521 if id == ImageId::INVALID {
1522 Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned invalid image"))))
1523 } else {
1524 let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
1525 id: Some(id),
1526 app_id: APP.id(),
1527 generation: app.process.generation(),
1528 size: PxSize::zero(),
1529 partial_size: PxSize::zero(),
1530 ppi: None,
1531 is_opaque: false,
1532 partial_pixels: None,
1533 pixels: None,
1534 is_mask: false,
1535 done_signal: SignalOnce::new(),
1536 })));
1537 app.loading_images.push(Arc::downgrade(&img.0));
1538 Ok(Ok(img))
1539 }
1540 }
1541 Err(e) => Ok(Err(e)),
1542 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1543 }
1544 }
1545
1546 pub fn write_image(&self, img: &ViewImage) -> Result<ClipboardResult<()>> {
1550 if img.is_loaded()
1551 && let Some(id) = img.id()
1552 {
1553 return VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::Image(id));
1554 }
1555 Ok(Err(ClipboardError::Other(Txt::from_static("image not loaded"))))
1556 }
1557
1558 pub fn read_file_list(&self) -> Result<ClipboardResult<Vec<PathBuf>>> {
1562 match VIEW_PROCESS.try_write()?.process.read_clipboard(ClipboardType::FileList)? {
1563 Ok(ClipboardData::FileList(f)) => Ok(Ok(f)),
1564 Err(e) => Ok(Err(e)),
1565 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1566 }
1567 }
1568
1569 pub fn write_file_list(&self, list: Vec<PathBuf>) -> Result<ClipboardResult<()>> {
1573 VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::FileList(list))
1574 }
1575
1576 pub fn read_extension(&self, data_type: Txt) -> Result<ClipboardResult<IpcBytes>> {
1580 match VIEW_PROCESS
1581 .try_write()?
1582 .process
1583 .read_clipboard(ClipboardType::Extension(data_type.clone()))?
1584 {
1585 Ok(ClipboardData::Extension { data_type: rt, data }) if rt == data_type => Ok(Ok(data)),
1586 Err(e) => Ok(Err(e)),
1587 _ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
1588 }
1589 }
1590
1591 pub fn write_extension(&self, data_type: Txt, data: IpcBytes) -> Result<ClipboardResult<()>> {
1595 VIEW_PROCESS
1596 .try_write()?
1597 .process
1598 .write_clipboard(ClipboardData::Extension { data_type, data })
1599 }
1600}