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, 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};
151#[cfg(feature = "image_cur")]
152use zng_view_api::api_extension::ApiExtensionName;
153use zng_view_api::{
154 ViewProcessInfo,
155 api_extension::{ApiExtensionId, ApiExtensionPayload},
156 dialog::{DialogId, FileDialog, MsgDialog, MsgDialogResponse},
157 drag_drop::*,
158 font::{FontFaceId, FontId, FontOptions, FontVariationName},
159 image::{ImageDecoded, ImageEncodeId, ImageEncodeRequest, ImageId, ImageMaskMode, ImageRequest, ImageTextureId},
160 keyboard::{Key, KeyCode, KeyState},
161 mouse::ButtonId,
162 raw_input::{InputDeviceCapability, InputDeviceEvent, InputDeviceId, InputDeviceInfo},
163 touch::{TouchId, TouchUpdate},
164 window::{
165 CursorIcon, CursorImage, EventCause, EventFrameRendered, FocusIndicator, FrameRequest, FrameUpdateRequest, FrameWaitId,
166 HeadlessOpenData, HeadlessRequest, MonitorId, MonitorInfo, VideoMode, WindowChanged, WindowId, WindowOpenData, WindowRequest,
167 WindowState, WindowStateAll,
168 },
169 *,
170};
171
172use rustc_hash::FxHashMap;
173
174use crate::{
175 audio_cache::{AudioCache, AudioTrack},
176 notification::NotificationService,
177};
178
179#[cfg(ipc)]
180zng_env::on_process_start!(|args| {
181 if std::env::var("ZNG_VIEW_NO_INIT_START").is_err() {
182 if !zng_env::about().is_test {
183 if args.yield_count == 0 {
184 return args.yield_once();
186 }
187 view_process_main();
188 } else {
189 tracing::debug!("view-process not inited in test app");
190 }
191 }
192});
193
194#[cfg(ipc)]
202pub fn view_process_main() {
203 let config = match ViewConfig::from_env() {
204 Some(c) => c,
205 None => return,
206 };
207
208 zng_env::set_process_name("view-process");
209
210 std::panic::set_hook(Box::new(init_abort));
211 config.assert_version(false);
212 let c = ipc::connect_view_process(config.server_name).expect("failed to connect to app-process");
213
214 let mut ext = ViewExtensions::new();
215 for e in extensions::VIEW_EXTENSIONS {
216 e(&mut ext);
217 }
218
219 if config.headless {
220 App::run_headless(c, ext);
221 } else {
222 App::run_headed(c, ext);
223 }
224
225 zng_env::exit(0)
226}
227
228#[cfg(ipc)]
229#[doc(hidden)]
230#[unsafe(no_mangle)] pub extern "C" fn extern_view_process_main(patch: &StaticPatch) {
232 std::panic::set_hook(Box::new(ffi_abort));
233
234 unsafe {
237 patch.install();
238 }
239
240 view_process_main()
241}
242
243pub fn run_same_process(run_app: impl FnOnce() + Send + 'static) {
264 run_same_process_extended(run_app, ViewExtensions::new)
265}
266
267pub fn run_same_process_extended(run_app: impl FnOnce() + Send + 'static, ext: fn() -> ViewExtensions) {
271 let app_thread = thread::Builder::new()
272 .name("app".to_owned())
273 .spawn(move || {
274 if let Err(e) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(run_app)) {
276 thread::Builder::new()
277 .name("ensure-exit".into())
278 .stack_size(256 * 1024)
279 .spawn(|| {
280 thread::sleep(std::time::Duration::from_secs(5));
285 eprintln!("run_same_process did not exit after 5s of a fatal panic, exiting now");
286 zng_env::exit(101);
287 })
288 .expect("failed to spawn thread");
289 std::panic::resume_unwind(e);
291 }
292 })
293 .unwrap();
294
295 let config = ViewConfig::wait_same_process();
296 config.assert_version(true);
297
298 let c = ipc::connect_view_process(config.server_name).expect("failed to connect to app in same process");
299
300 let mut ext = ext();
301 for e in extensions::VIEW_EXTENSIONS {
302 e(&mut ext);
303 }
304
305 if config.headless {
306 App::run_headless(c, ext);
307 } else {
308 App::run_headed(c, ext);
309 }
310
311 if let Err(p) = app_thread.join() {
312 std::panic::resume_unwind(p);
313 }
314}
315
316#[cfg(ipc)]
317#[doc(hidden)]
318#[unsafe(no_mangle)] pub extern "C" fn extern_run_same_process(patch: &StaticPatch, run_app: extern "C" fn()) {
320 std::panic::set_hook(Box::new(ffi_abort));
321
322 unsafe {
325 patch.install();
326 }
327
328 run_same_process(move || run_app())
329}
330#[cfg(ipc)]
331fn init_abort(info: &std::panic::PanicHookInfo) {
332 panic_hook(info, "note: aborting to respawn");
333}
334#[cfg(ipc)]
335fn ffi_abort(info: &std::panic::PanicHookInfo) {
336 panic_hook(info, "note: aborting to avoid unwind across FFI");
337}
338#[cfg(ipc)]
339fn panic_hook(info: &std::panic::PanicHookInfo, details: &str) {
340 let panic = util::SuppressedPanic::from_hook(info, std::backtrace::Backtrace::force_capture());
343
344 if crate::util::suppress_panic() {
345 crate::util::set_suppressed_panic(panic);
346 } else {
347 eprintln!("{panic}\n{details}");
348 zng_env::exit(101) }
350}
351
352pub(crate) struct App {
354 headless: bool,
355
356 exts: ViewExtensions,
357
358 gl_manager: GlContextManager,
359 winit_loop: util::WinitEventLoop,
360 app_sender: AppEventSender,
361 request_recv: Receiver<RequestEvent>,
362
363 response_sender: ipc::ResponseSender,
364 event_sender: ipc::EventSender,
365
366 image_cache: ImageCache,
367 audio_cache: AudioCache,
368
369 generation: ViewProcessGen,
370 device_events_filter: DeviceEventsFilter,
371
372 windows: Vec<Window>,
373 surfaces: Vec<Surface>,
374
375 monitor_id_gen: MonitorId,
376 monitor_ids: Vec<(MonitorId, MonitorHandle)>,
377 monitors: Vec<(MonitorId, MonitorInfo)>,
378
379 device_id_gen: InputDeviceId,
380 devices: Vec<(InputDeviceId, winit::event::DeviceId, InputDeviceInfo)>,
381
382 dialog_id_gen: DialogId,
383
384 resize_frame_wait_id_gen: FrameWaitId,
385
386 coalescing_event: Option<(Event, Instant)>,
387 cursor_entered_expect_move: Vec<WindowId>,
393
394 #[cfg(windows)]
395 skip_ralt: bool,
396
397 pressed_modifiers: FxHashMap<(Key, KeyLocation), (InputDeviceId, KeyCode)>,
398 pending_modifiers_update: Option<ModifiersState>,
399 pending_modifiers_focus_clear: bool,
400
401 #[cfg(not(any(windows, target_os = "android")))]
402 arboard: Option<arboard::Clipboard>,
403
404 low_memory_watcher: Option<low_memory::LowMemoryWatcher>,
405 last_pull_event: Instant,
406
407 config_listener_exit: Option<Box<dyn FnOnce()>>,
408
409 notifications: NotificationService,
410
411 app_state: AppState,
412 drag_drop_hovered: Option<(WindowId, DipPoint)>,
413 drag_drop_next_move: Option<(Instant, PathBuf)>,
414 exited: bool,
415}
416impl fmt::Debug for App {
417 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418 f.debug_struct("HeadlessBackend")
419 .field("app_state", &self.app_state)
420 .field("generation", &self.generation)
421 .field("device_events_filter", &self.device_events_filter)
422 .field("windows", &self.windows)
423 .field("surfaces", &self.surfaces)
424 .finish_non_exhaustive()
425 }
426}
427impl winit::application::ApplicationHandler<AppEvent> for App {
428 fn resumed(&mut self, winit_loop: &ActiveEventLoop) {
429 if let AppState::Suspended = self.app_state {
430 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
431
432 self.exts.resumed();
433 self.generation = self.generation.next();
434
435 self.init(self.generation.next(), true, self.headless);
436
437 winit_loop_guard.unset(&mut self.winit_loop);
438 } else {
439 self.exts.init(&self.app_sender);
440 }
441 self.app_state = AppState::Resumed;
442
443 self.update_pull_events(winit_loop);
444 }
445
446 fn window_event(&mut self, winit_loop: &ActiveEventLoop, window_id: winit::window::WindowId, event: WindowEvent) {
447 let i = if let Some((i, _)) = self.windows.iter_mut().enumerate().find(|(_, w)| w.window_id() == window_id) {
448 i
449 } else {
450 return;
451 };
452
453 let _s = tracing::trace_span!("on_window_event", ?event).entered();
454
455 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
456
457 self.windows[i].on_window_event(&event);
458
459 let id = self.windows[i].id();
460 let scale_factor = self.windows[i].scale_factor();
461
462 #[cfg(any(
465 target_os = "linux",
466 target_os = "dragonfly",
467 target_os = "freebsd",
468 target_os = "netbsd",
469 target_os = "openbsd"
470 ))]
471 let modal_dialog_active = self.windows[i].modal_dialog_active();
472 #[cfg(any(
473 target_os = "linux",
474 target_os = "dragonfly",
475 target_os = "freebsd",
476 target_os = "netbsd",
477 target_os = "openbsd"
478 ))]
479 macro_rules! linux_modal_dialog_bail {
480 () => {
481 if modal_dialog_active {
482 winit_loop_guard.unset(&mut self.winit_loop);
483 return;
484 }
485 };
486 }
487 #[cfg(not(any(
488 target_os = "linux",
489 target_os = "dragonfly",
490 target_os = "freebsd",
491 target_os = "netbsd",
492 target_os = "openbsd"
493 )))]
494 macro_rules! linux_modal_dialog_bail {
495 () => {};
496 }
497
498 match event {
499 WindowEvent::RedrawRequested => {
500 self.windows[i].redraw();
501
502 #[cfg(any(
505 target_os = "linux",
506 target_os = "dragonfly",
507 target_os = "freebsd",
508 target_os = "netbsd",
509 target_os = "openbsd"
510 ))]
511 if let Some(handle) = self.windows[i].monitor_change() {
512 self.refresh_monitors();
513 let m_id = self.monitor_handle_to_id(&handle);
514 let mut c = WindowChanged::monitor_changed(id, m_id, EventCause::System);
515 c.scale_factor = self.windows[i].scale_factor_change();
516 c.refresh_rate = self.windows[i].refresh_rate_change();
517 self.notify(Event::WindowChanged(c));
518 }
519 }
520 WindowEvent::Resized(_) => {
521 let mut size = if let Some(s) = self.windows[i].resized() {
522 s
523 } else {
524 if let Some(s) = self.windows[i].scale_factor_change() {
526 let mut c = WindowChanged::new(id, None, None, None, None, None, None, EventCause::System);
527 c.scale_factor = Some(s);
528 if let Some(handle) = self.windows[i].monitor_change() {
529 self.refresh_monitors();
530 let m_id = self.monitor_handle_to_id(&handle);
531 c.monitor = Some(m_id);
532 c.refresh_rate = self.windows[i].refresh_rate_change();
533 }
534 self.notify(Event::WindowChanged(c));
535 }
536
537 winit_loop_guard.unset(&mut self.winit_loop);
538 return;
539 };
540
541 let deadline = Instant::now() + Duration::from_millis(300);
545
546 if self.windows[i].is_rendering_frame() {
548 tracing::debug!("resize requested while still rendering");
549
550 while let Ok(req) = self.request_recv.recv_deadline_blocking(deadline) {
552 match req {
553 RequestEvent::Request(req) => {
554 let rsp = self.respond(req);
555 if rsp.must_be_send() {
556 let _ = self.response_sender.send(rsp);
557 }
558 }
559 RequestEvent::FrameReady(id, msg) => {
560 self.on_frame_ready(id, msg);
561 if id == self.windows[i].id() {
562 break;
563 }
564 }
565 }
566 }
567
568 if let Some(s) = self.windows[i].resized() {
569 size = s;
570 }
571 }
572
573 let wait_id = Some(self.resize_frame_wait_id_gen.incr());
574 let mut c = WindowChanged::resized(id, size, EventCause::System, wait_id);
575 c.state = self.windows[i].state_change();
576 c.scale_factor = self.windows[i].scale_factor_change();
577 if let Some(handle) = self.windows[i].monitor_change() {
578 self.refresh_monitors();
579 let m_id = self.monitor_handle_to_id(&handle);
580 let mut c = WindowChanged::monitor_changed(id, m_id, EventCause::System);
581 c.refresh_rate = self.windows[i].refresh_rate_change();
582 self.notify(Event::WindowChanged(c));
583 }
584
585 if let Some(state) = self.windows[i].state_change() {
586 self.notify(Event::WindowChanged(WindowChanged::state_changed(id, state, EventCause::System)));
587 }
588
589 self.notify(Event::WindowChanged(c));
591
592 self.flush_coalesced();
593
594 let mut received_frame = false;
596 loop {
597 match self.request_recv.recv_deadline_blocking(deadline) {
598 Ok(req) => {
599 match req {
600 RequestEvent::Request(req) => {
601 received_frame = req.is_frame(id, wait_id);
602 if received_frame || req.affects_window_rect(id) {
603 let rsp = self.respond(req);
605 if rsp.must_be_send() {
606 let _ = self.response_sender.send(rsp);
607 }
608 break;
609 } else {
610 let rsp = self.respond(req);
612 if rsp.must_be_send() {
613 let _ = self.response_sender.send(rsp);
614 }
615 }
616 }
617 RequestEvent::FrameReady(id, msg) => self.on_frame_ready(id, msg),
618 }
619 }
620
621 Err(ChannelError::Timeout) => {
622 break;
624 }
625 Err(e) => {
626 winit_loop_guard.unset(&mut self.winit_loop);
627 panic!("{e}");
628 }
629 }
630 }
631
632 if received_frame && deadline > Instant::now() {
634 while let Ok(req) = self.request_recv.recv_deadline_blocking(deadline) {
636 match req {
637 RequestEvent::Request(req) => {
638 let rsp = self.respond(req);
639 if rsp.must_be_send() {
640 let _ = self.response_sender.send(rsp);
641 }
642 }
643 RequestEvent::FrameReady(id, msg) => {
644 self.on_frame_ready(id, msg);
645 if id == self.windows[i].id() {
646 break;
647 }
648 }
649 }
650 }
651 }
652 }
653 WindowEvent::Moved(_) => {
654 let (global_position, position) = if let Some(p) = self.windows[i].moved() {
655 p
656 } else {
657 winit_loop_guard.unset(&mut self.winit_loop);
658 return;
659 };
660
661 let mut c = WindowChanged::moved(id, global_position, position, EventCause::System);
662 c.state = self.windows[i].state_change();
663
664 if let Some(handle) = self.windows[i].monitor_change() {
665 self.refresh_monitors();
666 let m_id = self.monitor_handle_to_id(&handle);
667 c.monitor = Some(m_id);
668 c.scale_factor = self.windows[i].scale_factor_change();
669 c.refresh_rate = self.windows[i].refresh_rate_change();
670 }
671 self.notify(Event::WindowChanged(c));
672 }
673 WindowEvent::CloseRequested => {
674 linux_modal_dialog_bail!();
675 self.notify(Event::WindowCloseRequested(id))
676 }
677 WindowEvent::Destroyed => {
678 self.windows.remove(i);
679 self.notify(Event::WindowClosed(id));
680 }
681 WindowEvent::HoveredFile(file) => {
682 linux_modal_dialog_bail!();
683
684 if self.device_events_filter.input.is_empty() {
688 winit_loop.listen_device_events(winit::event_loop::DeviceEvents::Always);
689 }
690 self.drag_drop_hovered = Some((id, DipPoint::splat(Dip::new(-1000))));
691 self.notify(Event::DragHovered {
692 window: id,
693 data: vec![DragDropData::Paths(vec![file])],
694 allowed: DragDropEffect::all(),
695 });
696 }
697 WindowEvent::DroppedFile(file) => {
698 linux_modal_dialog_bail!();
699
700 if self.device_events_filter.input.is_empty() {
701 winit_loop.listen_device_events(winit::event_loop::DeviceEvents::Never);
702 }
703
704 let mut delay_to_next_move = true;
705
706 if let Some(position) = self.windows[i].drag_drop_cursor_pos() {
708 self.notify(Event::DragMoved {
709 window: id,
710 coalesced_pos: vec![],
711 position,
712 });
713 delay_to_next_move = false;
714 } else if let Some((_, pos)) = self.drag_drop_hovered {
715 delay_to_next_move = pos.x < Dip::new(0);
716 }
717
718 if delay_to_next_move {
719 self.drag_drop_next_move = Some((Instant::now(), file));
720 } else {
721 self.notify(Event::DragDropped {
722 window: id,
723 data: vec![DragDropData::Paths(vec![file])],
724 allowed: DragDropEffect::all(),
725 drop_id: DragDropId(0),
726 });
727 }
728 }
729 WindowEvent::HoveredFileCancelled => {
730 linux_modal_dialog_bail!();
731
732 self.drag_drop_hovered = None;
733 if self.device_events_filter.input.is_empty() {
734 winit_loop.listen_device_events(winit::event_loop::DeviceEvents::Never);
735 }
736
737 if self.drag_drop_next_move.is_none() {
738 self.notify(Event::DragCancelled { window: id });
740 }
741 }
742 WindowEvent::Focused(mut focused) => {
743 if self.windows[i].focused_changed(&mut focused) {
744 if focused {
745 self.notify(Event::FocusChanged { prev: None, new: Some(id) });
746
747 if let Some(state) = self.windows[i].state_change() {
749 self.notify(Event::WindowChanged(WindowChanged::state_changed(id, state, EventCause::System)));
750 }
751 } else {
752 self.pending_modifiers_focus_clear = true;
753 self.notify(Event::FocusChanged { prev: Some(id), new: None });
754 }
755 }
756 }
757 WindowEvent::KeyboardInput {
758 device_id,
759 event,
760 is_synthetic,
761 } => {
762 linux_modal_dialog_bail!();
763
764 if !is_synthetic && self.windows[i].is_focused() {
765 #[cfg(windows)]
767 if self.skip_ralt
768 && let winit::keyboard::PhysicalKey::Code(winit::keyboard::KeyCode::AltRight) = event.physical_key
769 {
770 winit_loop_guard.unset(&mut self.winit_loop);
771 return;
772 }
773
774 let state = util::element_state_to_key_state(event.state);
775 #[cfg(not(target_os = "android"))]
776 let key = util::winit_key_to_key(event.key_without_modifiers());
777 let key_modified = util::winit_key_to_key(event.logical_key);
778 #[cfg(target_os = "android")]
779 let key = key_modified.clone();
780 let key_code = util::winit_physical_key_to_key_code(event.physical_key);
781 let key_location = util::winit_key_location_to_zng(event.location);
782 let d_id = self.input_device_id(device_id, InputDeviceCapability::KEY);
783
784 let mut send_event = true;
785
786 if key.is_modifier() {
787 match state {
788 KeyState::Pressed => {
789 send_event = self
790 .pressed_modifiers
791 .insert((key.clone(), key_location), (d_id, key_code))
792 .is_none();
793 }
794 KeyState::Released => send_event = self.pressed_modifiers.remove(&(key.clone(), key_location)).is_some(),
795 }
796 }
797
798 if send_event {
799 self.notify(Event::KeyboardInput {
800 window: id,
801 device: d_id,
802 key_code,
803 key_location,
804 state,
805 text: match event.text {
806 Some(s) => Txt::from_str(s.as_str()),
807 #[cfg(target_os = "android")]
808 None => match (state, &key) {
809 (KeyState::Pressed, Key::Char(c)) => Txt::from(*c),
810 (KeyState::Pressed, Key::Str(s)) => s.clone(),
811 _ => Txt::default(),
812 },
813 #[cfg(not(target_os = "android"))]
814 None => Txt::default(),
815 },
816 key,
817 key_modified,
818 });
819 }
820 }
821 }
822 WindowEvent::ModifiersChanged(m) => {
823 linux_modal_dialog_bail!();
824 if self.windows[i].is_focused() {
825 self.pending_modifiers_update = Some(m.state());
826 }
827 }
828 WindowEvent::CursorMoved { device_id, position, .. } => {
829 linux_modal_dialog_bail!();
830
831 let px_p = position.to_px();
832 let p = px_p.to_dip(scale_factor);
833 let d_id = self.input_device_id(device_id, InputDeviceCapability::POINTER_MOTION);
834
835 let mut is_after_cursor_enter = false;
836 if let Some(i) = self.cursor_entered_expect_move.iter().position(|&w| w == id) {
837 self.cursor_entered_expect_move.remove(i);
838 is_after_cursor_enter = true;
839 }
840
841 if self.windows[i].cursor_moved(p, d_id) || is_after_cursor_enter {
842 self.notify(Event::MouseMoved {
843 window: id,
844 device: d_id,
845 coalesced_pos: vec![],
846 position: p,
847 });
848 }
849
850 if let Some((drop_moment, file)) = self.drag_drop_next_move.take()
851 && drop_moment.elapsed() < Duration::from_millis(300)
852 {
853 let window_id = self.windows[i].id();
854 self.notify(Event::DragMoved {
855 window: window_id,
856 coalesced_pos: vec![],
857 position: p,
858 });
859 self.notify(Event::DragDropped {
860 window: window_id,
861 data: vec![DragDropData::Paths(vec![file])],
862 allowed: DragDropEffect::all(),
863 drop_id: DragDropId(0),
864 });
865 }
866 }
867 WindowEvent::CursorEntered { device_id } => {
868 linux_modal_dialog_bail!();
869 if self.windows[i].cursor_entered() {
870 let d_id = self.input_device_id(device_id, InputDeviceCapability::POINTER_MOTION);
871 self.notify(Event::MouseEntered { window: id, device: d_id });
872 self.cursor_entered_expect_move.push(id);
873 }
874 }
875 WindowEvent::CursorLeft { device_id } => {
876 linux_modal_dialog_bail!();
877 if self.windows[i].cursor_left() {
878 let d_id = self.input_device_id(device_id, InputDeviceCapability::POINTER_MOTION);
879 self.notify(Event::MouseLeft { window: id, device: d_id });
880
881 if let Some(i) = self.cursor_entered_expect_move.iter().position(|&w| w == id) {
883 self.cursor_entered_expect_move.remove(i);
884 }
885 }
886 }
887 WindowEvent::MouseWheel {
888 device_id, delta, phase, ..
889 } => {
890 linux_modal_dialog_bail!();
891 let d_id = self.input_device_id(device_id, InputDeviceCapability::SCROLL_MOTION);
892 self.notify(Event::MouseWheel {
893 window: id,
894 device: d_id,
895 delta: util::winit_mouse_wheel_delta_to_zng(delta),
896 phase: util::winit_touch_phase_to_zng(phase),
897 });
898 }
899 WindowEvent::MouseInput {
900 device_id, state, button, ..
901 } => {
902 linux_modal_dialog_bail!();
903 let d_id = self.input_device_id(device_id, InputDeviceCapability::BUTTON);
904 self.notify(Event::MouseInput {
905 window: id,
906 device: d_id,
907 state: util::element_state_to_button_state(state),
908 button: util::winit_mouse_button_to_zng(button),
909 });
910 }
911 WindowEvent::TouchpadPressure {
912 device_id,
913 pressure,
914 stage,
915 } => {
916 linux_modal_dialog_bail!();
917 let d_id = self.input_device_id(device_id, InputDeviceCapability::empty());
918 self.notify(Event::TouchpadPressure {
919 window: id,
920 device: d_id,
921 pressure,
922 stage,
923 });
924 }
925 WindowEvent::AxisMotion { device_id, axis, value } => {
926 linux_modal_dialog_bail!();
927 let d_id = self.input_device_id(device_id, InputDeviceCapability::AXIS_MOTION);
928 self.notify(Event::AxisMotion {
929 window: id,
930 device: d_id,
931 axis: AxisId(axis),
932 value,
933 });
934 }
935 WindowEvent::Touch(t) => {
936 let d_id = self.input_device_id(t.device_id, InputDeviceCapability::empty());
937 let position = t.location.to_px().to_dip(scale_factor);
938
939 let notify = match t.phase {
940 winit::event::TouchPhase::Moved => self.windows[i].touch_moved(position, d_id, t.id),
941 winit::event::TouchPhase::Started => true,
942 winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => {
943 self.windows[i].touch_end(d_id, t.id);
944 true
945 }
946 };
947
948 if notify {
949 self.notify(Event::Touch {
950 window: id,
951 device: d_id,
952 touches: vec![TouchUpdate::new(
953 TouchId(t.id),
954 util::winit_touch_phase_to_zng(t.phase),
955 position,
956 t.force.map(util::winit_force_to_zng),
957 )],
958 });
959 }
960 }
961 WindowEvent::ScaleFactorChanged { .. } => {
962 self.refresh_monitors();
963 }
967 WindowEvent::Ime(ime) => {
968 linux_modal_dialog_bail!();
969
970 match ime {
971 winit::event::Ime::Preedit(s, c) => {
972 let caret = c.unwrap_or((s.len(), s.len()));
973 let ime = Ime::Preview(s.into(), caret);
974 self.notify(Event::Ime { window: id, ime });
975 }
976 winit::event::Ime::Commit(s) => {
977 let ime = Ime::Commit(s.into());
978 self.notify(Event::Ime { window: id, ime });
979 }
980 winit::event::Ime::Enabled => {}
981 winit::event::Ime::Disabled => {}
982 }
983 }
984 WindowEvent::ThemeChanged(_) => {}
985 WindowEvent::Occluded(_) => {}
986 WindowEvent::ActivationTokenDone { .. } => {}
987 WindowEvent::PinchGesture { .. } => {}
988 WindowEvent::RotationGesture { .. } => {}
989 WindowEvent::DoubleTapGesture { .. } => {}
990 WindowEvent::PanGesture { .. } => {}
991 }
992
993 winit_loop_guard.unset(&mut self.winit_loop);
994 }
995
996 fn new_events(&mut self, winit_loop: &ActiveEventLoop, cause: winit::event::StartCause) {
997 if let winit::event::StartCause::ResumeTimeReached { .. } = cause {
1001 self.update_pull_events(winit_loop);
1002 }
1003 }
1004
1005 fn user_event(&mut self, winit_loop: &ActiveEventLoop, ev: AppEvent) {
1006 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1007 match ev {
1008 AppEvent::Request => {
1009 while let Ok(Some(req)) = self.request_recv.try_recv() {
1010 match req {
1011 RequestEvent::Request(req) => {
1012 let rsp = self.respond(req);
1013 if rsp.must_be_send() && self.response_sender.send(rsp).is_err() {
1014 self.exited = true;
1016 self.winit_loop.exit();
1017 }
1018 }
1019 RequestEvent::FrameReady(wid, msg) => {
1020 self.on_frame_ready(wid, msg);
1021 }
1022 }
1023 }
1024 }
1025 AppEvent::Notify(ev) => self.notify(ev),
1026 AppEvent::WinitFocused(window_id, focused) => self.window_event(winit_loop, window_id, WindowEvent::Focused(focused)),
1027 AppEvent::RefreshMonitors => self.refresh_monitors(),
1028 AppEvent::ParentProcessExited => {
1029 self.exited = true;
1030 self.winit_loop.exit();
1031 }
1032 AppEvent::ImageCanRender(data) => {
1033 self.image_cache.on_image_can_render(data);
1034 }
1035 AppEvent::AudioCanPlay(id, data) => {
1036 self.audio_cache.on_audio_can_play(id, data);
1037 }
1038 AppEvent::MonitorPowerChanged => {
1039 for w in &mut self.windows {
1041 w.redraw();
1042 }
1043 }
1044 AppEvent::SetDeviceEventsFilter(filter) => {
1045 self.set_device_events_filter(filter, Some(winit_loop));
1046 }
1047 }
1048 winit_loop_guard.unset(&mut self.winit_loop);
1049 }
1050
1051 fn device_event(&mut self, winit_loop: &ActiveEventLoop, device_id: winit::event::DeviceId, event: DeviceEvent) {
1052 let filter = self.device_events_filter.input;
1053
1054 if !filter.is_empty() {
1055 let _s = tracing::trace_span!("on_device_event", ?event);
1056
1057 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1058
1059 match &event {
1060 DeviceEvent::Added => {
1061 let _ = self.input_device_id(device_id, InputDeviceCapability::empty());
1062 }
1064 DeviceEvent::Removed => {
1065 if let Some(i) = self.devices.iter().position(|(_, id, _)| *id == device_id) {
1066 self.devices.remove(i);
1067 self.notify_input_devices_changed();
1068 }
1069 }
1070 DeviceEvent::MouseMotion { delta } => {
1071 let cap = InputDeviceCapability::POINTER_MOTION;
1072 if filter.contains(cap) {
1073 let d_id = self.input_device_id(device_id, cap);
1074 self.notify(Event::InputDeviceEvent {
1075 device: d_id,
1076 event: InputDeviceEvent::PointerMotion {
1077 delta: euclid::vec2(delta.0, delta.1),
1078 },
1079 });
1080 }
1081 }
1082 DeviceEvent::MouseWheel { delta } => {
1083 let cap = InputDeviceCapability::SCROLL_MOTION;
1084 if filter.contains(cap) {
1085 let d_id = self.input_device_id(device_id, cap);
1086 self.notify(Event::InputDeviceEvent {
1087 device: d_id,
1088 event: InputDeviceEvent::ScrollMotion {
1089 delta: util::winit_mouse_wheel_delta_to_zng(*delta),
1090 },
1091 });
1092 }
1093 }
1094 DeviceEvent::Motion { axis, value } => {
1095 let cap = InputDeviceCapability::AXIS_MOTION;
1096 if filter.contains(cap) {
1097 let d_id = self.input_device_id(device_id, cap);
1098 self.notify(Event::InputDeviceEvent {
1099 device: d_id,
1100 event: InputDeviceEvent::AxisMotion {
1101 axis: AxisId(*axis),
1102 value: *value,
1103 },
1104 });
1105 }
1106 }
1107 DeviceEvent::Button { button, state } => {
1108 let cap = InputDeviceCapability::BUTTON;
1109 if filter.contains(cap) {
1110 let d_id = self.input_device_id(device_id, cap);
1111 self.notify(Event::InputDeviceEvent {
1112 device: d_id,
1113 event: InputDeviceEvent::Button {
1114 button: ButtonId(*button),
1115 state: util::element_state_to_button_state(*state),
1116 },
1117 });
1118 }
1119 }
1120 DeviceEvent::Key(k) => {
1121 let cap = InputDeviceCapability::KEY;
1122 if filter.contains(cap) {
1123 let d_id = self.input_device_id(device_id, cap);
1124 self.notify(Event::InputDeviceEvent {
1125 device: d_id,
1126 event: InputDeviceEvent::Key {
1127 key_code: util::winit_physical_key_to_key_code(k.physical_key),
1128 state: util::element_state_to_key_state(k.state),
1129 },
1130 });
1131 }
1132 }
1133 }
1134
1135 winit_loop_guard.unset(&mut self.winit_loop);
1136 }
1137
1138 if let Some((id, pos)) = &mut self.drag_drop_hovered
1139 && let DeviceEvent::MouseMotion { .. } = &event
1140 && let Some(win) = self.windows.iter().find(|w| w.id() == *id)
1141 && let Some(new_pos) = win.drag_drop_cursor_pos()
1142 && *pos != new_pos
1143 {
1144 *pos = new_pos;
1145 let event = Event::DragMoved {
1146 window: *id,
1147 coalesced_pos: vec![],
1148 position: *pos,
1149 };
1150 self.notify(event);
1151 }
1152 }
1153
1154 fn about_to_wait(&mut self, winit_loop: &ActiveEventLoop) {
1155 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1156
1157 self.finish_cursor_entered_move();
1158 self.update_modifiers();
1159 self.flush_coalesced();
1160 #[cfg(windows)]
1161 {
1162 self.skip_ralt = false;
1163 }
1164
1165 winit_loop_guard.unset(&mut self.winit_loop);
1166 }
1167
1168 fn suspended(&mut self, _: &ActiveEventLoop) {
1169 #[cfg(target_os = "android")]
1170 if let Some(w) = &self.windows.first() {
1171 self.notify(Event::FocusChanged {
1172 prev: Some(w.id()),
1173 new: None,
1174 });
1175 }
1176
1177 self.app_state = AppState::Suspended;
1178 self.windows.clear();
1179 self.surfaces.clear();
1180 self.image_cache.clear();
1181 self.exts.suspended();
1182
1183 self.notify(Event::Suspended);
1184 }
1185
1186 fn exiting(&mut self, event_loop: &ActiveEventLoop) {
1187 let _ = event_loop;
1188 if let Some(t) = self.config_listener_exit.take() {
1189 t();
1190 }
1191 }
1192
1193 fn memory_warning(&mut self, winit_loop: &ActiveEventLoop) {
1194 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1195
1196 self.image_cache.on_low_memory();
1197 self.audio_cache.on_low_memory();
1198 for w in &mut self.windows {
1199 w.on_low_memory();
1200 }
1201 for s in &mut self.surfaces {
1202 s.on_low_memory();
1203 }
1204 self.exts.on_low_memory();
1205 self.notify(Event::LowMemory);
1206
1207 winit_loop_guard.unset(&mut self.winit_loop);
1208 }
1209}
1210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1211enum AppState {
1212 PreInitSuspended,
1213 Resumed,
1214 Suspended,
1215}
1216impl App {
1217 fn set_device_events_filter(&mut self, filter: DeviceEventsFilter, t: Option<&ActiveEventLoop>) {
1218 self.device_events_filter = filter;
1219
1220 if let Some(t) = t {
1221 if !self.device_events_filter.input.is_empty() {
1222 t.listen_device_events(winit::event_loop::DeviceEvents::Always);
1223 } else {
1224 t.listen_device_events(winit::event_loop::DeviceEvents::Never);
1225 }
1226 }
1227 }
1228
1229 pub fn run_headless(ipc: ipc::ViewChannels, ext: ViewExtensions) {
1230 tracing::info!("running headless view-process");
1231
1232 gl::warmup();
1233
1234 let (app_sender, app_receiver) = channel::unbounded();
1235 let (request_sender, request_receiver) = channel::unbounded();
1236 let mut app = App::new(
1237 AppEventSender::Headless(app_sender, request_sender),
1238 ipc.response_sender,
1239 ipc.event_sender,
1240 request_receiver,
1241 ext,
1242 );
1243 app.headless = true;
1244
1245 let winit_span = tracing::trace_span!("winit::EventLoop::new").entered();
1246 #[cfg(not(target_os = "android"))]
1247 let event_loop = EventLoop::builder().build().unwrap();
1248 #[cfg(target_os = "android")]
1249 let event_loop = EventLoop::builder()
1250 .with_android_app(platform::android::android_app())
1251 .build()
1252 .unwrap();
1253 drop(winit_span);
1254
1255 let mut app = HeadlessApp {
1256 app,
1257 request_receiver: Some(ipc.request_receiver),
1258 app_receiver,
1259 };
1260 if let Err(e) = event_loop.run_app(&mut app) {
1261 if app.app.exited {
1262 tracing::error!("winit event loop error after app exit, {e}");
1268 } else {
1269 panic!("winit event loop error, {e}");
1270 }
1271 }
1272
1273 struct HeadlessApp {
1274 app: App,
1275 request_receiver: Option<ipc::RequestReceiver>,
1276 app_receiver: Receiver<AppEvent>,
1277 }
1278 impl winit::application::ApplicationHandler<()> for HeadlessApp {
1279 fn resumed(&mut self, winit_loop: &ActiveEventLoop) {
1280 let mut winit_loop_guard = self.app.winit_loop.set(winit_loop);
1281
1282 self.app.resumed(winit_loop);
1283 self.app.start_receiving(self.request_receiver.take().unwrap());
1284
1285 'app_loop: while !self.app.exited {
1286 match self.app_receiver.recv_blocking() {
1287 Ok(app_ev) => match app_ev {
1288 AppEvent::Request => {
1289 while let Ok(Some(request)) = self.app.request_recv.try_recv() {
1290 match request {
1291 RequestEvent::Request(request) => {
1292 let response = self.app.respond(request);
1293 if response.must_be_send() && self.app.response_sender.send(response).is_err() {
1294 self.app.exited = true;
1295 break 'app_loop;
1296 }
1297 }
1298 RequestEvent::FrameReady(id, msg) => {
1299 let r = if let Some(s) = self.app.surfaces.iter_mut().find(|s| s.id() == id) {
1300 Some(s.on_frame_ready(msg, &mut self.app.image_cache))
1301 } else {
1302 None
1303 };
1304 if let Some((frame_id, image)) = r {
1305 self.app.notify(Event::FrameRendered(EventFrameRendered::new(id, frame_id, image)));
1306 }
1307 }
1308 }
1309 }
1310 }
1311 AppEvent::Notify(ev) => {
1312 if self.app.event_sender.send(ev).is_err() {
1313 self.app.exited = true;
1314 break 'app_loop;
1315 }
1316 }
1317 AppEvent::RefreshMonitors => {
1318 panic!("no monitor info in headless mode")
1319 }
1320 AppEvent::WinitFocused(_, _) => {
1321 panic!("no winit event loop in headless mode")
1322 }
1323 AppEvent::ParentProcessExited => {
1324 self.app.exited = true;
1325 break 'app_loop;
1326 }
1327 AppEvent::ImageCanRender(data) => {
1328 self.app.image_cache.on_image_can_render(data);
1329 }
1330 AppEvent::AudioCanPlay(meta, data) => {
1331 self.app.audio_cache.on_audio_can_play(meta, data);
1332 }
1333 AppEvent::MonitorPowerChanged => {} AppEvent::SetDeviceEventsFilter(filter) => {
1335 self.app.set_device_events_filter(filter, None);
1336 }
1337 },
1338 Err(_) => {
1339 self.app.exited = true;
1340 break 'app_loop;
1341 }
1342 }
1343 }
1344
1345 self.app.winit_loop.exit();
1346
1347 winit_loop_guard.unset(&mut self.app.winit_loop);
1348 }
1349
1350 fn window_event(&mut self, _: &ActiveEventLoop, _: winit::window::WindowId, _: WindowEvent) {}
1351
1352 fn suspended(&mut self, event_loop: &ActiveEventLoop) {
1353 self.app.suspended(event_loop);
1354 }
1355 }
1356 }
1357
1358 pub fn run_headed(ipc: ipc::ViewChannels, ext: ViewExtensions) {
1359 tracing::info!("running headed view-process");
1360
1361 gl::warmup();
1362
1363 let winit_span = tracing::trace_span!("winit::EventLoop::new").entered();
1364 #[cfg(not(target_os = "android"))]
1365 let event_loop = EventLoop::with_user_event().build().unwrap();
1366 #[cfg(target_os = "android")]
1367 let event_loop = EventLoop::with_user_event()
1368 .with_android_app(platform::android::android_app())
1369 .build()
1370 .unwrap();
1371 drop(winit_span);
1372 let app_sender = event_loop.create_proxy();
1373
1374 let (request_sender, request_receiver) = channel::unbounded();
1375 let mut app = App::new(
1376 AppEventSender::Headed(app_sender, request_sender),
1377 ipc.response_sender,
1378 ipc.event_sender,
1379 request_receiver,
1380 ext,
1381 );
1382 app.start_receiving(ipc.request_receiver);
1383
1384 app.config_listener_exit = config::spawn_listener(app.app_sender.clone());
1385
1386 if let Err(e) = event_loop.run_app(&mut app) {
1387 if app.exited {
1388 tracing::error!("winit event loop error after app exit, {e}");
1389 } else {
1390 panic!("winit event loop error, {e}");
1391 }
1392 }
1393 }
1394
1395 fn new(
1396 app_sender: AppEventSender,
1397 response_sender: ipc::ResponseSender,
1398 event_sender: ipc::EventSender,
1399 request_recv: channel::Receiver<RequestEvent>,
1400 mut exts: ViewExtensions,
1401 ) -> Self {
1402 exts.renderer("zng-view.webrender_debug", extensions::RendererDebugExt::new);
1403 #[cfg(windows)]
1404 {
1405 exts.window("zng-view.prefer_angle", extensions::PreferAngleExt::new);
1406 }
1407 #[cfg(feature = "image_cur")]
1408 exts.data("image_cur");
1409 #[cfg(feature = "image_meta_exif")]
1410 exts.data("image_meta_exif");
1411 #[cfg(feature = "image_meta_icc")]
1412 exts.data("image_meta_icc");
1413 App {
1414 headless: false,
1415 image_cache: ImageCache::new(
1416 app_sender.clone(),
1417 #[cfg(feature = "image_cur")]
1418 exts.id(&ApiExtensionName::new("image_cur").unwrap()).unwrap(),
1419 #[cfg(feature = "image_meta_exif")]
1420 exts.id(&ApiExtensionName::new("image_meta_exif").unwrap()).unwrap(),
1421 #[cfg(feature = "image_meta_icc")]
1422 exts.id(&ApiExtensionName::new("image_meta_icc").unwrap()).unwrap(),
1423 ),
1424 exts,
1425 gl_manager: GlContextManager::default(),
1426 audio_cache: AudioCache::new(app_sender.clone()),
1427 app_sender,
1428 request_recv,
1429 response_sender,
1430 event_sender,
1431 winit_loop: util::WinitEventLoop::default(),
1432 generation: ViewProcessGen::INVALID,
1433 device_events_filter: DeviceEventsFilter::empty(),
1434 windows: vec![],
1435 surfaces: vec![],
1436 monitors: vec![],
1437 monitor_ids: vec![],
1438 monitor_id_gen: MonitorId::INVALID,
1439 devices: vec![],
1440 device_id_gen: InputDeviceId::INVALID,
1441 dialog_id_gen: DialogId::INVALID,
1442 resize_frame_wait_id_gen: FrameWaitId::INVALID,
1443 coalescing_event: None,
1444 cursor_entered_expect_move: Vec::with_capacity(1),
1445 app_state: AppState::PreInitSuspended,
1446 exited: false,
1447 #[cfg(windows)]
1448 skip_ralt: false,
1449 pressed_modifiers: FxHashMap::default(),
1450 pending_modifiers_update: None,
1451 pending_modifiers_focus_clear: false,
1452 config_listener_exit: None,
1453 drag_drop_hovered: None,
1454 drag_drop_next_move: None,
1455 #[cfg(not(any(windows, target_os = "android")))]
1456 arboard: None,
1457 notifications: NotificationService::default(),
1458 low_memory_watcher: low_memory::LowMemoryWatcher::new(),
1459 last_pull_event: Instant::now(),
1460 }
1461 }
1462
1463 fn start_receiving(&mut self, mut request_recv: ipc::RequestReceiver) {
1464 let app_sender = self.app_sender.clone();
1465 thread::Builder::new()
1466 .name("request-recv".into())
1467 .stack_size(256 * 1024)
1468 .spawn(move || {
1469 while let Ok(r) = request_recv.recv() {
1470 if app_sender.request(r).is_err() {
1471 break;
1472 }
1473 }
1474 let _ = app_sender.send(AppEvent::ParentProcessExited);
1475 })
1476 .expect("failed to spawn thread");
1477 }
1478
1479 fn monitor_handle_to_id(&mut self, handle: &MonitorHandle) -> MonitorId {
1480 if let Some((id, _)) = self.monitor_ids.iter().find(|(_, h)| h == handle) {
1481 *id
1482 } else {
1483 self.refresh_monitors();
1484 if let Some((id, _)) = self.monitor_ids.iter().find(|(_, h)| h == handle) {
1485 *id
1486 } else {
1487 MonitorId::INVALID
1488 }
1489 }
1490 }
1491
1492 fn update_modifiers(&mut self) {
1493 if mem::take(&mut self.pending_modifiers_focus_clear) && self.windows.iter().all(|w| !w.is_focused()) {
1500 self.pressed_modifiers.clear();
1501 }
1502
1503 if let Some(m) = self.pending_modifiers_update.take()
1504 && let Some(id) = self.windows.iter().find(|w| w.is_focused()).map(|w| w.id())
1505 {
1506 let mut notify = vec![];
1507 self.pressed_modifiers.retain(|(key, location), (d_id, s_code)| {
1508 let mut retain = true;
1509 if matches!(key, Key::Super) && !m.super_key() {
1510 retain = false;
1511 notify.push(Event::KeyboardInput {
1512 window: id,
1513 device: *d_id,
1514 key_code: *s_code,
1515 state: KeyState::Released,
1516 key: key.clone(),
1517 key_location: *location,
1518 key_modified: key.clone(),
1519 text: Txt::from_str(""),
1520 });
1521 }
1522 if matches!(key, Key::Shift) && !m.shift_key() {
1523 retain = false;
1524 notify.push(Event::KeyboardInput {
1525 window: id,
1526 device: *d_id,
1527 key_code: *s_code,
1528 state: KeyState::Released,
1529 key: key.clone(),
1530 key_location: *location,
1531 key_modified: key.clone(),
1532 text: Txt::from_str(""),
1533 });
1534 }
1535 if matches!(key, Key::Alt | Key::AltGraph) && !m.alt_key() {
1536 retain = false;
1537 notify.push(Event::KeyboardInput {
1538 window: id,
1539 device: *d_id,
1540 key_code: *s_code,
1541 state: KeyState::Released,
1542 key: key.clone(),
1543 key_location: *location,
1544 key_modified: key.clone(),
1545 text: Txt::from_str(""),
1546 });
1547 }
1548 if matches!(key, Key::Ctrl) && !m.control_key() {
1549 retain = false;
1550 notify.push(Event::KeyboardInput {
1551 window: id,
1552 device: *d_id,
1553 key_code: *s_code,
1554 state: KeyState::Released,
1555 key: key.clone(),
1556 key_location: *location,
1557 key_modified: key.clone(),
1558 text: Txt::from_str(""),
1559 });
1560 }
1561 retain
1562 });
1563
1564 for ev in notify {
1565 self.notify(ev);
1566 }
1567 }
1568 }
1569
1570 fn refresh_monitors(&mut self) {
1571 let monitors = self.available_monitors();
1572 if self.monitors != monitors {
1573 self.monitors = monitors.clone();
1574 self.notify(Event::MonitorsChanged(monitors));
1575 }
1576 }
1577
1578 fn on_frame_ready(&mut self, window_id: WindowId, msg: FrameReadyMsg) {
1579 let _s = tracing::trace_span!("on_frame_ready").entered();
1580
1581 if let Some(w) = self.windows.iter_mut().find(|w| w.id() == window_id) {
1582 let r = w.on_frame_ready(msg, &mut self.image_cache);
1583
1584 let _ = self
1585 .event_sender
1586 .send(Event::FrameRendered(EventFrameRendered::new(window_id, r.frame_id, r.image)));
1587
1588 if r.first_frame {
1589 let size = w.size();
1590 self.notify(Event::WindowChanged(WindowChanged::resized(window_id, size, EventCause::App, None)));
1591 }
1592 } else if let Some(s) = self.surfaces.iter_mut().find(|w| w.id() == window_id) {
1593 let (frame_id, image) = s.on_frame_ready(msg, &mut self.image_cache);
1594
1595 self.notify(Event::FrameRendered(EventFrameRendered::new(window_id, frame_id, image)))
1596 }
1597 }
1598
1599 pub(crate) fn notify(&mut self, event: Event) {
1600 let now = Instant::now();
1601 if let Some((mut coal, timestamp)) = self.coalescing_event.take() {
1602 let r = if now.saturating_duration_since(timestamp) >= Duration::from_millis(16) {
1603 Err(event)
1604 } else {
1605 coal.coalesce(event)
1606 };
1607 match r {
1608 Ok(()) => self.coalescing_event = Some((coal, timestamp)),
1609 Err(event) => match (&mut coal, event) {
1610 (
1611 Event::KeyboardInput {
1612 window,
1613 device,
1614 state,
1615 text,
1616 ..
1617 },
1618 Event::KeyboardInput {
1619 window: n_window,
1620 device: n_device,
1621 text: n_text,
1622 ..
1623 },
1624 ) if !n_text.is_empty() && *window == n_window && *device == n_device && *state == KeyState::Pressed => {
1625 if text.is_empty() {
1627 *text = n_text;
1628 } else {
1629 text.push_str(&n_text);
1630 };
1631 self.coalescing_event = Some((coal, now));
1632 }
1633 (_, event) => {
1634 let mut error = self.event_sender.send(coal).is_err();
1635 error |= self.event_sender.send(event).is_err();
1636
1637 if error {
1638 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1639 }
1640 }
1641 },
1642 }
1643 } else {
1644 self.coalescing_event = Some((event, now));
1645 }
1646
1647 if self.headless {
1648 self.flush_coalesced();
1649 }
1650 }
1651
1652 pub(crate) fn finish_cursor_entered_move(&mut self) {
1653 let mut moves = vec![];
1654 for window_id in self.cursor_entered_expect_move.drain(..) {
1655 if let Some(w) = self.windows.iter().find(|w| w.id() == window_id) {
1656 let (position, device) = w.last_cursor_pos();
1657 moves.push(Event::MouseMoved {
1658 window: w.id(),
1659 device,
1660 coalesced_pos: vec![],
1661 position,
1662 });
1663 }
1664 }
1665 for ev in moves {
1666 self.notify(ev);
1667 }
1668 }
1669
1670 pub(crate) fn flush_coalesced(&mut self) {
1672 if let Some((coal, _)) = self.coalescing_event.take()
1673 && self.event_sender.send(coal).is_err()
1674 {
1675 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1676 }
1677 }
1678
1679 #[track_caller]
1680 fn assert_resumed(&self) {
1681 assert_eq!(self.app_state, AppState::Resumed);
1682 }
1683
1684 fn with_window<R>(&mut self, id: WindowId, action: impl FnOnce(&mut Window) -> R, not_found: impl FnOnce() -> R) -> R {
1685 self.assert_resumed();
1686 self.windows.iter_mut().find(|w| w.id() == id).map(action).unwrap_or_else(|| {
1687 tracing::error!("headed window `{id:?}` not found, will return fallback result");
1688 not_found()
1689 })
1690 }
1691
1692 fn monitor_id(&mut self, handle: &MonitorHandle) -> MonitorId {
1693 if let Some((id, _)) = self.monitor_ids.iter().find(|(_, h)| h == handle) {
1694 *id
1695 } else {
1696 let id = self.monitor_id_gen.incr();
1697 self.monitor_ids.push((id, handle.clone()));
1698 id
1699 }
1700 }
1701
1702 fn notify_input_devices_changed(&mut self) {
1703 let devices = self.devices.iter().map(|(id, _, info)| (*id, info.clone())).collect();
1704 self.notify(Event::InputDevicesChanged(devices));
1705 }
1706
1707 fn input_device_id(&mut self, device_id: winit::event::DeviceId, capability: InputDeviceCapability) -> InputDeviceId {
1709 if let Some((id, _, info)) = self.devices.iter_mut().find(|(_, id, _)| *id == device_id) {
1710 let id = *id;
1711 if !self.device_events_filter.input.is_empty() && !capability.is_empty() && !info.capabilities.contains(capability) {
1712 info.capabilities |= capability;
1713 self.notify_input_devices_changed();
1714 }
1715 id
1716 } else {
1717 let id = self.device_id_gen.incr();
1718
1719 #[cfg(not(windows))]
1720 let info = InputDeviceInfo::new("Winit Device", InputDeviceCapability::empty());
1721 #[cfg(windows)]
1722 let info = {
1723 use winit::platform::windows::DeviceIdExtWindows as _;
1724 if !self.device_events_filter.input.is_empty()
1725 && let Some(device_path) = device_id.persistent_identifier()
1726 {
1727 input_device_info::get(&device_path)
1728 } else {
1729 InputDeviceInfo::new("Winit Device", InputDeviceCapability::empty())
1730 }
1731 };
1732
1733 self.devices.push((id, device_id, info));
1734
1735 if !self.device_events_filter.input.is_empty() {
1736 self.notify_input_devices_changed();
1737 }
1738
1739 id
1740 }
1741 }
1742
1743 fn available_monitors(&mut self) -> Vec<(MonitorId, MonitorInfo)> {
1744 let _span = tracing::trace_span!("available_monitors").entered();
1745
1746 let primary = self.winit_loop.primary_monitor();
1747 let mut available: Vec<_> = self
1748 .winit_loop
1749 .available_monitors()
1750 .map(|m| (self.monitor_id(&m), primary.as_ref().map(|h| h == &m).unwrap_or(false), m))
1751 .collect();
1752 available.sort_by(|(_, a_is_primary, a), (_, b_is_primary, b)| {
1755 b_is_primary.cmp(a_is_primary).then_with(|| a.position().x.cmp(&b.position().x))
1756 });
1757 available
1758 .into_iter()
1759 .enumerate()
1760 .map(|(n, (id, is_primary, m))| (id, util::monitor_handle_to_info(&m, is_primary, n + 1)))
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 mut 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 msg.refresh_rate = win.refresh_rate();
2055
2056 self.windows.push(win);
2057
2058 self.notify(Event::WindowOpened(id, msg));
2059
2060 #[cfg(target_os = "android")]
2062 {
2063 self.windows.last_mut().unwrap().focused_changed(&mut true);
2064 self.notify(Event::FocusChanged { prev: None, new: Some(id) });
2065 }
2066 }
2067 }
2068
2069 fn open_headless(&mut self, config: HeadlessRequest) {
2070 let _s = tracing::debug_span!("open_headless", ?config).entered();
2071
2072 let id = config.id;
2073 let msg = self.open_headless_impl(config);
2074
2075 self.notify(Event::HeadlessOpened(id, msg));
2076 }
2077
2078 fn close(&mut self, id: WindowId) {
2079 self.assert_resumed();
2080 if let Some(i) = self.windows.iter().position(|w| w.id() == id) {
2081 let _ = self.windows.swap_remove(i);
2082 }
2083 if let Some(i) = self.surfaces.iter().position(|w| w.id() == id) {
2084 let _ = self.surfaces.swap_remove(i);
2085 }
2086 }
2087
2088 fn set_title(&mut self, id: WindowId, title: Txt) {
2089 self.with_window(id, |w| w.set_title(title), || ())
2090 }
2091
2092 fn set_visible(&mut self, id: WindowId, visible: bool) {
2093 self.with_window(id, |w| w.set_visible(visible), || ())
2094 }
2095
2096 fn set_always_on_top(&mut self, id: WindowId, always_on_top: bool) {
2097 self.with_window(id, |w| w.set_always_on_top(always_on_top), || ())
2098 }
2099
2100 fn set_movable(&mut self, id: WindowId, movable: bool) {
2101 self.with_window(id, |w| w.set_movable(movable), || ())
2102 }
2103
2104 fn set_resizable(&mut self, id: WindowId, resizable: bool) {
2105 self.with_window(id, |w| w.set_resizable(resizable), || ())
2106 }
2107
2108 fn set_taskbar_visible(&mut self, id: WindowId, visible: bool) {
2109 self.with_window(id, |w| w.set_taskbar_visible(visible), || ())
2110 }
2111
2112 fn bring_to_top(&mut self, id: WindowId) {
2113 self.with_window(id, |w| w.bring_to_top(), || ())
2114 }
2115
2116 fn set_state(&mut self, id: WindowId, state: WindowStateAll) {
2117 if let Some(w) = self.windows.iter_mut().find(|w| w.id() == id)
2118 && w.set_state(state.clone())
2119 {
2120 let mut change = WindowChanged::state_changed(id, state, EventCause::App);
2121
2122 change.size = w.resized();
2123 change.position = w.moved();
2124 if let Some(handle) = w.monitor_change() {
2125 self.refresh_monitors();
2126 let monitor = self.monitor_handle_to_id(&handle);
2127 change.monitor = Some(monitor);
2128 }
2129
2130 let _ = self.app_sender.send(AppEvent::Notify(Event::WindowChanged(change)));
2131 }
2132 }
2133
2134 fn set_headless_size(&mut self, renderer: WindowId, size: DipSize, scale_factor: Factor) {
2135 self.assert_resumed();
2136 if let Some(surf) = self.surfaces.iter_mut().find(|s| s.id() == renderer) {
2137 surf.set_size(size, scale_factor)
2138 }
2139 }
2140
2141 fn set_video_mode(&mut self, id: WindowId, mode: VideoMode) {
2142 self.with_window(id, |w| w.set_video_mode(mode), || ())
2143 }
2144
2145 fn set_icon(&mut self, id: WindowId, icon: Option<ImageId>) {
2146 let icon = icon.and_then(|i| self.image_cache.get(i)).and_then(|i| i.icon());
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<IpcBytes>) -> 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<IpcBytes>) -> 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).map_err(|e| clipboard::ClipboardError::Other(e.to_txt()))?,
2378 u64::MAX,
2379 None,
2380 None,
2381 ));
2382 Ok(clipboard::ClipboardData::Image(id))
2383 }
2384 clipboard::ClipboardType::Paths => {
2385 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2386
2387 clipboard_win::get(clipboard_win::formats::FileList)
2388 .map_err(util::clipboard_win_to_clip)
2389 .map(clipboard::ClipboardData::Paths)
2390 }
2391 clipboard::ClipboardType::Extension(_) => Err(clipboard::ClipboardError::NotSupported),
2392 _ => Err(clipboard::ClipboardError::NotSupported),
2393 };
2394 single.map(|d| vec![d])
2395 }
2396
2397 #[cfg(windows)]
2398 fn write_clipboard(&mut self, mut data: Vec<clipboard::ClipboardData>) -> Result<usize, clipboard::ClipboardError> {
2399 use zng_txt::formatx;
2400
2401 if data.is_empty() {
2402 return Ok(0);
2403 }
2404
2405 let r = match data.remove(0) {
2406 clipboard::ClipboardData::Text(t) => {
2407 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2408
2409 clipboard_win::set(clipboard_win::formats::Unicode, t).map_err(util::clipboard_win_to_clip)
2410 }
2411 clipboard::ClipboardData::Image(id) => {
2412 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2413
2414 if let Some(img) = self.image_cache.get(id) {
2415 let mut bmp = vec![];
2416 img.encode(vec![], ::image::ImageFormat::Bmp, &mut std::io::Cursor::new(&mut bmp))
2417 .map_err(|e| clipboard::ClipboardError::Other(formatx!("{e:?}")))?;
2418 clipboard_win::set(clipboard_win::formats::Bitmap, bmp).map_err(util::clipboard_win_to_clip)
2419 } else {
2420 Err(clipboard::ClipboardError::Other(Txt::from_str("image not found")))
2421 }
2422 }
2423 clipboard::ClipboardData::Paths(l) => {
2424 use clipboard_win::Setter;
2425 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2426
2427 let strs = l.into_iter().map(|p| p.display().to_string()).collect::<Vec<String>>();
2429 clipboard_win::formats::FileList
2430 .write_clipboard(&strs)
2431 .map_err(util::clipboard_win_to_clip)
2432 }
2433 clipboard::ClipboardData::Extension { .. } => Err(clipboard::ClipboardError::NotSupported),
2434 _ => Err(clipboard::ClipboardError::NotSupported),
2435 };
2436
2437 r.map(|()| 1)
2438 }
2439
2440 #[cfg(not(any(windows, target_os = "android")))]
2441 fn read_clipboard(
2442 &mut self,
2443 mut data_type: Vec<clipboard::ClipboardType>,
2444 _first: bool,
2445 ) -> Result<Vec<clipboard::ClipboardData>, clipboard::ClipboardError> {
2446 if data_type.is_empty() {
2447 return Ok(vec![]);
2448 }
2449
2450 use zng_txt::ToTxt as _;
2451 let single = match data_type.remove(0) {
2452 clipboard::ClipboardType::Text => self
2453 .arboard()?
2454 .get_text()
2455 .map_err(util::arboard_to_clip)
2456 .map(|s| clipboard::ClipboardData::Text(zng_txt::Txt::from(s))),
2457 clipboard::ClipboardType::Image => {
2458 let bitmap = self.arboard()?.get_image().map_err(util::arboard_to_clip)?;
2459 let mut data = bitmap.bytes.into_owned();
2460 for rgba in data.chunks_exact_mut(4) {
2461 rgba.swap(0, 2); }
2463 let id = self.image_cache.add(image::ImageRequest::new(
2464 image::ImageDataFormat::Bgra8 {
2465 size: zng_unit::PxSize::new(Px(bitmap.width as _), Px(bitmap.height as _)),
2466 density: None,
2467 original_color_type: zng_view_api::image::ColorType::RGBA8,
2468 },
2469 IpcBytes::from_vec_blocking(data).map_err(|e| clipboard::ClipboardError::Other(e.to_txt()))?,
2470 u64::MAX,
2471 None,
2472 None,
2473 ));
2474 Ok(clipboard::ClipboardData::Image(id))
2475 }
2476 clipboard::ClipboardType::Paths => self
2477 .arboard()?
2478 .get()
2479 .file_list()
2480 .map_err(util::arboard_to_clip)
2481 .map(clipboard::ClipboardData::Paths),
2482 clipboard::ClipboardType::Extension(_) => Err(clipboard::ClipboardError::NotSupported),
2483 _ => Err(clipboard::ClipboardError::NotSupported),
2484 };
2485
2486 single.map(|e| vec![e])
2487 }
2488
2489 #[cfg(not(any(windows, target_os = "android")))]
2490 fn write_clipboard(&mut self, mut data: Vec<clipboard::ClipboardData>) -> Result<usize, clipboard::ClipboardError> {
2491 if data.is_empty() {
2492 return Ok(0);
2493 }
2494
2495 let r = match data.remove(0) {
2496 clipboard::ClipboardData::Text(t) => self.arboard()?.set_text(t).map_err(util::arboard_to_clip),
2497 clipboard::ClipboardData::Image(id) => {
2498 self.arboard()?;
2499 if let Some(img) = self.image_cache.get(id) {
2500 let size = img.size();
2501 let mut data = img.pixels().clone().to_vec();
2502 for rgba in data.chunks_exact_mut(4) {
2503 rgba.swap(0, 2); }
2505 let board = self.arboard()?;
2506 let _ = board.set_image(arboard::ImageData {
2507 width: size.width.0 as _,
2508 height: size.height.0 as _,
2509 bytes: std::borrow::Cow::Owned(data),
2510 });
2511 Ok(())
2512 } else {
2513 Err(clipboard::ClipboardError::Other(zng_txt::Txt::from_static("image not found")))
2514 }
2515 }
2516 clipboard::ClipboardData::Paths(_) => Err(clipboard::ClipboardError::NotSupported),
2517 clipboard::ClipboardData::Extension { .. } => Err(clipboard::ClipboardError::NotSupported),
2518 _ => Err(clipboard::ClipboardError::NotSupported),
2519 };
2520
2521 r.map(|()| 1)
2522 }
2523
2524 #[cfg(target_os = "android")]
2525 fn read_clipboard(
2526 &mut self,
2527 data_type: Vec<clipboard::ClipboardType>,
2528 _first: bool,
2529 ) -> Result<Vec<clipboard::ClipboardData>, clipboard::ClipboardError> {
2530 if data_type.is_empty() {
2531 return Ok(vec![]);
2532 }
2533
2534 let _ = data_type;
2535 Err(clipboard::ClipboardError::Other(Txt::from_static(
2536 "clipboard not implemented for Android",
2537 )))
2538 }
2539
2540 #[cfg(target_os = "android")]
2541 fn write_clipboard(&mut self, data: Vec<clipboard::ClipboardData>) -> Result<usize, clipboard::ClipboardError> {
2542 if data.is_empty() {
2543 return Ok(0);
2544 }
2545
2546 let _ = data;
2547 Err(clipboard::ClipboardError::Other(Txt::from_static(
2548 "clipboard not implemented for Android",
2549 )))
2550 }
2551
2552 fn start_drag_drop(
2553 &mut self,
2554 id: WindowId,
2555 data: Vec<DragDropData>,
2556 allowed_effects: DragDropEffect,
2557 ) -> Result<DragDropId, DragDropError> {
2558 let _ = (id, data, allowed_effects);
2559 Err(DragDropError::NotSupported)
2560 }
2561
2562 fn cancel_drag_drop(&mut self, id: WindowId, drag_id: DragDropId) {
2563 let _ = (id, drag_id);
2564 }
2565
2566 fn drag_dropped(&mut self, id: WindowId, drop_id: DragDropId, applied: DragDropEffect) {
2567 let _ = (id, drop_id, applied);
2568 }
2569
2570 fn set_system_shutdown_warn(&mut self, id: WindowId, reason: Txt) {
2571 self.with_window(id, move |w| w.set_system_shutdown_warn(reason), || ())
2572 }
2573
2574 fn set_app_menu(&mut self, menu: menu::AppMenu) {
2575 let _ = menu;
2576 }
2577
2578 fn set_tray_icon(&mut self, indicator: menu::TrayIcon) {
2579 let _ = indicator;
2580 }
2581
2582 fn third_party_licenses(&mut self) -> Vec<zng_tp_licenses::LicenseUsed> {
2583 #[cfg(feature = "bundle_licenses")]
2584 {
2585 zng_tp_licenses::include_bundle!()
2586 }
2587 #[cfg(not(feature = "bundle_licenses"))]
2588 {
2589 vec![]
2590 }
2591 }
2592
2593 fn app_extension(&mut self, extension_id: ApiExtensionId, extension_request: ApiExtensionPayload) -> ApiExtensionPayload {
2594 self.exts.call_command(extension_id, extension_request)
2595 }
2596
2597 fn window_extension(
2598 &mut self,
2599 id: WindowId,
2600 extension_id: ApiExtensionId,
2601 extension_request: ApiExtensionPayload,
2602 ) -> ApiExtensionPayload {
2603 self.with_window(
2604 id,
2605 |w| w.window_extension(extension_id, extension_request),
2606 || ApiExtensionPayload::invalid_request(extension_id, "window not found"),
2607 )
2608 }
2609
2610 fn render_extension(
2611 &mut self,
2612 id: WindowId,
2613 extension_id: ApiExtensionId,
2614 extension_request: ApiExtensionPayload,
2615 ) -> ApiExtensionPayload {
2616 with_window_or_surface!(self, id, |w| w.render_extension(extension_id, extension_request), || {
2617 ApiExtensionPayload::invalid_request(extension_id, "renderer not found")
2618 })
2619 }
2620
2621 fn ping(&mut self, count: u16) -> u16 {
2622 self.notify(Event::Pong(count));
2623 count
2624 }
2625}
2626
2627#[derive(Debug)]
2629#[allow(clippy::large_enum_variant)]
2630pub(crate) enum AppEvent {
2631 Request,
2633 Notify(Event),
2635 #[cfg_attr(not(windows), allow(unused))]
2637 RefreshMonitors,
2638
2639 #[cfg_attr(not(windows), allow(unused))]
2641 WinitFocused(winit::window::WindowId, bool),
2642
2643 ParentProcessExited,
2645
2646 ImageCanRender(ImageDecoded),
2648
2649 #[cfg_attr(not(feature = "audio_any"), allow(unused))]
2651 AudioCanPlay(audio::AudioId, AudioTrack),
2652
2653 SetDeviceEventsFilter(DeviceEventsFilter),
2655
2656 #[allow(unused)]
2658 MonitorPowerChanged,
2659}
2660
2661#[allow(clippy::large_enum_variant)] #[derive(Debug)]
2667enum RequestEvent {
2668 Request(Request),
2670 FrameReady(WindowId, FrameReadyMsg),
2672}
2673
2674#[derive(Debug)]
2675pub(crate) struct FrameReadyMsg {
2676 pub composite_needed: bool,
2677}
2678
2679#[derive(Clone)]
2681pub(crate) enum AppEventSender {
2682 Headed(EventLoopProxy<AppEvent>, Sender<RequestEvent>),
2683 Headless(Sender<AppEvent>, Sender<RequestEvent>),
2684}
2685impl AppEventSender {
2686 fn send(&self, ev: AppEvent) -> Result<(), ChannelError> {
2688 match self {
2689 AppEventSender::Headed(p, _) => p.send_event(ev).map_err(ChannelError::disconnected_by),
2690 AppEventSender::Headless(p, _) => p.send_blocking(ev),
2691 }
2692 }
2693
2694 fn request(&self, req: Request) -> Result<(), ChannelError> {
2696 match self {
2697 AppEventSender::Headed(_, p) => p.send_blocking(RequestEvent::Request(req)),
2698 AppEventSender::Headless(_, p) => p.send_blocking(RequestEvent::Request(req)),
2699 }?;
2700 self.send(AppEvent::Request)
2701 }
2702
2703 fn frame_ready(&self, window_id: WindowId, msg: FrameReadyMsg) -> Result<(), ChannelError> {
2705 match self {
2706 AppEventSender::Headed(_, p) => p.send_blocking(RequestEvent::FrameReady(window_id, msg)),
2707 AppEventSender::Headless(_, p) => p.send_blocking(RequestEvent::FrameReady(window_id, msg)),
2708 }?;
2709 self.send(AppEvent::Request)
2710 }
2711}
2712
2713pub(crate) struct WrNotifier {
2715 id: WindowId,
2716 sender: AppEventSender,
2717}
2718impl WrNotifier {
2719 pub fn create(id: WindowId, sender: AppEventSender) -> Box<dyn RenderNotifier> {
2720 Box::new(WrNotifier { id, sender })
2721 }
2722}
2723impl RenderNotifier for WrNotifier {
2724 fn clone(&self) -> Box<dyn RenderNotifier> {
2725 Box::new(Self {
2726 id: self.id,
2727 sender: self.sender.clone(),
2728 })
2729 }
2730
2731 fn wake_up(&self, _: bool) {}
2732
2733 fn new_frame_ready(&self, _: DocumentId, _: FramePublishId, params: &FrameReadyParams) {
2734 let msg = FrameReadyMsg {
2736 composite_needed: params.render,
2737 };
2738 let _ = self.sender.frame_ready(self.id, msg);
2739 }
2740}
2741
2742#[cfg(target_arch = "wasm32")]
2743compile_error!("zng-view does not support Wasm");