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