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