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