1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
89#![doc(test(no_crate_inject))]
90#![warn(missing_docs)]
91#![warn(unused_extern_crates)]
92
93use std::{
94 fmt, mem,
95 path::PathBuf,
96 thread,
97 time::{Duration, Instant},
98};
99
100use extensions::ViewExtensions;
101use gl::GlContextManager;
102use image_cache::ImageCache;
103use keyboard::KeyLocation;
104use util::WinitToPx;
105use winit::{
106 event::{DeviceEvent, WindowEvent},
107 event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy},
108 keyboard::ModifiersState,
109 monitor::MonitorHandle,
110};
111use zng_task::channel::{self, ChannelError, IpcBytes, IpcReadHandle, IpcReceiver, Receiver, Sender};
112
113#[cfg(not(target_os = "android"))]
114use winit::platform::modifier_supplement::KeyEventExtModifierSupplement;
115
116#[cfg(target_os = "android")]
117use winit::platform::android::EventLoopBuilderExtAndroid;
118
119mod audio_cache;
120mod config;
121mod display_list;
122mod gl;
123mod image_cache;
124#[cfg(windows)]
125mod input_device_info;
126mod low_memory;
127mod notification;
128mod px_wr;
129mod surface;
130mod util;
131mod window;
132
133use surface::*;
134
135pub mod extensions;
136
137pub mod platform;
138
139#[doc(no_inline)]
141pub use webrender;
142
143#[doc(no_inline)]
145pub use gleam;
146
147use webrender::api::*;
148use window::Window;
149use zng_txt::Txt;
150use zng_unit::{Dip, DipPoint, DipRect, DipSideOffsets, DipSize, Factor, Px, PxPoint, PxRect, PxToDip};
151use zng_view_api::{
152 ViewProcessInfo,
153 api_extension::{ApiExtensionId, ApiExtensionPayload},
154 dialog::{DialogId, FileDialog, MsgDialog, MsgDialogResponse},
155 drag_drop::*,
156 font::{FontFaceId, FontId, FontOptions, FontVariationName},
157 image::{ImageDecoded, ImageEncodeId, ImageEncodeRequest, ImageId, ImageMaskMode, ImageRequest, ImageTextureId},
158 keyboard::{Key, KeyCode, KeyState},
159 mouse::ButtonId,
160 raw_input::{InputDeviceCapability, InputDeviceEvent, InputDeviceId, InputDeviceInfo},
161 touch::{TouchId, TouchUpdate},
162 window::{
163 CursorIcon, CursorImage, EventCause, EventFrameRendered, FocusIndicator, FrameRequest, FrameUpdateRequest, FrameWaitId,
164 HeadlessOpenData, HeadlessRequest, MonitorId, MonitorInfo, VideoMode, WindowChanged, WindowId, WindowOpenData, WindowRequest,
165 WindowState, WindowStateAll,
166 },
167 *,
168};
169
170use rustc_hash::FxHashMap;
171
172use crate::{
173 audio_cache::{AudioCache, AudioTrack},
174 notification::NotificationService,
175};
176
177#[cfg(ipc)]
178zng_env::on_process_start!(|args| {
179 if std::env::var("ZNG_VIEW_NO_INIT_START").is_err() {
180 if !zng_env::about().is_test {
181 if args.yield_count == 0 {
182 return args.yield_once();
184 }
185 view_process_main();
186 } else {
187 tracing::debug!("view-process not inited in test app");
188 }
189 }
190});
191
192#[cfg(ipc)]
200pub fn view_process_main() {
201 let config = match ViewConfig::from_env() {
202 Some(c) => c,
203 None => return,
204 };
205
206 zng_env::set_process_name("view-process");
207
208 std::panic::set_hook(Box::new(init_abort));
209 config.assert_version(false);
210 let c = ipc::connect_view_process(config.server_name).expect("failed to connect to app-process");
211
212 let mut ext = ViewExtensions::new();
213 for e in extensions::VIEW_EXTENSIONS {
214 e(&mut ext);
215 }
216
217 if config.headless {
218 App::run_headless(c, ext);
219 } else {
220 App::run_headed(c, ext);
221 }
222
223 zng_env::exit(0)
224}
225
226#[cfg(ipc)]
227#[doc(hidden)]
228#[unsafe(no_mangle)] pub extern "C" fn extern_view_process_main(patch: &StaticPatch) {
230 std::panic::set_hook(Box::new(ffi_abort));
231
232 unsafe {
235 patch.install();
236 }
237
238 view_process_main()
239}
240
241pub fn run_same_process(run_app: impl FnOnce() + Send + 'static) {
262 run_same_process_extended(run_app, ViewExtensions::new)
263}
264
265pub fn run_same_process_extended(run_app: impl FnOnce() + Send + 'static, ext: fn() -> ViewExtensions) {
269 let app_thread = thread::Builder::new()
270 .name("app".to_owned())
271 .spawn(move || {
272 if let Err(e) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(run_app)) {
274 thread::Builder::new()
275 .name("ensure-exit".into())
276 .stack_size(256 * 1024)
277 .spawn(|| {
278 thread::sleep(std::time::Duration::from_secs(5));
283 eprintln!("run_same_process did not exit after 5s of a fatal panic, exiting now");
284 zng_env::exit(101);
285 })
286 .expect("failed to spawn thread");
287 std::panic::resume_unwind(e);
289 }
290 })
291 .unwrap();
292
293 let config = ViewConfig::wait_same_process();
294 config.assert_version(true);
295
296 let c = ipc::connect_view_process(config.server_name).expect("failed to connect to app in same process");
297
298 let mut ext = ext();
299 for e in extensions::VIEW_EXTENSIONS {
300 e(&mut ext);
301 }
302
303 if config.headless {
304 App::run_headless(c, ext);
305 } else {
306 App::run_headed(c, ext);
307 }
308
309 if let Err(p) = app_thread.join() {
310 std::panic::resume_unwind(p);
311 }
312}
313
314#[cfg(ipc)]
315#[doc(hidden)]
316#[unsafe(no_mangle)] pub extern "C" fn extern_run_same_process(patch: &StaticPatch, run_app: extern "C" fn()) {
318 std::panic::set_hook(Box::new(ffi_abort));
319
320 unsafe {
323 patch.install();
324 }
325
326 run_same_process(move || run_app())
327}
328#[cfg(ipc)]
329fn init_abort(info: &std::panic::PanicHookInfo) {
330 panic_hook(info, "note: aborting to respawn");
331}
332#[cfg(ipc)]
333fn ffi_abort(info: &std::panic::PanicHookInfo) {
334 panic_hook(info, "note: aborting to avoid unwind across FFI");
335}
336#[cfg(ipc)]
337fn panic_hook(info: &std::panic::PanicHookInfo, details: &str) {
338 if crate::util::suppress_panic() {
341 let p = info.payload();
342 let msg = if let Some(s) = p.downcast_ref::<&str>() {
343 (*s).to_owned()
344 } else if let Some(s) = p.downcast_ref::<String>() {
345 s.clone()
346 } else {
347 String::new()
348 };
349 crate::util::set_suppressed_panic(zng_task::TaskPanicError::new(Box::new(msg)));
350 } else {
351 zng_task::process::tap::PanicInfo::eprint_panic(info, "");
352 eprintln!("{details}");
353 zng_env::exit(101) }
355}
356
357pub(crate) struct App {
359 headless: bool,
360
361 exts: ViewExtensions,
362
363 gl_manager: GlContextManager,
364 winit_loop: util::WinitEventLoop,
365 app_sender: AppEventSender,
366 request_recv: Receiver<RequestEvent>,
367
368 response_sender: ipc::ResponseSender,
369 event_sender: ipc::EventSender,
370
371 image_cache: ImageCache,
372 audio_cache: AudioCache,
373
374 generation: ViewProcessGen,
375 device_events_filter: DeviceEventsFilter,
376
377 windows: Vec<Window>,
378 surfaces: Vec<Surface>,
379
380 monitor_id_gen: MonitorId,
381 monitor_ids: Vec<(MonitorId, MonitorHandle)>,
382 monitors: Vec<(MonitorId, MonitorInfo)>,
383
384 device_id_gen: InputDeviceId,
385 devices: Vec<(InputDeviceId, winit::event::DeviceId, InputDeviceInfo)>,
386
387 dialog_id_gen: DialogId,
388
389 resize_frame_wait_id_gen: FrameWaitId,
390
391 coalescing_event: Option<(Event, Instant)>,
392 cursor_entered_expect_move: Vec<WindowId>,
398
399 #[cfg(windows)]
400 skip_ralt: bool,
401
402 pressed_modifiers: FxHashMap<(Key, KeyLocation), (InputDeviceId, KeyCode)>,
403 pending_modifiers_update: Option<ModifiersState>,
404 pending_modifiers_focus_clear: bool,
405
406 #[cfg(not(any(windows, target_os = "android")))]
407 arboard: Option<arboard::Clipboard>,
408
409 low_memory_watcher: Option<low_memory::LowMemoryWatcher>,
410 last_pull_event: Instant,
411
412 config_listener_exit: Option<Box<dyn FnOnce()>>,
413
414 notifications: NotificationService,
415
416 app_state: AppState,
417 drag_drop_hovered: Option<(WindowId, DipPoint)>,
418 drag_drop_next_move: Option<(Instant, PathBuf)>,
419 exited: bool,
420}
421impl fmt::Debug for App {
422 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 f.debug_struct("HeadlessBackend")
424 .field("app_state", &self.app_state)
425 .field("generation", &self.generation)
426 .field("device_events_filter", &self.device_events_filter)
427 .field("windows", &self.windows)
428 .field("surfaces", &self.surfaces)
429 .finish_non_exhaustive()
430 }
431}
432impl winit::application::ApplicationHandler<AppEvent> for App {
433 fn resumed(&mut self, winit_loop: &ActiveEventLoop) {
434 if let AppState::Suspended = self.app_state {
435 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
436
437 self.exts.resumed();
438 self.generation = self.generation.next();
439
440 self.init(self.generation.next(), true, self.headless);
441
442 winit_loop_guard.unset(&mut self.winit_loop);
443 } else {
444 self.exts.init(&self.app_sender);
445 }
446 self.app_state = AppState::Resumed;
447
448 self.update_pull_events(winit_loop);
449 }
450
451 fn window_event(&mut self, winit_loop: &ActiveEventLoop, window_id: winit::window::WindowId, event: WindowEvent) {
452 let i = if let Some((i, _)) = self.windows.iter_mut().enumerate().find(|(_, w)| w.window_id() == window_id) {
453 i
454 } else {
455 return;
456 };
457
458 let _s = tracing::trace_span!("on_window_event", ?event).entered();
459
460 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
461
462 self.windows[i].on_window_event(&event);
463
464 let id = self.windows[i].id();
465 let scale_factor = self.windows[i].scale_factor();
466
467 #[cfg(any(
470 target_os = "linux",
471 target_os = "dragonfly",
472 target_os = "freebsd",
473 target_os = "netbsd",
474 target_os = "openbsd"
475 ))]
476 let modal_dialog_active = self.windows[i].modal_dialog_active();
477 #[cfg(any(
478 target_os = "linux",
479 target_os = "dragonfly",
480 target_os = "freebsd",
481 target_os = "netbsd",
482 target_os = "openbsd"
483 ))]
484 macro_rules! linux_modal_dialog_bail {
485 () => {
486 if modal_dialog_active {
487 winit_loop_guard.unset(&mut self.winit_loop);
488 return;
489 }
490 };
491 }
492 #[cfg(not(any(
493 target_os = "linux",
494 target_os = "dragonfly",
495 target_os = "freebsd",
496 target_os = "netbsd",
497 target_os = "openbsd"
498 )))]
499 macro_rules! linux_modal_dialog_bail {
500 () => {};
501 }
502
503 match event {
504 WindowEvent::RedrawRequested => {
505 self.windows[i].redraw();
506
507 #[cfg(any(
510 target_os = "linux",
511 target_os = "dragonfly",
512 target_os = "freebsd",
513 target_os = "netbsd",
514 target_os = "openbsd"
515 ))]
516 if let Some(handle) = self.windows[i].monitor_change() {
517 self.refresh_monitors();
518 let m_id = self.monitor_handle_to_id(&handle);
519 let mut c = WindowChanged::monitor_changed(id, m_id, EventCause::System);
520 c.scale_factor = self.windows[i].scale_factor_change();
521 c.refresh_rate = self.windows[i].refresh_rate_change();
522 self.notify(Event::WindowChanged(c));
523 }
524 }
525 WindowEvent::Resized(_) => {
526 let mut size = if let Some(s) = self.windows[i].resized() {
527 s
528 } else {
529 if let Some(s) = self.windows[i].scale_factor_change() {
531 let mut c = WindowChanged::new(id, None, None, None, None, None, None, EventCause::System);
532 c.scale_factor = Some(s);
533 if let Some(handle) = self.windows[i].monitor_change() {
534 self.refresh_monitors();
535 let m_id = self.monitor_handle_to_id(&handle);
536 c.monitor = Some(m_id);
537 c.refresh_rate = self.windows[i].refresh_rate_change();
538 }
539 self.notify(Event::WindowChanged(c));
540 }
541
542 winit_loop_guard.unset(&mut self.winit_loop);
543 return;
544 };
545
546 let deadline = Instant::now() + Duration::from_millis(300);
550
551 if self.windows[i].is_rendering_frame() {
553 tracing::debug!("resize requested while still rendering");
554
555 while let Ok(req) = self.request_recv.recv_deadline_blocking(deadline) {
557 match req {
558 RequestEvent::Request(req) => {
559 let rsp = self.respond(req);
560 if rsp.must_be_send() {
561 let _ = self.response_sender.send(rsp);
562 }
563 }
564 RequestEvent::FrameReady(id, msg) => {
565 self.on_frame_ready(id, msg);
566 if id == self.windows[i].id() {
567 break;
568 }
569 }
570 }
571 }
572
573 if let Some(s) = self.windows[i].resized() {
574 size = s;
575 }
576 }
577
578 let wait_id = Some(self.resize_frame_wait_id_gen.incr());
579 let mut c = WindowChanged::resized(id, size, EventCause::System, wait_id);
580 c.state = self.windows[i].state_change();
581 c.scale_factor = self.windows[i].scale_factor_change();
582 if let Some(handle) = self.windows[i].monitor_change() {
583 self.refresh_monitors();
584 let m_id = self.monitor_handle_to_id(&handle);
585 let mut c = WindowChanged::monitor_changed(id, m_id, EventCause::System);
586 c.refresh_rate = self.windows[i].refresh_rate_change();
587 self.notify(Event::WindowChanged(c));
588 }
589
590 if let Some(state) = self.windows[i].state_change() {
591 self.notify(Event::WindowChanged(WindowChanged::state_changed(id, state, EventCause::System)));
592 }
593
594 self.notify(Event::WindowChanged(c));
596
597 self.flush_coalesced();
598
599 let mut received_frame = false;
601 loop {
602 match self.request_recv.recv_deadline_blocking(deadline) {
603 Ok(req) => {
604 match req {
605 RequestEvent::Request(req) => {
606 received_frame = req.is_frame(id, wait_id);
607 if received_frame || req.affects_window_rect(id) {
608 let rsp = self.respond(req);
610 if rsp.must_be_send() {
611 let _ = self.response_sender.send(rsp);
612 }
613 break;
614 } else {
615 let rsp = self.respond(req);
617 if rsp.must_be_send() {
618 let _ = self.response_sender.send(rsp);
619 }
620 }
621 }
622 RequestEvent::FrameReady(id, msg) => self.on_frame_ready(id, msg),
623 }
624 }
625
626 Err(ChannelError::Timeout) => {
627 break;
629 }
630 Err(e) => {
631 winit_loop_guard.unset(&mut self.winit_loop);
632 panic!("{e}");
633 }
634 }
635 }
636
637 if received_frame && deadline > Instant::now() {
639 while let Ok(req) = self.request_recv.recv_deadline_blocking(deadline) {
641 match req {
642 RequestEvent::Request(req) => {
643 let rsp = self.respond(req);
644 if rsp.must_be_send() {
645 let _ = self.response_sender.send(rsp);
646 }
647 }
648 RequestEvent::FrameReady(id, msg) => {
649 self.on_frame_ready(id, msg);
650 if id == self.windows[i].id() {
651 break;
652 }
653 }
654 }
655 }
656 }
657 }
658 WindowEvent::Moved(_) => {
659 let (global_position, position) = if let Some(p) = self.windows[i].moved() {
660 p
661 } else {
662 winit_loop_guard.unset(&mut self.winit_loop);
663 return;
664 };
665
666 let mut c = WindowChanged::moved(id, global_position, position, EventCause::System);
667 c.state = self.windows[i].state_change();
668
669 if let Some(handle) = self.windows[i].monitor_change() {
670 self.refresh_monitors();
671 let m_id = self.monitor_handle_to_id(&handle);
672 c.monitor = Some(m_id);
673 c.scale_factor = self.windows[i].scale_factor_change();
674 c.refresh_rate = self.windows[i].refresh_rate_change();
675 }
676 self.notify(Event::WindowChanged(c));
677 }
678 WindowEvent::CloseRequested => {
679 linux_modal_dialog_bail!();
680 self.notify(Event::WindowCloseRequested(id))
681 }
682 WindowEvent::Destroyed => {
683 self.windows.remove(i);
684 self.notify(Event::WindowClosed(id));
685 }
686 WindowEvent::HoveredFile(file) => {
687 linux_modal_dialog_bail!();
688
689 if self.device_events_filter.input.is_empty() {
693 winit_loop.listen_device_events(winit::event_loop::DeviceEvents::Always);
694 }
695 self.drag_drop_hovered = Some((id, DipPoint::splat(Dip::new(-1000))));
696 self.notify(Event::DragHovered {
697 window: id,
698 data: vec![DragDropData::Paths(vec![file])],
699 allowed: DragDropEffect::all(),
700 });
701 }
702 WindowEvent::DroppedFile(file) => {
703 linux_modal_dialog_bail!();
704
705 if self.device_events_filter.input.is_empty() {
706 winit_loop.listen_device_events(winit::event_loop::DeviceEvents::Never);
707 }
708
709 let mut delay_to_next_move = true;
710
711 if let Some(position) = self.windows[i].drag_drop_cursor_pos() {
713 self.notify(Event::DragMoved {
714 window: id,
715 coalesced_pos: vec![],
716 position,
717 });
718 delay_to_next_move = false;
719 } else if let Some((_, pos)) = self.drag_drop_hovered {
720 delay_to_next_move = pos.x < Dip::new(0);
721 }
722
723 if delay_to_next_move {
724 self.drag_drop_next_move = Some((Instant::now(), file));
725 } else {
726 self.notify(Event::DragDropped {
727 window: id,
728 data: vec![DragDropData::Paths(vec![file])],
729 allowed: DragDropEffect::all(),
730 drop_id: DragDropId(0),
731 });
732 }
733 }
734 WindowEvent::HoveredFileCancelled => {
735 linux_modal_dialog_bail!();
736
737 self.drag_drop_hovered = None;
738 if self.device_events_filter.input.is_empty() {
739 winit_loop.listen_device_events(winit::event_loop::DeviceEvents::Never);
740 }
741
742 if self.drag_drop_next_move.is_none() {
743 self.notify(Event::DragCancelled { window: id });
745 }
746 }
747 WindowEvent::Focused(mut focused) => {
748 if self.windows[i].focused_changed(&mut focused) {
749 if focused {
750 self.notify(Event::FocusChanged { prev: None, new: Some(id) });
751
752 if let Some(state) = self.windows[i].state_change() {
754 self.notify(Event::WindowChanged(WindowChanged::state_changed(id, state, EventCause::System)));
755 }
756 } else {
757 self.pending_modifiers_focus_clear = true;
758 self.notify(Event::FocusChanged { prev: Some(id), new: None });
759 }
760 }
761 }
762 WindowEvent::KeyboardInput {
763 device_id,
764 event,
765 is_synthetic,
766 } => {
767 linux_modal_dialog_bail!();
768
769 if !is_synthetic && self.windows[i].is_focused() {
770 #[cfg(windows)]
772 if self.skip_ralt
773 && let winit::keyboard::PhysicalKey::Code(winit::keyboard::KeyCode::AltRight) = event.physical_key
774 {
775 winit_loop_guard.unset(&mut self.winit_loop);
776 return;
777 }
778
779 let state = util::element_state_to_key_state(event.state);
780 #[cfg(not(target_os = "android"))]
781 let key = util::winit_key_to_key(event.key_without_modifiers());
782 let key_modified = util::winit_key_to_key(event.logical_key);
783 #[cfg(target_os = "android")]
784 let key = key_modified.clone();
785 let key_code = util::winit_physical_key_to_key_code(event.physical_key);
786 let key_location = util::winit_key_location_to_zng(event.location);
787 let d_id = self.input_device_id(device_id, InputDeviceCapability::KEY);
788
789 let mut send_event = true;
790
791 if key.is_modifier() {
792 match state {
793 KeyState::Pressed => {
794 send_event = self
795 .pressed_modifiers
796 .insert((key.clone(), key_location), (d_id, key_code))
797 .is_none();
798 }
799 KeyState::Released => send_event = self.pressed_modifiers.remove(&(key.clone(), key_location)).is_some(),
800 }
801 }
802
803 if send_event {
804 self.notify(Event::KeyboardInput {
805 window: id,
806 device: d_id,
807 key_code,
808 key_location,
809 state,
810 text: match event.text {
811 Some(s) => Txt::from_str(s.as_str()),
812 #[cfg(target_os = "android")]
813 None => match (state, &key) {
814 (KeyState::Pressed, Key::Char(c)) => Txt::from(*c),
815 (KeyState::Pressed, Key::Str(s)) => s.clone(),
816 _ => Txt::default(),
817 },
818 #[cfg(not(target_os = "android"))]
819 None => Txt::default(),
820 },
821 key,
822 key_modified,
823 });
824 }
825 }
826 }
827 WindowEvent::ModifiersChanged(m) => {
828 linux_modal_dialog_bail!();
829 if self.windows[i].is_focused() {
830 self.pending_modifiers_update = Some(m.state());
831 }
832 }
833 WindowEvent::CursorMoved { device_id, position, .. } => {
834 linux_modal_dialog_bail!();
835
836 let px_p = position.to_px();
837 let p = px_p.to_dip(scale_factor);
838 let d_id = self.input_device_id(device_id, InputDeviceCapability::POINTER_MOTION);
839
840 let mut is_after_cursor_enter = false;
841 if let Some(i) = self.cursor_entered_expect_move.iter().position(|&w| w == id) {
842 self.cursor_entered_expect_move.remove(i);
843 is_after_cursor_enter = true;
844 }
845
846 if self.windows[i].cursor_moved(p, d_id) || is_after_cursor_enter {
847 self.notify(Event::MouseMoved {
848 window: id,
849 device: d_id,
850 coalesced_pos: vec![],
851 position: p,
852 });
853 }
854
855 if let Some((drop_moment, file)) = self.drag_drop_next_move.take()
856 && drop_moment.elapsed() < Duration::from_millis(300)
857 {
858 let window_id = self.windows[i].id();
859 self.notify(Event::DragMoved {
860 window: window_id,
861 coalesced_pos: vec![],
862 position: p,
863 });
864 self.notify(Event::DragDropped {
865 window: window_id,
866 data: vec![DragDropData::Paths(vec![file])],
867 allowed: DragDropEffect::all(),
868 drop_id: DragDropId(0),
869 });
870 }
871 }
872 WindowEvent::CursorEntered { device_id } => {
873 linux_modal_dialog_bail!();
874 if self.windows[i].cursor_entered() {
875 let d_id = self.input_device_id(device_id, InputDeviceCapability::POINTER_MOTION);
876 self.notify(Event::MouseEntered { window: id, device: d_id });
877 self.cursor_entered_expect_move.push(id);
878 }
879 }
880 WindowEvent::CursorLeft { device_id } => {
881 linux_modal_dialog_bail!();
882 if self.windows[i].cursor_left() {
883 let d_id = self.input_device_id(device_id, InputDeviceCapability::POINTER_MOTION);
884 self.notify(Event::MouseLeft { window: id, device: d_id });
885
886 if let Some(i) = self.cursor_entered_expect_move.iter().position(|&w| w == id) {
888 self.cursor_entered_expect_move.remove(i);
889 }
890 }
891 }
892 WindowEvent::MouseWheel {
893 device_id, delta, phase, ..
894 } => {
895 linux_modal_dialog_bail!();
896 let d_id = self.input_device_id(device_id, InputDeviceCapability::SCROLL_MOTION);
897 self.notify(Event::MouseWheel {
898 window: id,
899 device: d_id,
900 delta: util::winit_mouse_wheel_delta_to_zng(delta),
901 phase: util::winit_touch_phase_to_zng(phase),
902 });
903 }
904 WindowEvent::MouseInput {
905 device_id, state, button, ..
906 } => {
907 linux_modal_dialog_bail!();
908 let d_id = self.input_device_id(device_id, InputDeviceCapability::BUTTON);
909 self.notify(Event::MouseInput {
910 window: id,
911 device: d_id,
912 state: util::element_state_to_button_state(state),
913 button: util::winit_mouse_button_to_zng(button),
914 });
915 }
916 WindowEvent::TouchpadPressure {
917 device_id,
918 pressure,
919 stage,
920 } => {
921 linux_modal_dialog_bail!();
922 let d_id = self.input_device_id(device_id, InputDeviceCapability::empty());
923 self.notify(Event::TouchpadPressure {
924 window: id,
925 device: d_id,
926 pressure,
927 stage,
928 });
929 }
930 WindowEvent::AxisMotion { device_id, axis, value } => {
931 linux_modal_dialog_bail!();
932 let d_id = self.input_device_id(device_id, InputDeviceCapability::AXIS_MOTION);
933 self.notify(Event::AxisMotion {
934 window: id,
935 device: d_id,
936 axis: AxisId(axis),
937 value,
938 });
939 }
940 WindowEvent::Touch(t) => {
941 let d_id = self.input_device_id(t.device_id, InputDeviceCapability::empty());
942 let position = t.location.to_px().to_dip(scale_factor);
943
944 let notify = match t.phase {
945 winit::event::TouchPhase::Moved => self.windows[i].touch_moved(position, d_id, t.id),
946 winit::event::TouchPhase::Started => true,
947 winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => {
948 self.windows[i].touch_end(d_id, t.id);
949 true
950 }
951 };
952
953 if notify {
954 self.notify(Event::Touch {
955 window: id,
956 device: d_id,
957 touches: vec![TouchUpdate::new(
958 TouchId(t.id),
959 util::winit_touch_phase_to_zng(t.phase),
960 position,
961 t.force.map(util::winit_force_to_zng),
962 )],
963 });
964 }
965 }
966 WindowEvent::ScaleFactorChanged { .. } => {
967 self.refresh_monitors();
968 }
972 WindowEvent::Ime(ime) => {
973 linux_modal_dialog_bail!();
974
975 match ime {
976 winit::event::Ime::Preedit(s, c) => {
977 let caret = c.unwrap_or((s.len(), s.len()));
978 let ime = Ime::Preview(s.into(), caret);
979 self.notify(Event::Ime { window: id, ime });
980 }
981 winit::event::Ime::Commit(s) => {
982 let ime = Ime::Commit(s.into());
983 self.notify(Event::Ime { window: id, ime });
984 }
985 winit::event::Ime::Enabled => {}
986 winit::event::Ime::Disabled => {}
987 }
988 }
989 WindowEvent::ThemeChanged(_) => {}
990 WindowEvent::Occluded(_) => {}
991 WindowEvent::ActivationTokenDone { .. } => {}
992 WindowEvent::PinchGesture { .. } => {}
993 WindowEvent::RotationGesture { .. } => {}
994 WindowEvent::DoubleTapGesture { .. } => {}
995 WindowEvent::PanGesture { .. } => {}
996 }
997
998 winit_loop_guard.unset(&mut self.winit_loop);
999 }
1000
1001 fn new_events(&mut self, winit_loop: &ActiveEventLoop, cause: winit::event::StartCause) {
1002 if let winit::event::StartCause::ResumeTimeReached { .. } = cause {
1006 self.update_pull_events(winit_loop);
1007 }
1008 }
1009
1010 fn user_event(&mut self, winit_loop: &ActiveEventLoop, ev: AppEvent) {
1011 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1012 match ev {
1013 AppEvent::Request => {
1014 while let Ok(Some(req)) = self.request_recv.try_recv() {
1015 match req {
1016 RequestEvent::Request(req) => {
1017 let rsp = self.respond(req);
1018 if rsp.must_be_send() && self.response_sender.send(rsp).is_err() {
1019 self.exited = true;
1021 self.winit_loop.exit();
1022 }
1023 }
1024 RequestEvent::FrameReady(wid, msg) => {
1025 self.on_frame_ready(wid, msg);
1026 }
1027 }
1028 }
1029 }
1030 AppEvent::Notify(ev) => self.notify(ev),
1031 AppEvent::WinitFocused(window_id, focused) => self.window_event(winit_loop, window_id, WindowEvent::Focused(focused)),
1032 AppEvent::RefreshMonitors => self.refresh_monitors(),
1033 AppEvent::ParentProcessExited => {
1034 self.exited = true;
1035 self.winit_loop.exit();
1036 }
1037 AppEvent::ImageCanRender(data) => {
1038 self.image_cache.on_image_can_render(data);
1039 }
1040 AppEvent::AudioCanPlay(id, data) => {
1041 self.audio_cache.on_audio_can_play(id, data);
1042 }
1043 AppEvent::MonitorPowerChanged => {
1044 for w in &mut self.windows {
1046 w.redraw();
1047 }
1048 }
1049 AppEvent::SetDeviceEventsFilter(filter) => {
1050 self.set_device_events_filter(filter, Some(winit_loop));
1051 }
1052 }
1053 winit_loop_guard.unset(&mut self.winit_loop);
1054 }
1055
1056 fn device_event(&mut self, winit_loop: &ActiveEventLoop, device_id: winit::event::DeviceId, event: DeviceEvent) {
1057 let filter = self.device_events_filter.input;
1058
1059 if !filter.is_empty() {
1060 let _s = tracing::trace_span!("on_device_event", ?event);
1061
1062 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1063
1064 match &event {
1065 DeviceEvent::Added => {
1066 let _ = self.input_device_id(device_id, InputDeviceCapability::empty());
1067 }
1069 DeviceEvent::Removed => {
1070 if let Some(i) = self.devices.iter().position(|(_, id, _)| *id == device_id) {
1071 self.devices.remove(i);
1072 self.notify_input_devices_changed();
1073 }
1074 }
1075 DeviceEvent::MouseMotion { delta } => {
1076 let cap = InputDeviceCapability::POINTER_MOTION;
1077 if filter.contains(cap) {
1078 let d_id = self.input_device_id(device_id, cap);
1079 self.notify(Event::InputDeviceEvent {
1080 device: d_id,
1081 event: InputDeviceEvent::PointerMotion {
1082 delta: euclid::vec2(delta.0, delta.1),
1083 },
1084 });
1085 }
1086 }
1087 DeviceEvent::MouseWheel { delta } => {
1088 let cap = InputDeviceCapability::SCROLL_MOTION;
1089 if filter.contains(cap) {
1090 let d_id = self.input_device_id(device_id, cap);
1091 self.notify(Event::InputDeviceEvent {
1092 device: d_id,
1093 event: InputDeviceEvent::ScrollMotion {
1094 delta: util::winit_mouse_wheel_delta_to_zng(*delta),
1095 },
1096 });
1097 }
1098 }
1099 DeviceEvent::Motion { axis, value } => {
1100 let cap = InputDeviceCapability::AXIS_MOTION;
1101 if filter.contains(cap) {
1102 let d_id = self.input_device_id(device_id, cap);
1103 self.notify(Event::InputDeviceEvent {
1104 device: d_id,
1105 event: InputDeviceEvent::AxisMotion {
1106 axis: AxisId(*axis),
1107 value: *value,
1108 },
1109 });
1110 }
1111 }
1112 DeviceEvent::Button { button, state } => {
1113 let cap = InputDeviceCapability::BUTTON;
1114 if filter.contains(cap) {
1115 let d_id = self.input_device_id(device_id, cap);
1116 self.notify(Event::InputDeviceEvent {
1117 device: d_id,
1118 event: InputDeviceEvent::Button {
1119 button: ButtonId(*button),
1120 state: util::element_state_to_button_state(*state),
1121 },
1122 });
1123 }
1124 }
1125 DeviceEvent::Key(k) => {
1126 let cap = InputDeviceCapability::KEY;
1127 if filter.contains(cap) {
1128 let d_id = self.input_device_id(device_id, cap);
1129 self.notify(Event::InputDeviceEvent {
1130 device: d_id,
1131 event: InputDeviceEvent::Key {
1132 key_code: util::winit_physical_key_to_key_code(k.physical_key),
1133 state: util::element_state_to_key_state(k.state),
1134 },
1135 });
1136 }
1137 }
1138 }
1139
1140 winit_loop_guard.unset(&mut self.winit_loop);
1141 }
1142
1143 if let Some((id, pos)) = &mut self.drag_drop_hovered
1144 && let DeviceEvent::MouseMotion { .. } = &event
1145 && let Some(win) = self.windows.iter().find(|w| w.id() == *id)
1146 && let Some(new_pos) = win.drag_drop_cursor_pos()
1147 && *pos != new_pos
1148 {
1149 *pos = new_pos;
1150 let event = Event::DragMoved {
1151 window: *id,
1152 coalesced_pos: vec![],
1153 position: *pos,
1154 };
1155 self.notify(event);
1156 }
1157 }
1158
1159 fn about_to_wait(&mut self, winit_loop: &ActiveEventLoop) {
1160 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1161
1162 self.finish_cursor_entered_move();
1163 self.update_modifiers();
1164 self.flush_coalesced();
1165 #[cfg(windows)]
1166 {
1167 self.skip_ralt = false;
1168 }
1169
1170 winit_loop_guard.unset(&mut self.winit_loop);
1171 }
1172
1173 fn suspended(&mut self, _: &ActiveEventLoop) {
1174 #[cfg(target_os = "android")]
1175 if let Some(w) = &self.windows.first() {
1176 self.notify(Event::FocusChanged {
1177 prev: Some(w.id()),
1178 new: None,
1179 });
1180 }
1181
1182 self.app_state = AppState::Suspended;
1183 self.windows.clear();
1184 self.surfaces.clear();
1185 self.image_cache.clear();
1186 self.exts.suspended();
1187
1188 self.notify(Event::Suspended);
1189 }
1190
1191 fn exiting(&mut self, event_loop: &ActiveEventLoop) {
1192 let _ = event_loop;
1193 if let Some(t) = self.config_listener_exit.take() {
1194 t();
1195 }
1196 }
1197
1198 fn memory_warning(&mut self, winit_loop: &ActiveEventLoop) {
1199 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1200
1201 self.image_cache.on_low_memory();
1202 self.audio_cache.on_low_memory();
1203 for w in &mut self.windows {
1204 w.on_low_memory();
1205 }
1206 for s in &mut self.surfaces {
1207 s.on_low_memory();
1208 }
1209 self.exts.on_low_memory();
1210 self.notify(Event::LowMemory);
1211
1212 winit_loop_guard.unset(&mut self.winit_loop);
1213 }
1214}
1215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1216enum AppState {
1217 PreInitSuspended,
1218 Resumed,
1219 Suspended,
1220}
1221impl App {
1222 fn set_device_events_filter(&mut self, filter: DeviceEventsFilter, t: Option<&ActiveEventLoop>) {
1223 self.device_events_filter = filter;
1224
1225 if let Some(t) = t {
1226 if !self.device_events_filter.input.is_empty() {
1227 t.listen_device_events(winit::event_loop::DeviceEvents::Always);
1228 } else {
1229 t.listen_device_events(winit::event_loop::DeviceEvents::Never);
1230 }
1231 }
1232 }
1233
1234 pub fn run_headless(ipc: ipc::ViewChannels, ext: ViewExtensions) {
1235 tracing::info!("running headless view-process");
1236
1237 let (app_sender, app_receiver) = channel::unbounded();
1238 let (request_sender, request_receiver) = channel::unbounded();
1239 let mut app = App::new(
1240 AppEventSender::Headless(app_sender, request_sender),
1241 ipc.response_sender,
1242 ipc.event_sender,
1243 request_receiver,
1244 ext,
1245 );
1246 app.headless = true;
1247
1248 let winit_span = tracing::trace_span!("winit::EventLoop::new").entered();
1249 #[cfg(not(target_os = "android"))]
1250 let event_loop = EventLoop::builder().build().unwrap();
1251 #[cfg(target_os = "android")]
1252 let event_loop = EventLoop::builder()
1253 .with_android_app(platform::android::android_app())
1254 .build()
1255 .unwrap();
1256 drop(winit_span);
1257
1258 let mut app = HeadlessApp {
1259 app,
1260 request_receiver: Some(ipc.request_receiver),
1261 app_receiver,
1262 };
1263 if let Err(e) = event_loop.run_app(&mut app) {
1264 if app.app.exited {
1265 tracing::error!("winit event loop error after app exit, {e}");
1271 } else {
1272 panic!("winit event loop error, {e}");
1273 }
1274 }
1275
1276 struct HeadlessApp {
1277 app: App,
1278 request_receiver: Option<ipc::RequestReceiver>,
1279 app_receiver: Receiver<AppEvent>,
1280 }
1281 impl winit::application::ApplicationHandler<()> for HeadlessApp {
1282 fn resumed(&mut self, winit_loop: &ActiveEventLoop) {
1283 let mut winit_loop_guard = self.app.winit_loop.set(winit_loop);
1284
1285 self.app.resumed(winit_loop);
1286 self.app.start_receiving(self.request_receiver.take().unwrap());
1287
1288 'app_loop: while !self.app.exited {
1289 match self.app_receiver.recv_blocking() {
1290 Ok(app_ev) => match app_ev {
1291 AppEvent::Request => {
1292 while let Ok(Some(request)) = self.app.request_recv.try_recv() {
1293 match request {
1294 RequestEvent::Request(request) => {
1295 let response = self.app.respond(request);
1296 if response.must_be_send() && self.app.response_sender.send(response).is_err() {
1297 self.app.exited = true;
1298 break 'app_loop;
1299 }
1300 }
1301 RequestEvent::FrameReady(id, msg) => {
1302 let r = if let Some(s) = self.app.surfaces.iter_mut().find(|s| s.id() == id) {
1303 Some(s.on_frame_ready(msg, &mut self.app.image_cache))
1304 } else {
1305 None
1306 };
1307 if let Some((frame_id, image)) = r {
1308 self.app.notify(Event::FrameRendered(EventFrameRendered::new(id, frame_id, image)));
1309 }
1310 }
1311 }
1312 }
1313 }
1314 AppEvent::Notify(ev) => {
1315 if self.app.event_sender.send(ev).is_err() {
1316 self.app.exited = true;
1317 break 'app_loop;
1318 }
1319 }
1320 AppEvent::RefreshMonitors => {
1321 panic!("no monitor info in headless mode")
1322 }
1323 AppEvent::WinitFocused(_, _) => {
1324 panic!("no winit event loop in headless mode")
1325 }
1326 AppEvent::ParentProcessExited => {
1327 self.app.exited = true;
1328 break 'app_loop;
1329 }
1330 AppEvent::ImageCanRender(data) => {
1331 self.app.image_cache.on_image_can_render(data);
1332 }
1333 AppEvent::AudioCanPlay(meta, data) => {
1334 self.app.audio_cache.on_audio_can_play(meta, data);
1335 }
1336 AppEvent::MonitorPowerChanged => {} AppEvent::SetDeviceEventsFilter(filter) => {
1338 self.app.set_device_events_filter(filter, None);
1339 }
1340 },
1341 Err(_) => {
1342 self.app.exited = true;
1343 break 'app_loop;
1344 }
1345 }
1346 }
1347
1348 self.app.winit_loop.exit();
1349
1350 winit_loop_guard.unset(&mut self.app.winit_loop);
1351 }
1352
1353 fn window_event(&mut self, _: &ActiveEventLoop, _: winit::window::WindowId, _: WindowEvent) {}
1354
1355 fn suspended(&mut self, event_loop: &ActiveEventLoop) {
1356 self.app.suspended(event_loop);
1357 }
1358 }
1359 }
1360
1361 pub fn run_headed(ipc: ipc::ViewChannels, ext: ViewExtensions) {
1362 tracing::info!("running headed view-process");
1363
1364 #[cfg(windows)]
1365 {
1366 let aumid = zng_env::about().windows_aumid();
1368 if !aumid.is_empty() {
1369 let r = unsafe {
1370 windows::Win32::UI::Shell::SetCurrentProcessExplicitAppUserModelID(&windows::core::HSTRING::from(aumid.as_str()))
1371 };
1372 if let Err(e) = r {
1373 tracing::error!("cannot set AUMID, {e}");
1374 }
1375 }
1376 }
1377
1378 let winit_span = tracing::trace_span!("winit::EventLoop::new").entered();
1379 #[cfg(not(target_os = "android"))]
1380 let event_loop = EventLoop::with_user_event().build().unwrap();
1381 #[cfg(target_os = "android")]
1382 let event_loop = EventLoop::with_user_event()
1383 .with_android_app(platform::android::android_app())
1384 .build()
1385 .unwrap();
1386 drop(winit_span);
1387 let app_sender = event_loop.create_proxy();
1388
1389 let (request_sender, request_receiver) = channel::unbounded();
1390 let mut app = App::new(
1391 AppEventSender::Headed(app_sender, request_sender),
1392 ipc.response_sender,
1393 ipc.event_sender,
1394 request_receiver,
1395 ext,
1396 );
1397 app.start_receiving(ipc.request_receiver);
1398
1399 app.config_listener_exit = config::spawn_listener(app.app_sender.clone());
1400
1401 if let Err(e) = event_loop.run_app(&mut app) {
1402 if app.exited {
1403 tracing::error!("winit event loop error after app exit, {e}");
1404 } else {
1405 panic!("winit event loop error, {e}");
1406 }
1407 }
1408 }
1409
1410 fn new(
1411 app_sender: AppEventSender,
1412 response_sender: ipc::ResponseSender,
1413 event_sender: ipc::EventSender,
1414 request_recv: channel::Receiver<RequestEvent>,
1415 mut exts: ViewExtensions,
1416 ) -> Self {
1417 exts.renderer("zng-view.webrender_debug", extensions::RendererDebugExt::new);
1418 #[cfg(windows)]
1419 {
1420 exts.window("zng-view.prefer_angle", extensions::PreferAngleExt::new);
1421 }
1422 #[cfg(feature = "image_cur")]
1423 exts.data("image_cur");
1424 #[cfg(feature = "image_meta_exif")]
1425 exts.data("image_meta_exif");
1426 #[cfg(feature = "image_meta_icc")]
1427 exts.data("image_meta_icc");
1428 App {
1429 headless: false,
1430 image_cache: ImageCache::new(
1431 app_sender.clone(),
1432 #[cfg(feature = "image_cur")]
1433 exts.id(&api_extension::ApiExtensionName::new("image_cur").unwrap()).unwrap(),
1434 #[cfg(feature = "image_meta_exif")]
1435 exts.id(&api_extension::ApiExtensionName::new("image_meta_exif").unwrap()).unwrap(),
1436 #[cfg(feature = "image_meta_icc")]
1437 exts.id(&api_extension::ApiExtensionName::new("image_meta_icc").unwrap()).unwrap(),
1438 ),
1439 exts,
1440 gl_manager: GlContextManager::default(),
1441 audio_cache: AudioCache::new(app_sender.clone()),
1442 app_sender,
1443 request_recv,
1444 response_sender,
1445 event_sender,
1446 winit_loop: util::WinitEventLoop::default(),
1447 generation: ViewProcessGen::INVALID,
1448 device_events_filter: DeviceEventsFilter::empty(),
1449 windows: vec![],
1450 surfaces: vec![],
1451 monitors: vec![],
1452 monitor_ids: vec![],
1453 monitor_id_gen: MonitorId::INVALID,
1454 devices: vec![],
1455 device_id_gen: InputDeviceId::INVALID,
1456 dialog_id_gen: DialogId::INVALID,
1457 resize_frame_wait_id_gen: FrameWaitId::INVALID,
1458 coalescing_event: None,
1459 cursor_entered_expect_move: Vec::with_capacity(1),
1460 app_state: AppState::PreInitSuspended,
1461 exited: false,
1462 #[cfg(windows)]
1463 skip_ralt: false,
1464 pressed_modifiers: FxHashMap::default(),
1465 pending_modifiers_update: None,
1466 pending_modifiers_focus_clear: false,
1467 config_listener_exit: None,
1468 drag_drop_hovered: None,
1469 drag_drop_next_move: None,
1470 #[cfg(not(any(windows, target_os = "android")))]
1471 arboard: None,
1472 notifications: NotificationService::default(),
1473 low_memory_watcher: low_memory::LowMemoryWatcher::new(),
1474 last_pull_event: Instant::now(),
1475 }
1476 }
1477
1478 fn start_receiving(&mut self, mut request_recv: ipc::RequestReceiver) {
1479 let app_sender = self.app_sender.clone();
1480 thread::Builder::new()
1481 .name("request-recv".into())
1482 .stack_size(256 * 1024)
1483 .spawn(move || {
1484 while let Ok(r) = request_recv.recv() {
1485 if app_sender.request(r).is_err() {
1486 break;
1487 }
1488 }
1489 let _ = app_sender.send(AppEvent::ParentProcessExited);
1490 })
1491 .expect("failed to spawn thread");
1492 }
1493
1494 fn monitor_handle_to_id(&mut self, handle: &MonitorHandle) -> MonitorId {
1495 if let Some((id, _)) = self.monitor_ids.iter().find(|(_, h)| h == handle) {
1496 *id
1497 } else {
1498 self.refresh_monitors();
1499 if let Some((id, _)) = self.monitor_ids.iter().find(|(_, h)| h == handle) {
1500 *id
1501 } else {
1502 MonitorId::INVALID
1503 }
1504 }
1505 }
1506
1507 fn update_modifiers(&mut self) {
1508 if mem::take(&mut self.pending_modifiers_focus_clear) && self.windows.iter().all(|w| !w.is_focused()) {
1515 self.pressed_modifiers.clear();
1516 }
1517
1518 if let Some(m) = self.pending_modifiers_update.take()
1519 && let Some(id) = self.windows.iter().find(|w| w.is_focused()).map(|w| w.id())
1520 {
1521 let mut notify = vec![];
1522 self.pressed_modifiers.retain(|(key, location), (d_id, s_code)| {
1523 let mut retain = true;
1524 if matches!(key, Key::Super) && !m.super_key() {
1525 retain = false;
1526 notify.push(Event::KeyboardInput {
1527 window: id,
1528 device: *d_id,
1529 key_code: *s_code,
1530 state: KeyState::Released,
1531 key: key.clone(),
1532 key_location: *location,
1533 key_modified: key.clone(),
1534 text: Txt::from_str(""),
1535 });
1536 }
1537 if matches!(key, Key::Shift) && !m.shift_key() {
1538 retain = false;
1539 notify.push(Event::KeyboardInput {
1540 window: id,
1541 device: *d_id,
1542 key_code: *s_code,
1543 state: KeyState::Released,
1544 key: key.clone(),
1545 key_location: *location,
1546 key_modified: key.clone(),
1547 text: Txt::from_str(""),
1548 });
1549 }
1550 if matches!(key, Key::Alt | Key::AltGraph) && !m.alt_key() {
1551 retain = false;
1552 notify.push(Event::KeyboardInput {
1553 window: id,
1554 device: *d_id,
1555 key_code: *s_code,
1556 state: KeyState::Released,
1557 key: key.clone(),
1558 key_location: *location,
1559 key_modified: key.clone(),
1560 text: Txt::from_str(""),
1561 });
1562 }
1563 if matches!(key, Key::Ctrl) && !m.control_key() {
1564 retain = false;
1565 notify.push(Event::KeyboardInput {
1566 window: id,
1567 device: *d_id,
1568 key_code: *s_code,
1569 state: KeyState::Released,
1570 key: key.clone(),
1571 key_location: *location,
1572 key_modified: key.clone(),
1573 text: Txt::from_str(""),
1574 });
1575 }
1576 retain
1577 });
1578
1579 for ev in notify {
1580 self.notify(ev);
1581 }
1582 }
1583 }
1584
1585 fn refresh_monitors(&mut self) {
1586 let monitors = self.available_monitors();
1587 if self.monitors != monitors {
1588 self.monitors = monitors.clone();
1589 self.notify(Event::MonitorsChanged(monitors));
1590 }
1591 }
1592
1593 fn on_frame_ready(&mut self, window_id: WindowId, msg: FrameReadyMsg) {
1594 let _s = tracing::trace_span!("on_frame_ready").entered();
1595
1596 if let Some(w) = self.windows.iter_mut().find(|w| w.id() == window_id) {
1597 let r = w.on_frame_ready(msg, &mut self.image_cache);
1598
1599 let _ = self
1600 .event_sender
1601 .send(Event::FrameRendered(EventFrameRendered::new(window_id, r.frame_id, r.image)));
1602
1603 if r.first_frame {
1604 let size = w.size();
1605 self.notify(Event::WindowChanged(WindowChanged::resized(window_id, size, EventCause::App, None)));
1606 }
1607 } else if let Some(s) = self.surfaces.iter_mut().find(|w| w.id() == window_id) {
1608 let (frame_id, image) = s.on_frame_ready(msg, &mut self.image_cache);
1609
1610 self.notify(Event::FrameRendered(EventFrameRendered::new(window_id, frame_id, image)))
1611 }
1612 }
1613
1614 pub(crate) fn notify(&mut self, event: Event) {
1615 let now = Instant::now();
1616 if let Some((mut coal, timestamp)) = self.coalescing_event.take() {
1617 let r = if now.saturating_duration_since(timestamp) >= Duration::from_millis(16) {
1618 Err(event)
1619 } else {
1620 coal.coalesce(event)
1621 };
1622 match r {
1623 Ok(()) => self.coalescing_event = Some((coal, timestamp)),
1624 Err(event) => match (&mut coal, event) {
1625 (
1626 Event::KeyboardInput {
1627 window,
1628 device,
1629 state,
1630 text,
1631 ..
1632 },
1633 Event::KeyboardInput {
1634 window: n_window,
1635 device: n_device,
1636 text: n_text,
1637 ..
1638 },
1639 ) if !n_text.is_empty() && *window == n_window && *device == n_device && *state == KeyState::Pressed => {
1640 if text.is_empty() {
1642 *text = n_text;
1643 } else {
1644 text.push_str(&n_text);
1645 };
1646 self.coalescing_event = Some((coal, now));
1647 }
1648 (_, event) => {
1649 let mut error = self.event_sender.send(coal).is_err();
1650 error |= self.event_sender.send(event).is_err();
1651
1652 if error {
1653 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1654 }
1655 }
1656 },
1657 }
1658 } else {
1659 self.coalescing_event = Some((event, now));
1660 }
1661
1662 if self.headless {
1663 self.flush_coalesced();
1664 }
1665 }
1666
1667 pub(crate) fn finish_cursor_entered_move(&mut self) {
1668 let mut moves = vec![];
1669 for window_id in self.cursor_entered_expect_move.drain(..) {
1670 if let Some(w) = self.windows.iter().find(|w| w.id() == window_id) {
1671 let (position, device) = w.last_cursor_pos();
1672 moves.push(Event::MouseMoved {
1673 window: w.id(),
1674 device,
1675 coalesced_pos: vec![],
1676 position,
1677 });
1678 }
1679 }
1680 for ev in moves {
1681 self.notify(ev);
1682 }
1683 }
1684
1685 pub(crate) fn flush_coalesced(&mut self) {
1687 if let Some((coal, _)) = self.coalescing_event.take()
1688 && self.event_sender.send(coal).is_err()
1689 {
1690 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1691 }
1692 }
1693
1694 #[track_caller]
1695 fn assert_resumed(&self) {
1696 assert_eq!(self.app_state, AppState::Resumed);
1697 }
1698
1699 fn with_window<R>(&mut self, id: WindowId, action: impl FnOnce(&mut Window) -> R, not_found: impl FnOnce() -> R) -> R {
1700 self.assert_resumed();
1701 self.windows.iter_mut().find(|w| w.id() == id).map(action).unwrap_or_else(|| {
1702 tracing::error!("headed window `{id:?}` not found, will return fallback result");
1703 not_found()
1704 })
1705 }
1706
1707 fn monitor_id(&mut self, handle: &MonitorHandle) -> MonitorId {
1708 if let Some((id, _)) = self.monitor_ids.iter().find(|(_, h)| h == handle) {
1709 *id
1710 } else {
1711 let id = self.monitor_id_gen.incr();
1712 self.monitor_ids.push((id, handle.clone()));
1713 id
1714 }
1715 }
1716
1717 fn notify_input_devices_changed(&mut self) {
1718 let devices = self.devices.iter().map(|(id, _, info)| (*id, info.clone())).collect();
1719 self.notify(Event::InputDevicesChanged(devices));
1720 }
1721
1722 fn input_device_id(&mut self, device_id: winit::event::DeviceId, capability: InputDeviceCapability) -> InputDeviceId {
1724 if let Some((id, _, info)) = self.devices.iter_mut().find(|(_, id, _)| *id == device_id) {
1725 let id = *id;
1726 if !self.device_events_filter.input.is_empty() && !capability.is_empty() && !info.capabilities.contains(capability) {
1727 info.capabilities |= capability;
1728 self.notify_input_devices_changed();
1729 }
1730 id
1731 } else {
1732 let id = self.device_id_gen.incr();
1733
1734 #[cfg(not(windows))]
1735 let info = InputDeviceInfo::new("Winit Device", InputDeviceCapability::empty());
1736 #[cfg(windows)]
1737 let info = {
1738 use winit::platform::windows::DeviceIdExtWindows as _;
1739 if !self.device_events_filter.input.is_empty()
1740 && let Some(device_path) = device_id.persistent_identifier()
1741 {
1742 input_device_info::get(&device_path)
1743 } else {
1744 InputDeviceInfo::new("Winit Device", InputDeviceCapability::empty())
1745 }
1746 };
1747
1748 self.devices.push((id, device_id, info));
1749
1750 if !self.device_events_filter.input.is_empty() {
1751 self.notify_input_devices_changed();
1752 }
1753
1754 id
1755 }
1756 }
1757
1758 fn available_monitors(&mut self) -> Vec<(MonitorId, MonitorInfo)> {
1759 let _span = tracing::trace_span!("available_monitors").entered();
1760
1761 let primary = self.winit_loop.primary_monitor();
1762 let mut available: Vec<_> = self
1763 .winit_loop
1764 .available_monitors()
1765 .map(|m| (self.monitor_id(&m), primary.as_ref().map(|h| h == &m).unwrap_or(false), m))
1766 .collect();
1767 available.sort_by(|(_, a_is_primary, a), (_, b_is_primary, b)| {
1770 b_is_primary.cmp(a_is_primary).then_with(|| a.position().x.cmp(&b.position().x))
1771 });
1772 available
1773 .into_iter()
1774 .enumerate()
1775 .map(|(n, (id, is_primary, m))| (id, util::monitor_handle_to_info(&m, is_primary, n + 1)))
1776 .collect()
1777 }
1778
1779 fn update_pull_events(&mut self, _winit_loop: &ActiveEventLoop) {
1780 const INTERVAL: Duration = Duration::from_secs(5);
1781 let any_event_source = self.low_memory_watcher.is_some();
1782 if !any_event_source {
1783 _winit_loop.set_control_flow(winit::event_loop::ControlFlow::Wait);
1784 return;
1785 }
1786
1787 let now = Instant::now();
1788 if now.duration_since(self.last_pull_event) >= INTERVAL {
1789 if let Some(w) = &mut self.low_memory_watcher
1792 && w.notify()
1793 {
1794 use winit::application::ApplicationHandler as _;
1795 self.memory_warning(_winit_loop);
1796 }
1797 }
1798
1799 _winit_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(now + INTERVAL));
1800 }
1801}
1802macro_rules! with_window_or_surface {
1803 ($self:ident, $id:ident, |$el:ident|$action:expr, ||$fallback:expr) => {
1804 if let Some($el) = $self.windows.iter_mut().find(|w| w.id() == $id) {
1805 $action
1806 } else if let Some($el) = $self.surfaces.iter_mut().find(|w| w.id() == $id) {
1807 $action
1808 } else {
1809 tracing::error!("window `{:?}` not found, will return fallback result", $id);
1810 $fallback
1811 }
1812 };
1813}
1814impl Drop for App {
1815 fn drop(&mut self) {
1816 if let Some(f) = self.config_listener_exit.take() {
1817 f();
1818 }
1819 }
1820}
1821impl App {
1822 fn open_headless_impl(&mut self, config: HeadlessRequest) -> HeadlessOpenData {
1823 self.assert_resumed();
1824 let surf = Surface::open(
1825 self.generation,
1826 config,
1827 &self.winit_loop,
1828 &mut self.gl_manager,
1829 self.exts.new_window(),
1830 self.exts.new_renderer(),
1831 self.app_sender.clone(),
1832 );
1833 let render_mode = surf.render_mode();
1834
1835 self.surfaces.push(surf);
1836
1837 HeadlessOpenData::new(render_mode)
1838 }
1839
1840 #[cfg(not(any(windows, target_os = "android")))]
1841 fn arboard(&mut self) -> Result<&mut arboard::Clipboard, clipboard::ClipboardError> {
1842 if self.arboard.is_none() {
1843 match arboard::Clipboard::new() {
1844 Ok(c) => self.arboard = Some(c),
1845 Err(e) => return Err(util::arboard_to_clip(e)),
1846 }
1847 }
1848 Ok(self.arboard.as_mut().unwrap())
1849 }
1850}
1851
1852impl Api for App {
1853 fn init(&mut self, vp_gen: ViewProcessGen, is_respawn: bool, headless: bool) {
1854 if self.exited {
1855 panic!("cannot restart exited");
1856 }
1857
1858 self.generation = vp_gen;
1859 self.headless = headless;
1860
1861 let mut info = ViewProcessInfo::new(vp_gen, is_respawn);
1862 if !headless {
1863 info.input_device |= InputDeviceCapability::KEY;
1865 info.input_device |= InputDeviceCapability::BUTTON;
1866 info.input_device |= InputDeviceCapability::SCROLL_MOTION;
1867 info.input_device |= InputDeviceCapability::AXIS_MOTION;
1868 info.input_device |= InputDeviceCapability::POINTER_MOTION;
1869 }
1870 info.image = crate::image_cache::FORMATS.to_vec();
1871 info.audio = crate::audio_cache::FORMATS.to_vec();
1872 info.extensions = self.exts.api_extensions();
1873
1874 use zng_view_api::window::WindowCapability;
1875 if !headless && !cfg!(target_os = "android") {
1876 info.window |= WindowCapability::SET_TITLE;
1877 info.window |= WindowCapability::SET_VISIBLE;
1878 info.window |= WindowCapability::SET_ALWAYS_ON_TOP;
1879 info.window |= WindowCapability::SET_RESIZABLE;
1880 info.window |= WindowCapability::BRING_TO_TOP;
1881 info.window |= WindowCapability::SET_CURSOR;
1882 info.window |= WindowCapability::SET_CURSOR_IMAGE;
1883 info.window |= WindowCapability::SET_FOCUS_INDICATOR;
1884 info.window |= WindowCapability::FOCUS;
1885 info.window |= WindowCapability::DRAG_MOVE;
1886 info.window |= WindowCapability::MINIMIZE;
1887 info.window |= WindowCapability::MAXIMIZE;
1888 info.window |= WindowCapability::FULLSCREEN;
1889 info.window |= WindowCapability::SET_SIZE;
1890
1891 if cfg!(windows) || std::env::var("WAYLAND_DISPLAY").is_err() {
1892 info.window |= WindowCapability::SYSTEM_CHROME;
1894 info.window |= WindowCapability::SET_CHROME;
1895 }
1896 }
1897 if !headless & cfg!(windows) {
1898 info.window |= WindowCapability::SET_ICON;
1899 info.window |= WindowCapability::SET_TASKBAR_VISIBLE;
1900 info.window |= WindowCapability::OPEN_TITLE_BAR_CONTEXT_MENU;
1901 info.window |= WindowCapability::SET_SYSTEM_SHUTDOWN_WARN;
1902 }
1903 if !headless && !cfg!(target_os = "android") && !cfg!(target_os = "macos") {
1904 info.window |= WindowCapability::DRAG_RESIZE;
1905 }
1906 if !headless && !cfg!(target_os = "android") && (!cfg!(unix) || std::env::var("WAYLAND_DISPLAY").is_err()) {
1908 info.window |= WindowCapability::RESTORE;
1910 info.window |= WindowCapability::EXCLUSIVE;
1912 info.window |= WindowCapability::SET_POSITION;
1913 }
1914 if !headless & (cfg!(windows) || cfg!(target_os = "macos")) {
1915 info.window |= WindowCapability::SET_CAN_CLOSE;
1917 info.window |= WindowCapability::SET_CAN_MINIMIZE;
1918
1919 if cfg!(target_os = "macos") {
1920 info.window |= WindowCapability::SET_CAN_FULLSCREEN;
1921 }
1922 if cfg!(target_os = "windows") {
1923 info.window |= WindowCapability::SET_CAN_MAXIMIZE;
1924 }
1925 }
1926 info.window |= WindowCapability::SET_IME_AREA;
1927
1928 use zng_view_api::dialog::DialogCapability;
1929 if !headless && !cfg!(target_os = "android") {
1930 info.dialog |= DialogCapability::MESSAGE;
1932 info.dialog |= DialogCapability::OPEN_FILE;
1933 info.dialog |= DialogCapability::OPEN_FILES;
1934 info.dialog |= DialogCapability::SAVE_FILE;
1935 info.dialog |= DialogCapability::SELECT_FOLDER;
1936 info.dialog |= DialogCapability::SELECT_FOLDERS;
1937 }
1938 info.dialog |= self.notifications.capabilities();
1939
1940 use zng_view_api::clipboard::ClipboardType;
1941 if !cfg!(target_os = "android") {
1942 info.clipboard.read.push(ClipboardType::Text);
1943 info.clipboard.read.push(ClipboardType::Image);
1944 info.clipboard.read.push(ClipboardType::Paths);
1945
1946 info.clipboard.write.push(ClipboardType::Text);
1947 info.clipboard.write.push(ClipboardType::Image);
1948 if cfg!(windows) {
1949 info.clipboard.write.push(ClipboardType::Paths);
1950 }
1951 }
1952
1953 self.notify(Event::Inited(info));
1954
1955 let available_monitors = self.available_monitors();
1956 self.notify(Event::MonitorsChanged(available_monitors));
1957
1958 let cfg = config::multi_click_config();
1959 if is_respawn || cfg != zng_view_api::config::MultiClickConfig::default() {
1960 self.notify(Event::MultiClickConfigChanged(cfg));
1961 }
1962
1963 let cfg = config::key_repeat_config();
1964 if is_respawn || cfg != zng_view_api::config::KeyRepeatConfig::default() {
1965 self.notify(Event::KeyRepeatConfigChanged(cfg));
1966 }
1967
1968 let cfg = config::touch_config();
1969 if is_respawn || cfg != zng_view_api::config::TouchConfig::default() {
1970 self.notify(Event::TouchConfigChanged(cfg));
1971 }
1972
1973 let cfg = config::font_aa();
1974 if is_respawn || cfg != zng_view_api::config::FontAntiAliasing::default() {
1975 self.notify(Event::FontAaChanged(cfg));
1976 }
1977
1978 let cfg = config::animations_config();
1979 if is_respawn || cfg != zng_view_api::config::AnimationsConfig::default() {
1980 self.notify(Event::AnimationsConfigChanged(cfg));
1981 }
1982
1983 let cfg = config::locale_config();
1984 if is_respawn || cfg != zng_view_api::config::LocaleConfig::default() {
1985 self.notify(Event::LocaleChanged(cfg));
1986 }
1987
1988 let cfg = config::colors_config();
1989 if is_respawn || cfg != zng_view_api::config::ColorsConfig::default() {
1990 self.notify(Event::ColorsConfigChanged(cfg));
1991 }
1992 }
1993
1994 fn exit(&mut self) {
1995 self.assert_resumed();
1996 self.exited = true;
1997 if let Some(t) = self.config_listener_exit.take() {
1998 t();
1999 }
2000 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
2002 }
2003
2004 fn set_device_events_filter(&mut self, filter: DeviceEventsFilter) {
2005 let _ = self.app_sender.send(AppEvent::SetDeviceEventsFilter(filter));
2006 }
2007
2008 fn open_window(&mut self, mut config: WindowRequest) {
2009 let _s = tracing::debug_span!("open_window", ?config).entered();
2010
2011 config.state.clamp_size();
2012 config.enforce_kiosk();
2013
2014 if self.headless {
2015 let id = config.id;
2016 let data = self.open_headless_impl(HeadlessRequest::new(
2017 config.id,
2018 Factor(1.0),
2019 config.state.restore_rect.size,
2020 config.render_mode,
2021 config.extensions,
2022 ));
2023 let msg = WindowOpenData::new(
2024 WindowStateAll::new(
2025 WindowState::Fullscreen,
2026 PxPoint::zero(),
2027 DipRect::from_size(config.state.restore_rect.size),
2028 WindowState::Fullscreen,
2029 None,
2030 DipSize::zero(),
2031 DipSize::new(Dip::MAX, Dip::MAX),
2032 false,
2033 ),
2034 None,
2035 (PxPoint::zero(), DipPoint::zero()),
2036 config.state.restore_rect.size,
2037 Factor(1.0),
2038 data.render_mode,
2039 DipSideOffsets::zero(),
2040 );
2041
2042 self.notify(Event::WindowOpened(id, msg));
2043 } else {
2044 self.assert_resumed();
2045
2046 #[cfg(target_os = "android")]
2047 if !self.windows.is_empty() {
2048 tracing::error!("android can only have one window");
2049 return;
2050 }
2051
2052 let id = config.id;
2053 let win = Window::open(
2054 self.generation,
2055 config
2056 .icon
2057 .and_then(|i| self.image_cache.get(i))
2058 .and_then(|i| i.icon(self.image_cache.resizer())),
2059 config
2060 .cursor_image
2061 .and_then(|(i, h)| self.image_cache.get(i).and_then(|i| i.cursor(h, &self.winit_loop))),
2062 config,
2063 &self.winit_loop,
2064 &mut self.gl_manager,
2065 self.exts.new_window(),
2066 self.exts.new_renderer(),
2067 self.app_sender.clone(),
2068 );
2069
2070 let mut msg = WindowOpenData::new(
2071 win.state(),
2072 win.monitor().map(|h| self.monitor_id(&h)),
2073 win.inner_position(),
2074 win.size(),
2075 win.scale_factor(),
2076 win.render_mode(),
2077 win.safe_padding(),
2078 );
2079 msg.refresh_rate = win.refresh_rate();
2080
2081 self.windows.push(win);
2082
2083 self.notify(Event::WindowOpened(id, msg));
2084
2085 #[cfg(target_os = "android")]
2087 {
2088 self.windows.last_mut().unwrap().focused_changed(&mut true);
2089 self.notify(Event::FocusChanged { prev: None, new: Some(id) });
2090 }
2091 }
2092 }
2093
2094 fn open_headless(&mut self, config: HeadlessRequest) {
2095 let _s = tracing::debug_span!("open_headless", ?config).entered();
2096
2097 let id = config.id;
2098 let msg = self.open_headless_impl(config);
2099
2100 self.notify(Event::HeadlessOpened(id, msg));
2101 }
2102
2103 fn close(&mut self, id: WindowId) {
2104 self.assert_resumed();
2105 if let Some(i) = self.windows.iter().position(|w| w.id() == id) {
2106 let _ = self.windows.swap_remove(i);
2107 }
2108 if let Some(i) = self.surfaces.iter().position(|w| w.id() == id) {
2109 let _ = self.surfaces.swap_remove(i);
2110 }
2111 }
2112
2113 fn set_title(&mut self, id: WindowId, title: Txt) {
2114 self.with_window(id, |w| w.set_title(title), || ())
2115 }
2116
2117 fn set_visible(&mut self, id: WindowId, visible: bool) {
2118 self.with_window(id, |w| w.set_visible(visible), || ())
2119 }
2120
2121 fn set_always_on_top(&mut self, id: WindowId, always_on_top: bool) {
2122 self.with_window(id, |w| w.set_always_on_top(always_on_top), || ())
2123 }
2124
2125 fn set_movable(&mut self, id: WindowId, movable: bool) {
2126 self.with_window(id, |w| w.set_movable(movable), || ())
2127 }
2128
2129 fn set_resizable(&mut self, id: WindowId, resizable: bool) {
2130 self.with_window(id, |w| w.set_resizable(resizable), || ())
2131 }
2132
2133 fn set_taskbar_visible(&mut self, id: WindowId, visible: bool) {
2134 self.with_window(id, |w| w.set_taskbar_visible(visible), || ())
2135 }
2136
2137 fn bring_to_top(&mut self, id: WindowId) {
2138 self.with_window(id, |w| w.bring_to_top(), || ())
2139 }
2140
2141 fn set_state(&mut self, id: WindowId, state: WindowStateAll) {
2142 if let Some(w) = self.windows.iter_mut().find(|w| w.id() == id)
2143 && let Some(state) = w.set_state(state)
2144 {
2145 let mut change = WindowChanged::state_changed(id, state, EventCause::App);
2146
2147 change.size = w.resized();
2148 change.position = w.moved();
2149 if let Some(handle) = w.monitor_change() {
2150 self.refresh_monitors();
2151 let monitor = self.monitor_handle_to_id(&handle);
2152 change.monitor = Some(monitor);
2153 }
2154
2155 let _ = self.app_sender.send(AppEvent::Notify(Event::WindowChanged(change)));
2156 }
2157 }
2158
2159 fn set_headless_size(&mut self, renderer: WindowId, size: DipSize, scale_factor: Factor) {
2160 self.assert_resumed();
2161 if let Some(surf) = self.surfaces.iter_mut().find(|s| s.id() == renderer) {
2162 surf.set_size(size, scale_factor)
2163 }
2164 }
2165
2166 fn set_video_mode(&mut self, id: WindowId, mode: VideoMode) {
2167 self.with_window(id, |w| w.set_video_mode(mode), || ())
2168 }
2169
2170 fn set_icon(&mut self, id: WindowId, icon: Option<ImageId>) {
2171 let icon = icon
2172 .and_then(|i| self.image_cache.get(i))
2173 .and_then(|i| i.icon(self.image_cache.resizer()));
2174 self.with_window(id, |w| w.set_icon(icon), || ())
2175 }
2176
2177 fn set_focus_indicator(&mut self, id: WindowId, request: Option<FocusIndicator>) {
2178 self.with_window(id, |w| w.set_focus_request(request), || ())
2179 }
2180
2181 fn focus(&mut self, id: WindowId) -> FocusResult {
2182 #[cfg(windows)]
2183 {
2184 let (r, s) = self.with_window(id, |w| w.focus(), || (FocusResult::Requested, false));
2185 self.skip_ralt = s;
2186 r
2187 }
2188
2189 #[cfg(not(windows))]
2190 {
2191 self.with_window(id, |w| w.focus(), || FocusResult::Requested)
2192 }
2193 }
2194
2195 fn drag_move(&mut self, id: WindowId) {
2196 self.with_window(id, |w| w.drag_move(), || ())
2197 }
2198
2199 fn drag_resize(&mut self, id: WindowId, direction: zng_view_api::window::ResizeDirection) {
2200 self.with_window(id, |w| w.drag_resize(direction), || ())
2201 }
2202
2203 fn set_can_minimize(&mut self, id: WindowId, can: bool) {
2204 self.with_window(id, |w| w.set_can_minimize(can), || ())
2205 }
2206 fn set_can_maximize(&mut self, id: WindowId, can: bool) {
2207 self.with_window(id, |w| w.set_can_maximize(can), || ())
2208 }
2209 fn set_can_fullscreen(&mut self, id: WindowId, can: bool) {
2210 self.with_window(id, |w| w.set_can_fullscreen(can), || ())
2211 }
2212 fn set_can_close(&mut self, id: WindowId, can: bool) {
2213 self.with_window(id, |w| w.set_can_close(can), || ())
2214 }
2215
2216 fn open_title_bar_context_menu(&mut self, id: WindowId, position: DipPoint) {
2217 self.with_window(id, |w| w.open_title_bar_context_menu(position), || ())
2218 }
2219
2220 fn set_cursor(&mut self, id: WindowId, icon: Option<CursorIcon>) {
2221 self.with_window(id, |w| w.set_cursor(icon), || ())
2222 }
2223
2224 fn set_cursor_image(&mut self, id: WindowId, icon: Option<CursorImage>) {
2225 let icon = icon.and_then(|img| self.image_cache.get(img.img).and_then(|i| i.cursor(img.hotspot, &self.winit_loop)));
2226 self.with_window(id, |w| w.set_cursor_image(icon), || ());
2227 }
2228
2229 fn set_ime_area(&mut self, id: WindowId, area: Option<DipRect>) {
2230 self.with_window(id, |w| w.set_ime_area(area), || ())
2231 }
2232
2233 fn add_image(&mut self, request: ImageRequest<IpcReadHandle>) -> ImageId {
2234 self.image_cache.add(request)
2235 }
2236
2237 fn add_image_pro(&mut self, request: ImageRequest<IpcReceiver<IpcBytes>>) -> ImageId {
2238 self.image_cache.add_pro(request)
2239 }
2240
2241 fn forget_image(&mut self, id: ImageId) {
2242 self.image_cache.forget(id)
2243 }
2244
2245 fn encode_image(&mut self, request: ImageEncodeRequest) -> ImageEncodeId {
2246 self.image_cache.encode(request)
2247 }
2248
2249 fn use_image(&mut self, id: WindowId, image_id: ImageId) -> ImageTextureId {
2250 if let Some(img) = self.image_cache.get(image_id) {
2251 with_window_or_surface!(self, id, |w| w.use_image(img), || ImageTextureId::INVALID)
2252 } else {
2253 ImageTextureId::INVALID
2254 }
2255 }
2256
2257 fn update_image_use(&mut self, id: WindowId, texture_id: ImageTextureId, image_id: ImageId, dirty_rect: Option<PxRect>) -> bool {
2258 if let Some(img) = self.image_cache.get(image_id) {
2259 with_window_or_surface!(self, id, |w| w.update_image(texture_id, img, dirty_rect), || false)
2260 } else {
2261 false
2262 }
2263 }
2264
2265 fn delete_image_use(&mut self, id: WindowId, texture_id: ImageTextureId) {
2266 with_window_or_surface!(self, id, |w| w.delete_image(texture_id), || ())
2267 }
2268
2269 fn add_audio(&mut self, request: audio::AudioRequest<IpcReadHandle>) -> audio::AudioId {
2270 self.audio_cache.add(request)
2271 }
2272
2273 fn add_audio_pro(&mut self, request: audio::AudioRequest<IpcReceiver<IpcBytes>>) -> audio::AudioId {
2274 self.audio_cache.add_pro(request)
2275 }
2276
2277 fn forget_audio(&mut self, id: audio::AudioId) {
2278 self.audio_cache.forget(id)
2279 }
2280
2281 fn open_audio_output(&mut self, request: audio::AudioOutputRequest) {
2282 self.audio_cache.open_output(request)
2283 }
2284
2285 fn update_audio_output(&mut self, request: audio::AudioOutputUpdateRequest) {
2286 self.audio_cache.update_output(request)
2287 }
2288
2289 fn close_audio_output(&mut self, id: audio::AudioOutputId) {
2290 self.audio_cache.close_output(id)
2291 }
2292
2293 fn cue_audio(&mut self, request: audio::AudioPlayRequest) -> audio::AudioPlayId {
2294 self.audio_cache.play(request)
2295 }
2296
2297 fn encode_audio(&mut self, _request: audio::AudioEncodeRequest) -> audio::AudioEncodeId {
2298 audio::AudioEncodeId::INVALID
2299 }
2300
2301 fn add_video(&mut self, request: video::VideoRequest<IpcReadHandle>) -> video::VideoId {
2302 let _ = request;
2303 video::VideoId::INVALID
2304 }
2305
2306 fn add_video_pro(&mut self, request: video::VideoRequest<IpcReceiver<IpcBytes>>) -> video::VideoId {
2307 let _ = request;
2308 video::VideoId::INVALID
2309 }
2310
2311 fn forget_video(&mut self, id: video::VideoId) {
2312 let _ = id;
2313 }
2314
2315 fn use_video(&mut self, id: WindowId, video_id: video::VideoId) -> video::VideoTextureId {
2316 let _ = (id, video_id);
2317 video::VideoTextureId::INVALID
2318 }
2319
2320 fn delete_video_use(&mut self, id: WindowId, texture_id: video::VideoTextureId) {
2322 let _ = (id, texture_id);
2323 }
2324
2325 fn add_font_face(&mut self, id: WindowId, bytes: font::IpcFontBytes, index: u32) -> FontFaceId {
2326 with_window_or_surface!(self, id, |w| w.add_font_face(bytes, index), || FontFaceId::INVALID)
2327 }
2328
2329 fn delete_font_face(&mut self, id: WindowId, font_face_id: FontFaceId) {
2330 with_window_or_surface!(self, id, |w| w.delete_font_face(font_face_id), || ())
2331 }
2332
2333 fn add_font(
2334 &mut self,
2335 id: WindowId,
2336 font_face_id: FontFaceId,
2337 glyph_size: Px,
2338 options: FontOptions,
2339 variations: Vec<(FontVariationName, f32)>,
2340 ) -> FontId {
2341 with_window_or_surface!(self, id, |w| w.add_font(font_face_id, glyph_size, options, variations), || {
2342 FontId::INVALID
2343 })
2344 }
2345
2346 fn delete_font(&mut self, id: WindowId, font_id: FontId) {
2347 with_window_or_surface!(self, id, |w| w.delete_font(font_id), || ())
2348 }
2349
2350 fn set_capture_mode(&mut self, id: WindowId, enabled: bool) {
2351 self.with_window(id, |w| w.set_capture_mode(enabled), || ())
2352 }
2353
2354 fn frame_image(&mut self, id: WindowId, mask: Option<ImageMaskMode>) -> ImageId {
2355 with_window_or_surface!(self, id, |w| w.frame_image(&mut self.image_cache, mask), || ImageId::INVALID)
2356 }
2357
2358 fn frame_image_rect(&mut self, id: WindowId, rect: PxRect, mask: Option<ImageMaskMode>) -> ImageId {
2359 with_window_or_surface!(self, id, |w| w.frame_image_rect(&mut self.image_cache, rect, mask), || {
2360 ImageId::INVALID
2361 })
2362 }
2363
2364 fn render(&mut self, id: WindowId, frame: FrameRequest) {
2365 with_window_or_surface!(self, id, |w| w.render(frame), || ())
2366 }
2367
2368 fn render_update(&mut self, id: WindowId, frame: FrameUpdateRequest) {
2369 with_window_or_surface!(self, id, |w| w.render_update(frame), || ())
2370 }
2371
2372 fn access_update(&mut self, id: WindowId, update: access::AccessTreeUpdate) {
2373 if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
2374 s.access_update(update, &self.app_sender);
2375 }
2376 }
2377
2378 fn message_dialog(&mut self, id: WindowId, dialog: MsgDialog) -> DialogId {
2379 let r_id = self.dialog_id_gen.incr();
2380 if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
2381 s.message_dialog(dialog, r_id, self.app_sender.clone());
2382 } else {
2383 let r = MsgDialogResponse::Error(Txt::from_static("window not found"));
2384 let _ = self.app_sender.send(AppEvent::Notify(Event::MsgDialogResponse(r_id, r)));
2385 }
2386 r_id
2387 }
2388
2389 fn file_dialog(&mut self, id: WindowId, dialog: FileDialog) -> DialogId {
2390 let r_id = self.dialog_id_gen.incr();
2391 if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
2392 s.file_dialog(dialog, r_id, self.app_sender.clone());
2393 } else {
2394 let r = MsgDialogResponse::Error(Txt::from_static("window not found"));
2395 let _ = self.app_sender.send(AppEvent::Notify(Event::MsgDialogResponse(r_id, r)));
2396 };
2397 r_id
2398 }
2399
2400 fn notification_dialog(&mut self, dialog: dialog::Notification) -> DialogId {
2401 let id = self.dialog_id_gen.incr();
2402 self.notifications.notification_dialog(&self.app_sender, id, dialog);
2403 id
2404 }
2405
2406 fn update_notification(&mut self, id: DialogId, dialog: dialog::Notification) {
2407 self.notifications.update_notification(&self.app_sender, id, dialog);
2408 }
2409
2410 #[cfg(windows)]
2411 fn read_clipboard(
2412 &mut self,
2413 mut data_type: Vec<clipboard::ClipboardType>,
2414 _first: bool,
2415 ) -> Result<Vec<clipboard::ClipboardData>, clipboard::ClipboardError> {
2416 if data_type.is_empty() {
2417 return Ok(vec![]);
2418 }
2419
2420 let single = match data_type.remove(0) {
2421 clipboard::ClipboardType::Text => {
2422 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2423
2424 clipboard_win::get(clipboard_win::formats::Unicode)
2425 .map_err(util::clipboard_win_to_clip)
2426 .map(|s: String| clipboard::ClipboardData::Text(Txt::from_str(&s)))
2427 }
2428 clipboard::ClipboardType::Image => {
2429 use zng_txt::ToTxt as _;
2430
2431 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2432
2433 let bitmap = clipboard_win::get(clipboard_win::formats::Bitmap).map_err(util::clipboard_win_to_clip)?;
2434
2435 let id = self.image_cache.add(ImageRequest::new(
2436 image::ImageDataFormat::FileExtension(Txt::from_str("bmp")),
2437 IpcBytes::from_vec_blocking(bitmap)
2438 .map_err(|e| clipboard::ClipboardError::Other(e.to_txt()))?
2439 .into(),
2440 u64::MAX,
2441 None,
2442 None,
2443 ));
2444 Ok(clipboard::ClipboardData::Image(id))
2445 }
2446 clipboard::ClipboardType::Paths => {
2447 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2448
2449 clipboard_win::get(clipboard_win::formats::FileList)
2450 .map_err(util::clipboard_win_to_clip)
2451 .map(clipboard::ClipboardData::Paths)
2452 }
2453 clipboard::ClipboardType::Extension(_) => Err(clipboard::ClipboardError::NotSupported),
2454 _ => Err(clipboard::ClipboardError::NotSupported),
2455 };
2456 single.map(|d| vec![d])
2457 }
2458
2459 #[cfg(windows)]
2460 fn write_clipboard(&mut self, mut data: Vec<clipboard::ClipboardData>) -> Result<usize, clipboard::ClipboardError> {
2461 use zng_txt::formatx;
2462
2463 if data.is_empty() {
2464 return Ok(0);
2465 }
2466
2467 let r = match data.remove(0) {
2468 clipboard::ClipboardData::Text(t) => {
2469 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2470
2471 clipboard_win::set(clipboard_win::formats::Unicode, t).map_err(util::clipboard_win_to_clip)
2472 }
2473 clipboard::ClipboardData::Image(id) => {
2474 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2475
2476 if let Some(img) = self.image_cache.get(id) {
2477 let mut bmp = vec![];
2478 img.encode(vec![], ::image::ImageFormat::Bmp, &mut std::io::Cursor::new(&mut bmp))
2479 .map_err(|e| clipboard::ClipboardError::Other(formatx!("{e:?}")))?;
2480 clipboard_win::set(clipboard_win::formats::Bitmap, bmp).map_err(util::clipboard_win_to_clip)
2481 } else {
2482 Err(clipboard::ClipboardError::Other(Txt::from_str("image not found")))
2483 }
2484 }
2485 clipboard::ClipboardData::Paths(l) => {
2486 use clipboard_win::Setter;
2487 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2488
2489 let strs = l.into_iter().map(|p| p.display().to_string()).collect::<Vec<String>>();
2491 clipboard_win::formats::FileList
2492 .write_clipboard(&strs)
2493 .map_err(util::clipboard_win_to_clip)
2494 }
2495 clipboard::ClipboardData::Extension { .. } => Err(clipboard::ClipboardError::NotSupported),
2496 _ => Err(clipboard::ClipboardError::NotSupported),
2497 };
2498
2499 r.map(|()| 1)
2500 }
2501
2502 #[cfg(not(any(windows, target_os = "android")))]
2503 fn read_clipboard(
2504 &mut self,
2505 mut data_type: Vec<clipboard::ClipboardType>,
2506 _first: bool,
2507 ) -> Result<Vec<clipboard::ClipboardData>, clipboard::ClipboardError> {
2508 if data_type.is_empty() {
2509 return Ok(vec![]);
2510 }
2511
2512 use zng_txt::ToTxt as _;
2513 let single = match data_type.remove(0) {
2514 clipboard::ClipboardType::Text => self
2515 .arboard()?
2516 .get_text()
2517 .map_err(util::arboard_to_clip)
2518 .map(|s| clipboard::ClipboardData::Text(zng_txt::Txt::from(s))),
2519 clipboard::ClipboardType::Image => {
2520 let bitmap = self.arboard()?.get_image().map_err(util::arboard_to_clip)?;
2521 let mut data = bitmap.bytes.into_owned();
2522 for rgba in data.as_chunks_mut::<4>().0 {
2523 rgba.swap(0, 2); }
2525 let id = self.image_cache.add(image::ImageRequest::new(
2526 image::ImageDataFormat::Bgra8 {
2527 size: zng_unit::PxSize::new(Px(bitmap.width as _), Px(bitmap.height as _)),
2528 density: None,
2529 original_color_type: zng_view_api::image::ColorType::RGBA8,
2530 },
2531 IpcBytes::from_vec_blocking(data)
2532 .map_err(|e| clipboard::ClipboardError::Other(e.to_txt()))?
2533 .into(),
2534 u64::MAX,
2535 None,
2536 None,
2537 ));
2538 Ok(clipboard::ClipboardData::Image(id))
2539 }
2540 clipboard::ClipboardType::Paths => self
2541 .arboard()?
2542 .get()
2543 .file_list()
2544 .map_err(util::arboard_to_clip)
2545 .map(clipboard::ClipboardData::Paths),
2546 clipboard::ClipboardType::Extension(_) => Err(clipboard::ClipboardError::NotSupported),
2547 _ => Err(clipboard::ClipboardError::NotSupported),
2548 };
2549
2550 single.map(|e| vec![e])
2551 }
2552
2553 #[cfg(not(any(windows, target_os = "android")))]
2554 fn write_clipboard(&mut self, mut data: Vec<clipboard::ClipboardData>) -> Result<usize, clipboard::ClipboardError> {
2555 if data.is_empty() {
2556 return Ok(0);
2557 }
2558
2559 let r = match data.remove(0) {
2560 clipboard::ClipboardData::Text(t) => self.arboard()?.set_text(t).map_err(util::arboard_to_clip),
2561 clipboard::ClipboardData::Image(id) => {
2562 self.arboard()?;
2563 if let Some(img) = self.image_cache.get(id) {
2564 let size = img.size();
2565 let mut data = img.pixels().clone().to_vec();
2566 for rgba in data.as_chunks_mut::<4>().0 {
2567 rgba.swap(0, 2); }
2569 let board = self.arboard()?;
2570 let _ = board.set_image(arboard::ImageData {
2571 width: size.width.0 as _,
2572 height: size.height.0 as _,
2573 bytes: std::borrow::Cow::Owned(data),
2574 });
2575 Ok(())
2576 } else {
2577 Err(clipboard::ClipboardError::Other(zng_txt::Txt::from_static("image not found")))
2578 }
2579 }
2580 clipboard::ClipboardData::Paths(_) => Err(clipboard::ClipboardError::NotSupported),
2581 clipboard::ClipboardData::Extension { .. } => Err(clipboard::ClipboardError::NotSupported),
2582 _ => Err(clipboard::ClipboardError::NotSupported),
2583 };
2584
2585 r.map(|()| 1)
2586 }
2587
2588 #[cfg(target_os = "android")]
2589 fn read_clipboard(
2590 &mut self,
2591 data_type: Vec<clipboard::ClipboardType>,
2592 _first: bool,
2593 ) -> Result<Vec<clipboard::ClipboardData>, clipboard::ClipboardError> {
2594 if data_type.is_empty() {
2595 return Ok(vec![]);
2596 }
2597
2598 let _ = data_type;
2599 Err(clipboard::ClipboardError::Other(Txt::from_static(
2600 "clipboard not implemented for Android",
2601 )))
2602 }
2603
2604 #[cfg(target_os = "android")]
2605 fn write_clipboard(&mut self, data: Vec<clipboard::ClipboardData>) -> Result<usize, clipboard::ClipboardError> {
2606 if data.is_empty() {
2607 return Ok(0);
2608 }
2609
2610 let _ = data;
2611 Err(clipboard::ClipboardError::Other(Txt::from_static(
2612 "clipboard not implemented for Android",
2613 )))
2614 }
2615
2616 fn start_drag_drop(
2617 &mut self,
2618 id: WindowId,
2619 data: Vec<DragDropData>,
2620 allowed_effects: DragDropEffect,
2621 ) -> Result<DragDropId, DragDropError> {
2622 let _ = (id, data, allowed_effects);
2623 Err(DragDropError::NotSupported)
2624 }
2625
2626 fn cancel_drag_drop(&mut self, id: WindowId, drag_id: DragDropId) {
2627 let _ = (id, drag_id);
2628 }
2629
2630 fn drag_dropped(&mut self, id: WindowId, drop_id: DragDropId, applied: DragDropEffect) {
2631 let _ = (id, drop_id, applied);
2632 }
2633
2634 fn set_system_shutdown_warn(&mut self, id: WindowId, reason: Txt) {
2635 self.with_window(id, move |w| w.set_system_shutdown_warn(reason), || ())
2636 }
2637
2638 fn set_app_menu(&mut self, menu: menu::AppMenu) {
2639 let _ = menu;
2640 }
2641
2642 fn set_tray_icon(&mut self, indicator: menu::TrayIcon) {
2643 let _ = indicator;
2644 }
2645
2646 fn third_party_licenses(&mut self) -> Vec<zng_tp_licenses::LicenseUsed> {
2647 #[cfg(feature = "embed_licenses")]
2648 {
2649 zng_tp_licenses::decode_embedding!()
2650 }
2651 #[cfg(not(feature = "embed_licenses"))]
2652 {
2653 vec![]
2654 }
2655 }
2656
2657 fn app_extension(&mut self, extension_id: ApiExtensionId, extension_request: ApiExtensionPayload) -> ApiExtensionPayload {
2658 self.exts.call_command(extension_id, extension_request)
2659 }
2660
2661 fn window_extension(
2662 &mut self,
2663 id: WindowId,
2664 extension_id: ApiExtensionId,
2665 extension_request: ApiExtensionPayload,
2666 ) -> ApiExtensionPayload {
2667 self.with_window(
2668 id,
2669 |w| w.window_extension(extension_id, extension_request),
2670 || ApiExtensionPayload::invalid_request(extension_id, "window not found"),
2671 )
2672 }
2673
2674 fn render_extension(
2675 &mut self,
2676 id: WindowId,
2677 extension_id: ApiExtensionId,
2678 extension_request: ApiExtensionPayload,
2679 ) -> ApiExtensionPayload {
2680 with_window_or_surface!(self, id, |w| w.render_extension(extension_id, extension_request), || {
2681 ApiExtensionPayload::invalid_request(extension_id, "renderer not found")
2682 })
2683 }
2684
2685 fn ping(&mut self, count: u16) -> u16 {
2686 self.notify(Event::Pong(count));
2687 count
2688 }
2689}
2690
2691#[derive(Debug)]
2693#[allow(clippy::large_enum_variant)]
2694pub(crate) enum AppEvent {
2695 Request,
2697 Notify(Event),
2699 #[cfg_attr(not(windows), allow(unused))]
2701 RefreshMonitors,
2702
2703 #[cfg_attr(not(windows), allow(unused))]
2705 WinitFocused(winit::window::WindowId, bool),
2706
2707 ParentProcessExited,
2709
2710 ImageCanRender(ImageDecoded),
2712
2713 #[cfg_attr(not(feature = "_audio_any"), allow(unused))]
2715 AudioCanPlay(audio::AudioId, AudioTrack),
2716
2717 SetDeviceEventsFilter(DeviceEventsFilter),
2719
2720 #[allow(unused)]
2722 MonitorPowerChanged,
2723}
2724
2725#[allow(clippy::large_enum_variant)] #[derive(Debug)]
2731enum RequestEvent {
2732 Request(Request),
2734 FrameReady(WindowId, FrameReadyMsg),
2736}
2737
2738#[derive(Debug)]
2739pub(crate) struct FrameReadyMsg {
2740 pub composite_needed: bool,
2741}
2742
2743#[derive(Clone)]
2745pub(crate) enum AppEventSender {
2746 Headed(EventLoopProxy<AppEvent>, Sender<RequestEvent>),
2747 Headless(Sender<AppEvent>, Sender<RequestEvent>),
2748}
2749impl AppEventSender {
2750 fn send(&self, ev: AppEvent) -> Result<(), ChannelError> {
2752 match self {
2753 AppEventSender::Headed(p, _) => p.send_event(ev).map_err(ChannelError::disconnected_by),
2754 AppEventSender::Headless(p, _) => p.send_blocking(ev),
2755 }
2756 }
2757
2758 fn request(&self, req: Request) -> Result<(), ChannelError> {
2760 match self {
2761 AppEventSender::Headed(_, p) => p.send_blocking(RequestEvent::Request(req)),
2762 AppEventSender::Headless(_, p) => p.send_blocking(RequestEvent::Request(req)),
2763 }?;
2764 self.send(AppEvent::Request)
2765 }
2766
2767 fn frame_ready(&self, window_id: WindowId, msg: FrameReadyMsg) -> Result<(), ChannelError> {
2769 match self {
2770 AppEventSender::Headed(_, p) => p.send_blocking(RequestEvent::FrameReady(window_id, msg)),
2771 AppEventSender::Headless(_, p) => p.send_blocking(RequestEvent::FrameReady(window_id, msg)),
2772 }?;
2773 self.send(AppEvent::Request)
2774 }
2775}
2776
2777pub(crate) struct WrNotifier {
2779 id: WindowId,
2780 sender: AppEventSender,
2781}
2782impl WrNotifier {
2783 pub fn create(id: WindowId, sender: AppEventSender) -> Box<dyn RenderNotifier> {
2784 Box::new(WrNotifier { id, sender })
2785 }
2786}
2787impl RenderNotifier for WrNotifier {
2788 fn clone(&self) -> Box<dyn RenderNotifier> {
2789 Box::new(Self {
2790 id: self.id,
2791 sender: self.sender.clone(),
2792 })
2793 }
2794
2795 fn wake_up(&self, _: bool) {}
2796
2797 fn new_frame_ready(&self, _: DocumentId, _: FramePublishId, params: &FrameReadyParams) {
2798 let msg = FrameReadyMsg {
2800 composite_needed: params.render,
2801 };
2802 let _ = self.sender.frame_ready(self.id, msg);
2803 }
2804}
2805
2806#[cfg(target_arch = "wasm32")]
2807compile_error!("zng-view does not support Wasm");