zng_view/
lib.rs

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//!
4//! View-Process implementation.
5//!
6//! This implementation supports headed and headless apps in Windows, Linux and MacOS.
7//!
8//! # Usage
9//!
10//! First add this to your `Cargo.toml`:
11//!
12//! ```toml
13//! [dependencies]
14//! zng = "0.20.4"
15//! zng-view = "0.15.3"
16//! ```
17//!
18//! Then call `zng::env::init` before any other code in `main` to setup a view-process that uses
19//! the same app executable:
20//!
21//! ```
22//! # macro_rules! _demo {()=>{
23//! use zng::prelude::*;
24//!
25//! fn main() {
26//!     zng::env::init!();
27//!
28//!     APP.defaults().run_window(|ctx| unimplemented!())
29//! }
30//! # }}
31//! ```
32//!
33//! When the app is executed `run_window` gets called and internally starts the view-process.
34//! The current executable is started this time configured to be a view-process, `init` detects this and highjacks the process
35//! **never returning**.
36//!
37//! # Software Backend
38//!
39//! The `webrender/swgl` software renderer can be used as fallback when no native OpenGL 3.2 driver is available, to build it
40//! the feature `"software"` must be enabled (it is by default) and on Windows MSVC the `clang-cl` dependency must be installed and
41//! associated with the `CC` and `CXX` environment variables, if requirements are not met a warning is emitted and the build fails.
42//!
43//! To install dependencies on Windows:
44//!
45//! * Install LLVM (<https://releases.llvm.org/>) and add it to the `PATH` variable:
46//! ```bat
47//! setx PATH %PATH%;C:\Program Files\LLVM\bin
48//! ```
49//! * Associate `CC` and `CXX` with `clang-cl`:
50//! ```bat
51//! setx CC clang-cl
52//! setx CXX clang-cl
53//! ```
54//! Note that you may need to reopen the terminal for the environment variables to be available (setx always requires this).
55//!
56//! # Pre-built
57//!
58//! There is a pre-built release of this crate, [`zng-view-prebuilt`], it works as a drop-in replacement
59// that dynamically links with a pre-built library, for Windows, Linux and MacOS.
60//!
61//! In the `Cargo.toml` file:
62//!
63//! ```toml
64//! zng-view-prebuilt = "0.1"
65//! ```
66//!
67//! The pre-built crate includes the `"software"` and `"ipc"` features, in fact `ipc` is required, even for running on the same process,
68//! you can also configure where the pre-build library is installed, see the [`zng-view-prebuilt`] documentation for details.
69//!
70//! The pre-build crate does not support [`extensions`].
71//!
72//! # API Extensions
73//!
74//! This implementation of the view API provides these extensions:
75//!
76//! * `"zng-view.webrender_debug"`: `{ flags: DebugFlags, profiler_ui: String }`, sets Webrender debug flags.
77//!     - The `zng-wgt-webrender-debug` crate implements a property that uses this extension.
78//! * `"zng-view.prefer_angle": bool`, on Windows, prefer ANGLE(EGL) over WGL if the `libEGL.dll` and `libGLESv2.dll`
79//!    libraries can by dynamically loaded. The `extend-view` example demonstrates this extension.
80//!
81//! You can also inject your own extensions, see the [`extensions`] module for more details.
82//!
83//! [`zng-view-prebuilt`]: https://crates.io/crates/zng-view-prebuilt/
84//!
85//! # Crate
86//!
87#![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/// Webrender build used in the view-process.
137#[doc(no_inline)]
138pub use webrender;
139
140/// OpenGL bindings used by Webrender.
141#[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            // give tracing handlers a chance to observe the view-process
174            return args.yield_once();
175        }
176
177        view_process_main();
178    }
179});
180
181/// Runs the view-process server.
182///
183/// Note that this only needs to be called if the view-process is not built on the same executable, if
184/// it is you only need to call [`zng_env::init!`] at the beginning of the executable main.
185///
186/// You can also disable start on init by setting the `"ZNG_VIEW_NO_INIT_START"` environment variable. In this
187/// case you must manually call this function.
188#[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)] // SAFETY: minimal risk of name collision, nothing else to do
218pub extern "C" fn extern_view_process_main(patch: &StaticPatch) {
219    std::panic::set_hook(Box::new(ffi_abort));
220
221    // SAFETY:
222    // safe because it is called before any view related code in the library.
223    unsafe {
224        patch.install();
225    }
226
227    view_process_main()
228}
229
230/// Runs the view-process server in the current process and calls `run_app` to also
231/// run the app in the current process. Note that `run_app` will be called in a different thread.
232///
233/// In this mode the app only uses a single process, reducing the memory footprint, but it is also not
234/// resilient to video driver crashes, the view server **does not** respawn in this mode.
235///
236/// # Panics
237///
238/// Panics if not called in the main thread, this is a requirement of some operating systems.
239///
240/// ## Background Panics Warning
241///
242/// Note that `webrender` can freeze due to panics in worker threads without propagating
243/// the panics to the main thread, this causes the app to stop responding while still receiving
244/// event signals, causing the operating system to not detect that the app is frozen. It is recommended
245/// that you build with `panic=abort` or use [`std::panic::set_hook`] to detect these background panics.
246///
247/// # Android
248///
249/// In Android builds `android::init_android_app` must be called before this function, otherwise it will panic.
250pub fn run_same_process(run_app: impl FnOnce() + Send + 'static) {
251    run_same_process_extended(run_app, ViewExtensions::new)
252}
253
254/// Like [`run_same_process`] but with custom API extensions.
255///
256/// Note that any linked [`view_process_extension!`] extensions are also run, after `ext`.
257pub 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            // SAFETY: we exit the process in case of panic.
262            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                        // Sometimes the channel does not disconnect on panic,
268                        // observed this issue on a panic in `AppExtension::init`.
269                        //
270                        // This workaround ensures that we don't become a zombie process.
271                        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                // Propagate panic in case the normal disconnect/shutdown handler works.
277                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)] // SAFETY minimal risk of name collision, nothing else to do
306pub 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    // SAFETY:
310    // safe because it is called before any view related code in the library.
311    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    // see `default_hook` in https://doc.rust-lang.org/src/std/panicking.rs.html#182
328
329    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) // Rust panic exit code.
336    }
337}
338
339/// The backend implementation.
340pub(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    // winit only sends a CursorMove after CursorEntered if the cursor is in a different position,
373    // but this makes refreshing hit-tests weird, do we hit-test the previous known point at each CursorEnter?
374    //
375    // This flag causes a MouseMove at the same previous position if no mouse move was send after CursorEnter and before
376    // MainEventsCleared.
377    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        // Linux dialog is not actually modal, the parent window can continue to receive interaction events,
446        // this macro return early when a modal dialog is open in Linux.
447        #[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                // give the app 300ms to send a new frame, this is the collaborative way to
492                // resize, it should reduce the changes of the user seeing the clear color.
493
494                let deadline = Instant::now() + Duration::from_millis(300);
495
496                // await already pending frames.
497                if self.windows[i].is_rendering_frame() {
498                    tracing::debug!("resize requested while still rendering");
499
500                    // forward requests until webrender finishes or timeout.
501                    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                // send event, the app code should send a frame in the new size as soon as possible.
532                self.notify(Event::WindowChanged(WindowChanged::resized(id, size, EventCause::System, wait_id)));
533
534                self.flush_coalesced();
535
536                // "modal" loop, breaks in 300ms or when a frame is received.
537                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                                        // received new frame
546                                        let rsp = self.respond(req);
547                                        if rsp.must_be_send() {
548                                            let _ = self.response_sender.send(rsp);
549                                        }
550                                        break;
551                                    } else {
552                                        // received some other request, forward it.
553                                        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                            // did not receive a new frame in time.
565                            break;
566                        }
567                        Err(e) => {
568                            winit_loop_guard.unset(&mut self.winit_loop);
569                            panic!("{e}");
570                        }
571                    }
572                }
573
574                // if we are still within 300ms, await webrender.
575                if received_frame && deadline > Instant::now() {
576                    // forward requests until webrender finishes or timeout.
577                    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                // winit does not provide mouse move events during drag/drop,
632                // so we enable device events to get mouse move, and use native APIs to get
633                // the cursor position on the window.
634                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                // some systems (x11) don't receive even device mouse move
654                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                    // x11 sends a cancelled after drop
686                    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                        // some platforms (Wayland) don't change size on minimize/restore, so we check here too.
695                        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                    // see the Window::focus comments.
713                    #[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                    // unlikely but possible?
829                    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        // pull events sets a timeout, winit constantly polls if the timeout is elapsed,
975        // so in case the pull timer elapses and a normal event happens the normal event
976        // is processed first and the pull events update on the next poll.
977        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                                // lost connection to app-process
992                                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                // if a window opens in power-off it is blank until redraw.
1014                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                    // already notifies here
1037                }
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                // Ubuntu CI runs can get an error here:
1247                //
1248                //  "GLXBadWindow", error_code: 170, request_code: 150, minor_code: 32
1249                //
1250                // The app run exit ok, so we just log and ignore.
1251                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 => {} // headless
1315                            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        // Winit monitors the modifiers keys directly, so this generates events
1461        // that are not send to the window by the operating system.
1462        //
1463        // An Example:
1464        // In Windows +LShift +RShift -LShift -RShift only generates +LShift +RShift -RShift, notice the missing -LShift.
1465
1466        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                        // text after key-press
1608                        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    /// Send pending coalesced events.
1653    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    /// update `capability` by usage as device metadata query is not implemented for all systems yet.
1690    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            // pull all events
1752
1753            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        // not really, but just to exit winit loop
1877        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            // winit does not notify focus for Android window
1958            #[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                // clipboard_win does not implement write from PathBuf
2310                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); // to bgra
2334                }
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); // to rgba
2369                    }
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); // TODO, wait winit
2410        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); // TODO, wait winit
2419    }
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/// Message inserted in the event loop from the view-process.
2471#[derive(Debug)]
2472#[allow(clippy::large_enum_variant)]
2473pub(crate) enum AppEvent {
2474    /// One or more [`RequestEvent`] are pending in the request channel.
2475    Request,
2476    /// Notify an event.
2477    Notify(Event),
2478    /// Re-query available monitors and send update event.
2479    #[cfg_attr(not(windows), allow(unused))]
2480    RefreshMonitors,
2481
2482    /// Simulate winit window event Focused.
2483    #[cfg_attr(not(windows), allow(unused))]
2484    WinitFocused(winit::window::WindowId, bool),
2485
2486    /// Lost connection with app-process.
2487    ParentProcessExited,
2488
2489    /// Image finished decoding, must call [`ImageCache::loaded`].
2490    ImageLoaded(ImageLoadedData),
2491
2492    /// Enable disable winit device events.
2493    SetDeviceEventsFilter(DeviceEventsFilter),
2494
2495    /// Send when monitor was turned on/off by the OS, need to redraw all screens to avoid blank issue.
2496    #[allow(unused)]
2497    MonitorPowerChanged,
2498}
2499
2500/// Message inserted in the request loop from the view-process.
2501///
2502/// These *events* are detached from [`AppEvent`] so that we can continue receiving requests while
2503/// the main loop is blocked in a resize operation.
2504#[allow(clippy::large_enum_variant)] // Request is the largest, but also most common
2505#[derive(Debug)]
2506enum RequestEvent {
2507    /// A request from the [`Api`].
2508    Request(Request),
2509    /// Webrender finished rendering a frame, ready for redraw.
2510    FrameReady(WindowId, FrameReadyMsg),
2511}
2512
2513#[derive(Debug)]
2514pub(crate) struct FrameReadyMsg {
2515    pub composite_needed: bool,
2516}
2517
2518/// Abstraction over channel senders that can inject [`AppEvent`] in the app loop.
2519#[derive(Clone)]
2520pub(crate) enum AppEventSender {
2521    Headed(EventLoopProxy<AppEvent>, Sender<RequestEvent>),
2522    Headless(Sender<AppEvent>, Sender<RequestEvent>),
2523}
2524impl AppEventSender {
2525    /// Send an event.
2526    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    /// Send a request.
2534    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    /// Send a frame-ready.
2543    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
2552/// Webrender frame-ready notifier.
2553pub(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        // render is composite_needed (https://github.com/servo/webrender/commit/82860cfd6ebb012a009d639629eeb29078e2974f)
2574        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");