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