zng_view/
lib.rs

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