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