Skip to main content

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