zng_view/
window.rs

1use std::{
2    collections::VecDeque,
3    fmt, mem,
4    sync::{
5        Arc,
6        atomic::{AtomicBool, Ordering},
7    },
8};
9
10use tracing::span::EnteredSpan;
11use webrender::{
12    RenderApi, Renderer, Transaction, UploadMethod, VertexUsageHint,
13    api::{DocumentId, DynamicProperties, FontInstanceKey, FontKey, FontVariation, PipelineId},
14};
15
16use winit::{
17    event_loop::ActiveEventLoop,
18    monitor::{MonitorHandle, VideoModeHandle as GVideoMode},
19    window::{CustomCursor, Fullscreen, Icon, Window as GWindow, WindowAttributes},
20};
21use zng_txt::{ToTxt, Txt};
22use zng_unit::{DipPoint, DipRect, DipSideOffsets, DipSize, DipToPx, Factor, Px, PxPoint, PxRect, PxToDip, PxVector, Rgba};
23use zng_view_api::{
24    DeviceId, Event, ViewProcessGen,
25    api_extension::{ApiExtensionId, ApiExtensionPayload},
26    font::{FontFaceId, FontId, FontOptions, FontVariationName},
27    image::{ImageId, ImageLoadedData, ImageMaskMode, ImageTextureId},
28    window::{
29        CursorIcon, FocusIndicator, FrameCapture, FrameId, FrameRequest, FrameUpdateRequest, RenderMode, ResizeDirection, VideoMode,
30        WindowButton, WindowId, WindowRequest, WindowState, WindowStateAll,
31    },
32};
33
34use zng_view_api::dialog as dlg_api;
35
36#[cfg(windows)]
37use zng_view_api::keyboard::{Key, KeyCode, KeyState};
38
39use crate::{
40    AppEvent, AppEventSender, FrameReadyMsg, WrNotifier,
41    display_list::{DisplayListCache, display_list_to_webrender},
42    extensions::{
43        self, BlobExtensionsImgHandler, DisplayListExtAdapter, FrameReadyArgs, RedrawArgs, RendererCommandArgs, RendererConfigArgs,
44        RendererDeinitedArgs, RendererExtension, RendererInitedArgs, WindowCommandArgs, WindowConfigArgs, WindowDeinitedArgs,
45        WindowExtension, WindowInitedArgs,
46    },
47    gl::{GlContext, GlContextManager},
48    image_cache::{Image, ImageCache, ImageUseMap, WrImageCache},
49    px_wr::PxToWr as _,
50    util::{
51        CursorToWinit, DipToWinit, PxToWinit, ResizeDirectionToWinit as _, WindowButtonsToWinit as _, WinitToDip, WinitToPx,
52        frame_render_reasons, frame_update_render_reasons,
53    },
54};
55
56/// A headed window.
57pub(crate) struct Window {
58    id: WindowId,
59    pipeline_id: PipelineId,
60    document_id: DocumentId,
61
62    api: RenderApi,
63    image_use: ImageUseMap,
64
65    display_list_cache: DisplayListCache,
66    clear_color: Option<Rgba>,
67
68    context: GlContext, // context must be dropped before window.
69    window: GWindow,
70    renderer: Option<Renderer>,
71    window_exts: Vec<(ApiExtensionId, Box<dyn WindowExtension>)>,
72    renderer_exts: Vec<(ApiExtensionId, Box<dyn RendererExtension>)>,
73    external_images: extensions::ExternalImages,
74    capture_mode: bool,
75
76    pending_frames: VecDeque<(FrameId, FrameCapture, Option<EnteredSpan>)>,
77    rendered_frame_id: FrameId,
78    kiosk: bool,
79
80    resized: bool,
81
82    video_mode: VideoMode,
83
84    state: WindowStateAll,
85
86    prev_pos: PxPoint, // in the global space
87    prev_size: DipSize,
88
89    prev_monitor: Option<MonitorHandle>,
90
91    visible: bool,
92    is_always_on_top: bool,
93    waiting_first_frame: bool,
94    steal_init_focus: bool,
95    init_focus_request: Option<FocusIndicator>,
96
97    taskbar_visible: bool,
98
99    movable: bool,
100
101    cursor_pos: DipPoint,
102    cursor_device: DeviceId,
103    cursor_over: bool,
104
105    touch_pos: Vec<((DeviceId, u64), DipPoint)>,
106
107    focused: Option<bool>,
108
109    render_mode: RenderMode,
110
111    modal_dialog_active: Arc<AtomicBool>,
112
113    access: Option<accesskit_winit::Adapter>, // None if has panicked
114
115    ime_area: Option<DipRect>,
116    #[cfg(windows)]
117    has_shutdown_warn: bool,
118
119    cursor: Option<CursorIcon>,
120    cursor_img: Option<CustomCursor>,
121
122    #[cfg(any(
123        target_os = "linux",
124        target_os = "dragonfly",
125        target_os = "freebsd",
126        target_os = "netbsd",
127        target_os = "openbsd"
128    ))]
129    xlib_maximize: bool,
130}
131impl fmt::Debug for Window {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        f.debug_struct("Window")
134            .field("id", &self.id)
135            .field("pipeline_id", &self.pipeline_id)
136            .field("document_id", &self.document_id)
137            .finish_non_exhaustive()
138    }
139}
140impl Window {
141    #[expect(clippy::too_many_arguments)]
142    pub fn open(
143        vp_gen: ViewProcessGen,
144        cfg_icon: Option<Icon>,
145        cfg_cursor_image: Option<CustomCursor>,
146        cfg: WindowRequest,
147        winit_loop: &ActiveEventLoop,
148        gl_manager: &mut GlContextManager,
149        mut window_exts: Vec<(ApiExtensionId, Box<dyn WindowExtension>)>,
150        mut renderer_exts: Vec<(ApiExtensionId, Box<dyn RendererExtension>)>,
151        event_sender: AppEventSender,
152    ) -> Self {
153        let id = cfg.id;
154
155        let window_scope = tracing::trace_span!("glutin").entered();
156
157        // create window and OpenGL context
158        let mut winit = WindowAttributes::default()
159            .with_title(cfg.title)
160            .with_resizable(cfg.resizable)
161            .with_transparent(cfg.transparent && cfg!(not(target_os = "android")))
162            .with_window_icon(cfg_icon);
163
164        let mut s = cfg.state;
165        s.clamp_size();
166
167        let mut monitor = winit_loop.primary_monitor();
168        let mut monitor_is_primary = true;
169        for m in winit_loop.available_monitors() {
170            let pos = m.position();
171            let size = m.size();
172            let rect = PxRect::new(pos.to_px(), size.to_px());
173
174            if rect.contains(s.global_position) {
175                let m = Some(m);
176                monitor_is_primary = m == monitor;
177                monitor = m;
178                break;
179            }
180        }
181        let monitor = monitor;
182        let monitor_is_primary = monitor_is_primary;
183
184        if let WindowState::Normal = s.state {
185            winit = winit
186                .with_min_inner_size(s.min_size.to_winit())
187                .with_max_inner_size(s.max_size.to_winit())
188                .with_inner_size(s.restore_rect.size.to_winit());
189
190            if let Some(m) = monitor {
191                if cfg.default_position {
192                    if (cfg!(windows) && !monitor_is_primary) || cfg!(target_os = "linux") {
193                        // default Windows position is in the primary only.
194                        // default X11 position is outer zero.
195
196                        let mut pos = m.position();
197                        pos.x += 120;
198                        pos.y += 80;
199                        winit = winit.with_position(pos);
200                    }
201                } else {
202                    let mut pos_in_monitor = s.restore_rect.origin.to_px(Factor(m.scale_factor() as _));
203
204                    let monitor_size = m.size();
205                    if pos_in_monitor.x.0 > monitor_size.width as _ {
206                        pos_in_monitor.x.0 = 120;
207                    }
208                    if pos_in_monitor.y.0 > monitor_size.height as _ {
209                        pos_in_monitor.y.0 = 80;
210                    }
211
212                    let mut pos = m.position();
213                    pos.x += pos_in_monitor.x.0;
214                    pos.y += pos_in_monitor.y.0;
215
216                    winit = winit.with_position(pos);
217                }
218            }
219        } else if let Some(m) = monitor {
220            // fallback to center.
221            let screen_size = m.size().to_px().to_dip(Factor(m.scale_factor() as _));
222            s.restore_rect.origin.x = (screen_size.width - s.restore_rect.size.width) / 2.0;
223            s.restore_rect.origin.y = (screen_size.height - s.restore_rect.size.height) / 2.0;
224
225            // place on monitor
226            winit = winit.with_position(m.position());
227        }
228
229        winit = winit
230            .with_decorations(s.chrome_visible)
231            // we wait for the first frame to show the window,
232            // so that there is no white frame when it's opening.
233            //
234            // unless its "kiosk" mode.
235            .with_visible(cfg!(target_os = "android") || cfg.kiosk || matches!(s.state, WindowState::Exclusive));
236
237        let mut render_mode = cfg.render_mode;
238        if !cfg!(feature = "software") && render_mode == RenderMode::Software {
239            tracing::warn!("ignoring `RenderMode::Software` because did not build with \"software\" feature");
240            render_mode = RenderMode::Integrated;
241        }
242
243        #[cfg(windows)]
244        let mut prefer_egl = false;
245        #[cfg(not(windows))]
246        let prefer_egl = false;
247
248        for (id, ext) in &mut window_exts {
249            ext.configure(&mut WindowConfigArgs {
250                config: cfg.extensions.iter().find(|(k, _)| k == id).map(|(_, p)| p),
251                window: Some(&mut winit),
252            });
253
254            #[cfg(windows)]
255            if let Some(ext) = ext.as_any().downcast_ref::<crate::extensions::PreferAngleExt>() {
256                prefer_egl = ext.prefer_egl;
257            }
258        }
259
260        let (winit_window, mut context) = gl_manager.create_headed(id, winit, winit_loop, render_mode, &event_sender, prefer_egl);
261
262        render_mode = context.render_mode();
263
264        window_exts.retain_mut(|(_, ext)| {
265            ext.window_inited(&mut WindowInitedArgs {
266                window: &winit_window,
267                context: &mut context,
268            });
269            !ext.is_init_only()
270        });
271
272        // * Extend the winit Windows window to not block the Alt+F4 key press.
273        // * Check if the window is actually keyboard focused until first focus.
274        // * Block system shutdown if a block is set.
275        #[cfg(windows)]
276        {
277            let event_sender = event_sender.clone();
278
279            let mut first_focus = false;
280
281            let window_id = winit_window.id();
282            let hwnd = crate::util::winit_to_hwnd(&winit_window);
283            crate::util::set_raw_windows_event_handler(hwnd, u32::from_ne_bytes(*b"alf4") as _, move |_, msg, wparam, _| {
284                if !first_focus && unsafe { windows_sys::Win32::UI::WindowsAndMessaging::GetForegroundWindow() } == hwnd {
285                    // Windows sends a `WM_SETFOCUS` when the window open, even if the user changed focus to something
286                    // else before the process opens the window so that the window title bar shows the unfocused visual and
287                    // we are not actually keyboard focused. We block this in `focused_changed` but then become out-of-sync
288                    // with the native window state, to recover from this we check the system wide foreground window at every
289                    // opportunity until we actually become the keyboard focus, at that point we can stop checking because we are in sync with
290                    // the native window state and the native window state is in sync with the system wide state.
291                    first_focus = true;
292                    let _ = event_sender.send(AppEvent::WinitFocused(window_id, true));
293                }
294
295                match msg {
296                    windows_sys::Win32::UI::WindowsAndMessaging::WM_SYSKEYDOWN => {
297                        if wparam as windows_sys::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY
298                            == windows_sys::Win32::UI::Input::KeyboardAndMouse::VK_F4
299                        {
300                            // winit always blocks ALT+F4 we want to allow it so that the shortcut is handled in the same way as other commands.
301
302                            let _ = event_sender.send(AppEvent::Notify(Event::KeyboardInput {
303                                window: id,
304                                device: DeviceId::INVALID, // same as winit
305                                key_code: KeyCode::F4,
306                                state: KeyState::Pressed,
307                                key: Key::F4,
308                                key_modified: Key::F4,
309                                text: Txt::from_static(""),
310                                key_location: zng_view_api::keyboard::KeyLocation::Standard,
311                            }));
312                            return Some(0);
313                        }
314                    }
315                    windows_sys::Win32::UI::WindowsAndMessaging::WM_QUERYENDSESSION => {
316                        let mut reason = [0u16; 256];
317                        let mut reason_size = reason.len() as u32;
318                        let ok = unsafe {
319                            windows_sys::Win32::System::Shutdown::ShutdownBlockReasonQuery(hwnd, reason.as_mut_ptr(), &mut reason_size)
320                        };
321                        if ok != 0 {
322                            match windows::core::HSTRING::from_wide(&reason) {
323                                Ok(s) => {
324                                    tracing::warn!("blocked system shutdown, reason: {}", s);
325                                }
326                                Err(e) => {
327                                    tracing::error!("blocked system shutdown, error retrieving reason: {e}");
328                                }
329                            }
330                            // send a close requested to hopefully cause the normal close/cancel dialog to appear.
331                            let _ = event_sender.send(AppEvent::Notify(Event::WindowCloseRequested(id)));
332                            return Some(0);
333                        }
334                    }
335                    _ => {}
336                }
337
338                None
339            });
340        }
341
342        drop(window_scope);
343        let wr_scope = tracing::trace_span!("webrender").entered();
344
345        // create renderer and start the first frame.
346
347        let device_size = winit_window.inner_size().to_px().to_wr_device();
348
349        let mut opts = webrender::WebRenderOptions {
350            // text-aa config from Firefox.
351            enable_aa: true,
352            enable_subpixel_aa: cfg!(not(target_os = "android")),
353
354            renderer_id: Some(((vp_gen.get() as u64) << 32) | id.get() as u64),
355
356            // this clear color paints over the one set using `Renderer::set_clear_color`.
357            clear_color: webrender::api::ColorF::new(0.0, 0.0, 0.0, 0.0),
358
359            allow_advanced_blend_equation: context.is_software(),
360            clear_caches_with_quads: !context.is_software(),
361            enable_gpu_markers: !context.is_software(),
362
363            // best for GL
364            upload_method: UploadMethod::PixelBuffer(VertexUsageHint::Dynamic),
365
366            // extensions expect this to be set.
367            workers: Some(crate::util::wr_workers()),
368
369            // rendering is broken on Android emulators with unoptimized shaders.
370            // see: https://github.com/servo/servo/pull/31727
371            // webrender issue: https://bugzilla.mozilla.org/show_bug.cgi?id=1887337
372            #[cfg(target_os = "android")]
373            use_optimized_shaders: true,
374
375            //panic_on_gl_error: true,
376            ..Default::default()
377        };
378        let mut blobs = BlobExtensionsImgHandler(vec![]);
379        for (id, ext) in &mut renderer_exts {
380            ext.configure(&mut RendererConfigArgs {
381                config: cfg.extensions.iter().find(|(k, _)| k == id).map(|(_, p)| p),
382                options: &mut opts,
383                blobs: &mut blobs.0,
384                window: Some(&winit_window),
385                context: &mut context,
386            });
387        }
388        if !opts.enable_multithreading {
389            for b in &mut blobs.0 {
390                b.enable_multithreading(false);
391            }
392        }
393        opts.blob_image_handler = Some(Box::new(blobs));
394
395        let (mut renderer, sender) =
396            webrender::create_webrender_instance(context.gl().clone(), WrNotifier::create(id, event_sender.clone()), opts, None).unwrap();
397        renderer.set_external_image_handler(WrImageCache::new_boxed());
398
399        let mut external_images = extensions::ExternalImages::default();
400
401        let mut api = sender.create_api();
402        let document_id = api.add_document(device_size);
403        let pipeline_id = webrender::api::PipelineId(vp_gen.get(), id.get());
404
405        renderer_exts.retain_mut(|(_, ext)| {
406            ext.renderer_inited(&mut RendererInitedArgs {
407                renderer: &mut renderer,
408                external_images: &mut external_images,
409                api_sender: &sender,
410                api: &mut api,
411                document_id,
412                pipeline_id,
413                window: Some(&winit_window),
414                context: &mut context,
415            });
416            !ext.is_init_only()
417        });
418
419        drop(wr_scope);
420
421        let access = accesskit_winit::Adapter::with_direct_handlers(
422            winit_loop,
423            &winit_window,
424            AccessActivateHandler {
425                id,
426                event_sender: event_sender.clone(),
427            },
428            AccessActionSender {
429                id,
430                event_sender: event_sender.clone(),
431            },
432            AccessDeactivateHandler { id, event_sender },
433        );
434
435        let mut win = Self {
436            id,
437            image_use: ImageUseMap::default(),
438            prev_pos: winit_window.inner_position().unwrap_or_default().to_px(),
439            prev_size: winit_window.inner_size().to_px().to_dip(Factor(winit_window.scale_factor() as _)),
440            prev_monitor: winit_window.current_monitor(),
441            state: s,
442            kiosk: cfg.kiosk,
443            window: winit_window,
444            context,
445            capture_mode: cfg.capture_mode,
446            renderer: Some(renderer),
447            window_exts,
448            renderer_exts,
449            external_images,
450            video_mode: cfg.video_mode,
451            display_list_cache: DisplayListCache::new(pipeline_id, api.get_namespace_id()),
452            api,
453            document_id,
454            pipeline_id,
455            resized: true,
456            waiting_first_frame: true,
457            steal_init_focus: cfg.focus,
458            init_focus_request: cfg.focus_indicator,
459            visible: cfg.visible,
460            is_always_on_top: false,
461            taskbar_visible: true,
462            movable: cfg.movable,
463            pending_frames: VecDeque::new(),
464            rendered_frame_id: FrameId::INVALID,
465            cursor_pos: DipPoint::zero(),
466            touch_pos: vec![],
467            cursor_device: DeviceId::INVALID,
468            cursor_over: false,
469            clear_color: None,
470            focused: None,
471            modal_dialog_active: Arc::new(AtomicBool::new(false)),
472            render_mode,
473            access: Some(access),
474            ime_area: cfg.ime_area,
475            #[cfg(windows)]
476            has_shutdown_warn: false,
477            cursor: None,
478            cursor_img: None,
479
480            #[cfg(any(
481                target_os = "linux",
482                target_os = "dragonfly",
483                target_os = "freebsd",
484                target_os = "netbsd",
485                target_os = "openbsd"
486            ))]
487            xlib_maximize: false,
488        };
489
490        if !cfg.default_position && win.state.state == WindowState::Normal {
491            win.set_inner_position(win.state.restore_rect.origin);
492        }
493
494        if cfg.always_on_top {
495            win.set_always_on_top(true);
496        }
497
498        win.cursor = cfg.cursor;
499        win.cursor_img = cfg_cursor_image;
500        win.update_cursor();
501
502        win.set_taskbar_visible(cfg.taskbar_visible);
503
504        win.set_enabled_buttons(cfg.enabled_buttons);
505
506        if !cfg.system_shutdown_warn.is_empty() {
507            win.set_system_shutdown_warn(cfg.system_shutdown_warn);
508        }
509
510        if win.ime_area.is_some() {
511            win.window.set_ime_allowed(true);
512        }
513
514        // settings these in the builder causes flickering
515        match win.state.state {
516            WindowState::Normal | WindowState::Minimized => {}
517            WindowState::Maximized => win.window.set_maximized(true),
518            WindowState::Fullscreen => win.window.set_fullscreen(Some(Fullscreen::Borderless(None))),
519            WindowState::Exclusive => win.window.set_fullscreen(Some(if let Some(mode) = win.video_mode() {
520                Fullscreen::Exclusive(mode)
521            } else {
522                Fullscreen::Borderless(None)
523            })),
524        }
525
526        win.state.global_position = win.window.inner_position().unwrap_or_default().to_px();
527        let monitor_offset = if let Some(m) = win.window.current_monitor() {
528            m.position().to_px().to_vector()
529        } else {
530            PxVector::zero()
531        };
532
533        if win.state.state == WindowState::Normal && cfg.default_position {
534            // system position.
535            win.state.restore_rect.origin = (win.state.global_position - monitor_offset).to_dip(win.scale_factor());
536        }
537
538        win
539    }
540
541    pub fn id(&self) -> WindowId {
542        self.id
543    }
544
545    pub fn monitor(&self) -> Option<winit::monitor::MonitorHandle> {
546        self.window.current_monitor()
547    }
548
549    pub fn window_id(&self) -> winit::window::WindowId {
550        self.window.id()
551    }
552
553    /// Latest rendered frame.
554    pub fn frame_id(&self) -> FrameId {
555        self.rendered_frame_id
556    }
557
558    pub fn set_title(&self, title: Txt) {
559        self.window.set_title(&title);
560    }
561
562    /// Window event should ignore interaction events.
563    ///
564    /// Dialogs are already modal in Windows and Mac, but Linux
565    pub fn modal_dialog_active(&self) -> bool {
566        self.modal_dialog_active.load(Ordering::Relaxed)
567    }
568
569    /// Returns `true` if the cursor actually moved.
570    pub fn cursor_moved(&mut self, pos: DipPoint, device: DeviceId) -> bool {
571        if !self.cursor_over {
572            // this can happen on X11
573            return false;
574        }
575
576        let moved = self.cursor_pos != pos || self.cursor_device != device;
577
578        if moved {
579            self.cursor_pos = pos;
580            self.cursor_device = device;
581        }
582
583        moved
584    }
585
586    /// Returns `true` if the touch actually moved.
587    pub fn touch_moved(&mut self, pos: DipPoint, device: DeviceId, touch: u64) -> bool {
588        if let Some(p) = self.touch_pos.iter_mut().find(|p| p.0 == (device, touch)) {
589            let moved = p.1 != pos;
590            p.1 = pos;
591            moved
592        } else {
593            self.touch_pos.push(((device, touch), pos));
594            true
595        }
596    }
597
598    /// Clear touch position.
599    pub fn touch_end(&mut self, device: DeviceId, touch: u64) {
600        if let Some(i) = self.touch_pos.iter().position(|p| p.0 == (device, touch)) {
601            self.touch_pos.swap_remove(i);
602        }
603    }
604
605    #[cfg(windows)]
606    fn windows_is_foreground(&self) -> bool {
607        let foreground = unsafe { windows_sys::Win32::UI::WindowsAndMessaging::GetForegroundWindow() };
608        foreground == crate::util::winit_to_hwnd(&self.window)
609    }
610
611    pub fn is_focused(&self) -> bool {
612        self.focused.unwrap_or(false)
613    }
614
615    /// Returns `true` if the previous focused status is different from `focused`.
616    ///
617    /// Sets the `focused` to if the window is actually the foreground keyboard focused window.
618    pub fn focused_changed(&mut self, focused: &mut bool) -> bool {
619        #[cfg(windows)]
620        if self.focused.is_none() {
621            *focused = self.windows_is_foreground();
622        }
623
624        let focused = Some(*focused);
625
626        let changed = self.focused != focused;
627        if changed {
628            self.focused = focused;
629        }
630        changed
631    }
632
633    /// Returns the last cursor moved data.
634    pub fn last_cursor_pos(&self) -> (DipPoint, DeviceId) {
635        (self.cursor_pos, self.cursor_device)
636    }
637
638    /// Returns `true` if the cursor was not over the window.
639    pub fn cursor_entered(&mut self) -> bool {
640        let changed = !self.cursor_over;
641        self.cursor_over = true;
642        changed
643    }
644
645    /// Returns `true` if the cursor was over the window.
646    pub fn cursor_left(&mut self) -> bool {
647        let changed = self.cursor_over;
648        self.cursor_over = false;
649        changed
650    }
651
652    pub fn set_visible(&mut self, visible: bool) {
653        if self.kiosk && !self.visible {
654            tracing::error!("window in `kiosk` mode cannot be hidden");
655        }
656
657        if !self.waiting_first_frame {
658            let _s = tracing::trace_span!("set_visible", %visible).entered();
659
660            self.visible = visible;
661
662            if visible {
663                if self.state.state != WindowState::Minimized {
664                    self.window.set_minimized(false);
665                }
666
667                self.window.set_visible(true);
668                self.apply_state(self.state.clone(), true);
669            } else {
670                if self.state.state != WindowState::Minimized {
671                    // if the state is maximized or fullscreen the window is not hidden, a white
672                    // "restored" window is shown instead.
673                    self.window.set_minimized(true);
674                }
675
676                self.window.set_visible(false);
677            }
678        }
679    }
680
681    pub fn set_always_on_top(&mut self, always_on_top: bool) {
682        self.window.set_window_level(if always_on_top {
683            winit::window::WindowLevel::AlwaysOnTop
684        } else {
685            winit::window::WindowLevel::Normal
686        });
687        self.is_always_on_top = always_on_top;
688    }
689
690    pub fn set_movable(&mut self, movable: bool) {
691        self.movable = movable;
692    }
693
694    pub fn set_resizable(&mut self, resizable: bool) {
695        self.window.set_resizable(resizable)
696    }
697
698    #[cfg(windows)]
699    pub fn bring_to_top(&mut self) {
700        use windows_sys::Win32::UI::WindowsAndMessaging::*;
701
702        if !self.is_always_on_top {
703            let hwnd = crate::util::winit_to_hwnd(&self.window);
704
705            unsafe {
706                let _ = SetWindowPos(
707                    hwnd as _,
708                    HWND_TOP,
709                    0,
710                    0,
711                    0,
712                    0,
713                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW,
714                );
715            }
716        }
717    }
718
719    #[cfg(not(windows))]
720    pub fn bring_to_top(&mut self) {
721        if !self.is_always_on_top {
722            self.set_always_on_top(true);
723            self.set_always_on_top(false);
724        }
725    }
726
727    /// Returns `Some((new_global_pos, new_pos))` if the window position is different from the previous call to this function.
728    pub fn moved(&mut self) -> Option<(PxPoint, DipPoint)> {
729        if !self.visible {
730            return None;
731        }
732
733        let new_pos = match self.window.inner_position() {
734            Ok(p) => p.to_px(),
735            Err(e) => {
736                tracing::error!("cannot get inner_position, {e}");
737                PxPoint::zero()
738            }
739        };
740        if self.prev_pos != new_pos {
741            self.prev_pos = new_pos;
742
743            let monitor_offset = if let Some(m) = self.window.current_monitor() {
744                m.position().to_px().to_vector()
745            } else {
746                PxVector::zero()
747            };
748
749            Some((new_pos, (new_pos - monitor_offset).to_dip(self.scale_factor())))
750        } else {
751            None
752        }
753    }
754
755    /// Returns `Some(new_size)` if the window size is different from the previous call to this function.
756    pub fn resized(&mut self) -> Option<DipSize> {
757        if !self.visible {
758            return None;
759        }
760
761        #[cfg(any(
762            target_os = "linux",
763            target_os = "dragonfly",
764            target_os = "freebsd",
765            target_os = "netbsd",
766            target_os = "openbsd"
767        ))]
768        if std::mem::take(&mut self.xlib_maximize) {
769            // X11 does not open maximized
770            // to work we need to set inner_size (after first frame) and request maximized here after xlib resizes.
771            self.window.set_maximized(true);
772            return None;
773        }
774
775        let new_size = self.window.inner_size().to_px().to_dip(self.scale_factor());
776        if self.prev_size != new_size {
777            #[cfg(windows)]
778            if matches!(self.state.state, WindowState::Maximized | WindowState::Fullscreen)
779                && self.window.current_monitor() != self.window.primary_monitor()
780            {
781                // workaround issue when opening a window maximized in a non-primary monitor
782                // causes it to use the maximized style, but not the size.
783
784                match self.state.state {
785                    WindowState::Maximized => {
786                        self.window.set_maximized(false);
787                        self.window.set_maximized(true);
788                    }
789                    WindowState::Fullscreen => {
790                        self.window.set_fullscreen(None);
791                        self.window.set_fullscreen(Some(Fullscreen::Borderless(None)));
792                    }
793                    _ => unreachable!(),
794                }
795
796                let new_size = self.window.inner_size().to_px().to_dip(self.scale_factor());
797                return if self.prev_size != new_size {
798                    self.prev_size = new_size;
799                    self.resized = true;
800
801                    Some(new_size)
802                } else {
803                    None
804                };
805            }
806
807            self.prev_size = new_size;
808            self.resized = true;
809
810            Some(new_size)
811        } else {
812            None
813        }
814    }
815
816    /// Returns `Some(new_monitor)` if the parent monitor changed from the previous call to this function.
817    pub fn monitor_change(&mut self) -> Option<MonitorHandle> {
818        let handle = self.window.current_monitor();
819        if self.prev_monitor != handle {
820            self.prev_monitor.clone_from(&handle);
821            handle
822        } else {
823            None
824        }
825    }
826
827    #[cfg(windows)]
828    fn windows_set_restore(&self) {
829        use windows_sys::Win32::Graphics::Gdi::{GetMonitorInfoW, MONITORINFO, MONITORINFOEXW};
830        use windows_sys::Win32::{
831            Foundation::{POINT, RECT},
832            UI::WindowsAndMessaging::*,
833        };
834        use winit::platform::windows::MonitorHandleExtWindows;
835
836        if let Some(monitor) = self.window.current_monitor() {
837            let hwnd = crate::util::winit_to_hwnd(&self.window) as _;
838            let mut placement = WINDOWPLACEMENT {
839                length: mem::size_of::<WINDOWPLACEMENT>() as _,
840                flags: 0,
841                showCmd: 0,
842                ptMinPosition: POINT { x: 0, y: 0 },
843                ptMaxPosition: POINT { x: 0, y: 0 },
844                rcNormalPosition: RECT {
845                    left: 0,
846                    top: 0,
847                    right: 0,
848                    bottom: 0,
849                },
850            };
851            if unsafe { GetWindowPlacement(hwnd, &mut placement) } != 0 {
852                let scale_factor = self.scale_factor();
853                let mut left_top = self.state.restore_rect.origin.to_px(scale_factor);
854
855                // placement is in "workspace", window is in "virtual screen space".
856                let hmonitor = monitor.hmonitor() as _;
857                let mut monitor_info = MONITORINFOEXW {
858                    monitorInfo: MONITORINFO {
859                        cbSize: mem::size_of::<MONITORINFOEXW>() as _,
860                        rcMonitor: RECT {
861                            left: 0,
862                            top: 0,
863                            right: 0,
864                            bottom: 0,
865                        },
866                        rcWork: RECT {
867                            left: 0,
868                            top: 0,
869                            right: 0,
870                            bottom: 0,
871                        },
872                        dwFlags: 0,
873                    },
874                    szDevice: [0; 32],
875                };
876                if unsafe { GetMonitorInfoW(hmonitor, &mut monitor_info as *mut MONITORINFOEXW as *mut MONITORINFO) } != 0 {
877                    left_top.x.0 += monitor_info.monitorInfo.rcWork.left;
878                    left_top.y.0 += monitor_info.monitorInfo.rcWork.top;
879                }
880
881                // placement includes the non-client area.
882                let outer_offset =
883                    self.window.outer_position().unwrap_or_default().to_px() - self.window.inner_position().unwrap_or_default().to_px();
884                let size_offset = self.window.outer_size().to_px() - self.window.inner_size().to_px();
885
886                left_top += outer_offset;
887                let bottom_right = left_top + self.state.restore_rect.size.to_px(scale_factor) + size_offset;
888
889                placement.rcNormalPosition.top = left_top.y.0;
890                placement.rcNormalPosition.left = left_top.x.0;
891                placement.rcNormalPosition.bottom = bottom_right.y.0;
892                placement.rcNormalPosition.right = bottom_right.x.0;
893
894                let _ = unsafe { SetWindowPlacement(hwnd, &placement) };
895            }
896        }
897    }
898
899    pub fn set_icon(&mut self, icon: Option<Icon>) {
900        self.window.set_window_icon(icon);
901    }
902
903    /// Set named cursor.
904    pub fn set_cursor(&mut self, icon: Option<CursorIcon>) {
905        self.cursor = icon;
906        self.update_cursor();
907    }
908
909    /// Set custom cursor.
910    pub fn set_cursor_image(&mut self, img: Option<CustomCursor>) {
911        self.cursor_img = img;
912        self.update_cursor();
913    }
914
915    fn update_cursor(&self) {
916        match (&self.cursor_img, self.cursor) {
917            (Some(i), _) => {
918                self.window.set_cursor(i.clone());
919                self.window.set_cursor_visible(true);
920            }
921            (None, Some(i)) => {
922                self.window.set_cursor(i.to_winit());
923                self.window.set_cursor_visible(true);
924            }
925            (None, None) => {
926                self.window.set_cursor_visible(false);
927                self.window.set_cursor(CursorIcon::Default.to_winit());
928            }
929        }
930    }
931
932    /// Sets the focus request indicator.
933    pub fn set_focus_request(&mut self, request: Option<FocusIndicator>) {
934        if self.waiting_first_frame {
935            self.init_focus_request = request;
936        } else {
937            self.window.request_user_attention(request.map(|r| match r {
938                FocusIndicator::Critical => winit::window::UserAttentionType::Critical,
939                FocusIndicator::Info => winit::window::UserAttentionType::Informational,
940            }));
941        }
942    }
943
944    /// Steal input focus.
945    #[cfg(not(windows))]
946    pub fn focus(&mut self) -> zng_view_api::FocusResult {
947        if self.waiting_first_frame {
948            self.steal_init_focus = true;
949        } else if !self.modal_dialog_active() {
950            self.window.focus_window();
951        }
952        if self.window.has_focus() {
953            zng_view_api::FocusResult::AlreadyFocused
954        } else {
955            zng_view_api::FocusResult::Requested
956        }
957    }
958
959    /// Steal input focus.
960    ///
961    /// Returns if the next `RAlt` press and release key inputs must be ignored.
962    #[cfg(windows)]
963    #[must_use]
964    pub fn focus(&mut self) -> (zng_view_api::FocusResult, bool) {
965        let skip_ralt = if self.waiting_first_frame {
966            self.steal_init_focus = true;
967            false
968        } else if !self.modal_dialog_active() && !self.windows_is_foreground() {
969            // winit uses a hack to steal focus that causes a `RAlt` key press.
970            self.window.focus_window();
971            self.windows_is_foreground()
972        } else {
973            false
974        };
975        let r = if self.window.has_focus() {
976            zng_view_api::FocusResult::AlreadyFocused
977        } else {
978            zng_view_api::FocusResult::Requested
979        };
980        (r, skip_ralt)
981    }
982
983    /// Gets the current Maximized status as early as possible.
984    fn is_maximized(&self) -> bool {
985        #[cfg(windows)]
986        {
987            let hwnd = crate::util::winit_to_hwnd(&self.window);
988            // SAFETY: function does not fail.
989            return unsafe { windows_sys::Win32::UI::WindowsAndMessaging::IsZoomed(hwnd as _) } != 0;
990        }
991
992        #[allow(unreachable_code)]
993        {
994            // this changes only after the Resized event, we want state change detection before the Moved also.
995            self.window.is_maximized()
996        }
997    }
998
999    /// Gets the current Maximized status.
1000    fn is_minimized(&self) -> bool {
1001        let size = self.window.inner_size();
1002        if size.width == 0 || size.height == 0 {
1003            return true;
1004        }
1005
1006        #[cfg(windows)]
1007        {
1008            let hwnd = crate::util::winit_to_hwnd(&self.window);
1009            // SAFETY: function does not fail.
1010            return unsafe { windows_sys::Win32::UI::WindowsAndMessaging::IsIconic(hwnd as _) } != 0;
1011        }
1012
1013        #[allow(unreachable_code)]
1014        false
1015    }
1016
1017    fn probe_state(&self) -> WindowStateAll {
1018        let mut state = self.state.clone();
1019
1020        state.global_position = match self.window.inner_position() {
1021            Ok(p) => p.to_px(),
1022            Err(e) => {
1023                tracing::error!("cannot get inner_position, {e}");
1024                PxPoint::zero()
1025            }
1026        };
1027
1028        if self.is_minimized() {
1029            state.state = WindowState::Minimized;
1030        } else if let Some(h) = self.window.fullscreen() {
1031            state.state = match h {
1032                Fullscreen::Exclusive(_) => WindowState::Exclusive,
1033                Fullscreen::Borderless(_) => WindowState::Fullscreen,
1034            };
1035        } else if self.is_maximized() {
1036            state.state = WindowState::Maximized;
1037        } else {
1038            state.state = WindowState::Normal;
1039
1040            let scale_factor = self.scale_factor();
1041
1042            let monitor_offset = if let Some(monitor) = self.window.current_monitor() {
1043                monitor.position().to_px().to_vector()
1044            } else {
1045                PxVector::zero()
1046            };
1047
1048            state.restore_rect = DipRect::new(
1049                (state.global_position - monitor_offset).to_dip(scale_factor),
1050                self.window.inner_size().to_px().to_dip(scale_factor),
1051            );
1052        }
1053
1054        state
1055    }
1056
1057    /// Probe state, returns `Some(new_state)`
1058    pub fn state_change(&mut self) -> Option<WindowStateAll> {
1059        if !self.visible {
1060            return None;
1061        }
1062
1063        let mut new_state = self.probe_state();
1064
1065        if self.state.state == WindowState::Minimized && self.state.restore_state == WindowState::Fullscreen {
1066            self.window.set_fullscreen(Some(Fullscreen::Borderless(None)));
1067        } else if new_state.state == WindowState::Normal && self.state.state != WindowState::Normal {
1068            new_state.restore_rect = self.state.restore_rect;
1069
1070            self.set_inner_position(new_state.restore_rect.origin);
1071            let new_size = new_state.restore_rect.size.to_winit();
1072            if let Some(immediate_new_size) = self.window.request_inner_size(new_size) {
1073                if immediate_new_size == new_size.to_physical(self.window.scale_factor()) {
1074                    // size changed immediately, winit says: "resize event in such case may not be generated"
1075                    // * Review of Windows and Linux shows that the resize event is send.
1076                    tracing::debug!("immediate resize may not have notified, new size: {immediate_new_size:?}");
1077                }
1078            }
1079
1080            self.window.set_min_inner_size(Some(new_state.min_size.to_winit()));
1081            self.window.set_max_inner_size(Some(new_state.max_size.to_winit()));
1082        }
1083
1084        new_state.set_restore_state_from(self.state.state);
1085
1086        if new_state != self.state {
1087            self.state = new_state.clone();
1088            Some(new_state)
1089        } else {
1090            None
1091        }
1092    }
1093
1094    fn video_mode(&self) -> Option<GVideoMode> {
1095        let mode = &self.video_mode;
1096        self.window.current_monitor().and_then(|m| {
1097            let mut candidate: Option<GVideoMode> = None;
1098            for m in m.video_modes() {
1099                // filter out video modes larger than requested
1100                if m.size().width <= mode.size.width.0 as u32
1101                    && m.size().height <= mode.size.height.0 as u32
1102                    && m.bit_depth() <= mode.bit_depth
1103                    && m.refresh_rate_millihertz() <= mode.refresh_rate
1104                {
1105                    // select closest match to the requested video mode
1106                    if let Some(c) = &candidate {
1107                        if m.size().width >= c.size().width
1108                            && m.size().height >= c.size().height
1109                            && m.bit_depth() >= c.bit_depth()
1110                            && m.refresh_rate_millihertz() >= c.refresh_rate_millihertz()
1111                        {
1112                            candidate = Some(m);
1113                        }
1114                    } else {
1115                        candidate = Some(m);
1116                    }
1117                }
1118            }
1119            candidate
1120        })
1121    }
1122
1123    pub fn set_video_mode(&mut self, mode: VideoMode) {
1124        self.video_mode = mode;
1125        if let WindowState::Exclusive = self.state.state {
1126            self.window.set_fullscreen(None);
1127
1128            if let Some(mode) = self.video_mode() {
1129                self.window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
1130            } else {
1131                self.window.set_fullscreen(Some(Fullscreen::Borderless(None)));
1132            }
1133        }
1134    }
1135
1136    #[cfg(not(windows))]
1137    pub fn set_taskbar_visible(&mut self, visible: bool) {
1138        if visible != self.taskbar_visible {
1139            return;
1140        }
1141        self.taskbar_visible = visible;
1142        tracing::warn!("`set_taskbar_visible` not implemented for {}", std::env::consts::OS);
1143    }
1144
1145    #[cfg(windows)]
1146    pub fn set_taskbar_visible(&mut self, visible: bool) {
1147        if visible == self.taskbar_visible {
1148            return;
1149        }
1150        self.taskbar_visible = visible;
1151
1152        use windows_sys::Win32::System::Com::*;
1153
1154        use crate::util::taskbar_com;
1155
1156        // winit already initializes COM
1157
1158        unsafe {
1159            let mut taskbar_list2: *mut taskbar_com::ITaskbarList2 = std::ptr::null_mut();
1160            match CoCreateInstance(
1161                &taskbar_com::CLSID_TaskbarList,
1162                std::ptr::null_mut(),
1163                CLSCTX_ALL,
1164                &taskbar_com::IID_ITaskbarList2,
1165                &mut taskbar_list2 as *mut _ as *mut _,
1166            ) {
1167                0 => {
1168                    let result = if visible {
1169                        let add_tab = (*(*taskbar_list2).lpVtbl).parent.AddTab;
1170                        add_tab(taskbar_list2.cast(), crate::util::winit_to_hwnd(&self.window) as _)
1171                    } else {
1172                        let delete_tab = (*(*taskbar_list2).lpVtbl).parent.DeleteTab;
1173                        delete_tab(taskbar_list2.cast(), crate::util::winit_to_hwnd(&self.window) as _)
1174                    };
1175                    if result != 0 {
1176                        let mtd_name = if visible { "AddTab" } else { "DeleteTab" };
1177                        tracing::error!(
1178                            target: "window",
1179                            "cannot set `taskbar_visible`, `ITaskbarList::{mtd_name}` failed, error: 0x{result:x}",
1180                        )
1181                    }
1182
1183                    let release = (*(*taskbar_list2).lpVtbl).parent.parent.Release;
1184                    let result = release(taskbar_list2.cast());
1185                    if result != 0 {
1186                        tracing::error!(
1187                            target: "window",
1188                            "failed to release `taskbar_list`, error: 0x{result:x}"
1189                        )
1190                    }
1191                }
1192                error => {
1193                    tracing::error!(
1194                        target: "window",
1195                        "cannot set `taskbar_visible`, failed to create instance of `ITaskbarList`, error: 0x{error:x}",
1196                    )
1197                }
1198            }
1199        }
1200    }
1201
1202    /// Returns of the last update state.
1203    pub fn state(&self) -> WindowStateAll {
1204        self.state.clone()
1205    }
1206
1207    fn set_inner_position(&self, pos: DipPoint) {
1208        let monitor_offset = if let Some(m) = self.window.current_monitor() {
1209            m.position().to_px().to_vector()
1210        } else {
1211            PxVector::zero()
1212        };
1213
1214        let outer_pos = self.window.outer_position().unwrap_or_default();
1215        let inner_pos = self.window.inner_position().unwrap_or_default();
1216        let inner_offset = PxVector::new(Px(outer_pos.x - inner_pos.x), Px(outer_pos.y - inner_pos.y));
1217        let pos = pos.to_px(self.scale_factor()) + monitor_offset + inner_offset;
1218        self.window.set_outer_position(pos.to_winit());
1219    }
1220
1221    /// Reset all window state.
1222    ///
1223    /// Returns `true` if the state changed.
1224    pub fn set_state(&mut self, new_state: WindowStateAll) -> bool {
1225        if self.state == new_state {
1226            return false;
1227        }
1228
1229        if !self.visible {
1230            // will force apply when set to visible again.
1231            self.state = new_state;
1232            return true;
1233        }
1234
1235        self.apply_state(new_state, false);
1236
1237        true
1238    }
1239
1240    /// Moves the window with the left mouse button until the button is released.
1241    pub fn drag_move(&self) {
1242        if let Err(e) = self.window.drag_window() {
1243            tracing::error!("failed to drag_move, {e}");
1244        }
1245    }
1246
1247    /// Resizes the window with the left mouse button until the button is released.
1248    pub fn drag_resize(&self, direction: ResizeDirection) {
1249        if let Err(e) = self.window.drag_resize_window(direction.to_winit()) {
1250            tracing::error!("failed to drag_resize, {e}");
1251        }
1252    }
1253
1254    /// Set enabled chrome buttons.
1255    pub fn set_enabled_buttons(&self, buttons: WindowButton) {
1256        self.window.set_enabled_buttons(buttons.to_winit());
1257    }
1258
1259    /// Open windows title bar context menu.
1260    pub fn open_title_bar_context_menu(&self, pos: DipPoint) {
1261        self.window.show_window_menu(pos.to_winit())
1262    }
1263
1264    fn apply_state(&mut self, new_state: WindowStateAll, force: bool) {
1265        if self.state.chrome_visible != new_state.chrome_visible {
1266            self.window.set_decorations(new_state.chrome_visible);
1267        }
1268
1269        if self.state.state != new_state.state || force {
1270            // unset previous state.
1271            match self.state.state {
1272                WindowState::Normal => {}
1273                WindowState::Minimized => self.window.set_minimized(false),
1274                WindowState::Maximized => {
1275                    if !new_state.state.is_fullscreen() && new_state.state != WindowState::Minimized {
1276                        self.window.set_maximized(false);
1277                    }
1278                }
1279                WindowState::Fullscreen | WindowState::Exclusive => self.window.set_fullscreen(None),
1280            }
1281
1282            // set new state.
1283            match new_state.state {
1284                WindowState::Normal => {}
1285                WindowState::Minimized => self.window.set_minimized(true),
1286                WindowState::Maximized => self.window.set_maximized(true),
1287                WindowState::Fullscreen => {
1288                    self.window.set_fullscreen(Some(Fullscreen::Borderless(None)));
1289                }
1290                WindowState::Exclusive => {
1291                    if let Some(mode) = self.video_mode() {
1292                        self.window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
1293                    } else {
1294                        self.window.set_fullscreen(Some(Fullscreen::Borderless(None)));
1295                    }
1296                }
1297            }
1298        }
1299
1300        self.state = new_state;
1301
1302        if self.state.state == WindowState::Normal {
1303            let _ = self.window.request_inner_size(self.state.restore_rect.size.to_winit());
1304
1305            let outer_offset = match (self.window.outer_position(), self.window.inner_position()) {
1306                (Ok(o), Ok(i)) => (i.x - o.x, i.y - o.y),
1307                _ => (0, 0),
1308            };
1309            let mut origin = self
1310                .state
1311                .restore_rect
1312                .origin
1313                .to_winit()
1314                .to_physical::<i32>(self.window.scale_factor());
1315            origin.x -= outer_offset.0;
1316            origin.y -= outer_offset.1;
1317            self.window.set_outer_position(origin);
1318
1319            self.window.set_min_inner_size(Some(self.state.min_size.to_winit()));
1320            self.window.set_max_inner_size(Some(self.state.max_size.to_winit()));
1321
1322            // this can happen if minimized from "Task Manager"
1323            //
1324            // - Set to Fullscreen.
1325            // - Minimize from Windows Task Manager.
1326            // - Restore from Taskbar.
1327            // - Set the state to Normal.
1328            //
1329            // Without this hack the window stays minimized and then restores
1330            // Normal but at the fullscreen size.
1331            #[cfg(windows)]
1332            if self.is_minimized() {
1333                self.windows_set_restore();
1334
1335                self.window.set_minimized(true);
1336                self.window.set_minimized(false);
1337            }
1338        }
1339
1340        // Update restore placement for Windows to avoid rendering incorrect frame when the OS restores the window.
1341        //
1342        // Windows changes the size if it considers the window "restored", that is the case for `Normal` and `Borderless` fullscreen.
1343        #[cfg(windows)]
1344        if !matches!(self.state.state, WindowState::Normal | WindowState::Fullscreen) {
1345            self.windows_set_restore();
1346        }
1347    }
1348
1349    pub fn use_image(&mut self, image: &Image) -> ImageTextureId {
1350        self.image_use.new_use(image, self.document_id, &mut self.api)
1351    }
1352
1353    pub fn update_image(&mut self, texture_id: ImageTextureId, image: &Image) {
1354        self.image_use.update_use(texture_id, image, self.document_id, &mut self.api);
1355    }
1356
1357    pub fn delete_image(&mut self, texture_id: ImageTextureId) {
1358        self.image_use.delete(texture_id, self.document_id, &mut self.api);
1359    }
1360
1361    pub fn add_font_face(&mut self, font: Vec<u8>, index: u32) -> FontFaceId {
1362        #[cfg(target_os = "macos")]
1363        let index = {
1364            if index != 0 {
1365                tracing::error!("webrender does not support font index on macOS, ignoring `{index}` will use `0`");
1366            }
1367            0
1368        };
1369        let key = self.api.generate_font_key();
1370        let mut txn = webrender::Transaction::new();
1371        txn.add_raw_font(key, font, index);
1372        self.api.send_transaction(self.document_id, txn);
1373        FontFaceId::from_raw(key.1)
1374    }
1375
1376    pub fn delete_font_face(&mut self, font_face_id: FontFaceId) {
1377        let mut txn = webrender::Transaction::new();
1378        txn.delete_font(FontKey(self.api.get_namespace_id(), font_face_id.get()));
1379        self.api.send_transaction(self.document_id, txn);
1380    }
1381
1382    pub fn add_font(
1383        &mut self,
1384        font_face_id: FontFaceId,
1385        glyph_size: Px,
1386        options: FontOptions,
1387        variations: Vec<(FontVariationName, f32)>,
1388    ) -> FontId {
1389        let key = self.api.generate_font_instance_key();
1390        let mut txn = webrender::Transaction::new();
1391        txn.add_font_instance(
1392            key,
1393            FontKey(self.api.get_namespace_id(), font_face_id.get()),
1394            glyph_size.to_wr().get(),
1395            options.to_wr(),
1396            None,
1397            variations
1398                .into_iter()
1399                .map(|(n, v)| FontVariation {
1400                    tag: u32::from_be_bytes(n),
1401                    value: v,
1402                })
1403                .collect(),
1404        );
1405        self.api.send_transaction(self.document_id, txn);
1406        FontId::from_raw(key.1)
1407    }
1408
1409    pub fn delete_font(&mut self, font_id: FontId) {
1410        let mut txn = webrender::Transaction::new();
1411        txn.delete_font_instance(FontInstanceKey(self.api.get_namespace_id(), font_id.get()));
1412        self.api.send_transaction(self.document_id, txn);
1413    }
1414
1415    pub fn set_capture_mode(&mut self, enabled: bool) {
1416        self.capture_mode = enabled;
1417    }
1418
1419    /// Start rendering a new frame.
1420    ///
1421    /// The [callback](#callback) will be called when the frame is ready to be [presented](Self::present).
1422    pub fn render(&mut self, frame: FrameRequest) {
1423        let _scope = tracing::trace_span!("render", ?frame.id).entered();
1424
1425        self.renderer.as_mut().unwrap().set_clear_color(frame.clear_color.to_wr());
1426
1427        let mut txn = Transaction::new();
1428        txn.set_root_pipeline(self.pipeline_id);
1429        self.push_resize(&mut txn);
1430        txn.generate_frame(frame.id.get(), frame_render_reasons(&frame));
1431
1432        let display_list = display_list_to_webrender(
1433            frame.display_list,
1434            &mut DisplayListExtAdapter {
1435                frame_id: frame.id,
1436                extensions: &mut self.renderer_exts,
1437                transaction: &mut txn,
1438                renderer: self.renderer.as_mut().unwrap(),
1439                api: &mut self.api,
1440                external_images: &mut self.external_images,
1441            },
1442            &mut self.display_list_cache,
1443        );
1444
1445        txn.reset_dynamic_properties();
1446        txn.append_dynamic_properties(DynamicProperties {
1447            transforms: vec![],
1448            floats: vec![],
1449            colors: vec![],
1450        });
1451
1452        self.renderer.as_mut().unwrap().set_clear_color(frame.clear_color.to_wr());
1453        self.clear_color = Some(frame.clear_color);
1454
1455        txn.set_display_list(webrender::api::Epoch(frame.id.epoch()), (self.pipeline_id, display_list));
1456
1457        let frame_scope =
1458            tracing::trace_span!("<frame>", ?frame.id, capture = ?frame.capture, from_update = false, thread = "<webrender>").entered();
1459
1460        self.pending_frames.push_back((frame.id, frame.capture, Some(frame_scope)));
1461
1462        self.api.send_transaction(self.document_id, txn);
1463    }
1464
1465    /// Start rendering a new frame based on the data of the last frame.
1466    pub fn render_update(&mut self, frame: FrameUpdateRequest) {
1467        let _scope = tracing::trace_span!("render_update", ?frame.id).entered();
1468
1469        let render_reasons = frame_update_render_reasons(&frame);
1470
1471        if let Some(color) = frame.clear_color {
1472            self.clear_color = Some(color);
1473            self.renderer.as_mut().unwrap().set_clear_color(color.to_wr());
1474        }
1475
1476        let resized = self.resized;
1477
1478        let mut txn = Transaction::new();
1479        txn.set_root_pipeline(self.pipeline_id);
1480        self.push_resize(&mut txn);
1481        txn.generate_frame(self.frame_id().get(), render_reasons);
1482
1483        let frame_scope = match self.display_list_cache.update(
1484            &mut DisplayListExtAdapter {
1485                frame_id: self.frame_id(),
1486                extensions: &mut self.renderer_exts,
1487                transaction: &mut txn,
1488                renderer: self.renderer.as_mut().unwrap(),
1489                api: &mut self.api,
1490                external_images: &mut self.external_images,
1491            },
1492            frame.transforms,
1493            frame.floats,
1494            frame.colors,
1495            frame.extensions,
1496            resized,
1497        ) {
1498            Ok(p) => {
1499                if let Some(p) = p {
1500                    txn.append_dynamic_properties(p);
1501                }
1502
1503                tracing::trace_span!("<frame-update>", ?frame.id, capture = ?frame.capture, thread = "<webrender>")
1504            }
1505            Err(d) => {
1506                txn.reset_dynamic_properties();
1507                txn.append_dynamic_properties(DynamicProperties {
1508                    transforms: vec![],
1509                    floats: vec![],
1510                    colors: vec![],
1511                });
1512
1513                txn.set_display_list(webrender::api::Epoch(frame.id.epoch()), (self.pipeline_id, d));
1514
1515                tracing::trace_span!("<frame>", ?frame.id, capture = ?frame.capture, from_update = true, thread = "<webrender>")
1516            }
1517        };
1518
1519        self.pending_frames
1520            .push_back((frame.id, frame.capture, Some(frame_scope.entered())));
1521
1522        self.api.send_transaction(self.document_id, txn);
1523    }
1524
1525    /// Returns info for `FrameRendered` and if this is the first frame.
1526    #[must_use = "events must be generated from the result"]
1527    pub fn on_frame_ready(&mut self, msg: FrameReadyMsg, images: &mut ImageCache) -> FrameReadyResult {
1528        let (frame_id, capture, _) = self
1529            .pending_frames
1530            .pop_front()
1531            .unwrap_or((self.rendered_frame_id, FrameCapture::None, None));
1532        self.rendered_frame_id = frame_id;
1533
1534        let first_frame = self.waiting_first_frame;
1535
1536        let mut ext_args = FrameReadyArgs {
1537            frame_id,
1538            redraw: msg.composite_needed || self.waiting_first_frame,
1539        };
1540        for (_, ext) in &mut self.renderer_exts {
1541            ext.frame_ready(&mut ext_args);
1542            ext_args.redraw |= msg.composite_needed || self.waiting_first_frame;
1543        }
1544
1545        if self.waiting_first_frame {
1546            let _s = tracing::trace_span!("first-draw").entered();
1547            debug_assert!(msg.composite_needed);
1548
1549            self.waiting_first_frame = false;
1550            let s = self.window.inner_size();
1551            self.context.make_current();
1552            self.context.resize(s);
1553            self.redraw();
1554            if self.kiosk {
1555                self.window.request_redraw();
1556            } else if self.visible {
1557                self.set_visible(true);
1558
1559                // X11 does not open maximized
1560                // to work we need to set inner_size and after xlib resizes it request maximized...
1561                #[cfg(any(
1562                    target_os = "linux",
1563                    target_os = "dragonfly",
1564                    target_os = "freebsd",
1565                    target_os = "netbsd",
1566                    target_os = "openbsd"
1567                ))]
1568                if let (raw_window_handle::RawWindowHandle::Xlib(_), WindowState::Maximized) = (
1569                    raw_window_handle::HasWindowHandle::window_handle(&self.window).unwrap().as_raw(),
1570                    self.state.state,
1571                ) {
1572                    self.xlib_maximize = self.window.request_inner_size(self.window.inner_size()).is_none();
1573                }
1574
1575                if mem::take(&mut self.steal_init_focus) {
1576                    self.window.focus_window();
1577                }
1578                if let Some(r) = self.init_focus_request.take() {
1579                    self.set_focus_request(Some(r));
1580                }
1581            }
1582        } else if ext_args.redraw || msg.composite_needed {
1583            self.window.request_redraw();
1584        }
1585
1586        let scale_factor = self.scale_factor();
1587
1588        let capture = match capture {
1589            FrameCapture::None => None,
1590            FrameCapture::Full => Some(None),
1591            FrameCapture::Mask(m) => Some(Some(m)),
1592        };
1593        let image = if let Some(mask) = capture {
1594            let _s = tracing::trace_span!("capture_image").entered();
1595            if ext_args.redraw || msg.composite_needed {
1596                self.redraw();
1597            }
1598            Some(images.frame_image_data(
1599                &**self.context.gl(),
1600                PxRect::from_size(self.window.inner_size().to_px()),
1601                scale_factor,
1602                mask,
1603            ))
1604        } else {
1605            None
1606        };
1607
1608        FrameReadyResult {
1609            frame_id,
1610            image,
1611            first_frame,
1612        }
1613    }
1614
1615    pub fn redraw(&mut self) {
1616        let span = tracing::trace_span!("redraw", stats = tracing::field::Empty).entered();
1617
1618        self.context.make_current();
1619
1620        let scale_factor = self.scale_factor();
1621        let size = self.window.inner_size().to_px();
1622
1623        let renderer = self.renderer.as_mut().unwrap();
1624        renderer.update();
1625
1626        let r = renderer.render(size.to_wr_device(), 0).unwrap();
1627        span.record("stats", tracing::field::debug(&r.stats));
1628
1629        for (_, ext) in &mut self.renderer_exts {
1630            ext.redraw(&mut RedrawArgs {
1631                scale_factor,
1632                size,
1633                context: &mut self.context,
1634            });
1635        }
1636
1637        let _ = renderer.flush_pipeline_info();
1638
1639        self.window.pre_present_notify();
1640        self.context.swap_buffers();
1641    }
1642
1643    pub fn is_rendering_frame(&self) -> bool {
1644        !self.pending_frames.is_empty()
1645    }
1646
1647    fn push_resize(&mut self, txn: &mut Transaction) {
1648        if self.resized {
1649            self.resized = false;
1650
1651            self.context.make_current();
1652            let size = self.window.inner_size();
1653            self.context.resize(size);
1654            txn.set_document_view(PxRect::from_size(size.to_px()).to_wr_device());
1655        }
1656    }
1657
1658    pub fn frame_image(&mut self, images: &mut ImageCache, mask: Option<ImageMaskMode>) -> ImageId {
1659        let scale_factor = self.scale_factor();
1660        if !self.context.is_software() {
1661            self.redraw(); // refresh back buffer
1662        }
1663        images.frame_image(
1664            &**self.context.gl(),
1665            PxRect::from_size(self.window.inner_size().to_px()),
1666            self.id,
1667            self.rendered_frame_id,
1668            scale_factor,
1669            mask,
1670        )
1671    }
1672
1673    pub fn frame_image_rect(&mut self, images: &mut ImageCache, rect: PxRect, mask: Option<ImageMaskMode>) -> ImageId {
1674        let scale_factor = self.scale_factor();
1675        let rect = PxRect::from_size(self.window.inner_size().to_px())
1676            .intersection(&rect)
1677            .unwrap_or_default();
1678        if !self.context.is_software() {
1679            self.redraw(); // refresh back buffer
1680        }
1681        images.frame_image(&**self.context.gl(), rect, self.id, self.rendered_frame_id, scale_factor, mask)
1682    }
1683
1684    /// (global_position, monitor_position)
1685    pub fn inner_position(&self) -> (PxPoint, DipPoint) {
1686        let global_pos = self.window.inner_position().unwrap_or_default().to_px();
1687        let monitor_offset = if let Some(m) = self.window.current_monitor() {
1688            m.position().to_px().to_vector()
1689        } else {
1690            PxVector::zero()
1691        };
1692
1693        (global_pos, (global_pos - monitor_offset).to_dip(self.scale_factor()))
1694    }
1695
1696    pub fn size(&self) -> DipSize {
1697        self.window.inner_size().to_logical(self.window.scale_factor()).to_dip()
1698    }
1699
1700    pub fn safe_padding(&self) -> DipSideOffsets {
1701        #[cfg(target_os = "android")]
1702        match self.try_get_insets() {
1703            Ok(s) => s,
1704            Err(e) => {
1705                tracing::error!("cannot get insets, {e}");
1706                DipSideOffsets::zero()
1707            }
1708        }
1709
1710        #[cfg(not(target_os = "android"))]
1711        {
1712            DipSideOffsets::zero()
1713        }
1714    }
1715    #[cfg(target_os = "android")]
1716    fn try_get_insets(&self) -> jni::errors::Result<DipSideOffsets> {
1717        let ctx = ndk_context::android_context();
1718        let vm = unsafe { jni::JavaVM::from_raw(ctx.vm() as _) }?;
1719
1720        let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
1721        let mut env = vm.attach_current_thread()?;
1722
1723        let jni_window = env.call_method(&activity, "getWindow", "()Landroid/view/Window;", &[])?.l()?;
1724
1725        let view = env.call_method(jni_window, "getDecorView", "()Landroid/view/View;", &[])?.l()?;
1726
1727        let insets = env
1728            .call_method(view, "getRootWindowInsets", "()Landroid/view/WindowInsets;", &[])?
1729            .l()?;
1730        let cutout = env
1731            .call_method(insets, "getDisplayCutout", "()Landroid/view/DisplayCutout;", &[])?
1732            .l()?;
1733        let top = env.call_method(&cutout, "getSafeInsetTop", "()I", &[])?.i()? as i32;
1734        let right = env.call_method(&cutout, "getSafeInsetRight", "()I", &[])?.i()? as i32;
1735        let bottom = env.call_method(&cutout, "getSafeInsetBottom", "()I", &[])?.i()? as i32;
1736        let left = env.call_method(&cutout, "getSafeInsetLeft", "()I", &[])?.i()? as i32;
1737
1738        let offsets = zng_unit::PxSideOffsets::new(Px(top), Px(right), Px(bottom), Px(left));
1739
1740        Ok(offsets.to_dip(self.scale_factor()))
1741    }
1742
1743    pub fn scale_factor(&self) -> Factor {
1744        Factor(self.window.scale_factor() as f32)
1745    }
1746
1747    /// Window actual render mode.
1748    pub fn render_mode(&self) -> RenderMode {
1749        self.render_mode
1750    }
1751
1752    /// Calls the window extension command.
1753    pub fn window_extension(&mut self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> ApiExtensionPayload {
1754        for (key, ext) in &mut self.window_exts {
1755            if *key == extension_id {
1756                return ext.command(&mut WindowCommandArgs {
1757                    window: &self.window,
1758                    context: &mut self.context,
1759                    request,
1760                });
1761            }
1762        }
1763        ApiExtensionPayload::unknown_extension(extension_id)
1764    }
1765
1766    /// Calls the render extension command.
1767    pub fn render_extension(&mut self, extension_id: ApiExtensionId, request: ApiExtensionPayload) -> ApiExtensionPayload {
1768        for (key, ext) in &mut self.renderer_exts {
1769            if *key == extension_id {
1770                let mut redraw = false;
1771                let r = ext.command(&mut RendererCommandArgs {
1772                    renderer: self.renderer.as_mut().unwrap(),
1773                    api: &mut self.api,
1774                    request,
1775                    window: Some(&self.window),
1776                    context: &mut self.context,
1777                    redraw: &mut redraw,
1778                });
1779                if redraw {
1780                    self.window.request_redraw();
1781                }
1782
1783                return r;
1784            }
1785        }
1786        ApiExtensionPayload::unknown_extension(extension_id)
1787    }
1788
1789    #[cfg(not(target_os = "android"))]
1790    fn enter_dialog(&self, id: dlg_api::DialogId, event_sender: &AppEventSender) -> bool {
1791        let already_open = self.modal_dialog_active.swap(true, Ordering::Acquire);
1792        if already_open {
1793            let _ = event_sender.send(AppEvent::Notify(Event::MsgDialogResponse(
1794                id,
1795                dlg_api::MsgDialogResponse::Error(Txt::from_static("dialog already open")),
1796            )));
1797        }
1798        already_open
1799    }
1800
1801    #[cfg(target_os = "android")]
1802    pub(crate) fn message_dialog(&self, dialog: dlg_api::MsgDialog, id: dlg_api::DialogId, event_sender: AppEventSender) {
1803        let _ = dialog;
1804        let _ = event_sender.send(AppEvent::Notify(Event::MsgDialogResponse(
1805            id,
1806            dlg_api::MsgDialogResponse::Error(Txt::from_static("native dialogs not implemented for android")),
1807        )));
1808    }
1809
1810    /// Shows a native message dialog.
1811    #[cfg(not(target_os = "android"))]
1812    pub(crate) fn message_dialog(&self, dialog: dlg_api::MsgDialog, id: dlg_api::DialogId, event_sender: AppEventSender) {
1813        if self.enter_dialog(id, &event_sender) {
1814            return;
1815        }
1816
1817        let dlg = rfd::AsyncMessageDialog::new()
1818            .set_level(match dialog.icon {
1819                dlg_api::MsgDialogIcon::Info => rfd::MessageLevel::Info,
1820                dlg_api::MsgDialogIcon::Warn => rfd::MessageLevel::Warning,
1821                dlg_api::MsgDialogIcon::Error => rfd::MessageLevel::Error,
1822            })
1823            .set_buttons(match dialog.buttons {
1824                dlg_api::MsgDialogButtons::Ok => rfd::MessageButtons::Ok,
1825                dlg_api::MsgDialogButtons::OkCancel => rfd::MessageButtons::OkCancel,
1826                dlg_api::MsgDialogButtons::YesNo => rfd::MessageButtons::YesNo,
1827            })
1828            .set_title(dialog.title.as_str())
1829            .set_description(dialog.message.as_str())
1830            .set_parent(&self.window);
1831
1832        let modal_dialog_active = self.modal_dialog_active.clone();
1833        Self::run_dialog(async move {
1834            let r = dlg.show().await;
1835
1836            let r = match dialog.buttons {
1837                dlg_api::MsgDialogButtons::Ok => dlg_api::MsgDialogResponse::Ok,
1838                dlg_api::MsgDialogButtons::OkCancel => match r {
1839                    rfd::MessageDialogResult::Yes => dlg_api::MsgDialogResponse::Ok,
1840                    rfd::MessageDialogResult::No => dlg_api::MsgDialogResponse::Cancel,
1841                    rfd::MessageDialogResult::Ok => dlg_api::MsgDialogResponse::Ok,
1842                    rfd::MessageDialogResult::Cancel => dlg_api::MsgDialogResponse::Cancel,
1843                    rfd::MessageDialogResult::Custom(_) => dlg_api::MsgDialogResponse::Cancel,
1844                },
1845                dlg_api::MsgDialogButtons::YesNo => match r {
1846                    rfd::MessageDialogResult::Yes => dlg_api::MsgDialogResponse::Yes,
1847                    rfd::MessageDialogResult::No => dlg_api::MsgDialogResponse::No,
1848                    rfd::MessageDialogResult::Ok => dlg_api::MsgDialogResponse::Yes,
1849                    rfd::MessageDialogResult::Cancel => dlg_api::MsgDialogResponse::No,
1850                    rfd::MessageDialogResult::Custom(_) => dlg_api::MsgDialogResponse::No,
1851                },
1852            };
1853            modal_dialog_active.store(false, Ordering::Release);
1854            let _ = event_sender.send(AppEvent::Notify(Event::MsgDialogResponse(id, r)));
1855        });
1856    }
1857
1858    #[cfg(target_os = "android")]
1859    pub(crate) fn file_dialog(&self, dialog: dlg_api::FileDialog, id: dlg_api::DialogId, event_sender: AppEventSender) {
1860        let _ = dialog;
1861        let _ = event_sender.send(AppEvent::Notify(Event::MsgDialogResponse(
1862            id,
1863            dlg_api::MsgDialogResponse::Error(Txt::from_static("native dialogs not implemented for android")),
1864        )));
1865    }
1866
1867    /// Shows a native file dialog.
1868    #[cfg(not(target_os = "android"))]
1869    pub(crate) fn file_dialog(&self, dialog: dlg_api::FileDialog, id: dlg_api::DialogId, event_sender: AppEventSender) {
1870        if self.enter_dialog(id, &event_sender) {
1871            return;
1872        }
1873
1874        let mut dlg = rfd::AsyncFileDialog::new()
1875            .set_title(dialog.title.as_str())
1876            .set_directory(&dialog.starting_dir)
1877            .set_file_name(dialog.starting_name.as_str())
1878            .set_parent(&self.window);
1879        for (name, patterns) in dialog.iter_filters() {
1880            dlg = dlg.add_filter(
1881                name,
1882                &patterns
1883                    .map(|s| {
1884                        let s = s.trim_start_matches(['*', '.']);
1885                        if s.is_empty() { "*" } else { s }
1886                    })
1887                    .collect::<Vec<_>>(),
1888            );
1889        }
1890
1891        let modal_dialog_active = self.modal_dialog_active.clone();
1892        Self::run_dialog(async move {
1893            let selection: Vec<_> = match dialog.kind {
1894                dlg_api::FileDialogKind::OpenFile => dlg.pick_file().await.into_iter().map(Into::into).collect(),
1895                dlg_api::FileDialogKind::OpenFiles => dlg.pick_files().await.into_iter().flatten().map(Into::into).collect(),
1896                dlg_api::FileDialogKind::SelectFolder => dlg.pick_folder().await.into_iter().map(Into::into).collect(),
1897                dlg_api::FileDialogKind::SelectFolders => dlg.pick_folders().await.into_iter().flatten().map(Into::into).collect(),
1898                dlg_api::FileDialogKind::SaveFile => dlg.save_file().await.into_iter().map(Into::into).collect(),
1899            };
1900
1901            let r = if selection.is_empty() {
1902                dlg_api::FileDialogResponse::Cancel
1903            } else {
1904                dlg_api::FileDialogResponse::Selected(selection)
1905            };
1906
1907            modal_dialog_active.store(false, Ordering::Release);
1908            let _ = event_sender.send(AppEvent::Notify(Event::FileDialogResponse(id, r)));
1909        });
1910    }
1911    /// Run dialog unblocked.
1912    #[cfg(not(target_os = "android"))]
1913    fn run_dialog(run: impl Future + Send + 'static) {
1914        let mut task = Box::pin(run);
1915        std::thread::spawn(move || {
1916            struct ThreadWaker(std::thread::Thread);
1917            impl std::task::Wake for ThreadWaker {
1918                fn wake(self: std::sync::Arc<Self>) {
1919                    self.0.unpark();
1920                }
1921            }
1922            let waker = Arc::new(ThreadWaker(std::thread::current())).into();
1923            let mut cx = std::task::Context::from_waker(&waker);
1924            loop {
1925                match task.as_mut().poll(&mut cx) {
1926                    std::task::Poll::Ready(_) => return,
1927                    std::task::Poll::Pending => std::thread::park(),
1928                }
1929            }
1930        });
1931    }
1932
1933    /// Pump the accessibility adapter and window extensions.
1934    pub fn on_window_event(&mut self, event: &winit::event::WindowEvent) {
1935        if let Some(a) = &mut self.access {
1936            a.process_event(&self.window, event);
1937        }
1938        for (_, ext) in &mut self.window_exts {
1939            ext.event(&mut extensions::WindowEventArgs {
1940                window: &self.window,
1941                context: &mut self.context,
1942                event,
1943            });
1944        }
1945    }
1946
1947    /// Update the accessibility info.
1948    pub fn access_update(&mut self, update: zng_view_api::access::AccessTreeUpdate, event_sender: &AppEventSender) {
1949        if let Some(a) = &mut self.access {
1950            // SAFETY: we drop `access` in case of panic.
1951            let mut a = std::panic::AssertUnwindSafe(a);
1952            let panic = crate::util::catch_suppress(move || {
1953                a.update_if_active(|| crate::util::access_tree_update_to_kit(update));
1954            });
1955            if let Err(p) = panic {
1956                self.access = None;
1957
1958                let _ = event_sender.send(AppEvent::Notify(Event::RecoveredFromComponentPanic {
1959                    component: Txt::from_static("accesskit_winit::Adapter::update_if_active"),
1960                    recover: Txt::from_static("accessibility disabled for this window instance"),
1961                    panic: p.to_txt(),
1962                }));
1963            }
1964        }
1965    }
1966
1967    pub(crate) fn on_low_memory(&mut self) {
1968        self.api.notify_memory_pressure();
1969
1970        for (_, ext) in &mut self.renderer_exts {
1971            ext.low_memory();
1972        }
1973    }
1974
1975    pub(crate) fn set_ime_area(&mut self, area: Option<DipRect>) {
1976        if let Some(a) = area {
1977            if self.ime_area != Some(a) {
1978                if self.ime_area.is_none() {
1979                    self.window.set_ime_allowed(true);
1980
1981                    #[cfg(target_os = "android")]
1982                    self.set_mobile_keyboard_vis(true);
1983                }
1984
1985                self.ime_area = Some(a);
1986                self.window.set_ime_cursor_area(a.origin.to_winit(), a.size.to_winit());
1987            }
1988        } else if self.ime_area.is_some() {
1989            self.window.set_ime_allowed(false);
1990            self.ime_area = None;
1991
1992            #[cfg(target_os = "android")]
1993            self.set_mobile_keyboard_vis(false);
1994        }
1995    }
1996    #[cfg(target_os = "android")]
1997    fn set_mobile_keyboard_vis(&self, visible: bool) {
1998        // this does not work
1999        //
2000        // let app = crate::platform::android::android_app();
2001        // if visible {
2002        //     app.show_soft_input(false);
2003        // } else {
2004        //     app.hide_soft_input(false);
2005        // }
2006
2007        if let Err(e) = self.try_show_hide_soft_keyboard(visible) {
2008            tracing::error!("cannot {} mobile keyboard, {e}", if visible { "show" } else { "hide" });
2009        }
2010    }
2011    #[cfg(target_os = "android")]
2012    fn try_show_hide_soft_keyboard(&self, show: bool) -> jni::errors::Result<()> {
2013        use jni::objects::JValue;
2014
2015        let ctx = ndk_context::android_context();
2016        let vm = unsafe { jni::JavaVM::from_raw(ctx.vm() as _) }?;
2017
2018        let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
2019        let mut env = vm.attach_current_thread()?;
2020
2021        let class_ctx = env.find_class("android/content/Context")?;
2022        let ims = env.get_static_field(class_ctx, "INPUT_METHOD_SERVICE", "Ljava/lang/String;")?;
2023
2024        let im_manager = env
2025            .call_method(
2026                &activity,
2027                "getSystemService",
2028                "(Ljava/lang/String;)Ljava/lang/Object;",
2029                &[ims.borrow()],
2030            )?
2031            .l()?;
2032
2033        let jni_window = env.call_method(&activity, "getWindow", "()Landroid/view/Window;", &[])?.l()?;
2034
2035        let view = env.call_method(jni_window, "getDecorView", "()Landroid/view/View;", &[])?.l()?;
2036
2037        if show {
2038            env.call_method(&view, "requestFocus", "()Z", &[])?;
2039
2040            env.call_method(
2041                im_manager,
2042                "showSoftInput",
2043                "(Landroid/view/View;I)Z",
2044                &[JValue::Object(&view), 0i32.into()],
2045            )?;
2046        } else {
2047            let window_token = env.call_method(view, "getWindowToken", "()Landroid/os/IBinder;", &[])?.l()?;
2048            let jvalue_window_token = jni::objects::JValueGen::Object(&window_token);
2049
2050            env.call_method(
2051                im_manager,
2052                "hideSoftInputFromWindow",
2053                "(Landroid/os/IBinder;I)Z",
2054                &[jvalue_window_token, 0i32.into()],
2055            )?;
2056        }
2057
2058        Ok(())
2059    }
2060
2061    #[cfg(windows)]
2062    pub(crate) fn set_system_shutdown_warn(&mut self, reason: Txt) {
2063        if !reason.is_empty() {
2064            self.has_shutdown_warn = true;
2065            let hwnd = crate::util::winit_to_hwnd(&self.window);
2066            let reason = windows::core::HSTRING::from(reason.as_str());
2067            // SAFETY: function return handled.
2068            let created = unsafe { windows_sys::Win32::System::Shutdown::ShutdownBlockReasonCreate(hwnd, reason.as_ptr()) } != 0;
2069            if !created {
2070                let error = unsafe { windows_sys::Win32::Foundation::GetLastError() };
2071                tracing::error!("failed to set system shutdown warn ({error:#X}), requested warn reason was: {reason}");
2072            }
2073        } else if mem::take(&mut self.has_shutdown_warn) {
2074            let hwnd = crate::util::winit_to_hwnd(&self.window);
2075            // SAFETY: function return handled.
2076            let destroyed = unsafe { windows_sys::Win32::System::Shutdown::ShutdownBlockReasonDestroy(hwnd) } != 0;
2077            if !destroyed {
2078                let error = unsafe { windows_sys::Win32::Foundation::GetLastError() };
2079                tracing::error!("failed to unset system shutdown warn ({error:#X})");
2080            }
2081        }
2082    }
2083
2084    #[cfg(not(windows))]
2085    pub(crate) fn set_system_shutdown_warn(&mut self, reason: Txt) {
2086        if !reason.is_empty() {
2087            tracing::warn!("system shutdown warn not implemented on {}", std::env::consts::OS);
2088        }
2089    }
2090
2091    pub(crate) fn drag_drop_cursor_pos(&self) -> Option<DipPoint> {
2092        #[cfg(windows)]
2093        {
2094            let mut pt = windows::Win32::Foundation::POINT::default();
2095            // SAFETY: normal call
2096            if unsafe { windows::Win32::UI::WindowsAndMessaging::GetCursorPos(&mut pt) }.is_ok() {
2097                let cursor_pos = PxPoint::new(Px(pt.x), Px(pt.y));
2098                let win_pos = self.window.inner_position().unwrap_or_default().to_px();
2099                let pos = cursor_pos - win_pos.to_vector();
2100                if pos.x >= Px(0) && pos.y >= Px(0) {
2101                    let size = self.window.inner_size().to_px();
2102                    if pos.x <= size.width && pos.y <= size.height {
2103                        return Some(pos.to_dip(self.scale_factor()));
2104                    }
2105                }
2106            }
2107        }
2108        None
2109    }
2110}
2111impl Drop for Window {
2112    fn drop(&mut self) {
2113        self.set_system_shutdown_warn(Txt::from(""));
2114
2115        self.api.stop_render_backend();
2116        self.api.shut_down(true);
2117
2118        // webrender deinit panics if the context is not current.
2119        self.context.make_current();
2120        self.renderer.take().unwrap().deinit();
2121
2122        for (_, ext) in &mut self.renderer_exts {
2123            ext.renderer_deinited(&mut RendererDeinitedArgs {
2124                document_id: self.document_id,
2125                pipeline_id: self.pipeline_id,
2126                context: &mut self.context,
2127                window: Some(&self.window),
2128            })
2129        }
2130        for (_, ext) in &mut self.window_exts {
2131            ext.window_deinited(&mut WindowDeinitedArgs {
2132                window: &self.window,
2133                context: &mut self.context,
2134            });
2135        }
2136    }
2137}
2138
2139pub(crate) struct FrameReadyResult {
2140    pub frame_id: FrameId,
2141    pub image: Option<ImageLoadedData>,
2142    pub first_frame: bool,
2143}
2144
2145struct AccessActivateHandler {
2146    id: WindowId,
2147    event_sender: AppEventSender,
2148}
2149impl accesskit::ActivationHandler for AccessActivateHandler {
2150    fn request_initial_tree(&mut self) -> Option<accesskit::TreeUpdate> {
2151        let _ = self.event_sender.send(AppEvent::Notify(Event::AccessInit { window: self.id }));
2152        None
2153    }
2154}
2155
2156struct AccessDeactivateHandler {
2157    id: WindowId,
2158    event_sender: AppEventSender,
2159}
2160
2161impl accesskit::DeactivationHandler for AccessDeactivateHandler {
2162    fn deactivate_accessibility(&mut self) {
2163        let _ = self.event_sender.send(AppEvent::Notify(Event::AccessDeinit { window: self.id }));
2164    }
2165}
2166
2167struct AccessActionSender {
2168    id: WindowId,
2169    event_sender: AppEventSender,
2170}
2171impl accesskit::ActionHandler for AccessActionSender {
2172    fn do_action(&mut self, request: accesskit::ActionRequest) {
2173        if let Some(ev) = crate::util::accesskit_to_event(self.id, request) {
2174            let _ = self.event_sender.send(AppEvent::Notify(ev));
2175        }
2176    }
2177}