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 let winit_span = tracing::trace_span!("winit::EventLoop::new").entered();
1365 #[cfg(not(target_os = "android"))]
1366 let event_loop = EventLoop::with_user_event().build().unwrap();
1367 #[cfg(target_os = "android")]
1368 let event_loop = EventLoop::with_user_event()
1369 .with_android_app(platform::android::android_app())
1370 .build()
1371 .unwrap();
1372 drop(winit_span);
1373 let app_sender = event_loop.create_proxy();
1374
1375 let (request_sender, request_receiver) = channel::unbounded();
1376 let mut app = App::new(
1377 AppEventSender::Headed(app_sender, request_sender),
1378 ipc.response_sender,
1379 ipc.event_sender,
1380 request_receiver,
1381 ext,
1382 );
1383 app.start_receiving(ipc.request_receiver);
1384
1385 app.config_listener_exit = config::spawn_listener(app.app_sender.clone());
1386
1387 if let Err(e) = event_loop.run_app(&mut app) {
1388 if app.exited {
1389 tracing::error!("winit event loop error after app exit, {e}");
1390 } else {
1391 panic!("winit event loop error, {e}");
1392 }
1393 }
1394 }
1395
1396 fn new(
1397 app_sender: AppEventSender,
1398 response_sender: ipc::ResponseSender,
1399 event_sender: ipc::EventSender,
1400 request_recv: channel::Receiver<RequestEvent>,
1401 mut exts: ViewExtensions,
1402 ) -> Self {
1403 exts.renderer("zng-view.webrender_debug", extensions::RendererDebugExt::new);
1404 #[cfg(windows)]
1405 {
1406 exts.window("zng-view.prefer_angle", extensions::PreferAngleExt::new);
1407 }
1408 #[cfg(feature = "image_cur")]
1409 exts.data("image_cur");
1410 #[cfg(feature = "image_meta_exif")]
1411 exts.data("image_meta_exif");
1412 #[cfg(feature = "image_meta_icc")]
1413 exts.data("image_meta_icc");
1414 App {
1415 headless: false,
1416 image_cache: ImageCache::new(
1417 app_sender.clone(),
1418 #[cfg(feature = "image_cur")]
1419 exts.id(&api_extension::ApiExtensionName::new("image_cur").unwrap()).unwrap(),
1420 #[cfg(feature = "image_meta_exif")]
1421 exts.id(&api_extension::ApiExtensionName::new("image_meta_exif").unwrap()).unwrap(),
1422 #[cfg(feature = "image_meta_icc")]
1423 exts.id(&api_extension::ApiExtensionName::new("image_meta_icc").unwrap()).unwrap(),
1424 ),
1425 exts,
1426 gl_manager: GlContextManager::default(),
1427 audio_cache: AudioCache::new(app_sender.clone()),
1428 app_sender,
1429 request_recv,
1430 response_sender,
1431 event_sender,
1432 winit_loop: util::WinitEventLoop::default(),
1433 generation: ViewProcessGen::INVALID,
1434 device_events_filter: DeviceEventsFilter::empty(),
1435 windows: vec![],
1436 surfaces: vec![],
1437 monitors: vec![],
1438 monitor_ids: vec![],
1439 monitor_id_gen: MonitorId::INVALID,
1440 devices: vec![],
1441 device_id_gen: InputDeviceId::INVALID,
1442 dialog_id_gen: DialogId::INVALID,
1443 resize_frame_wait_id_gen: FrameWaitId::INVALID,
1444 coalescing_event: None,
1445 cursor_entered_expect_move: Vec::with_capacity(1),
1446 app_state: AppState::PreInitSuspended,
1447 exited: false,
1448 #[cfg(windows)]
1449 skip_ralt: false,
1450 pressed_modifiers: FxHashMap::default(),
1451 pending_modifiers_update: None,
1452 pending_modifiers_focus_clear: false,
1453 config_listener_exit: None,
1454 drag_drop_hovered: None,
1455 drag_drop_next_move: None,
1456 #[cfg(not(any(windows, target_os = "android")))]
1457 arboard: None,
1458 notifications: NotificationService::default(),
1459 low_memory_watcher: low_memory::LowMemoryWatcher::new(),
1460 last_pull_event: Instant::now(),
1461 }
1462 }
1463
1464 fn start_receiving(&mut self, mut request_recv: ipc::RequestReceiver) {
1465 let app_sender = self.app_sender.clone();
1466 thread::Builder::new()
1467 .name("request-recv".into())
1468 .stack_size(256 * 1024)
1469 .spawn(move || {
1470 while let Ok(r) = request_recv.recv() {
1471 if app_sender.request(r).is_err() {
1472 break;
1473 }
1474 }
1475 let _ = app_sender.send(AppEvent::ParentProcessExited);
1476 })
1477 .expect("failed to spawn thread");
1478 }
1479
1480 fn monitor_handle_to_id(&mut self, handle: &MonitorHandle) -> MonitorId {
1481 if let Some((id, _)) = self.monitor_ids.iter().find(|(_, h)| h == handle) {
1482 *id
1483 } else {
1484 self.refresh_monitors();
1485 if let Some((id, _)) = self.monitor_ids.iter().find(|(_, h)| h == handle) {
1486 *id
1487 } else {
1488 MonitorId::INVALID
1489 }
1490 }
1491 }
1492
1493 fn update_modifiers(&mut self) {
1494 if mem::take(&mut self.pending_modifiers_focus_clear) && self.windows.iter().all(|w| !w.is_focused()) {
1501 self.pressed_modifiers.clear();
1502 }
1503
1504 if let Some(m) = self.pending_modifiers_update.take()
1505 && let Some(id) = self.windows.iter().find(|w| w.is_focused()).map(|w| w.id())
1506 {
1507 let mut notify = vec![];
1508 self.pressed_modifiers.retain(|(key, location), (d_id, s_code)| {
1509 let mut retain = true;
1510 if matches!(key, Key::Super) && !m.super_key() {
1511 retain = false;
1512 notify.push(Event::KeyboardInput {
1513 window: id,
1514 device: *d_id,
1515 key_code: *s_code,
1516 state: KeyState::Released,
1517 key: key.clone(),
1518 key_location: *location,
1519 key_modified: key.clone(),
1520 text: Txt::from_str(""),
1521 });
1522 }
1523 if matches!(key, Key::Shift) && !m.shift_key() {
1524 retain = false;
1525 notify.push(Event::KeyboardInput {
1526 window: id,
1527 device: *d_id,
1528 key_code: *s_code,
1529 state: KeyState::Released,
1530 key: key.clone(),
1531 key_location: *location,
1532 key_modified: key.clone(),
1533 text: Txt::from_str(""),
1534 });
1535 }
1536 if matches!(key, Key::Alt | Key::AltGraph) && !m.alt_key() {
1537 retain = false;
1538 notify.push(Event::KeyboardInput {
1539 window: id,
1540 device: *d_id,
1541 key_code: *s_code,
1542 state: KeyState::Released,
1543 key: key.clone(),
1544 key_location: *location,
1545 key_modified: key.clone(),
1546 text: Txt::from_str(""),
1547 });
1548 }
1549 if matches!(key, Key::Ctrl) && !m.control_key() {
1550 retain = false;
1551 notify.push(Event::KeyboardInput {
1552 window: id,
1553 device: *d_id,
1554 key_code: *s_code,
1555 state: KeyState::Released,
1556 key: key.clone(),
1557 key_location: *location,
1558 key_modified: key.clone(),
1559 text: Txt::from_str(""),
1560 });
1561 }
1562 retain
1563 });
1564
1565 for ev in notify {
1566 self.notify(ev);
1567 }
1568 }
1569 }
1570
1571 fn refresh_monitors(&mut self) {
1572 let monitors = self.available_monitors();
1573 if self.monitors != monitors {
1574 self.monitors = monitors.clone();
1575 self.notify(Event::MonitorsChanged(monitors));
1576 }
1577 }
1578
1579 fn on_frame_ready(&mut self, window_id: WindowId, msg: FrameReadyMsg) {
1580 let _s = tracing::trace_span!("on_frame_ready").entered();
1581
1582 if let Some(w) = self.windows.iter_mut().find(|w| w.id() == window_id) {
1583 let r = w.on_frame_ready(msg, &mut self.image_cache);
1584
1585 let _ = self
1586 .event_sender
1587 .send(Event::FrameRendered(EventFrameRendered::new(window_id, r.frame_id, r.image)));
1588
1589 if r.first_frame {
1590 let size = w.size();
1591 self.notify(Event::WindowChanged(WindowChanged::resized(window_id, size, EventCause::App, None)));
1592 }
1593 } else if let Some(s) = self.surfaces.iter_mut().find(|w| w.id() == window_id) {
1594 let (frame_id, image) = s.on_frame_ready(msg, &mut self.image_cache);
1595
1596 self.notify(Event::FrameRendered(EventFrameRendered::new(window_id, frame_id, image)))
1597 }
1598 }
1599
1600 pub(crate) fn notify(&mut self, event: Event) {
1601 let now = Instant::now();
1602 if let Some((mut coal, timestamp)) = self.coalescing_event.take() {
1603 let r = if now.saturating_duration_since(timestamp) >= Duration::from_millis(16) {
1604 Err(event)
1605 } else {
1606 coal.coalesce(event)
1607 };
1608 match r {
1609 Ok(()) => self.coalescing_event = Some((coal, timestamp)),
1610 Err(event) => match (&mut coal, event) {
1611 (
1612 Event::KeyboardInput {
1613 window,
1614 device,
1615 state,
1616 text,
1617 ..
1618 },
1619 Event::KeyboardInput {
1620 window: n_window,
1621 device: n_device,
1622 text: n_text,
1623 ..
1624 },
1625 ) if !n_text.is_empty() && *window == n_window && *device == n_device && *state == KeyState::Pressed => {
1626 if text.is_empty() {
1628 *text = n_text;
1629 } else {
1630 text.push_str(&n_text);
1631 };
1632 self.coalescing_event = Some((coal, now));
1633 }
1634 (_, event) => {
1635 let mut error = self.event_sender.send(coal).is_err();
1636 error |= self.event_sender.send(event).is_err();
1637
1638 if error {
1639 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1640 }
1641 }
1642 },
1643 }
1644 } else {
1645 self.coalescing_event = Some((event, now));
1646 }
1647
1648 if self.headless {
1649 self.flush_coalesced();
1650 }
1651 }
1652
1653 pub(crate) fn finish_cursor_entered_move(&mut self) {
1654 let mut moves = vec![];
1655 for window_id in self.cursor_entered_expect_move.drain(..) {
1656 if let Some(w) = self.windows.iter().find(|w| w.id() == window_id) {
1657 let (position, device) = w.last_cursor_pos();
1658 moves.push(Event::MouseMoved {
1659 window: w.id(),
1660 device,
1661 coalesced_pos: vec![],
1662 position,
1663 });
1664 }
1665 }
1666 for ev in moves {
1667 self.notify(ev);
1668 }
1669 }
1670
1671 pub(crate) fn flush_coalesced(&mut self) {
1673 if let Some((coal, _)) = self.coalescing_event.take()
1674 && self.event_sender.send(coal).is_err()
1675 {
1676 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1677 }
1678 }
1679
1680 #[track_caller]
1681 fn assert_resumed(&self) {
1682 assert_eq!(self.app_state, AppState::Resumed);
1683 }
1684
1685 fn with_window<R>(&mut self, id: WindowId, action: impl FnOnce(&mut Window) -> R, not_found: impl FnOnce() -> R) -> R {
1686 self.assert_resumed();
1687 self.windows.iter_mut().find(|w| w.id() == id).map(action).unwrap_or_else(|| {
1688 tracing::error!("headed window `{id:?}` not found, will return fallback result");
1689 not_found()
1690 })
1691 }
1692
1693 fn monitor_id(&mut self, handle: &MonitorHandle) -> MonitorId {
1694 if let Some((id, _)) = self.monitor_ids.iter().find(|(_, h)| h == handle) {
1695 *id
1696 } else {
1697 let id = self.monitor_id_gen.incr();
1698 self.monitor_ids.push((id, handle.clone()));
1699 id
1700 }
1701 }
1702
1703 fn notify_input_devices_changed(&mut self) {
1704 let devices = self.devices.iter().map(|(id, _, info)| (*id, info.clone())).collect();
1705 self.notify(Event::InputDevicesChanged(devices));
1706 }
1707
1708 fn input_device_id(&mut self, device_id: winit::event::DeviceId, capability: InputDeviceCapability) -> InputDeviceId {
1710 if let Some((id, _, info)) = self.devices.iter_mut().find(|(_, id, _)| *id == device_id) {
1711 let id = *id;
1712 if !self.device_events_filter.input.is_empty() && !capability.is_empty() && !info.capabilities.contains(capability) {
1713 info.capabilities |= capability;
1714 self.notify_input_devices_changed();
1715 }
1716 id
1717 } else {
1718 let id = self.device_id_gen.incr();
1719
1720 #[cfg(not(windows))]
1721 let info = InputDeviceInfo::new("Winit Device", InputDeviceCapability::empty());
1722 #[cfg(windows)]
1723 let info = {
1724 use winit::platform::windows::DeviceIdExtWindows as _;
1725 if !self.device_events_filter.input.is_empty()
1726 && let Some(device_path) = device_id.persistent_identifier()
1727 {
1728 input_device_info::get(&device_path)
1729 } else {
1730 InputDeviceInfo::new("Winit Device", InputDeviceCapability::empty())
1731 }
1732 };
1733
1734 self.devices.push((id, device_id, info));
1735
1736 if !self.device_events_filter.input.is_empty() {
1737 self.notify_input_devices_changed();
1738 }
1739
1740 id
1741 }
1742 }
1743
1744 fn available_monitors(&mut self) -> Vec<(MonitorId, MonitorInfo)> {
1745 let _span = tracing::trace_span!("available_monitors").entered();
1746
1747 let primary = self.winit_loop.primary_monitor();
1748 let mut available: Vec<_> = self
1749 .winit_loop
1750 .available_monitors()
1751 .map(|m| (self.monitor_id(&m), primary.as_ref().map(|h| h == &m).unwrap_or(false), m))
1752 .collect();
1753 available.sort_by(|(_, a_is_primary, a), (_, b_is_primary, b)| {
1756 b_is_primary.cmp(a_is_primary).then_with(|| a.position().x.cmp(&b.position().x))
1757 });
1758 available
1759 .into_iter()
1760 .enumerate()
1761 .map(|(n, (id, is_primary, m))| (id, util::monitor_handle_to_info(&m, is_primary, n + 1)))
1762 .collect()
1763 }
1764
1765 fn update_pull_events(&mut self, _winit_loop: &ActiveEventLoop) {
1766 const INTERVAL: Duration = Duration::from_secs(5);
1767 let any_event_source = self.low_memory_watcher.is_some();
1768 if !any_event_source {
1769 _winit_loop.set_control_flow(winit::event_loop::ControlFlow::Wait);
1770 return;
1771 }
1772
1773 let now = Instant::now();
1774 if now.duration_since(self.last_pull_event) >= INTERVAL {
1775 if let Some(w) = &mut self.low_memory_watcher
1778 && w.notify()
1779 {
1780 use winit::application::ApplicationHandler as _;
1781 self.memory_warning(_winit_loop);
1782 }
1783 }
1784
1785 _winit_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(now + INTERVAL));
1786 }
1787}
1788macro_rules! with_window_or_surface {
1789 ($self:ident, $id:ident, |$el:ident|$action:expr, ||$fallback:expr) => {
1790 if let Some($el) = $self.windows.iter_mut().find(|w| w.id() == $id) {
1791 $action
1792 } else if let Some($el) = $self.surfaces.iter_mut().find(|w| w.id() == $id) {
1793 $action
1794 } else {
1795 tracing::error!("window `{:?}` not found, will return fallback result", $id);
1796 $fallback
1797 }
1798 };
1799}
1800impl Drop for App {
1801 fn drop(&mut self) {
1802 if let Some(f) = self.config_listener_exit.take() {
1803 f();
1804 }
1805 }
1806}
1807impl App {
1808 fn open_headless_impl(&mut self, config: HeadlessRequest) -> HeadlessOpenData {
1809 self.assert_resumed();
1810 let surf = Surface::open(
1811 self.generation,
1812 config,
1813 &self.winit_loop,
1814 &mut self.gl_manager,
1815 self.exts.new_window(),
1816 self.exts.new_renderer(),
1817 self.app_sender.clone(),
1818 );
1819 let render_mode = surf.render_mode();
1820
1821 self.surfaces.push(surf);
1822
1823 HeadlessOpenData::new(render_mode)
1824 }
1825
1826 #[cfg(not(any(windows, target_os = "android")))]
1827 fn arboard(&mut self) -> Result<&mut arboard::Clipboard, clipboard::ClipboardError> {
1828 if self.arboard.is_none() {
1829 match arboard::Clipboard::new() {
1830 Ok(c) => self.arboard = Some(c),
1831 Err(e) => return Err(util::arboard_to_clip(e)),
1832 }
1833 }
1834 Ok(self.arboard.as_mut().unwrap())
1835 }
1836}
1837
1838impl Api for App {
1839 fn init(&mut self, vp_gen: ViewProcessGen, is_respawn: bool, headless: bool) {
1840 if self.exited {
1841 panic!("cannot restart exited");
1842 }
1843
1844 self.generation = vp_gen;
1845 self.headless = headless;
1846
1847 let mut info = ViewProcessInfo::new(vp_gen, is_respawn);
1848 if !headless {
1849 info.input_device |= InputDeviceCapability::KEY;
1851 info.input_device |= InputDeviceCapability::BUTTON;
1852 info.input_device |= InputDeviceCapability::SCROLL_MOTION;
1853 info.input_device |= InputDeviceCapability::AXIS_MOTION;
1854 info.input_device |= InputDeviceCapability::POINTER_MOTION;
1855 }
1856 info.image = crate::image_cache::FORMATS.to_vec();
1857 info.audio = crate::audio_cache::FORMATS.to_vec();
1858 info.extensions = self.exts.api_extensions();
1859
1860 use zng_view_api::window::WindowCapability;
1861 if !headless && !cfg!(target_os = "android") {
1862 info.window |= WindowCapability::SET_TITLE;
1863 info.window |= WindowCapability::SET_VISIBLE;
1864 info.window |= WindowCapability::SET_ALWAYS_ON_TOP;
1865 info.window |= WindowCapability::SET_RESIZABLE;
1866 info.window |= WindowCapability::BRING_TO_TOP;
1867 info.window |= WindowCapability::SET_CURSOR;
1868 info.window |= WindowCapability::SET_CURSOR_IMAGE;
1869 info.window |= WindowCapability::SET_FOCUS_INDICATOR;
1870 info.window |= WindowCapability::FOCUS;
1871 info.window |= WindowCapability::DRAG_MOVE;
1872 info.window |= WindowCapability::MINIMIZE;
1873 info.window |= WindowCapability::MAXIMIZE;
1874 info.window |= WindowCapability::FULLSCREEN;
1875 info.window |= WindowCapability::SET_SIZE;
1876
1877 if cfg!(windows) || std::env::var("WAYLAND_DISPLAY").is_err() {
1878 info.window |= WindowCapability::SYSTEM_CHROME;
1880 info.window |= WindowCapability::SET_CHROME;
1881 }
1882 }
1883 if !headless & cfg!(windows) {
1884 info.window |= WindowCapability::SET_ICON;
1885 info.window |= WindowCapability::SET_TASKBAR_VISIBLE;
1886 info.window |= WindowCapability::OPEN_TITLE_BAR_CONTEXT_MENU;
1887 info.window |= WindowCapability::SET_SYSTEM_SHUTDOWN_WARN;
1888 }
1889 if !headless && !cfg!(target_os = "android") && !cfg!(target_os = "macos") {
1890 info.window |= WindowCapability::DRAG_RESIZE;
1891 }
1892 if !headless && !cfg!(target_os = "android") && (!cfg!(unix) || std::env::var("WAYLAND_DISPLAY").is_err()) {
1894 info.window |= WindowCapability::RESTORE;
1896 info.window |= WindowCapability::EXCLUSIVE;
1898 info.window |= WindowCapability::SET_POSITION;
1899 }
1900 if !headless & (cfg!(windows) || cfg!(target_os = "macos")) {
1901 info.window |= WindowCapability::SET_CAN_CLOSE;
1903 info.window |= WindowCapability::SET_CAN_MINIMIZE;
1904
1905 if cfg!(target_os = "macos") {
1906 info.window |= WindowCapability::SET_CAN_FULLSCREEN;
1907 }
1908 if cfg!(target_os = "windows") {
1909 info.window |= WindowCapability::SET_CAN_MAXIMIZE;
1910 }
1911 }
1912 info.window |= WindowCapability::SET_IME_AREA;
1913
1914 use zng_view_api::dialog::DialogCapability;
1915 if !headless && !cfg!(target_os = "android") {
1916 info.dialog |= DialogCapability::MESSAGE;
1918 info.dialog |= DialogCapability::OPEN_FILE;
1919 info.dialog |= DialogCapability::OPEN_FILES;
1920 info.dialog |= DialogCapability::SAVE_FILE;
1921 info.dialog |= DialogCapability::SELECT_FOLDER;
1922 info.dialog |= DialogCapability::SELECT_FOLDERS;
1923 }
1924 info.dialog |= self.notifications.capabilities();
1925
1926 use zng_view_api::clipboard::ClipboardType;
1927 if !cfg!(target_os = "android") {
1928 info.clipboard.read.push(ClipboardType::Text);
1929 info.clipboard.read.push(ClipboardType::Image);
1930 info.clipboard.read.push(ClipboardType::Paths);
1931
1932 info.clipboard.write.push(ClipboardType::Text);
1933 info.clipboard.write.push(ClipboardType::Image);
1934 if cfg!(windows) {
1935 info.clipboard.write.push(ClipboardType::Paths);
1936 }
1937 }
1938
1939 self.notify(Event::Inited(info));
1940
1941 let available_monitors = self.available_monitors();
1942 self.notify(Event::MonitorsChanged(available_monitors));
1943
1944 let cfg = config::multi_click_config();
1945 if is_respawn || cfg != zng_view_api::config::MultiClickConfig::default() {
1946 self.notify(Event::MultiClickConfigChanged(cfg));
1947 }
1948
1949 let cfg = config::key_repeat_config();
1950 if is_respawn || cfg != zng_view_api::config::KeyRepeatConfig::default() {
1951 self.notify(Event::KeyRepeatConfigChanged(cfg));
1952 }
1953
1954 let cfg = config::touch_config();
1955 if is_respawn || cfg != zng_view_api::config::TouchConfig::default() {
1956 self.notify(Event::TouchConfigChanged(cfg));
1957 }
1958
1959 let cfg = config::font_aa();
1960 if is_respawn || cfg != zng_view_api::config::FontAntiAliasing::default() {
1961 self.notify(Event::FontAaChanged(cfg));
1962 }
1963
1964 let cfg = config::animations_config();
1965 if is_respawn || cfg != zng_view_api::config::AnimationsConfig::default() {
1966 self.notify(Event::AnimationsConfigChanged(cfg));
1967 }
1968
1969 let cfg = config::locale_config();
1970 if is_respawn || cfg != zng_view_api::config::LocaleConfig::default() {
1971 self.notify(Event::LocaleChanged(cfg));
1972 }
1973
1974 let cfg = config::colors_config();
1975 if is_respawn || cfg != zng_view_api::config::ColorsConfig::default() {
1976 self.notify(Event::ColorsConfigChanged(cfg));
1977 }
1978 }
1979
1980 fn exit(&mut self) {
1981 self.assert_resumed();
1982 self.exited = true;
1983 if let Some(t) = self.config_listener_exit.take() {
1984 t();
1985 }
1986 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1988 }
1989
1990 fn set_device_events_filter(&mut self, filter: DeviceEventsFilter) {
1991 let _ = self.app_sender.send(AppEvent::SetDeviceEventsFilter(filter));
1992 }
1993
1994 fn open_window(&mut self, mut config: WindowRequest) {
1995 let _s = tracing::debug_span!("open_window", ?config).entered();
1996
1997 config.state.clamp_size();
1998 config.enforce_kiosk();
1999
2000 if self.headless {
2001 let id = config.id;
2002 let data = self.open_headless_impl(HeadlessRequest::new(
2003 config.id,
2004 Factor(1.0),
2005 config.state.restore_rect.size,
2006 config.render_mode,
2007 config.extensions,
2008 ));
2009 let msg = WindowOpenData::new(
2010 WindowStateAll::new(
2011 WindowState::Fullscreen,
2012 PxPoint::zero(),
2013 DipRect::from_size(config.state.restore_rect.size),
2014 WindowState::Fullscreen,
2015 None,
2016 DipSize::zero(),
2017 DipSize::new(Dip::MAX, Dip::MAX),
2018 false,
2019 ),
2020 None,
2021 (PxPoint::zero(), DipPoint::zero()),
2022 config.state.restore_rect.size,
2023 Factor(1.0),
2024 data.render_mode,
2025 DipSideOffsets::zero(),
2026 );
2027
2028 self.notify(Event::WindowOpened(id, msg));
2029 } else {
2030 self.assert_resumed();
2031
2032 #[cfg(target_os = "android")]
2033 if !self.windows.is_empty() {
2034 tracing::error!("android can only have one window");
2035 return;
2036 }
2037
2038 let id = config.id;
2039 let win = Window::open(
2040 self.generation,
2041 config
2042 .icon
2043 .and_then(|i| self.image_cache.get(i))
2044 .and_then(|i| i.icon(self.image_cache.resizer())),
2045 config
2046 .cursor_image
2047 .and_then(|(i, h)| self.image_cache.get(i).and_then(|i| i.cursor(h, &self.winit_loop))),
2048 config,
2049 &self.winit_loop,
2050 &mut self.gl_manager,
2051 self.exts.new_window(),
2052 self.exts.new_renderer(),
2053 self.app_sender.clone(),
2054 );
2055
2056 let mut msg = WindowOpenData::new(
2057 win.state(),
2058 win.monitor().map(|h| self.monitor_id(&h)),
2059 win.inner_position(),
2060 win.size(),
2061 win.scale_factor(),
2062 win.render_mode(),
2063 win.safe_padding(),
2064 );
2065 msg.refresh_rate = win.refresh_rate();
2066
2067 self.windows.push(win);
2068
2069 self.notify(Event::WindowOpened(id, msg));
2070
2071 #[cfg(target_os = "android")]
2073 {
2074 self.windows.last_mut().unwrap().focused_changed(&mut true);
2075 self.notify(Event::FocusChanged { prev: None, new: Some(id) });
2076 }
2077 }
2078 }
2079
2080 fn open_headless(&mut self, config: HeadlessRequest) {
2081 let _s = tracing::debug_span!("open_headless", ?config).entered();
2082
2083 let id = config.id;
2084 let msg = self.open_headless_impl(config);
2085
2086 self.notify(Event::HeadlessOpened(id, msg));
2087 }
2088
2089 fn close(&mut self, id: WindowId) {
2090 self.assert_resumed();
2091 if let Some(i) = self.windows.iter().position(|w| w.id() == id) {
2092 let _ = self.windows.swap_remove(i);
2093 }
2094 if let Some(i) = self.surfaces.iter().position(|w| w.id() == id) {
2095 let _ = self.surfaces.swap_remove(i);
2096 }
2097 }
2098
2099 fn set_title(&mut self, id: WindowId, title: Txt) {
2100 self.with_window(id, |w| w.set_title(title), || ())
2101 }
2102
2103 fn set_visible(&mut self, id: WindowId, visible: bool) {
2104 self.with_window(id, |w| w.set_visible(visible), || ())
2105 }
2106
2107 fn set_always_on_top(&mut self, id: WindowId, always_on_top: bool) {
2108 self.with_window(id, |w| w.set_always_on_top(always_on_top), || ())
2109 }
2110
2111 fn set_movable(&mut self, id: WindowId, movable: bool) {
2112 self.with_window(id, |w| w.set_movable(movable), || ())
2113 }
2114
2115 fn set_resizable(&mut self, id: WindowId, resizable: bool) {
2116 self.with_window(id, |w| w.set_resizable(resizable), || ())
2117 }
2118
2119 fn set_taskbar_visible(&mut self, id: WindowId, visible: bool) {
2120 self.with_window(id, |w| w.set_taskbar_visible(visible), || ())
2121 }
2122
2123 fn bring_to_top(&mut self, id: WindowId) {
2124 self.with_window(id, |w| w.bring_to_top(), || ())
2125 }
2126
2127 fn set_state(&mut self, id: WindowId, state: WindowStateAll) {
2128 if let Some(w) = self.windows.iter_mut().find(|w| w.id() == id)
2129 && let Some(state) = w.set_state(state)
2130 {
2131 let mut change = WindowChanged::state_changed(id, state, EventCause::App);
2132
2133 change.size = w.resized();
2134 change.position = w.moved();
2135 if let Some(handle) = w.monitor_change() {
2136 self.refresh_monitors();
2137 let monitor = self.monitor_handle_to_id(&handle);
2138 change.monitor = Some(monitor);
2139 }
2140
2141 let _ = self.app_sender.send(AppEvent::Notify(Event::WindowChanged(change)));
2142 }
2143 }
2144
2145 fn set_headless_size(&mut self, renderer: WindowId, size: DipSize, scale_factor: Factor) {
2146 self.assert_resumed();
2147 if let Some(surf) = self.surfaces.iter_mut().find(|s| s.id() == renderer) {
2148 surf.set_size(size, scale_factor)
2149 }
2150 }
2151
2152 fn set_video_mode(&mut self, id: WindowId, mode: VideoMode) {
2153 self.with_window(id, |w| w.set_video_mode(mode), || ())
2154 }
2155
2156 fn set_icon(&mut self, id: WindowId, icon: Option<ImageId>) {
2157 let icon = icon
2158 .and_then(|i| self.image_cache.get(i))
2159 .and_then(|i| i.icon(self.image_cache.resizer()));
2160 self.with_window(id, |w| w.set_icon(icon), || ())
2161 }
2162
2163 fn set_focus_indicator(&mut self, id: WindowId, request: Option<FocusIndicator>) {
2164 self.with_window(id, |w| w.set_focus_request(request), || ())
2165 }
2166
2167 fn focus(&mut self, id: WindowId) -> FocusResult {
2168 #[cfg(windows)]
2169 {
2170 let (r, s) = self.with_window(id, |w| w.focus(), || (FocusResult::Requested, false));
2171 self.skip_ralt = s;
2172 r
2173 }
2174
2175 #[cfg(not(windows))]
2176 {
2177 self.with_window(id, |w| w.focus(), || FocusResult::Requested)
2178 }
2179 }
2180
2181 fn drag_move(&mut self, id: WindowId) {
2182 self.with_window(id, |w| w.drag_move(), || ())
2183 }
2184
2185 fn drag_resize(&mut self, id: WindowId, direction: zng_view_api::window::ResizeDirection) {
2186 self.with_window(id, |w| w.drag_resize(direction), || ())
2187 }
2188
2189 fn set_can_minimize(&mut self, id: WindowId, can: bool) {
2190 self.with_window(id, |w| w.set_can_minimize(can), || ())
2191 }
2192 fn set_can_maximize(&mut self, id: WindowId, can: bool) {
2193 self.with_window(id, |w| w.set_can_maximize(can), || ())
2194 }
2195 fn set_can_fullscreen(&mut self, id: WindowId, can: bool) {
2196 self.with_window(id, |w| w.set_can_fullscreen(can), || ())
2197 }
2198 fn set_can_close(&mut self, id: WindowId, can: bool) {
2199 self.with_window(id, |w| w.set_can_close(can), || ())
2200 }
2201
2202 fn open_title_bar_context_menu(&mut self, id: WindowId, position: DipPoint) {
2203 self.with_window(id, |w| w.open_title_bar_context_menu(position), || ())
2204 }
2205
2206 fn set_cursor(&mut self, id: WindowId, icon: Option<CursorIcon>) {
2207 self.with_window(id, |w| w.set_cursor(icon), || ())
2208 }
2209
2210 fn set_cursor_image(&mut self, id: WindowId, icon: Option<CursorImage>) {
2211 let icon = icon.and_then(|img| self.image_cache.get(img.img).and_then(|i| i.cursor(img.hotspot, &self.winit_loop)));
2212 self.with_window(id, |w| w.set_cursor_image(icon), || ());
2213 }
2214
2215 fn set_ime_area(&mut self, id: WindowId, area: Option<DipRect>) {
2216 self.with_window(id, |w| w.set_ime_area(area), || ())
2217 }
2218
2219 fn add_image(&mut self, request: ImageRequest<IpcReadHandle>) -> ImageId {
2220 self.image_cache.add(request)
2221 }
2222
2223 fn add_image_pro(&mut self, request: ImageRequest<IpcReceiver<IpcBytes>>) -> ImageId {
2224 self.image_cache.add_pro(request)
2225 }
2226
2227 fn forget_image(&mut self, id: ImageId) {
2228 self.image_cache.forget(id)
2229 }
2230
2231 fn encode_image(&mut self, request: ImageEncodeRequest) -> ImageEncodeId {
2232 self.image_cache.encode(request)
2233 }
2234
2235 fn use_image(&mut self, id: WindowId, image_id: ImageId) -> ImageTextureId {
2236 if let Some(img) = self.image_cache.get(image_id) {
2237 with_window_or_surface!(self, id, |w| w.use_image(img), || ImageTextureId::INVALID)
2238 } else {
2239 ImageTextureId::INVALID
2240 }
2241 }
2242
2243 fn update_image_use(&mut self, id: WindowId, texture_id: ImageTextureId, image_id: ImageId, dirty_rect: Option<PxRect>) -> bool {
2244 if let Some(img) = self.image_cache.get(image_id) {
2245 with_window_or_surface!(self, id, |w| w.update_image(texture_id, img, dirty_rect), || false)
2246 } else {
2247 false
2248 }
2249 }
2250
2251 fn delete_image_use(&mut self, id: WindowId, texture_id: ImageTextureId) {
2252 with_window_or_surface!(self, id, |w| w.delete_image(texture_id), || ())
2253 }
2254
2255 fn add_audio(&mut self, request: audio::AudioRequest<IpcReadHandle>) -> audio::AudioId {
2256 self.audio_cache.add(request)
2257 }
2258
2259 fn add_audio_pro(&mut self, request: audio::AudioRequest<IpcReceiver<IpcBytes>>) -> audio::AudioId {
2260 self.audio_cache.add_pro(request)
2261 }
2262
2263 fn forget_audio(&mut self, id: audio::AudioId) {
2264 self.audio_cache.forget(id)
2265 }
2266
2267 fn open_audio_output(&mut self, request: audio::AudioOutputRequest) {
2268 self.audio_cache.open_output(request)
2269 }
2270
2271 fn update_audio_output(&mut self, request: audio::AudioOutputUpdateRequest) {
2272 self.audio_cache.update_output(request)
2273 }
2274
2275 fn close_audio_output(&mut self, id: audio::AudioOutputId) {
2276 self.audio_cache.close_output(id)
2277 }
2278
2279 fn cue_audio(&mut self, request: audio::AudioPlayRequest) -> audio::AudioPlayId {
2280 self.audio_cache.play(request)
2281 }
2282
2283 fn encode_audio(&mut self, _request: audio::AudioEncodeRequest) -> audio::AudioEncodeId {
2284 audio::AudioEncodeId::INVALID
2285 }
2286
2287 fn add_video(&mut self, request: video::VideoRequest<IpcReadHandle>) -> video::VideoId {
2288 let _ = request;
2289 video::VideoId::INVALID
2290 }
2291
2292 fn add_video_pro(&mut self, request: video::VideoRequest<IpcReceiver<IpcBytes>>) -> video::VideoId {
2293 let _ = request;
2294 video::VideoId::INVALID
2295 }
2296
2297 fn forget_video(&mut self, id: video::VideoId) {
2298 let _ = id;
2299 }
2300
2301 fn use_video(&mut self, id: WindowId, video_id: video::VideoId) -> video::VideoTextureId {
2302 let _ = (id, video_id);
2303 video::VideoTextureId::INVALID
2304 }
2305
2306 fn delete_video_use(&mut self, id: WindowId, texture_id: video::VideoTextureId) {
2308 let _ = (id, texture_id);
2309 }
2310
2311 fn add_font_face(&mut self, id: WindowId, bytes: font::IpcFontBytes, index: u32) -> FontFaceId {
2312 with_window_or_surface!(self, id, |w| w.add_font_face(bytes, index), || FontFaceId::INVALID)
2313 }
2314
2315 fn delete_font_face(&mut self, id: WindowId, font_face_id: FontFaceId) {
2316 with_window_or_surface!(self, id, |w| w.delete_font_face(font_face_id), || ())
2317 }
2318
2319 fn add_font(
2320 &mut self,
2321 id: WindowId,
2322 font_face_id: FontFaceId,
2323 glyph_size: Px,
2324 options: FontOptions,
2325 variations: Vec<(FontVariationName, f32)>,
2326 ) -> FontId {
2327 with_window_or_surface!(self, id, |w| w.add_font(font_face_id, glyph_size, options, variations), || {
2328 FontId::INVALID
2329 })
2330 }
2331
2332 fn delete_font(&mut self, id: WindowId, font_id: FontId) {
2333 with_window_or_surface!(self, id, |w| w.delete_font(font_id), || ())
2334 }
2335
2336 fn set_capture_mode(&mut self, id: WindowId, enabled: bool) {
2337 self.with_window(id, |w| w.set_capture_mode(enabled), || ())
2338 }
2339
2340 fn frame_image(&mut self, id: WindowId, mask: Option<ImageMaskMode>) -> ImageId {
2341 with_window_or_surface!(self, id, |w| w.frame_image(&mut self.image_cache, mask), || ImageId::INVALID)
2342 }
2343
2344 fn frame_image_rect(&mut self, id: WindowId, rect: PxRect, mask: Option<ImageMaskMode>) -> ImageId {
2345 with_window_or_surface!(self, id, |w| w.frame_image_rect(&mut self.image_cache, rect, mask), || {
2346 ImageId::INVALID
2347 })
2348 }
2349
2350 fn render(&mut self, id: WindowId, frame: FrameRequest) {
2351 with_window_or_surface!(self, id, |w| w.render(frame), || ())
2352 }
2353
2354 fn render_update(&mut self, id: WindowId, frame: FrameUpdateRequest) {
2355 with_window_or_surface!(self, id, |w| w.render_update(frame), || ())
2356 }
2357
2358 fn access_update(&mut self, id: WindowId, update: access::AccessTreeUpdate) {
2359 if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
2360 s.access_update(update, &self.app_sender);
2361 }
2362 }
2363
2364 fn message_dialog(&mut self, id: WindowId, dialog: MsgDialog) -> DialogId {
2365 let r_id = self.dialog_id_gen.incr();
2366 if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
2367 s.message_dialog(dialog, r_id, self.app_sender.clone());
2368 } else {
2369 let r = MsgDialogResponse::Error(Txt::from_static("window not found"));
2370 let _ = self.app_sender.send(AppEvent::Notify(Event::MsgDialogResponse(r_id, r)));
2371 }
2372 r_id
2373 }
2374
2375 fn file_dialog(&mut self, id: WindowId, dialog: FileDialog) -> DialogId {
2376 let r_id = self.dialog_id_gen.incr();
2377 if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
2378 s.file_dialog(dialog, r_id, self.app_sender.clone());
2379 } else {
2380 let r = MsgDialogResponse::Error(Txt::from_static("window not found"));
2381 let _ = self.app_sender.send(AppEvent::Notify(Event::MsgDialogResponse(r_id, r)));
2382 };
2383 r_id
2384 }
2385
2386 fn notification_dialog(&mut self, dialog: dialog::Notification) -> DialogId {
2387 let id = self.dialog_id_gen.incr();
2388 self.notifications.notification_dialog(&self.app_sender, id, dialog);
2389 id
2390 }
2391
2392 fn update_notification(&mut self, id: DialogId, dialog: dialog::Notification) {
2393 self.notifications.update_notification(&self.app_sender, id, dialog);
2394 }
2395
2396 #[cfg(windows)]
2397 fn read_clipboard(
2398 &mut self,
2399 mut data_type: Vec<clipboard::ClipboardType>,
2400 _first: bool,
2401 ) -> Result<Vec<clipboard::ClipboardData>, clipboard::ClipboardError> {
2402 if data_type.is_empty() {
2403 return Ok(vec![]);
2404 }
2405
2406 let single = match data_type.remove(0) {
2407 clipboard::ClipboardType::Text => {
2408 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2409
2410 clipboard_win::get(clipboard_win::formats::Unicode)
2411 .map_err(util::clipboard_win_to_clip)
2412 .map(|s: String| clipboard::ClipboardData::Text(Txt::from_str(&s)))
2413 }
2414 clipboard::ClipboardType::Image => {
2415 use zng_txt::ToTxt as _;
2416
2417 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2418
2419 let bitmap = clipboard_win::get(clipboard_win::formats::Bitmap).map_err(util::clipboard_win_to_clip)?;
2420
2421 let id = self.image_cache.add(ImageRequest::new(
2422 image::ImageDataFormat::FileExtension(Txt::from_str("bmp")),
2423 IpcBytes::from_vec_blocking(bitmap)
2424 .map_err(|e| clipboard::ClipboardError::Other(e.to_txt()))?
2425 .into(),
2426 u64::MAX,
2427 None,
2428 None,
2429 ));
2430 Ok(clipboard::ClipboardData::Image(id))
2431 }
2432 clipboard::ClipboardType::Paths => {
2433 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2434
2435 clipboard_win::get(clipboard_win::formats::FileList)
2436 .map_err(util::clipboard_win_to_clip)
2437 .map(clipboard::ClipboardData::Paths)
2438 }
2439 clipboard::ClipboardType::Extension(_) => Err(clipboard::ClipboardError::NotSupported),
2440 _ => Err(clipboard::ClipboardError::NotSupported),
2441 };
2442 single.map(|d| vec![d])
2443 }
2444
2445 #[cfg(windows)]
2446 fn write_clipboard(&mut self, mut data: Vec<clipboard::ClipboardData>) -> Result<usize, clipboard::ClipboardError> {
2447 use zng_txt::formatx;
2448
2449 if data.is_empty() {
2450 return Ok(0);
2451 }
2452
2453 let r = match data.remove(0) {
2454 clipboard::ClipboardData::Text(t) => {
2455 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2456
2457 clipboard_win::set(clipboard_win::formats::Unicode, t).map_err(util::clipboard_win_to_clip)
2458 }
2459 clipboard::ClipboardData::Image(id) => {
2460 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2461
2462 if let Some(img) = self.image_cache.get(id) {
2463 let mut bmp = vec![];
2464 img.encode(vec![], ::image::ImageFormat::Bmp, &mut std::io::Cursor::new(&mut bmp))
2465 .map_err(|e| clipboard::ClipboardError::Other(formatx!("{e:?}")))?;
2466 clipboard_win::set(clipboard_win::formats::Bitmap, bmp).map_err(util::clipboard_win_to_clip)
2467 } else {
2468 Err(clipboard::ClipboardError::Other(Txt::from_str("image not found")))
2469 }
2470 }
2471 clipboard::ClipboardData::Paths(l) => {
2472 use clipboard_win::Setter;
2473 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2474
2475 let strs = l.into_iter().map(|p| p.display().to_string()).collect::<Vec<String>>();
2477 clipboard_win::formats::FileList
2478 .write_clipboard(&strs)
2479 .map_err(util::clipboard_win_to_clip)
2480 }
2481 clipboard::ClipboardData::Extension { .. } => Err(clipboard::ClipboardError::NotSupported),
2482 _ => Err(clipboard::ClipboardError::NotSupported),
2483 };
2484
2485 r.map(|()| 1)
2486 }
2487
2488 #[cfg(not(any(windows, target_os = "android")))]
2489 fn read_clipboard(
2490 &mut self,
2491 mut data_type: Vec<clipboard::ClipboardType>,
2492 _first: bool,
2493 ) -> Result<Vec<clipboard::ClipboardData>, clipboard::ClipboardError> {
2494 if data_type.is_empty() {
2495 return Ok(vec![]);
2496 }
2497
2498 use zng_txt::ToTxt as _;
2499 let single = match data_type.remove(0) {
2500 clipboard::ClipboardType::Text => self
2501 .arboard()?
2502 .get_text()
2503 .map_err(util::arboard_to_clip)
2504 .map(|s| clipboard::ClipboardData::Text(zng_txt::Txt::from(s))),
2505 clipboard::ClipboardType::Image => {
2506 let bitmap = self.arboard()?.get_image().map_err(util::arboard_to_clip)?;
2507 let mut data = bitmap.bytes.into_owned();
2508 for rgba in data.chunks_exact_mut(4) {
2509 rgba.swap(0, 2); }
2511 let id = self.image_cache.add(image::ImageRequest::new(
2512 image::ImageDataFormat::Bgra8 {
2513 size: zng_unit::PxSize::new(Px(bitmap.width as _), Px(bitmap.height as _)),
2514 density: None,
2515 original_color_type: zng_view_api::image::ColorType::RGBA8,
2516 },
2517 IpcBytes::from_vec_blocking(data)
2518 .map_err(|e| clipboard::ClipboardError::Other(e.to_txt()))?
2519 .into(),
2520 u64::MAX,
2521 None,
2522 None,
2523 ));
2524 Ok(clipboard::ClipboardData::Image(id))
2525 }
2526 clipboard::ClipboardType::Paths => self
2527 .arboard()?
2528 .get()
2529 .file_list()
2530 .map_err(util::arboard_to_clip)
2531 .map(clipboard::ClipboardData::Paths),
2532 clipboard::ClipboardType::Extension(_) => Err(clipboard::ClipboardError::NotSupported),
2533 _ => Err(clipboard::ClipboardError::NotSupported),
2534 };
2535
2536 single.map(|e| vec![e])
2537 }
2538
2539 #[cfg(not(any(windows, target_os = "android")))]
2540 fn write_clipboard(&mut self, mut data: Vec<clipboard::ClipboardData>) -> Result<usize, clipboard::ClipboardError> {
2541 if data.is_empty() {
2542 return Ok(0);
2543 }
2544
2545 let r = match data.remove(0) {
2546 clipboard::ClipboardData::Text(t) => self.arboard()?.set_text(t).map_err(util::arboard_to_clip),
2547 clipboard::ClipboardData::Image(id) => {
2548 self.arboard()?;
2549 if let Some(img) = self.image_cache.get(id) {
2550 let size = img.size();
2551 let mut data = img.pixels().clone().to_vec();
2552 for rgba in data.chunks_exact_mut(4) {
2553 rgba.swap(0, 2); }
2555 let board = self.arboard()?;
2556 let _ = board.set_image(arboard::ImageData {
2557 width: size.width.0 as _,
2558 height: size.height.0 as _,
2559 bytes: std::borrow::Cow::Owned(data),
2560 });
2561 Ok(())
2562 } else {
2563 Err(clipboard::ClipboardError::Other(zng_txt::Txt::from_static("image not found")))
2564 }
2565 }
2566 clipboard::ClipboardData::Paths(_) => Err(clipboard::ClipboardError::NotSupported),
2567 clipboard::ClipboardData::Extension { .. } => Err(clipboard::ClipboardError::NotSupported),
2568 _ => Err(clipboard::ClipboardError::NotSupported),
2569 };
2570
2571 r.map(|()| 1)
2572 }
2573
2574 #[cfg(target_os = "android")]
2575 fn read_clipboard(
2576 &mut self,
2577 data_type: Vec<clipboard::ClipboardType>,
2578 _first: bool,
2579 ) -> Result<Vec<clipboard::ClipboardData>, clipboard::ClipboardError> {
2580 if data_type.is_empty() {
2581 return Ok(vec![]);
2582 }
2583
2584 let _ = data_type;
2585 Err(clipboard::ClipboardError::Other(Txt::from_static(
2586 "clipboard not implemented for Android",
2587 )))
2588 }
2589
2590 #[cfg(target_os = "android")]
2591 fn write_clipboard(&mut self, data: Vec<clipboard::ClipboardData>) -> Result<usize, clipboard::ClipboardError> {
2592 if data.is_empty() {
2593 return Ok(0);
2594 }
2595
2596 let _ = data;
2597 Err(clipboard::ClipboardError::Other(Txt::from_static(
2598 "clipboard not implemented for Android",
2599 )))
2600 }
2601
2602 fn start_drag_drop(
2603 &mut self,
2604 id: WindowId,
2605 data: Vec<DragDropData>,
2606 allowed_effects: DragDropEffect,
2607 ) -> Result<DragDropId, DragDropError> {
2608 let _ = (id, data, allowed_effects);
2609 Err(DragDropError::NotSupported)
2610 }
2611
2612 fn cancel_drag_drop(&mut self, id: WindowId, drag_id: DragDropId) {
2613 let _ = (id, drag_id);
2614 }
2615
2616 fn drag_dropped(&mut self, id: WindowId, drop_id: DragDropId, applied: DragDropEffect) {
2617 let _ = (id, drop_id, applied);
2618 }
2619
2620 fn set_system_shutdown_warn(&mut self, id: WindowId, reason: Txt) {
2621 self.with_window(id, move |w| w.set_system_shutdown_warn(reason), || ())
2622 }
2623
2624 fn set_app_menu(&mut self, menu: menu::AppMenu) {
2625 let _ = menu;
2626 }
2627
2628 fn set_tray_icon(&mut self, indicator: menu::TrayIcon) {
2629 let _ = indicator;
2630 }
2631
2632 fn third_party_licenses(&mut self) -> Vec<zng_tp_licenses::LicenseUsed> {
2633 #[cfg(feature = "embed_licenses")]
2634 {
2635 zng_tp_licenses::decode_embedding!()
2636 }
2637 #[cfg(not(feature = "embed_licenses"))]
2638 {
2639 vec![]
2640 }
2641 }
2642
2643 fn app_extension(&mut self, extension_id: ApiExtensionId, extension_request: ApiExtensionPayload) -> ApiExtensionPayload {
2644 self.exts.call_command(extension_id, extension_request)
2645 }
2646
2647 fn window_extension(
2648 &mut self,
2649 id: WindowId,
2650 extension_id: ApiExtensionId,
2651 extension_request: ApiExtensionPayload,
2652 ) -> ApiExtensionPayload {
2653 self.with_window(
2654 id,
2655 |w| w.window_extension(extension_id, extension_request),
2656 || ApiExtensionPayload::invalid_request(extension_id, "window not found"),
2657 )
2658 }
2659
2660 fn render_extension(
2661 &mut self,
2662 id: WindowId,
2663 extension_id: ApiExtensionId,
2664 extension_request: ApiExtensionPayload,
2665 ) -> ApiExtensionPayload {
2666 with_window_or_surface!(self, id, |w| w.render_extension(extension_id, extension_request), || {
2667 ApiExtensionPayload::invalid_request(extension_id, "renderer not found")
2668 })
2669 }
2670
2671 fn ping(&mut self, count: u16) -> u16 {
2672 self.notify(Event::Pong(count));
2673 count
2674 }
2675}
2676
2677#[derive(Debug)]
2679#[allow(clippy::large_enum_variant)]
2680pub(crate) enum AppEvent {
2681 Request,
2683 Notify(Event),
2685 #[cfg_attr(not(windows), allow(unused))]
2687 RefreshMonitors,
2688
2689 #[cfg_attr(not(windows), allow(unused))]
2691 WinitFocused(winit::window::WindowId, bool),
2692
2693 ParentProcessExited,
2695
2696 ImageCanRender(ImageDecoded),
2698
2699 #[cfg_attr(not(feature = "_audio_any"), allow(unused))]
2701 AudioCanPlay(audio::AudioId, AudioTrack),
2702
2703 SetDeviceEventsFilter(DeviceEventsFilter),
2705
2706 #[allow(unused)]
2708 MonitorPowerChanged,
2709}
2710
2711#[allow(clippy::large_enum_variant)] #[derive(Debug)]
2717enum RequestEvent {
2718 Request(Request),
2720 FrameReady(WindowId, FrameReadyMsg),
2722}
2723
2724#[derive(Debug)]
2725pub(crate) struct FrameReadyMsg {
2726 pub composite_needed: bool,
2727}
2728
2729#[derive(Clone)]
2731pub(crate) enum AppEventSender {
2732 Headed(EventLoopProxy<AppEvent>, Sender<RequestEvent>),
2733 Headless(Sender<AppEvent>, Sender<RequestEvent>),
2734}
2735impl AppEventSender {
2736 fn send(&self, ev: AppEvent) -> Result<(), ChannelError> {
2738 match self {
2739 AppEventSender::Headed(p, _) => p.send_event(ev).map_err(ChannelError::disconnected_by),
2740 AppEventSender::Headless(p, _) => p.send_blocking(ev),
2741 }
2742 }
2743
2744 fn request(&self, req: Request) -> Result<(), ChannelError> {
2746 match self {
2747 AppEventSender::Headed(_, p) => p.send_blocking(RequestEvent::Request(req)),
2748 AppEventSender::Headless(_, p) => p.send_blocking(RequestEvent::Request(req)),
2749 }?;
2750 self.send(AppEvent::Request)
2751 }
2752
2753 fn frame_ready(&self, window_id: WindowId, msg: FrameReadyMsg) -> Result<(), ChannelError> {
2755 match self {
2756 AppEventSender::Headed(_, p) => p.send_blocking(RequestEvent::FrameReady(window_id, msg)),
2757 AppEventSender::Headless(_, p) => p.send_blocking(RequestEvent::FrameReady(window_id, msg)),
2758 }?;
2759 self.send(AppEvent::Request)
2760 }
2761}
2762
2763pub(crate) struct WrNotifier {
2765 id: WindowId,
2766 sender: AppEventSender,
2767}
2768impl WrNotifier {
2769 pub fn create(id: WindowId, sender: AppEventSender) -> Box<dyn RenderNotifier> {
2770 Box::new(WrNotifier { id, sender })
2771 }
2772}
2773impl RenderNotifier for WrNotifier {
2774 fn clone(&self) -> Box<dyn RenderNotifier> {
2775 Box::new(Self {
2776 id: self.id,
2777 sender: self.sender.clone(),
2778 })
2779 }
2780
2781 fn wake_up(&self, _: bool) {}
2782
2783 fn new_frame_ready(&self, _: DocumentId, _: FramePublishId, params: &FrameReadyParams) {
2784 let msg = FrameReadyMsg {
2786 composite_needed: params.render,
2787 };
2788 let _ = self.sender.frame_ready(self.id, msg);
2789 }
2790}
2791
2792#[cfg(target_arch = "wasm32")]
2793compile_error!("zng-view does not support Wasm");