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