1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
90#![doc(test(no_crate_inject))]
91#![warn(missing_docs)]
92#![warn(unused_extern_crates)]
93
94use std::{
95 fmt, mem,
96 path::PathBuf,
97 thread,
98 time::{Duration, Instant},
99};
100
101use extensions::ViewExtensions;
102use gl::GlContextManager;
103use image_cache::ImageCache;
104use keyboard::KeyLocation;
105use util::WinitToPx;
106use winit::{
107 event::{DeviceEvent, WindowEvent},
108 event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy},
109 keyboard::ModifiersState,
110 monitor::MonitorHandle,
111};
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 config;
120mod display_list;
121mod gl;
122mod image_cache;
123mod low_memory;
124mod px_wr;
125mod surface;
126mod util;
127mod window;
128
129use surface::*;
130
131pub mod extensions;
132
133pub mod platform;
134
135#[doc(no_inline)]
137pub use webrender;
138
139#[doc(no_inline)]
141pub use gleam;
142
143use webrender::api::*;
144use window::Window;
145use zng_txt::Txt;
146use zng_unit::{Dip, DipPoint, DipRect, DipSideOffsets, DipSize, Factor, Px, PxPoint, PxRect, PxToDip};
147use zng_view_api::{
148 Inited,
149 api_extension::{ApiExtensionId, ApiExtensionPayload},
150 dialog::{DialogId, FileDialog, MsgDialog, MsgDialogResponse},
151 drag_drop::*,
152 font::{FontFaceId, FontId, FontOptions, FontVariationName},
153 image::{ImageId, ImageLoadedData, ImageMaskMode, ImageRequest, ImageTextureId},
154 ipc::{IpcBytes, IpcBytesReceiver},
155 keyboard::{Key, KeyCode, KeyState},
156 mouse::ButtonId,
157 touch::{TouchId, TouchUpdate},
158 window::{
159 CursorIcon, CursorImage, EventCause, EventFrameRendered, FocusIndicator, FrameRequest, FrameUpdateRequest, FrameWaitId,
160 HeadlessOpenData, HeadlessRequest, MonitorId, MonitorInfo, VideoMode, WindowChanged, WindowId, WindowOpenData, WindowRequest,
161 WindowState, WindowStateAll,
162 },
163 *,
164};
165
166use rustc_hash::FxHashMap;
167
168#[cfg(ipc)]
169zng_env::on_process_start!(|_| {
170 if std::env::var("ZNG_VIEW_NO_INIT_START").is_err() {
171 view_process_main();
172 }
173});
174
175#[cfg(ipc)]
183pub fn view_process_main() {
184 let config = match ViewConfig::from_env() {
185 Some(c) => c,
186 None => return,
187 };
188
189 std::panic::set_hook(Box::new(init_abort));
190 config.assert_version(false);
191 let c = ipc::connect_view_process(config.server_name).expect("failed to connect to app-process");
192
193 let mut ext = ViewExtensions::new();
194 for e in extensions::VIEW_EXTENSIONS {
195 e(&mut ext);
196 }
197
198 if config.headless {
199 App::run_headless(c, ext);
200 } else {
201 App::run_headed(c, ext);
202 }
203
204 zng_env::exit(0)
205}
206
207#[cfg(ipc)]
208#[doc(hidden)]
209#[unsafe(no_mangle)] pub extern "C" fn extern_view_process_main() {
211 std::panic::set_hook(Box::new(ffi_abort));
212 view_process_main()
213}
214
215pub fn run_same_process(run_app: impl FnOnce() + Send + 'static) {
236 run_same_process_extended(run_app, ViewExtensions::new)
237}
238
239pub fn run_same_process_extended(run_app: impl FnOnce() + Send + 'static, ext: fn() -> ViewExtensions) {
243 let app_thread = thread::Builder::new()
244 .name("app".to_owned())
245 .spawn(move || {
246 if let Err(e) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(run_app)) {
248 thread::spawn(|| {
249 thread::sleep(std::time::Duration::from_secs(5));
254 eprintln!("run_same_process did not exit after 5s of a fatal panic, exiting now");
255 zng_env::exit(101);
256 });
257 std::panic::resume_unwind(e);
259 }
260 })
261 .unwrap();
262
263 let config = ViewConfig::wait_same_process();
264 config.assert_version(true);
265
266 let c = ipc::connect_view_process(config.server_name).expect("failed to connect to app in same process");
267
268 let mut ext = ext();
269 for e in extensions::VIEW_EXTENSIONS {
270 e(&mut ext);
271 }
272
273 if config.headless {
274 App::run_headless(c, ext);
275 } else {
276 App::run_headed(c, ext);
277 }
278
279 if let Err(p) = app_thread.join() {
280 std::panic::resume_unwind(p);
281 }
282}
283
284#[cfg(ipc)]
285#[doc(hidden)]
286#[unsafe(no_mangle)] pub extern "C" fn extern_run_same_process(patch: &StaticPatch, run_app: extern "C" fn()) {
288 std::panic::set_hook(Box::new(ffi_abort));
289
290 unsafe {
293 patch.install();
294 }
295
296 #[expect(clippy::redundant_closure)] run_same_process(move || run_app())
298}
299#[cfg(ipc)]
300fn init_abort(info: &std::panic::PanicHookInfo) {
301 panic_hook(info, "note: aborting to respawn");
302}
303#[cfg(ipc)]
304fn ffi_abort(info: &std::panic::PanicHookInfo) {
305 panic_hook(info, "note: aborting to avoid unwind across FFI");
306}
307#[cfg(ipc)]
308fn panic_hook(info: &std::panic::PanicHookInfo, details: &str) {
309 let panic = util::SuppressedPanic::from_hook(info, std::backtrace::Backtrace::force_capture());
312
313 if crate::util::suppress_panic() {
314 crate::util::set_suppressed_panic(panic);
315 } else {
316 eprintln!("{panic}\n{details}");
317 zng_env::exit(101) }
319}
320
321pub(crate) struct App {
323 headless: bool,
324
325 exts: ViewExtensions,
326
327 gl_manager: GlContextManager,
328 winit_loop: util::WinitEventLoop,
329 idle: IdleTrace,
330 app_sender: AppEventSender,
331 request_recv: flume::Receiver<RequestEvent>,
332
333 response_sender: ipc::ResponseSender,
334 event_sender: ipc::EventSender,
335 image_cache: ImageCache,
336
337 generation: ViewProcessGen,
338 device_events: bool,
339
340 windows: Vec<Window>,
341 surfaces: Vec<Surface>,
342
343 monitor_id_gen: MonitorId,
344 pub monitors: Vec<(MonitorId, MonitorHandle)>,
345
346 device_id_gen: DeviceId,
347 devices: Vec<(DeviceId, winit::event::DeviceId)>,
348
349 dialog_id_gen: DialogId,
350
351 resize_frame_wait_id_gen: FrameWaitId,
352
353 coalescing_event: Option<(Event, Instant)>,
354 cursor_entered_expect_move: Vec<WindowId>,
360
361 #[cfg(windows)]
362 skip_ralt: bool,
363
364 pressed_modifiers: FxHashMap<(Key, KeyLocation), (DeviceId, KeyCode)>,
365 pending_modifiers_update: Option<ModifiersState>,
366 pending_modifiers_focus_clear: bool,
367
368 #[cfg(not(any(windows, target_os = "android")))]
369 arboard: Option<arboard::Clipboard>,
370
371 #[cfg(windows)]
372 low_memory_monitor: Option<low_memory::LowMemoryMonitor>,
373
374 config_listener_exit: Option<Box<dyn FnOnce()>>,
375
376 app_state: AppState,
377 drag_drop_hovered: Option<(WindowId, DipPoint)>,
378 drag_drop_next_move: Option<(Instant, PathBuf)>,
379 exited: bool,
380}
381impl fmt::Debug for App {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 f.debug_struct("HeadlessBackend")
384 .field("app_state", &self.app_state)
385 .field("generation", &self.generation)
386 .field("device_events", &self.device_events)
387 .field("windows", &self.windows)
388 .field("surfaces", &self.surfaces)
389 .finish_non_exhaustive()
390 }
391}
392impl winit::application::ApplicationHandler<AppEvent> for App {
393 fn resumed(&mut self, winit_loop: &ActiveEventLoop) {
394 if let AppState::Suspended = self.app_state {
395 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
396
397 self.exts.resumed();
398 self.generation = self.generation.next();
399 let available_monitors = self.available_monitors();
400 self.notify(Event::Inited(Inited {
401 generation: self.generation,
402 is_respawn: true,
403 available_monitors,
404 multi_click_config: config::multi_click_config(),
405 key_repeat_config: config::key_repeat_config(),
406 touch_config: config::touch_config(),
407 font_aa: config::font_aa(),
408 animations_config: config::animations_config(),
409 locale_config: config::locale_config(),
410 colors_config: config::colors_config(),
411 chrome_config: config::chrome_config(),
412 extensions: self.exts.api_extensions(),
413 }));
414
415 winit_loop_guard.unset(&mut self.winit_loop);
416 } else {
417 self.exts.init(&self.app_sender);
418 }
419 self.app_state = AppState::Resumed;
420
421 self.update_memory_monitor(winit_loop);
422 }
423
424 fn window_event(&mut self, winit_loop: &ActiveEventLoop, window_id: winit::window::WindowId, event: WindowEvent) {
425 let i = if let Some((i, _)) = self.windows.iter_mut().enumerate().find(|(_, w)| w.window_id() == window_id) {
426 i
427 } else {
428 return;
429 };
430
431 let _s = tracing::trace_span!("on_window_event", ?event).entered();
432
433 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
434
435 self.windows[i].on_window_event(&event);
436
437 let id = self.windows[i].id();
438 let scale_factor = self.windows[i].scale_factor();
439
440 #[cfg(any(
443 target_os = "linux",
444 target_os = "dragonfly",
445 target_os = "freebsd",
446 target_os = "netbsd",
447 target_os = "openbsd"
448 ))]
449 let modal_dialog_active = self.windows[i].modal_dialog_active();
450 #[cfg(any(
451 target_os = "linux",
452 target_os = "dragonfly",
453 target_os = "freebsd",
454 target_os = "netbsd",
455 target_os = "openbsd"
456 ))]
457 macro_rules! linux_modal_dialog_bail {
458 () => {
459 if modal_dialog_active {
460 winit_loop_guard.unset(&mut self.winit_loop);
461 return;
462 }
463 };
464 }
465 #[cfg(not(any(
466 target_os = "linux",
467 target_os = "dragonfly",
468 target_os = "freebsd",
469 target_os = "netbsd",
470 target_os = "openbsd"
471 )))]
472 macro_rules! linux_modal_dialog_bail {
473 () => {};
474 }
475
476 match event {
477 WindowEvent::RedrawRequested => self.windows[i].redraw(),
478 WindowEvent::Resized(_) => {
479 let size = if let Some(size) = self.windows[i].resized() {
480 size
481 } else {
482 winit_loop_guard.unset(&mut self.winit_loop);
483 return;
484 };
485
486 let deadline = Instant::now() + Duration::from_millis(300);
490
491 if self.windows[i].is_rendering_frame() {
493 tracing::debug!("resize requested while still rendering");
494
495 while let Ok(req) = self.request_recv.recv_deadline(deadline) {
497 match req {
498 RequestEvent::Request(req) => {
499 let rsp = self.respond(req);
500 if rsp.must_be_send() {
501 let _ = self.response_sender.send(rsp);
502 }
503 }
504 RequestEvent::FrameReady(id, msg) => {
505 self.on_frame_ready(id, msg);
506 if id == self.windows[i].id() {
507 break;
508 }
509 }
510 }
511 }
512 }
513
514 if let Some(state) = self.windows[i].state_change() {
515 self.notify(Event::WindowChanged(WindowChanged::state_changed(id, state, EventCause::System)));
516 }
517
518 if let Some(handle) = self.windows[i].monitor_change() {
519 let m_id = self.monitor_handle_to_id(&handle);
520
521 self.notify(Event::WindowChanged(WindowChanged::monitor_changed(id, m_id, EventCause::System)));
522 }
523
524 let wait_id = Some(self.resize_frame_wait_id_gen.incr());
525
526 self.notify(Event::WindowChanged(WindowChanged::resized(id, size, EventCause::System, wait_id)));
528
529 self.flush_coalesced();
530
531 let mut received_frame = false;
533 loop {
534 match self.request_recv.recv_deadline(deadline) {
535 Ok(req) => {
536 match req {
537 RequestEvent::Request(req) => {
538 received_frame = req.is_frame(id, wait_id);
539 if received_frame || req.affects_window_rect(id) {
540 let rsp = self.respond(req);
542 if rsp.must_be_send() {
543 let _ = self.response_sender.send(rsp);
544 }
545 break;
546 } else {
547 let rsp = self.respond(req);
549 if rsp.must_be_send() {
550 let _ = self.response_sender.send(rsp);
551 }
552 }
553 }
554 RequestEvent::FrameReady(id, msg) => self.on_frame_ready(id, msg),
555 }
556 }
557
558 Err(flume::RecvTimeoutError::Timeout) => {
559 break;
561 }
562 Err(flume::RecvTimeoutError::Disconnected) => {
563 winit_loop_guard.unset(&mut self.winit_loop);
564 unreachable!()
565 }
566 }
567 }
568
569 if received_frame && deadline > Instant::now() {
571 while let Ok(req) = self.request_recv.recv_deadline(deadline) {
573 match req {
574 RequestEvent::Request(req) => {
575 let rsp = self.respond(req);
576 if rsp.must_be_send() {
577 let _ = self.response_sender.send(rsp);
578 }
579 }
580 RequestEvent::FrameReady(id, msg) => {
581 self.on_frame_ready(id, msg);
582 if id == self.windows[i].id() {
583 break;
584 }
585 }
586 }
587 }
588 }
589 }
590 WindowEvent::Moved(_) => {
591 let (global_position, position) = if let Some(p) = self.windows[i].moved() {
592 p
593 } else {
594 winit_loop_guard.unset(&mut self.winit_loop);
595 return;
596 };
597
598 if let Some(state) = self.windows[i].state_change() {
599 self.notify(Event::WindowChanged(WindowChanged::state_changed(id, state, EventCause::System)));
600 }
601
602 self.notify(Event::WindowChanged(WindowChanged::moved(
603 id,
604 global_position,
605 position,
606 EventCause::System,
607 )));
608
609 if let Some(handle) = self.windows[i].monitor_change() {
610 let m_id = self.monitor_handle_to_id(&handle);
611
612 self.notify(Event::WindowChanged(WindowChanged::monitor_changed(id, m_id, EventCause::System)));
613 }
614 }
615 WindowEvent::CloseRequested => {
616 linux_modal_dialog_bail!();
617 self.notify(Event::WindowCloseRequested(id))
618 }
619 WindowEvent::Destroyed => {
620 self.windows.remove(i);
621 self.notify(Event::WindowClosed(id));
622 }
623 WindowEvent::HoveredFile(file) => {
624 linux_modal_dialog_bail!();
625
626 if !self.device_events {
630 winit_loop.listen_device_events(winit::event_loop::DeviceEvents::Always);
631 }
632 self.drag_drop_hovered = Some((id, DipPoint::splat(Dip::new(-1000))));
633 self.notify(Event::DragHovered {
634 window: id,
635 data: vec![DragDropData::Path(file)],
636 allowed: DragDropEffect::all(),
637 });
638 }
639 WindowEvent::DroppedFile(file) => {
640 linux_modal_dialog_bail!();
641
642 if !self.device_events {
643 winit_loop.listen_device_events(winit::event_loop::DeviceEvents::Never);
644 }
645
646 let mut delay_to_next_move = true;
647
648 if let Some(position) = self.windows[i].drag_drop_cursor_pos() {
650 self.notify(Event::DragMoved {
651 window: id,
652 coalesced_pos: vec![],
653 position,
654 });
655 delay_to_next_move = false;
656 } else if let Some((_, pos)) = self.drag_drop_hovered {
657 delay_to_next_move = pos.x < Dip::new(0);
658 }
659
660 if delay_to_next_move {
661 self.drag_drop_next_move = Some((Instant::now(), file));
662 } else {
663 self.notify(Event::DragDropped {
664 window: id,
665 data: vec![DragDropData::Path(file)],
666 allowed: DragDropEffect::all(),
667 drop_id: DragDropId(0),
668 });
669 }
670 }
671 WindowEvent::HoveredFileCancelled => {
672 linux_modal_dialog_bail!();
673
674 self.drag_drop_hovered = None;
675 if !self.device_events {
676 winit_loop.listen_device_events(winit::event_loop::DeviceEvents::Never);
677 }
678
679 if self.drag_drop_next_move.is_none() {
680 self.notify(Event::DragCancelled { window: id });
682 }
683 }
684 WindowEvent::Focused(mut focused) => {
685 if self.windows[i].focused_changed(&mut focused) {
686 if focused {
687 self.notify(Event::FocusChanged { prev: None, new: Some(id) });
688
689 if let Some(state) = self.windows[i].state_change() {
691 self.notify(Event::WindowChanged(WindowChanged::state_changed(id, state, EventCause::System)));
692 }
693 } else {
694 self.pending_modifiers_focus_clear = true;
695 self.notify(Event::FocusChanged { prev: Some(id), new: None });
696 }
697 }
698 }
699 WindowEvent::KeyboardInput {
700 device_id,
701 event,
702 is_synthetic,
703 } => {
704 linux_modal_dialog_bail!();
705
706 if !is_synthetic && self.windows[i].is_focused() {
707 #[cfg(windows)]
709 if self.skip_ralt {
710 if let winit::keyboard::PhysicalKey::Code(winit::keyboard::KeyCode::AltRight) = event.physical_key {
711 winit_loop_guard.unset(&mut self.winit_loop);
712 return;
713 }
714 }
715
716 let state = util::element_state_to_key_state(event.state);
717 #[cfg(not(target_os = "android"))]
718 let key = util::winit_key_to_key(event.key_without_modifiers());
719 let key_modified = util::winit_key_to_key(event.logical_key);
720 #[cfg(target_os = "android")]
721 let key = key_modified.clone();
722 let key_code = util::winit_physical_key_to_key_code(event.physical_key);
723 let key_location = util::winit_key_location_to_zng(event.location);
724 let d_id = self.device_id(device_id);
725
726 let mut send_event = true;
727
728 if key.is_modifier() {
729 match state {
730 KeyState::Pressed => {
731 send_event = self
732 .pressed_modifiers
733 .insert((key.clone(), key_location), (d_id, key_code))
734 .is_none();
735 }
736 KeyState::Released => send_event = self.pressed_modifiers.remove(&(key.clone(), key_location)).is_some(),
737 }
738 }
739
740 if send_event {
741 self.notify(Event::KeyboardInput {
742 window: id,
743 device: d_id,
744 key_code,
745 key_location,
746 state,
747 text: match event.text {
748 Some(s) => Txt::from_str(s.as_str()),
749 #[cfg(target_os = "android")]
750 None => match (state, &key) {
751 (KeyState::Pressed, Key::Char(c)) => Txt::from(*c),
752 (KeyState::Pressed, Key::Str(s)) => s.clone(),
753 _ => Txt::default(),
754 },
755 #[cfg(not(target_os = "android"))]
756 None => Txt::default(),
757 },
758 key,
759 key_modified,
760 });
761 }
762 }
763 }
764 WindowEvent::ModifiersChanged(m) => {
765 linux_modal_dialog_bail!();
766 if self.windows[i].is_focused() {
767 self.pending_modifiers_update = Some(m.state());
768 }
769 }
770 WindowEvent::CursorMoved { device_id, position, .. } => {
771 linux_modal_dialog_bail!();
772
773 let px_p = position.to_px();
774 let p = px_p.to_dip(scale_factor);
775 let d_id = self.device_id(device_id);
776
777 let mut is_after_cursor_enter = false;
778 if let Some(i) = self.cursor_entered_expect_move.iter().position(|&w| w == id) {
779 self.cursor_entered_expect_move.remove(i);
780 is_after_cursor_enter = true;
781 }
782
783 if self.windows[i].cursor_moved(p, d_id) || is_after_cursor_enter {
784 self.notify(Event::MouseMoved {
785 window: id,
786 device: d_id,
787 coalesced_pos: vec![],
788 position: p,
789 });
790 }
791
792 if let Some((drop_moment, file)) = self.drag_drop_next_move.take() {
793 if drop_moment.elapsed() < Duration::from_millis(300) {
794 let window_id = self.windows[i].id();
795 self.notify(Event::DragMoved {
796 window: window_id,
797 coalesced_pos: vec![],
798 position: p,
799 });
800 self.notify(Event::DragDropped {
801 window: window_id,
802 data: vec![DragDropData::Path(file)],
803 allowed: DragDropEffect::all(),
804 drop_id: DragDropId(0),
805 });
806 }
807 }
808 }
809 WindowEvent::CursorEntered { device_id } => {
810 linux_modal_dialog_bail!();
811 if self.windows[i].cursor_entered() {
812 let d_id = self.device_id(device_id);
813 self.notify(Event::MouseEntered { window: id, device: d_id });
814 self.cursor_entered_expect_move.push(id);
815 }
816 }
817 WindowEvent::CursorLeft { device_id } => {
818 linux_modal_dialog_bail!();
819 if self.windows[i].cursor_left() {
820 let d_id = self.device_id(device_id);
821 self.notify(Event::MouseLeft { window: id, device: d_id });
822
823 if let Some(i) = self.cursor_entered_expect_move.iter().position(|&w| w == id) {
825 self.cursor_entered_expect_move.remove(i);
826 }
827 }
828 }
829 WindowEvent::MouseWheel {
830 device_id, delta, phase, ..
831 } => {
832 linux_modal_dialog_bail!();
833 let d_id = self.device_id(device_id);
834 self.notify(Event::MouseWheel {
835 window: id,
836 device: d_id,
837 delta: util::winit_mouse_wheel_delta_to_zng(delta),
838 phase: util::winit_touch_phase_to_zng(phase),
839 });
840 }
841 WindowEvent::MouseInput {
842 device_id, state, button, ..
843 } => {
844 linux_modal_dialog_bail!();
845 let d_id = self.device_id(device_id);
846 self.notify(Event::MouseInput {
847 window: id,
848 device: d_id,
849 state: util::element_state_to_button_state(state),
850 button: util::winit_mouse_button_to_zng(button),
851 });
852 }
853 WindowEvent::TouchpadPressure {
854 device_id,
855 pressure,
856 stage,
857 } => {
858 linux_modal_dialog_bail!();
859 let d_id = self.device_id(device_id);
860 self.notify(Event::TouchpadPressure {
861 window: id,
862 device: d_id,
863 pressure,
864 stage,
865 });
866 }
867 WindowEvent::AxisMotion { device_id, axis, value } => {
868 linux_modal_dialog_bail!();
869 let d_id = self.device_id(device_id);
870 self.notify(Event::AxisMotion {
871 window: id,
872 device: d_id,
873 axis: AxisId(axis),
874 value,
875 });
876 }
877 WindowEvent::Touch(t) => {
878 let d_id = self.device_id(t.device_id);
879 let position = t.location.to_px().to_dip(scale_factor);
880
881 let notify = match t.phase {
882 winit::event::TouchPhase::Moved => self.windows[i].touch_moved(position, d_id, t.id),
883 winit::event::TouchPhase::Started => true,
884 winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => {
885 self.windows[i].touch_end(d_id, t.id);
886 true
887 }
888 };
889
890 if notify {
891 self.notify(Event::Touch {
892 window: id,
893 device: d_id,
894 touches: vec![TouchUpdate {
895 phase: util::winit_touch_phase_to_zng(t.phase),
896 position,
897 force: t.force.map(util::winit_force_to_zng),
898 touch: TouchId(t.id),
899 }],
900 });
901 }
902 }
903 WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
904 let monitor;
905 let mut is_monitor_change = false;
906
907 if let Some(new_monitor) = self.windows[i].monitor_change() {
908 monitor = Some(new_monitor);
909 is_monitor_change = true;
910 } else {
911 monitor = self.windows[i].monitor();
912 }
913
914 let monitor = if let Some(handle) = monitor {
915 self.monitor_handle_to_id(&handle)
916 } else {
917 MonitorId::INVALID
918 };
919
920 if is_monitor_change {
921 self.notify(Event::WindowChanged(WindowChanged::monitor_changed(
922 id,
923 monitor,
924 EventCause::System,
925 )));
926 }
927 self.notify(Event::ScaleFactorChanged {
928 monitor,
929 windows: vec![id],
930 scale_factor: scale_factor as f32,
931 });
932
933 if let Some(size) = self.windows[i].resized() {
934 self.notify(Event::WindowChanged(WindowChanged::resized(id, size, EventCause::System, None)));
935 }
936 }
937 WindowEvent::Ime(ime) => {
938 linux_modal_dialog_bail!();
939
940 match ime {
941 winit::event::Ime::Preedit(s, c) => {
942 let caret = c.unwrap_or((s.len(), s.len()));
943 let ime = Ime::Preview(s.into(), caret);
944 self.notify(Event::Ime { window: id, ime });
945 }
946 winit::event::Ime::Commit(s) => {
947 let ime = Ime::Commit(s.into());
948 self.notify(Event::Ime { window: id, ime });
949 }
950 winit::event::Ime::Enabled => {}
951 winit::event::Ime::Disabled => {}
952 }
953 }
954 WindowEvent::ThemeChanged(_) => {}
955 WindowEvent::Occluded(_) => {}
956 WindowEvent::ActivationTokenDone { .. } => {}
957 WindowEvent::PinchGesture { .. } => {}
958 WindowEvent::RotationGesture { .. } => {}
959 WindowEvent::DoubleTapGesture { .. } => {}
960 WindowEvent::PanGesture { .. } => {}
961 }
962
963 winit_loop_guard.unset(&mut self.winit_loop);
964 }
965
966 fn new_events(&mut self, _winit_loop: &ActiveEventLoop, _cause: winit::event::StartCause) {
967 self.idle.exit();
968
969 #[cfg(windows)]
970 if let winit::event::StartCause::ResumeTimeReached { .. } = _cause {
971 self.update_memory_monitor(_winit_loop);
972 }
973 }
974
975 fn user_event(&mut self, winit_loop: &ActiveEventLoop, ev: AppEvent) {
976 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
977 match ev {
978 AppEvent::Request => {
979 while let Ok(req) = self.request_recv.try_recv() {
980 match req {
981 RequestEvent::Request(req) => {
982 let rsp = self.respond(req);
983 if rsp.must_be_send() && self.response_sender.send(rsp).is_err() {
984 self.exited = true;
986 self.winit_loop.exit();
987 }
988 }
989 RequestEvent::FrameReady(wid, msg) => self.on_frame_ready(wid, msg),
990 }
991 }
992 }
993 AppEvent::Notify(ev) => self.notify(ev),
994 AppEvent::WinitFocused(window_id, focused) => self.window_event(winit_loop, window_id, WindowEvent::Focused(focused)),
995 AppEvent::RefreshMonitors => self.refresh_monitors(),
996 AppEvent::ParentProcessExited => {
997 self.exited = true;
998 self.winit_loop.exit();
999 }
1000 AppEvent::ImageLoaded(data) => {
1001 self.image_cache.loaded(data);
1002 }
1003 AppEvent::MonitorPowerChanged => {
1004 for w in &mut self.windows {
1006 w.redraw();
1007 }
1008 }
1009 AppEvent::InitDeviceEvents(enabled) => {
1010 self.init_device_events(enabled, Some(winit_loop));
1011 }
1012 }
1013 winit_loop_guard.unset(&mut self.winit_loop);
1014 }
1015
1016 fn device_event(&mut self, winit_loop: &ActiveEventLoop, device_id: winit::event::DeviceId, event: DeviceEvent) {
1017 if self.device_events {
1018 let _s = tracing::trace_span!("on_device_event", ?event);
1019
1020 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1021
1022 let d_id = self.device_id(device_id);
1023 match &event {
1024 DeviceEvent::Added => self.notify(Event::DeviceAdded(d_id)),
1025 DeviceEvent::Removed => self.notify(Event::DeviceRemoved(d_id)),
1026 DeviceEvent::MouseMotion { delta } => self.notify(Event::DeviceMouseMotion {
1027 device: d_id,
1028 delta: euclid::vec2(delta.0, delta.1),
1029 }),
1030 DeviceEvent::MouseWheel { delta } => self.notify(Event::DeviceMouseWheel {
1031 device: d_id,
1032 delta: util::winit_mouse_wheel_delta_to_zng(*delta),
1033 }),
1034 DeviceEvent::Motion { axis, value } => self.notify(Event::DeviceMotion {
1035 device: d_id,
1036 axis: AxisId(*axis),
1037 value: *value,
1038 }),
1039 DeviceEvent::Button { button, state } => self.notify(Event::DeviceButton {
1040 device: d_id,
1041 button: ButtonId(*button),
1042 state: util::element_state_to_button_state(*state),
1043 }),
1044 DeviceEvent::Key(k) => self.notify(Event::DeviceKey {
1045 device: d_id,
1046 key_code: util::winit_physical_key_to_key_code(k.physical_key),
1047 state: util::element_state_to_key_state(k.state),
1048 }),
1049 }
1050
1051 winit_loop_guard.unset(&mut self.winit_loop);
1052 }
1053
1054 if let Some((id, pos)) = &mut self.drag_drop_hovered {
1055 if let DeviceEvent::MouseMotion { .. } = &event {
1056 if let Some(win) = self.windows.iter().find(|w| w.id() == *id) {
1057 if let Some(new_pos) = win.drag_drop_cursor_pos() {
1058 if *pos != new_pos {
1059 *pos = new_pos;
1060 let event = Event::DragMoved {
1061 window: *id,
1062 coalesced_pos: vec![],
1063 position: *pos,
1064 };
1065 self.notify(event);
1066 }
1067 }
1068 }
1069 }
1070 }
1071 }
1072
1073 fn about_to_wait(&mut self, winit_loop: &ActiveEventLoop) {
1074 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1075
1076 self.finish_cursor_entered_move();
1077 self.update_modifiers();
1078 self.flush_coalesced();
1079 #[cfg(windows)]
1080 {
1081 self.skip_ralt = false;
1082 }
1083 self.idle.enter();
1084
1085 winit_loop_guard.unset(&mut self.winit_loop);
1086 }
1087
1088 fn suspended(&mut self, _: &ActiveEventLoop) {
1089 #[cfg(target_os = "android")]
1090 if let Some(w) = &self.windows.first() {
1091 self.notify(Event::FocusChanged {
1092 prev: Some(w.id()),
1093 new: None,
1094 });
1095 }
1096
1097 self.app_state = AppState::Suspended;
1098 self.windows.clear();
1099 self.surfaces.clear();
1100 self.image_cache.clear();
1101 self.exts.suspended();
1102
1103 self.notify(Event::Suspended);
1104 }
1105
1106 fn exiting(&mut self, event_loop: &ActiveEventLoop) {
1107 let _ = event_loop;
1108 if let Some(t) = self.config_listener_exit.take() {
1109 t();
1110 }
1111 }
1112
1113 fn memory_warning(&mut self, winit_loop: &ActiveEventLoop) {
1114 let mut winit_loop_guard = self.winit_loop.set(winit_loop);
1115
1116 self.image_cache.on_low_memory();
1117 for w in &mut self.windows {
1118 w.on_low_memory();
1119 }
1120 for s in &mut self.surfaces {
1121 s.on_low_memory();
1122 }
1123 self.exts.on_low_memory();
1124 self.notify(Event::LowMemory);
1125
1126 winit_loop_guard.unset(&mut self.winit_loop);
1127 }
1128}
1129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1130enum AppState {
1131 PreInitSuspended,
1132 Resumed,
1133 Suspended,
1134}
1135
1136struct IdleTrace(Option<tracing::span::EnteredSpan>);
1137impl IdleTrace {
1138 pub fn enter(&mut self) {
1139 self.0 = Some(tracing::trace_span!("<winit-idle>").entered());
1140 }
1141 pub fn exit(&mut self) {
1142 self.0 = None;
1143 }
1144}
1145impl App {
1146 fn init_device_events(&mut self, enabled: bool, t: Option<&ActiveEventLoop>) {
1147 self.device_events = enabled;
1148
1149 if let Some(t) = t {
1150 if enabled {
1151 t.listen_device_events(winit::event_loop::DeviceEvents::Always);
1152 } else {
1153 t.listen_device_events(winit::event_loop::DeviceEvents::Never);
1154 }
1155 } else {
1156 self.device_events = false;
1157 }
1158 }
1159
1160 pub fn run_headless(ipc: ipc::ViewChannels, ext: ViewExtensions) {
1161 tracing::info!("running headless view-process");
1162
1163 gl::warmup();
1164
1165 let (app_sender, app_receiver) = flume::unbounded();
1166 let (request_sender, request_receiver) = flume::unbounded();
1167 let mut app = App::new(
1168 AppEventSender::Headless(app_sender, request_sender),
1169 ipc.response_sender,
1170 ipc.event_sender,
1171 request_receiver,
1172 ext,
1173 );
1174 app.headless = true;
1175
1176 let winit_span = tracing::trace_span!("winit::EventLoop::new").entered();
1177 #[cfg(not(target_os = "android"))]
1178 let event_loop = EventLoop::builder().build().unwrap();
1179 #[cfg(target_os = "android")]
1180 let event_loop = EventLoop::builder()
1181 .with_android_app(platform::android::android_app())
1182 .build()
1183 .unwrap();
1184 drop(winit_span);
1185
1186 let mut app = HeadlessApp {
1187 app,
1188 request_receiver: Some(ipc.request_receiver),
1189 app_receiver,
1190 };
1191 if let Err(e) = event_loop.run_app(&mut app) {
1192 if app.app.exited {
1193 tracing::error!("winit event loop error after app exit, {e}");
1199 } else {
1200 panic!("winit event loop error, {e}");
1201 }
1202 }
1203
1204 struct HeadlessApp {
1205 app: App,
1206 request_receiver: Option<ipc::RequestReceiver>,
1207 app_receiver: flume::Receiver<AppEvent>,
1208 }
1209 impl winit::application::ApplicationHandler<()> for HeadlessApp {
1210 fn resumed(&mut self, winit_loop: &ActiveEventLoop) {
1211 let mut winit_loop_guard = self.app.winit_loop.set(winit_loop);
1212
1213 self.app.resumed(winit_loop);
1214 self.app.start_receiving(self.request_receiver.take().unwrap());
1215
1216 'app_loop: while !self.app.exited {
1217 match self.app_receiver.recv() {
1218 Ok(app_ev) => match app_ev {
1219 AppEvent::Request => {
1220 while let Ok(request) = self.app.request_recv.try_recv() {
1221 match request {
1222 RequestEvent::Request(request) => {
1223 let response = self.app.respond(request);
1224 if response.must_be_send() && self.app.response_sender.send(response).is_err() {
1225 self.app.exited = true;
1226 break 'app_loop;
1227 }
1228 }
1229 RequestEvent::FrameReady(id, msg) => {
1230 let r = if let Some(s) = self.app.surfaces.iter_mut().find(|s| s.id() == id) {
1231 Some(s.on_frame_ready(msg, &mut self.app.image_cache))
1232 } else {
1233 None
1234 };
1235 if let Some((frame_id, image)) = r {
1236 self.app.notify(Event::FrameRendered(EventFrameRendered {
1237 window: id,
1238 frame: frame_id,
1239 frame_image: image,
1240 }));
1241 }
1242 }
1243 }
1244 }
1245 }
1246 AppEvent::Notify(ev) => {
1247 if self.app.event_sender.send(ev).is_err() {
1248 self.app.exited = true;
1249 break 'app_loop;
1250 }
1251 }
1252 AppEvent::RefreshMonitors => {
1253 panic!("no monitor info in headless mode")
1254 }
1255 AppEvent::WinitFocused(_, _) => {
1256 panic!("no winit event loop in headless mode")
1257 }
1258 AppEvent::ParentProcessExited => {
1259 self.app.exited = true;
1260 break 'app_loop;
1261 }
1262 AppEvent::ImageLoaded(data) => {
1263 self.app.image_cache.loaded(data);
1264 }
1265 AppEvent::MonitorPowerChanged => {} AppEvent::InitDeviceEvents(enabled) => {
1267 self.app.init_device_events(enabled, None);
1268 }
1269 },
1270 Err(_) => {
1271 self.app.exited = true;
1272 break 'app_loop;
1273 }
1274 }
1275 }
1276
1277 self.app.winit_loop.exit();
1278
1279 winit_loop_guard.unset(&mut self.app.winit_loop);
1280 }
1281
1282 fn window_event(&mut self, _: &ActiveEventLoop, _: winit::window::WindowId, _: WindowEvent) {}
1283
1284 fn suspended(&mut self, event_loop: &ActiveEventLoop) {
1285 self.app.suspended(event_loop);
1286 }
1287 }
1288 }
1289
1290 pub fn run_headed(ipc: ipc::ViewChannels, ext: ViewExtensions) {
1291 tracing::info!("running headed view-process");
1292
1293 gl::warmup();
1294
1295 let winit_span = tracing::trace_span!("winit::EventLoop::new").entered();
1296 #[cfg(not(target_os = "android"))]
1297 let event_loop = EventLoop::with_user_event().build().unwrap();
1298 #[cfg(target_os = "android")]
1299 let event_loop = EventLoop::with_user_event()
1300 .with_android_app(platform::android::android_app())
1301 .build()
1302 .unwrap();
1303 drop(winit_span);
1304 let app_sender = event_loop.create_proxy();
1305
1306 let (request_sender, request_receiver) = flume::unbounded();
1307 let mut app = App::new(
1308 AppEventSender::Headed(app_sender, request_sender),
1309 ipc.response_sender,
1310 ipc.event_sender,
1311 request_receiver,
1312 ext,
1313 );
1314 app.start_receiving(ipc.request_receiver);
1315
1316 app.config_listener_exit = config::spawn_listener(app.app_sender.clone());
1317
1318 if let Err(e) = event_loop.run_app(&mut app) {
1319 if app.exited {
1320 tracing::error!("winit event loop error after app exit, {e}");
1321 } else {
1322 panic!("winit event loop error, {e}");
1323 }
1324 }
1325 }
1326
1327 fn new(
1328 app_sender: AppEventSender,
1329 response_sender: ipc::ResponseSender,
1330 event_sender: ipc::EventSender,
1331 request_recv: flume::Receiver<RequestEvent>,
1332 mut exts: ViewExtensions,
1333 ) -> Self {
1334 exts.renderer("zng-view.webrender_debug", extensions::RendererDebugExt::new);
1335 #[cfg(windows)]
1336 {
1337 exts.window("zng-view.prefer_angle", extensions::PreferAngleExt::new);
1338 }
1339 let mut idle = IdleTrace(None);
1340 idle.enter();
1341 App {
1342 headless: false,
1343 exts,
1344 idle,
1345 gl_manager: GlContextManager::default(),
1346 image_cache: ImageCache::new(app_sender.clone()),
1347 app_sender,
1348 request_recv,
1349 response_sender,
1350 event_sender,
1351 winit_loop: util::WinitEventLoop::default(),
1352 generation: ViewProcessGen::INVALID,
1353 device_events: false,
1354 windows: vec![],
1355 surfaces: vec![],
1356 monitors: vec![],
1357 monitor_id_gen: MonitorId::INVALID,
1358 devices: vec![],
1359 device_id_gen: DeviceId::INVALID,
1360 dialog_id_gen: DialogId::INVALID,
1361 resize_frame_wait_id_gen: FrameWaitId::INVALID,
1362 coalescing_event: None,
1363 cursor_entered_expect_move: Vec::with_capacity(1),
1364 app_state: AppState::PreInitSuspended,
1365 exited: false,
1366 #[cfg(windows)]
1367 skip_ralt: false,
1368 pressed_modifiers: FxHashMap::default(),
1369 pending_modifiers_update: None,
1370 pending_modifiers_focus_clear: false,
1371 config_listener_exit: None,
1372 drag_drop_hovered: None,
1373 drag_drop_next_move: None,
1374
1375 #[cfg(not(any(windows, target_os = "android")))]
1376 arboard: None,
1377 #[cfg(windows)]
1378 low_memory_monitor: low_memory::LowMemoryMonitor::new(),
1379 }
1380 }
1381
1382 fn start_receiving(&mut self, mut request_recv: ipc::RequestReceiver) {
1383 let app_sender = self.app_sender.clone();
1384 thread::spawn(move || {
1385 while let Ok(r) = request_recv.recv() {
1386 if let Err(ipc::Disconnected) = app_sender.request(r) {
1387 break;
1388 }
1389 }
1390 let _ = app_sender.send(AppEvent::ParentProcessExited);
1391 });
1392 }
1393
1394 fn monitor_handle_to_id(&mut self, handle: &MonitorHandle) -> MonitorId {
1395 if let Some((id, _)) = self.monitors.iter().find(|(_, h)| h == handle) {
1396 *id
1397 } else {
1398 self.refresh_monitors();
1399 if let Some((id, _)) = self.monitors.iter().find(|(_, h)| h == handle) {
1400 *id
1401 } else {
1402 MonitorId::INVALID
1403 }
1404 }
1405 }
1406
1407 fn update_modifiers(&mut self) {
1408 if mem::take(&mut self.pending_modifiers_focus_clear) && self.windows.iter().all(|w| !w.is_focused()) {
1415 self.pressed_modifiers.clear();
1416 }
1417
1418 if let Some(m) = self.pending_modifiers_update.take() {
1419 if let Some(id) = self.windows.iter().find(|w| w.is_focused()).map(|w| w.id()) {
1420 let mut notify = vec![];
1421 self.pressed_modifiers.retain(|(key, location), (d_id, s_code)| {
1422 let mut retain = true;
1423 if matches!(key, Key::Super) && !m.super_key() {
1424 retain = false;
1425 notify.push(Event::KeyboardInput {
1426 window: id,
1427 device: *d_id,
1428 key_code: *s_code,
1429 state: KeyState::Released,
1430 key: key.clone(),
1431 key_location: *location,
1432 key_modified: key.clone(),
1433 text: Txt::from_str(""),
1434 });
1435 }
1436 if matches!(key, Key::Shift) && !m.shift_key() {
1437 retain = false;
1438 notify.push(Event::KeyboardInput {
1439 window: id,
1440 device: *d_id,
1441 key_code: *s_code,
1442 state: KeyState::Released,
1443 key: key.clone(),
1444 key_location: *location,
1445 key_modified: key.clone(),
1446 text: Txt::from_str(""),
1447 });
1448 }
1449 if matches!(key, Key::Alt | Key::AltGraph) && !m.alt_key() {
1450 retain = false;
1451 notify.push(Event::KeyboardInput {
1452 window: id,
1453 device: *d_id,
1454 key_code: *s_code,
1455 state: KeyState::Released,
1456 key: key.clone(),
1457 key_location: *location,
1458 key_modified: key.clone(),
1459 text: Txt::from_str(""),
1460 });
1461 }
1462 if matches!(key, Key::Ctrl) && !m.control_key() {
1463 retain = false;
1464 notify.push(Event::KeyboardInput {
1465 window: id,
1466 device: *d_id,
1467 key_code: *s_code,
1468 state: KeyState::Released,
1469 key: key.clone(),
1470 key_location: *location,
1471 key_modified: key.clone(),
1472 text: Txt::from_str(""),
1473 });
1474 }
1475 retain
1476 });
1477
1478 for ev in notify {
1479 self.notify(ev);
1480 }
1481 }
1482 }
1483 }
1484
1485 fn refresh_monitors(&mut self) {
1486 let mut monitors = Vec::with_capacity(self.monitors.len());
1487
1488 let mut changed = false;
1489
1490 for (fresh_handle, (id, handle)) in self.winit_loop.available_monitors().zip(&self.monitors) {
1491 let id = if &fresh_handle == handle {
1492 *id
1493 } else {
1494 changed = true;
1495 self.monitor_id_gen.incr()
1496 };
1497 monitors.push((id, fresh_handle))
1498 }
1499
1500 if changed {
1501 self.monitors = monitors;
1502
1503 let monitors = self.available_monitors();
1504 self.notify(Event::MonitorsChanged(monitors));
1505 }
1506 }
1507
1508 fn on_frame_ready(&mut self, window_id: WindowId, msg: FrameReadyMsg) {
1509 let _s = tracing::trace_span!("on_frame_ready").entered();
1510
1511 if let Some(w) = self.windows.iter_mut().find(|w| w.id() == window_id) {
1512 let r = w.on_frame_ready(msg, &mut self.image_cache);
1513
1514 let _ = self.event_sender.send(Event::FrameRendered(EventFrameRendered {
1515 window: window_id,
1516 frame: r.frame_id,
1517 frame_image: r.image,
1518 }));
1519
1520 if r.first_frame {
1521 let size = w.size();
1522 self.notify(Event::WindowChanged(WindowChanged::resized(window_id, size, EventCause::App, None)));
1523 }
1524 } else if let Some(s) = self.surfaces.iter_mut().find(|w| w.id() == window_id) {
1525 let (frame_id, image) = s.on_frame_ready(msg, &mut self.image_cache);
1526
1527 self.notify(Event::FrameRendered(EventFrameRendered {
1528 window: window_id,
1529 frame: frame_id,
1530 frame_image: image,
1531 }))
1532 }
1533 }
1534
1535 pub(crate) fn notify(&mut self, event: Event) {
1536 let now = Instant::now();
1537 if let Some((mut coal, timestamp)) = self.coalescing_event.take() {
1538 let r = if now.saturating_duration_since(timestamp) >= Duration::from_millis(16) {
1539 Err(event)
1540 } else {
1541 coal.coalesce(event)
1542 };
1543 match r {
1544 Ok(()) => self.coalescing_event = Some((coal, timestamp)),
1545 Err(event) => match (&mut coal, event) {
1546 (
1547 Event::KeyboardInput {
1548 window,
1549 device,
1550 state,
1551 text,
1552 ..
1553 },
1554 Event::KeyboardInput {
1555 window: n_window,
1556 device: n_device,
1557 text: n_text,
1558 ..
1559 },
1560 ) if !n_text.is_empty() && *window == n_window && *device == n_device && *state == KeyState::Pressed => {
1561 if text.is_empty() {
1563 *text = n_text;
1564 } else {
1565 text.push_str(&n_text);
1566 };
1567 self.coalescing_event = Some((coal, now));
1568 }
1569 (_, event) => {
1570 let mut error = self.event_sender.send(coal).is_err();
1571 error |= self.event_sender.send(event).is_err();
1572
1573 if error {
1574 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1575 }
1576 }
1577 },
1578 }
1579 } else {
1580 self.coalescing_event = Some((event, now));
1581 }
1582
1583 if self.headless {
1584 self.flush_coalesced();
1585 }
1586 }
1587
1588 pub(crate) fn finish_cursor_entered_move(&mut self) {
1589 let mut moves = vec![];
1590 for window_id in self.cursor_entered_expect_move.drain(..) {
1591 if let Some(w) = self.windows.iter().find(|w| w.id() == window_id) {
1592 let (position, device) = w.last_cursor_pos();
1593 moves.push(Event::MouseMoved {
1594 window: w.id(),
1595 device,
1596 coalesced_pos: vec![],
1597 position,
1598 });
1599 }
1600 }
1601 for ev in moves {
1602 self.notify(ev);
1603 }
1604 }
1605
1606 pub(crate) fn flush_coalesced(&mut self) {
1608 if let Some((coal, _)) = self.coalescing_event.take() {
1609 if self.event_sender.send(coal).is_err() {
1610 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1611 }
1612 }
1613 }
1614
1615 #[track_caller]
1616 fn assert_resumed(&self) {
1617 assert_eq!(self.app_state, AppState::Resumed);
1618 }
1619
1620 fn with_window<R>(&mut self, id: WindowId, action: impl FnOnce(&mut Window) -> R, not_found: impl FnOnce() -> R) -> R {
1621 self.assert_resumed();
1622 self.windows.iter_mut().find(|w| w.id() == id).map(action).unwrap_or_else(|| {
1623 tracing::error!("headed window `{id:?}` not found, will return fallback result");
1624 not_found()
1625 })
1626 }
1627
1628 fn monitor_id(&mut self, handle: &MonitorHandle) -> MonitorId {
1629 if let Some((id, _)) = self.monitors.iter().find(|(_, h)| h == handle) {
1630 *id
1631 } else {
1632 let id = self.monitor_id_gen.incr();
1633 self.monitors.push((id, handle.clone()));
1634 id
1635 }
1636 }
1637
1638 fn device_id(&mut self, device_id: winit::event::DeviceId) -> DeviceId {
1639 if let Some((id, _)) = self.devices.iter().find(|(_, id)| *id == device_id) {
1640 *id
1641 } else {
1642 let id = self.device_id_gen.incr();
1643 self.devices.push((id, device_id));
1644 id
1645 }
1646 }
1647
1648 fn available_monitors(&mut self) -> Vec<(MonitorId, MonitorInfo)> {
1649 let _span = tracing::trace_span!("available_monitors").entered();
1650
1651 let primary = self.winit_loop.primary_monitor();
1652 self.winit_loop
1653 .available_monitors()
1654 .map(|m| {
1655 let id = self.monitor_id(&m);
1656 let is_primary = primary.as_ref().map(|h| h == &m).unwrap_or(false);
1657 let mut info = util::monitor_handle_to_info(&m);
1658 info.is_primary = is_primary;
1659 (id, info)
1660 })
1661 .collect()
1662 }
1663
1664 fn update_memory_monitor(&mut self, _winit_loop: &ActiveEventLoop) {
1665 #[cfg(windows)]
1666 if let Some(m) = &mut self.low_memory_monitor {
1667 if m.notify() {
1668 use winit::application::ApplicationHandler as _;
1669 self.memory_warning(_winit_loop);
1670 }
1671 _winit_loop.set_control_flow(winit::event_loop::ControlFlow::wait_duration(Duration::from_secs(5)));
1672 }
1673 }
1674}
1675macro_rules! with_window_or_surface {
1676 ($self:ident, $id:ident, |$el:ident|$action:expr, ||$fallback:expr) => {
1677 if let Some($el) = $self.windows.iter_mut().find(|w| w.id() == $id) {
1678 $action
1679 } else if let Some($el) = $self.surfaces.iter_mut().find(|w| w.id() == $id) {
1680 $action
1681 } else {
1682 tracing::error!("window `{:?}` not found, will return fallback result", $id);
1683 $fallback
1684 }
1685 };
1686}
1687impl Drop for App {
1688 fn drop(&mut self) {
1689 if let Some(f) = self.config_listener_exit.take() {
1690 f();
1691 }
1692 }
1693}
1694impl App {
1695 fn open_headless_impl(&mut self, config: HeadlessRequest) -> HeadlessOpenData {
1696 self.assert_resumed();
1697 let surf = Surface::open(
1698 self.generation,
1699 config,
1700 &self.winit_loop,
1701 &mut self.gl_manager,
1702 self.exts.new_window(),
1703 self.exts.new_renderer(),
1704 self.app_sender.clone(),
1705 );
1706 let render_mode = surf.render_mode();
1707
1708 self.surfaces.push(surf);
1709
1710 HeadlessOpenData { render_mode }
1711 }
1712
1713 #[cfg(not(any(windows, target_os = "android")))]
1714 fn arboard(&mut self) -> Result<&mut arboard::Clipboard, clipboard::ClipboardError> {
1715 if self.arboard.is_none() {
1716 match arboard::Clipboard::new() {
1717 Ok(c) => self.arboard = Some(c),
1718 Err(e) => return Err(util::arboard_to_clip(e)),
1719 }
1720 }
1721 Ok(self.arboard.as_mut().unwrap())
1722 }
1723}
1724
1725impl Api for App {
1726 fn init(&mut self, vp_gen: ViewProcessGen, is_respawn: bool, device_events: bool, headless: bool) {
1727 if self.exited {
1728 panic!("cannot restart exited");
1729 }
1730
1731 self.generation = vp_gen;
1732 self.device_events = device_events;
1733 self.headless = headless;
1734
1735 self.app_sender.send(AppEvent::InitDeviceEvents(device_events)).unwrap();
1736
1737 let available_monitors = self.available_monitors();
1738 self.notify(Event::Inited(Inited {
1739 generation: vp_gen,
1740 is_respawn,
1741 available_monitors,
1742 multi_click_config: config::multi_click_config(),
1743 key_repeat_config: config::key_repeat_config(),
1744 touch_config: config::touch_config(),
1745 font_aa: config::font_aa(),
1746 animations_config: config::animations_config(),
1747 locale_config: config::locale_config(),
1748 colors_config: config::colors_config(),
1749 chrome_config: config::chrome_config(),
1750 extensions: self.exts.api_extensions(),
1751 }));
1752 }
1753
1754 fn exit(&mut self) {
1755 self.assert_resumed();
1756 self.exited = true;
1757 if let Some(t) = self.config_listener_exit.take() {
1758 t();
1759 }
1760 let _ = self.app_sender.send(AppEvent::ParentProcessExited);
1762 }
1763
1764 fn open_window(&mut self, mut config: WindowRequest) {
1765 let _s = tracing::debug_span!("open", ?config).entered();
1766
1767 config.state.clamp_size();
1768 config.enforce_kiosk();
1769
1770 if self.headless {
1771 let id = config.id;
1772 let data = self.open_headless_impl(HeadlessRequest {
1773 id: config.id,
1774 scale_factor: Factor(1.0),
1775 size: config.state.restore_rect.size,
1776 render_mode: config.render_mode,
1777 extensions: config.extensions,
1778 });
1779 let msg = WindowOpenData {
1780 render_mode: data.render_mode,
1781 monitor: None,
1782 position: (PxPoint::zero(), DipPoint::zero()),
1783 size: config.state.restore_rect.size,
1784 scale_factor: Factor(1.0),
1785 safe_padding: DipSideOffsets::zero(),
1786 state: WindowStateAll {
1787 state: WindowState::Fullscreen,
1788 global_position: PxPoint::zero(),
1789 restore_rect: DipRect::from_size(config.state.restore_rect.size),
1790 restore_state: WindowState::Fullscreen,
1791 min_size: DipSize::zero(),
1792 max_size: DipSize::new(Dip::MAX, Dip::MAX),
1793 chrome_visible: false,
1794 },
1795 };
1796
1797 self.notify(Event::WindowOpened(id, msg));
1798 } else {
1799 self.assert_resumed();
1800
1801 #[cfg(target_os = "android")]
1802 if !self.windows.is_empty() {
1803 tracing::error!("android can only have one window");
1804 return;
1805 }
1806
1807 let id = config.id;
1808 let win = Window::open(
1809 self.generation,
1810 config.icon.and_then(|i| self.image_cache.get(i)).and_then(|i| i.icon()),
1811 config
1812 .cursor_image
1813 .and_then(|(i, h)| self.image_cache.get(i).and_then(|i| i.cursor(h, &self.winit_loop))),
1814 config,
1815 &self.winit_loop,
1816 &mut self.gl_manager,
1817 self.exts.new_window(),
1818 self.exts.new_renderer(),
1819 self.app_sender.clone(),
1820 );
1821
1822 let msg = WindowOpenData {
1823 monitor: win.monitor().map(|h| self.monitor_id(&h)),
1824 position: win.inner_position(),
1825 size: win.size(),
1826 scale_factor: win.scale_factor(),
1827 render_mode: win.render_mode(),
1828 state: win.state(),
1829 safe_padding: win.safe_padding(),
1830 };
1831
1832 self.windows.push(win);
1833
1834 self.notify(Event::WindowOpened(id, msg));
1835
1836 #[cfg(target_os = "android")]
1838 {
1839 self.windows.last_mut().unwrap().focused_changed(&mut true);
1840 self.notify(Event::FocusChanged { prev: None, new: Some(id) });
1841 }
1842 }
1843 }
1844
1845 fn open_headless(&mut self, config: HeadlessRequest) {
1846 let _s = tracing::debug_span!("open_headless", ?config).entered();
1847
1848 let id = config.id;
1849 let msg = self.open_headless_impl(config);
1850
1851 self.notify(Event::HeadlessOpened(id, msg));
1852 }
1853
1854 fn close(&mut self, id: WindowId) {
1855 let _s = tracing::debug_span!("close_window", ?id);
1856
1857 self.assert_resumed();
1858 if let Some(i) = self.windows.iter().position(|w| w.id() == id) {
1859 let _ = self.windows.swap_remove(i);
1860 }
1861 if let Some(i) = self.surfaces.iter().position(|w| w.id() == id) {
1862 let _ = self.surfaces.swap_remove(i);
1863 }
1864 }
1865
1866 fn set_title(&mut self, id: WindowId, title: Txt) {
1867 self.with_window(id, |w| w.set_title(title), || ())
1868 }
1869
1870 fn set_visible(&mut self, id: WindowId, visible: bool) {
1871 self.with_window(id, |w| w.set_visible(visible), || ())
1872 }
1873
1874 fn set_always_on_top(&mut self, id: WindowId, always_on_top: bool) {
1875 self.with_window(id, |w| w.set_always_on_top(always_on_top), || ())
1876 }
1877
1878 fn set_movable(&mut self, id: WindowId, movable: bool) {
1879 self.with_window(id, |w| w.set_movable(movable), || ())
1880 }
1881
1882 fn set_resizable(&mut self, id: WindowId, resizable: bool) {
1883 self.with_window(id, |w| w.set_resizable(resizable), || ())
1884 }
1885
1886 fn set_taskbar_visible(&mut self, id: WindowId, visible: bool) {
1887 self.with_window(id, |w| w.set_taskbar_visible(visible), || ())
1888 }
1889
1890 fn bring_to_top(&mut self, id: WindowId) {
1891 self.with_window(id, |w| w.bring_to_top(), || ())
1892 }
1893
1894 fn set_state(&mut self, id: WindowId, state: WindowStateAll) {
1895 if let Some(w) = self.windows.iter_mut().find(|w| w.id() == id) {
1896 if w.set_state(state.clone()) {
1897 let mut change = WindowChanged::state_changed(id, state, EventCause::App);
1898
1899 change.size = w.resized();
1900 change.position = w.moved();
1901 if let Some(handle) = w.monitor_change() {
1902 let monitor = self.monitor_handle_to_id(&handle);
1903 change.monitor = Some(monitor);
1904 }
1905
1906 let _ = self.app_sender.send(AppEvent::Notify(Event::WindowChanged(change)));
1907 }
1908 }
1909 }
1910
1911 fn set_headless_size(&mut self, renderer: WindowId, size: DipSize, scale_factor: Factor) {
1912 self.assert_resumed();
1913 if let Some(surf) = self.surfaces.iter_mut().find(|s| s.id() == renderer) {
1914 surf.set_size(size, scale_factor)
1915 }
1916 }
1917
1918 fn set_video_mode(&mut self, id: WindowId, mode: VideoMode) {
1919 self.with_window(id, |w| w.set_video_mode(mode), || ())
1920 }
1921
1922 fn set_icon(&mut self, id: WindowId, icon: Option<ImageId>) {
1923 let icon = icon.and_then(|i| self.image_cache.get(i)).and_then(|i| i.icon());
1924 self.with_window(id, |w| w.set_icon(icon), || ())
1925 }
1926
1927 fn set_focus_indicator(&mut self, id: WindowId, request: Option<FocusIndicator>) {
1928 self.with_window(id, |w| w.set_focus_request(request), || ())
1929 }
1930
1931 fn focus(&mut self, id: WindowId) -> FocusResult {
1932 #[cfg(windows)]
1933 {
1934 let (r, s) = self.with_window(id, |w| w.focus(), || (FocusResult::Requested, false));
1935 self.skip_ralt = s;
1936 r
1937 }
1938
1939 #[cfg(not(windows))]
1940 {
1941 self.with_window(id, |w| w.focus(), || FocusResult::Requested)
1942 }
1943 }
1944
1945 fn drag_move(&mut self, id: WindowId) {
1946 self.with_window(id, |w| w.drag_move(), || ())
1947 }
1948
1949 fn drag_resize(&mut self, id: WindowId, direction: zng_view_api::window::ResizeDirection) {
1950 self.with_window(id, |w| w.drag_resize(direction), || ())
1951 }
1952
1953 fn set_enabled_buttons(&mut self, id: WindowId, buttons: zng_view_api::window::WindowButton) {
1954 self.with_window(id, |w| w.set_enabled_buttons(buttons), || ())
1955 }
1956
1957 fn open_title_bar_context_menu(&mut self, id: WindowId, position: DipPoint) {
1958 self.with_window(id, |w| w.open_title_bar_context_menu(position), || ())
1959 }
1960
1961 fn set_cursor(&mut self, id: WindowId, icon: Option<CursorIcon>) {
1962 self.with_window(id, |w| w.set_cursor(icon), || ())
1963 }
1964
1965 fn set_cursor_image(&mut self, id: WindowId, icon: Option<CursorImage>) {
1966 let icon = icon.and_then(|img| self.image_cache.get(img.img).and_then(|i| i.cursor(img.hotspot, &self.winit_loop)));
1967 self.with_window(id, |w| w.set_cursor_image(icon), || ());
1968 }
1969
1970 fn set_ime_area(&mut self, id: WindowId, area: Option<DipRect>) {
1971 self.with_window(id, |w| w.set_ime_area(area), || ())
1972 }
1973
1974 fn image_decoders(&mut self) -> Vec<Txt> {
1975 image_cache::DECODERS.iter().map(|&s| Txt::from_static(s)).collect()
1976 }
1977
1978 fn image_encoders(&mut self) -> Vec<Txt> {
1979 image_cache::ENCODERS.iter().map(|&s| Txt::from_static(s)).collect()
1980 }
1981
1982 fn add_image(&mut self, request: ImageRequest<IpcBytes>) -> ImageId {
1983 self.image_cache.add(request)
1984 }
1985
1986 fn add_image_pro(&mut self, request: ImageRequest<IpcBytesReceiver>) -> ImageId {
1987 self.image_cache.add_pro(request)
1988 }
1989
1990 fn forget_image(&mut self, id: ImageId) {
1991 self.image_cache.forget(id)
1992 }
1993
1994 fn encode_image(&mut self, id: ImageId, format: Txt) {
1995 self.image_cache.encode(id, format)
1996 }
1997
1998 fn use_image(&mut self, id: WindowId, image_id: ImageId) -> ImageTextureId {
1999 if let Some(img) = self.image_cache.get(image_id) {
2000 with_window_or_surface!(self, id, |w| w.use_image(img), || ImageTextureId::INVALID)
2001 } else {
2002 ImageTextureId::INVALID
2003 }
2004 }
2005
2006 fn update_image_use(&mut self, id: WindowId, texture_id: ImageTextureId, image_id: ImageId) {
2007 if let Some(img) = self.image_cache.get(image_id) {
2008 with_window_or_surface!(self, id, |w| w.update_image(texture_id, img), || ())
2009 }
2010 }
2011
2012 fn delete_image_use(&mut self, id: WindowId, texture_id: ImageTextureId) {
2013 with_window_or_surface!(self, id, |w| w.delete_image(texture_id), || ())
2014 }
2015
2016 fn add_font_face(&mut self, id: WindowId, bytes: IpcBytes, index: u32) -> FontFaceId {
2017 with_window_or_surface!(self, id, |w| w.add_font_face(bytes.to_vec(), index), || FontFaceId::INVALID)
2018 }
2019
2020 fn delete_font_face(&mut self, id: WindowId, font_face_id: FontFaceId) {
2021 with_window_or_surface!(self, id, |w| w.delete_font_face(font_face_id), || ())
2022 }
2023
2024 fn add_font(
2025 &mut self,
2026 id: WindowId,
2027 font_face_id: FontFaceId,
2028 glyph_size: Px,
2029 options: FontOptions,
2030 variations: Vec<(FontVariationName, f32)>,
2031 ) -> FontId {
2032 with_window_or_surface!(self, id, |w| w.add_font(font_face_id, glyph_size, options, variations), || {
2033 FontId::INVALID
2034 })
2035 }
2036
2037 fn delete_font(&mut self, id: WindowId, font_id: FontId) {
2038 with_window_or_surface!(self, id, |w| w.delete_font(font_id), || ())
2039 }
2040
2041 fn set_capture_mode(&mut self, id: WindowId, enabled: bool) {
2042 self.with_window(id, |w| w.set_capture_mode(enabled), || ())
2043 }
2044
2045 fn frame_image(&mut self, id: WindowId, mask: Option<ImageMaskMode>) -> ImageId {
2046 with_window_or_surface!(self, id, |w| w.frame_image(&mut self.image_cache, mask), || ImageId::INVALID)
2047 }
2048
2049 fn frame_image_rect(&mut self, id: WindowId, rect: PxRect, mask: Option<ImageMaskMode>) -> ImageId {
2050 with_window_or_surface!(self, id, |w| w.frame_image_rect(&mut self.image_cache, rect, mask), || {
2051 ImageId::INVALID
2052 })
2053 }
2054
2055 fn render(&mut self, id: WindowId, frame: FrameRequest) {
2056 with_window_or_surface!(self, id, |w| w.render(frame), || ())
2057 }
2058
2059 fn render_update(&mut self, id: WindowId, frame: FrameUpdateRequest) {
2060 with_window_or_surface!(self, id, |w| w.render_update(frame), || ())
2061 }
2062
2063 fn access_update(&mut self, id: WindowId, update: access::AccessTreeUpdate) {
2064 if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
2065 s.access_update(update, &self.app_sender);
2066 }
2067 }
2068
2069 fn message_dialog(&mut self, id: WindowId, dialog: MsgDialog) -> DialogId {
2070 let r_id = self.dialog_id_gen.incr();
2071 if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
2072 s.message_dialog(dialog, r_id, self.app_sender.clone());
2073 } else {
2074 let r = MsgDialogResponse::Error(Txt::from_static("window not found"));
2075 let _ = self.app_sender.send(AppEvent::Notify(Event::MsgDialogResponse(r_id, r)));
2076 }
2077 r_id
2078 }
2079
2080 fn file_dialog(&mut self, id: WindowId, dialog: FileDialog) -> DialogId {
2081 let r_id = self.dialog_id_gen.incr();
2082 if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
2083 s.file_dialog(dialog, r_id, self.app_sender.clone());
2084 } else {
2085 let r = MsgDialogResponse::Error(Txt::from_static("window not found"));
2086 let _ = self.app_sender.send(AppEvent::Notify(Event::MsgDialogResponse(r_id, r)));
2087 };
2088 r_id
2089 }
2090
2091 #[cfg(windows)]
2092 fn read_clipboard(&mut self, data_type: clipboard::ClipboardType) -> Result<clipboard::ClipboardData, clipboard::ClipboardError> {
2093 match data_type {
2094 clipboard::ClipboardType::Text => {
2095 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2096
2097 clipboard_win::get(clipboard_win::formats::Unicode)
2098 .map_err(util::clipboard_win_to_clip)
2099 .map(|s: String| clipboard::ClipboardData::Text(Txt::from_str(&s)))
2100 }
2101 clipboard::ClipboardType::Image => {
2102 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2103
2104 let bitmap = clipboard_win::get(clipboard_win::formats::Bitmap).map_err(util::clipboard_win_to_clip)?;
2105
2106 let id = self.image_cache.add(ImageRequest {
2107 format: image::ImageDataFormat::FileExtension(Txt::from_str("bmp")),
2108 data: IpcBytes::from_vec(bitmap),
2109 max_decoded_len: u64::MAX,
2110 downscale: None,
2111 mask: None,
2112 });
2113 Ok(clipboard::ClipboardData::Image(id))
2114 }
2115 clipboard::ClipboardType::FileList => {
2116 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2117
2118 clipboard_win::get(clipboard_win::formats::FileList)
2119 .map_err(util::clipboard_win_to_clip)
2120 .map(clipboard::ClipboardData::FileList)
2121 }
2122 clipboard::ClipboardType::Extension(_) => Err(clipboard::ClipboardError::NotSupported),
2123 }
2124 }
2125
2126 #[cfg(windows)]
2127 fn write_clipboard(&mut self, data: clipboard::ClipboardData) -> Result<(), clipboard::ClipboardError> {
2128 use zng_txt::formatx;
2129
2130 match data {
2131 clipboard::ClipboardData::Text(t) => {
2132 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2133
2134 clipboard_win::set(clipboard_win::formats::Unicode, t).map_err(util::clipboard_win_to_clip)
2135 }
2136 clipboard::ClipboardData::Image(id) => {
2137 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2138
2139 if let Some(img) = self.image_cache.get(id) {
2140 let mut bmp = vec![];
2141 img.encode(::image::ImageFormat::Bmp, &mut bmp)
2142 .map_err(|e| clipboard::ClipboardError::Other(formatx!("{e:?}")))?;
2143 clipboard_win::set(clipboard_win::formats::Bitmap, bmp).map_err(util::clipboard_win_to_clip)
2144 } else {
2145 Err(clipboard::ClipboardError::Other(Txt::from_str("image not found")))
2146 }
2147 }
2148 clipboard::ClipboardData::FileList(l) => {
2149 use clipboard_win::Setter;
2150 let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
2151
2152 let strs = l.into_iter().map(|p| p.display().to_string()).collect::<Vec<String>>();
2154 clipboard_win::formats::FileList
2155 .write_clipboard(&strs)
2156 .map_err(util::clipboard_win_to_clip)
2157 }
2158 clipboard::ClipboardData::Extension { .. } => Err(clipboard::ClipboardError::NotSupported),
2159 }
2160 }
2161
2162 #[cfg(not(any(windows, target_os = "android")))]
2163 fn read_clipboard(&mut self, data_type: clipboard::ClipboardType) -> Result<clipboard::ClipboardData, clipboard::ClipboardError> {
2164 match data_type {
2165 clipboard::ClipboardType::Text => self
2166 .arboard()?
2167 .get_text()
2168 .map_err(util::arboard_to_clip)
2169 .map(|s| clipboard::ClipboardData::Text(zng_txt::Txt::from(s))),
2170 clipboard::ClipboardType::Image => {
2171 let bitmap = self.arboard()?.get_image().map_err(util::arboard_to_clip)?;
2172 let mut data = bitmap.bytes.into_owned();
2173 for rgba in data.chunks_exact_mut(4) {
2174 rgba.swap(0, 2); }
2176 let id = self.image_cache.add(image::ImageRequest {
2177 format: image::ImageDataFormat::Bgra8 {
2178 size: zng_unit::PxSize::new(Px(bitmap.width as _), Px(bitmap.height as _)),
2179 ppi: None,
2180 },
2181 data: IpcBytes::from_vec(data),
2182 max_decoded_len: u64::MAX,
2183 downscale: None,
2184 mask: None,
2185 });
2186 Ok(clipboard::ClipboardData::Image(id))
2187 }
2188 clipboard::ClipboardType::FileList => Err(clipboard::ClipboardError::NotSupported),
2189 clipboard::ClipboardType::Extension(_) => Err(clipboard::ClipboardError::NotSupported),
2190 }
2191 }
2192
2193 #[cfg(not(any(windows, target_os = "android")))]
2194 fn write_clipboard(&mut self, data: clipboard::ClipboardData) -> Result<(), clipboard::ClipboardError> {
2195 match data {
2196 clipboard::ClipboardData::Text(t) => self.arboard()?.set_text(t).map_err(util::arboard_to_clip),
2197 clipboard::ClipboardData::Image(id) => {
2198 self.arboard()?;
2199 if let Some(img) = self.image_cache.get(id) {
2200 let size = img.size();
2201 let mut data = img.pixels().clone().to_vec();
2202 for rgba in data.chunks_exact_mut(4) {
2203 rgba.swap(0, 2); }
2205 let board = self.arboard()?;
2206 let _ = board.set_image(arboard::ImageData {
2207 width: size.width.0 as _,
2208 height: size.height.0 as _,
2209 bytes: std::borrow::Cow::Owned(data),
2210 });
2211 Ok(())
2212 } else {
2213 Err(clipboard::ClipboardError::Other(zng_txt::Txt::from_static("image not found")))
2214 }
2215 }
2216 clipboard::ClipboardData::FileList(_) => Err(clipboard::ClipboardError::NotSupported),
2217 clipboard::ClipboardData::Extension { .. } => Err(clipboard::ClipboardError::NotSupported),
2218 }
2219 }
2220
2221 #[cfg(target_os = "android")]
2222 fn read_clipboard(&mut self, data_type: clipboard::ClipboardType) -> Result<clipboard::ClipboardData, clipboard::ClipboardError> {
2223 let _ = data_type;
2224 Err(clipboard::ClipboardError::Other(Txt::from_static(
2225 "clipboard not implemented for Android",
2226 )))
2227 }
2228
2229 #[cfg(target_os = "android")]
2230 fn write_clipboard(&mut self, data: clipboard::ClipboardData) -> Result<(), clipboard::ClipboardError> {
2231 let _ = data;
2232 Err(clipboard::ClipboardError::Other(Txt::from_static(
2233 "clipboard not implemented for Android",
2234 )))
2235 }
2236
2237 fn start_drag_drop(
2238 &mut self,
2239 id: WindowId,
2240 data: Vec<DragDropData>,
2241 allowed_effects: DragDropEffect,
2242 ) -> Result<DragDropId, DragDropError> {
2243 let _ = (id, data, allowed_effects); Err(DragDropError::NotSupported)
2245 }
2246
2247 fn cancel_drag_drop(&mut self, id: WindowId, drag_id: DragDropId) {
2248 let _ = (id, drag_id);
2249 }
2250
2251 fn drag_dropped(&mut self, id: WindowId, drop_id: DragDropId, applied: DragDropEffect) {
2252 let _ = (id, drop_id, applied); }
2254
2255 fn set_system_shutdown_warn(&mut self, id: WindowId, reason: Txt) {
2256 self.with_window(id, move |w| w.set_system_shutdown_warn(reason), || ())
2257 }
2258
2259 fn third_party_licenses(&mut self) -> Vec<zng_tp_licenses::LicenseUsed> {
2260 #[cfg(feature = "bundle_licenses")]
2261 {
2262 zng_tp_licenses::include_bundle!()
2263 }
2264 #[cfg(not(feature = "bundle_licenses"))]
2265 {
2266 vec![]
2267 }
2268 }
2269
2270 fn app_extension(&mut self, extension_id: ApiExtensionId, extension_request: ApiExtensionPayload) -> ApiExtensionPayload {
2271 self.exts.call_command(extension_id, extension_request)
2272 }
2273
2274 fn window_extension(
2275 &mut self,
2276 id: WindowId,
2277 extension_id: ApiExtensionId,
2278 extension_request: ApiExtensionPayload,
2279 ) -> ApiExtensionPayload {
2280 self.with_window(
2281 id,
2282 |w| w.window_extension(extension_id, extension_request),
2283 || ApiExtensionPayload::invalid_request(extension_id, "window not found"),
2284 )
2285 }
2286
2287 fn render_extension(
2288 &mut self,
2289 id: WindowId,
2290 extension_id: ApiExtensionId,
2291 extension_request: ApiExtensionPayload,
2292 ) -> ApiExtensionPayload {
2293 with_window_or_surface!(self, id, |w| w.render_extension(extension_id, extension_request), || {
2294 ApiExtensionPayload::invalid_request(extension_id, "renderer not found")
2295 })
2296 }
2297}
2298
2299#[derive(Debug)]
2301pub(crate) enum AppEvent {
2302 Request,
2304 Notify(Event),
2306 #[cfg_attr(not(windows), allow(unused))]
2308 RefreshMonitors,
2309
2310 #[cfg_attr(not(windows), allow(unused))]
2312 WinitFocused(winit::window::WindowId, bool),
2313
2314 ParentProcessExited,
2316
2317 ImageLoaded(ImageLoadedData),
2319
2320 InitDeviceEvents(bool),
2322
2323 #[allow(unused)]
2325 MonitorPowerChanged,
2326}
2327
2328#[derive(Debug)]
2333enum RequestEvent {
2334 Request(Request),
2336 FrameReady(WindowId, FrameReadyMsg),
2338}
2339
2340#[derive(Debug)]
2341pub(crate) struct FrameReadyMsg {
2342 pub composite_needed: bool,
2343}
2344
2345#[derive(Clone)]
2347pub(crate) enum AppEventSender {
2348 Headed(EventLoopProxy<AppEvent>, flume::Sender<RequestEvent>),
2349 Headless(flume::Sender<AppEvent>, flume::Sender<RequestEvent>),
2350}
2351impl AppEventSender {
2352 fn send(&self, ev: AppEvent) -> Result<(), ipc::Disconnected> {
2354 match self {
2355 AppEventSender::Headed(p, _) => p.send_event(ev).map_err(|_| ipc::Disconnected),
2356 AppEventSender::Headless(p, _) => p.send(ev).map_err(|_| ipc::Disconnected),
2357 }
2358 }
2359
2360 fn request(&self, req: Request) -> Result<(), ipc::Disconnected> {
2362 match self {
2363 AppEventSender::Headed(_, p) => p.send(RequestEvent::Request(req)).map_err(|_| ipc::Disconnected),
2364 AppEventSender::Headless(_, p) => p.send(RequestEvent::Request(req)).map_err(|_| ipc::Disconnected),
2365 }?;
2366 self.send(AppEvent::Request)
2367 }
2368
2369 fn frame_ready(&self, window_id: WindowId, msg: FrameReadyMsg) -> Result<(), ipc::Disconnected> {
2371 match self {
2372 AppEventSender::Headed(_, p) => p.send(RequestEvent::FrameReady(window_id, msg)).map_err(|_| ipc::Disconnected),
2373 AppEventSender::Headless(_, p) => p.send(RequestEvent::FrameReady(window_id, msg)).map_err(|_| ipc::Disconnected),
2374 }?;
2375 self.send(AppEvent::Request)
2376 }
2377}
2378
2379pub(crate) struct WrNotifier {
2381 id: WindowId,
2382 sender: AppEventSender,
2383}
2384impl WrNotifier {
2385 pub fn create(id: WindowId, sender: AppEventSender) -> Box<dyn RenderNotifier> {
2386 Box::new(WrNotifier { id, sender })
2387 }
2388}
2389impl RenderNotifier for WrNotifier {
2390 fn clone(&self) -> Box<dyn RenderNotifier> {
2391 Box::new(Self {
2392 id: self.id,
2393 sender: self.sender.clone(),
2394 })
2395 }
2396
2397 fn wake_up(&self, _: bool) {}
2398
2399 fn new_frame_ready(&self, _document_id: DocumentId, _scrolled: bool, composite_needed: bool, _: FramePublishId) {
2400 let msg = FrameReadyMsg { composite_needed };
2401 let _ = self.sender.frame_ready(self.id, msg);
2402 }
2403}
2404
2405#[cfg(target_arch = "wasm32")]
2406compile_error!("zng-view does not support Wasm");