zng_view/
util.rs

1use std::any::Any;
2use std::backtrace::Backtrace;
3use std::cell::RefCell;
4use std::marker::PhantomData;
5use std::{cell::Cell, sync::Arc};
6use std::{fmt, ops};
7
8use rayon::ThreadPoolBuilder;
9use webrender::api as wr;
10use winit::event_loop::ActiveEventLoop;
11use winit::{event::ElementState, monitor::MonitorHandle};
12use zng_txt::{ToTxt, Txt};
13use zng_unit::*;
14use zng_view_api::access::AccessNodeId;
15use zng_view_api::keyboard::{KeyLocation, NativeKeyCode};
16use zng_view_api::window::{FrameCapture, FrameRequest, FrameUpdateRequest, ResizeDirection, WindowButton};
17use zng_view_api::{
18    keyboard::{Key, KeyCode, KeyState},
19    mouse::{ButtonState, MouseButton, MouseScrollDelta},
20    touch::{TouchForce, TouchPhase},
21    window::{CursorIcon, MonitorInfo, VideoMode},
22};
23
24#[cfg(not(target_os = "android"))]
25use zng_view_api::clipboard as clipboard_api;
26
27/// Sets a window subclass that calls a raw event handler.
28///
29/// Use this to receive Windows OS events not covered in [`raw_events`].
30///
31/// Returns if adding a subclass handler succeeded.
32///
33/// # Handler
34///
35/// The handler inputs are the first 4 arguments of a [`SUBCLASSPROC`].
36/// You can use closure capture to include extra data.
37///
38/// The handler must return `Some(LRESULT)` to stop the propagation of a specific message.
39///
40/// The handler is dropped after it receives the `WM_DESTROY` message.
41///
42/// # Panics
43///
44/// Panics in headless mode.
45///
46/// [`raw_events`]: crate::app::raw_events
47/// [`SUBCLASSPROC`]: https://docs.microsoft.com/en-us/windows/win32/api/commctrl/nc-commctrl-subclassproc
48#[cfg(windows)]
49pub fn set_raw_windows_event_handler<H>(hwnd: windows_sys::Win32::Foundation::HWND, subclass_id: usize, handler: H) -> bool
50where
51    H: FnMut(
52            windows_sys::Win32::Foundation::HWND,
53            u32,
54            windows_sys::Win32::Foundation::WPARAM,
55            windows_sys::Win32::Foundation::LPARAM,
56        ) -> Option<windows_sys::Win32::Foundation::LRESULT>
57        + 'static,
58{
59    let data = Box::new(handler);
60    unsafe {
61        windows_sys::Win32::UI::Shell::SetWindowSubclass(hwnd, Some(subclass_raw_event_proc::<H>), subclass_id, Box::into_raw(data) as _)
62            != 0
63    }
64}
65
66#[cfg(windows)]
67unsafe extern "system" fn subclass_raw_event_proc<H>(
68    hwnd: windows_sys::Win32::Foundation::HWND,
69    msg: u32,
70    wparam: windows_sys::Win32::Foundation::WPARAM,
71    lparam: windows_sys::Win32::Foundation::LPARAM,
72    _id: usize,
73    data: usize,
74) -> windows_sys::Win32::Foundation::LRESULT
75where
76    H: FnMut(
77            windows_sys::Win32::Foundation::HWND,
78            u32,
79            windows_sys::Win32::Foundation::WPARAM,
80            windows_sys::Win32::Foundation::LPARAM,
81        ) -> Option<windows_sys::Win32::Foundation::LRESULT>
82        + 'static,
83{
84    use windows_sys::Win32::UI::WindowsAndMessaging::WM_DESTROY;
85    match msg {
86        WM_DESTROY => {
87            // last call and cleanup.
88            let mut handler = unsafe { Box::from_raw(data as *mut H) };
89            handler(hwnd, msg, wparam, lparam).unwrap_or_default()
90        }
91
92        msg => {
93            let handler = unsafe { &mut *(data as *mut H) };
94            if let Some(r) = handler(hwnd, msg, wparam, lparam) {
95                r
96            } else {
97                unsafe { windows_sys::Win32::UI::Shell::DefSubclassProc(hwnd, msg, wparam, lparam) }
98            }
99        }
100    }
101}
102
103/// Conversion from `winit` logical units to [`Dip`].
104///
105/// All conversions are 1 to 1.
106pub(crate) trait WinitToDip {
107    /// `Self` equivalent in [`Dip`] units.
108    type AsDip;
109
110    /// Returns `self` in [`Dip`] units.
111    fn to_dip(self) -> Self::AsDip;
112}
113
114/// Conversion from `winit` physical units to [`Dip`].
115///
116/// All conversions are 1 to 1.
117pub(crate) trait WinitToPx {
118    /// `Self` equivalent in [`Px`] units.
119    type AsPx;
120
121    /// Returns `self` in [`Px`] units.
122    fn to_px(self) -> Self::AsPx;
123}
124
125/// Conversion from [`Dip`] to `winit` logical units.
126pub(crate) trait DipToWinit {
127    /// `Self` equivalent in `winit` logical units.
128    type AsWinit;
129
130    /// Returns `self` in `winit` logical units.
131    fn to_winit(self) -> Self::AsWinit;
132}
133
134/// Conversion from [`Px`] to `winit` physical units.
135pub(crate) trait PxToWinit {
136    /// `Self` equivalent in `winit` logical units.
137    type AsWinit;
138
139    /// Returns `self` in `winit` logical units.
140    fn to_winit(self) -> Self::AsWinit;
141}
142
143impl PxToWinit for PxSize {
144    type AsWinit = winit::dpi::PhysicalSize<u32>;
145
146    fn to_winit(self) -> Self::AsWinit {
147        winit::dpi::PhysicalSize::new(self.width.0 as _, self.height.0 as _)
148    }
149}
150impl PxToWinit for PxPoint {
151    type AsWinit = winit::dpi::PhysicalPosition<i32>;
152
153    fn to_winit(self) -> Self::AsWinit {
154        winit::dpi::PhysicalPosition::new(self.x.0, self.y.0)
155    }
156}
157
158impl DipToWinit for DipPoint {
159    type AsWinit = winit::dpi::LogicalPosition<f32>;
160
161    fn to_winit(self) -> Self::AsWinit {
162        winit::dpi::LogicalPosition::new(self.x.to_f32(), self.y.to_f32())
163    }
164}
165
166impl WinitToDip for winit::dpi::LogicalPosition<f64> {
167    type AsDip = DipPoint;
168
169    fn to_dip(self) -> Self::AsDip {
170        DipPoint::new(Dip::new_f32(self.x as f32), Dip::new_f32(self.y as f32))
171    }
172}
173
174impl WinitToPx for winit::dpi::PhysicalPosition<i32> {
175    type AsPx = PxPoint;
176
177    fn to_px(self) -> Self::AsPx {
178        PxPoint::new(Px(self.x), Px(self.y))
179    }
180}
181
182impl WinitToPx for winit::dpi::PhysicalPosition<f64> {
183    type AsPx = PxPoint;
184
185    fn to_px(self) -> Self::AsPx {
186        PxPoint::new(Px(self.x as i32), Px(self.y as i32))
187    }
188}
189
190impl DipToWinit for DipSize {
191    type AsWinit = winit::dpi::LogicalSize<f32>;
192
193    fn to_winit(self) -> Self::AsWinit {
194        winit::dpi::LogicalSize::new(self.width.to_f32(), self.height.to_f32())
195    }
196}
197
198impl WinitToDip for winit::dpi::LogicalSize<f64> {
199    type AsDip = DipSize;
200
201    fn to_dip(self) -> Self::AsDip {
202        DipSize::new(Dip::new_f32(self.width as f32), Dip::new_f32(self.height as f32))
203    }
204}
205
206impl WinitToPx for winit::dpi::PhysicalSize<u32> {
207    type AsPx = PxSize;
208
209    fn to_px(self) -> Self::AsPx {
210        PxSize::new(Px(self.width as i32), Px(self.height as i32))
211    }
212}
213pub trait ResizeDirectionToWinit {
214    fn to_winit(self) -> winit::window::ResizeDirection;
215}
216impl ResizeDirectionToWinit for ResizeDirection {
217    fn to_winit(self) -> winit::window::ResizeDirection {
218        use winit::window::ResizeDirection::*;
219        match self {
220            ResizeDirection::East => East,
221            ResizeDirection::North => North,
222            ResizeDirection::NorthEast => NorthEast,
223            ResizeDirection::NorthWest => NorthWest,
224            ResizeDirection::South => South,
225            ResizeDirection::SouthEast => SouthEast,
226            ResizeDirection::SouthWest => SouthWest,
227            ResizeDirection::West => West,
228        }
229    }
230}
231
232pub trait WindowButtonsToWinit {
233    fn to_winit(self) -> winit::window::WindowButtons;
234}
235impl WindowButtonsToWinit for WindowButton {
236    fn to_winit(self) -> winit::window::WindowButtons {
237        let mut r = winit::window::WindowButtons::empty();
238        r.set(winit::window::WindowButtons::CLOSE, self.contains(WindowButton::CLOSE));
239        r.set(winit::window::WindowButtons::MINIMIZE, self.contains(WindowButton::MINIMIZE));
240        r.set(winit::window::WindowButtons::MAXIMIZE, self.contains(WindowButton::MAXIMIZE));
241
242        r
243    }
244}
245
246pub trait CursorToWinit {
247    fn to_winit(self) -> winit::window::CursorIcon;
248}
249impl CursorToWinit for CursorIcon {
250    fn to_winit(self) -> winit::window::CursorIcon {
251        use winit::window::CursorIcon::*;
252        match self {
253            CursorIcon::Default => Default,
254            CursorIcon::ContextMenu => ContextMenu,
255            CursorIcon::Help => Help,
256            CursorIcon::Pointer => Pointer,
257            CursorIcon::Progress => Progress,
258            CursorIcon::Wait => Wait,
259            CursorIcon::Cell => Cell,
260            CursorIcon::Crosshair => Crosshair,
261            CursorIcon::Text => Text,
262            CursorIcon::VerticalText => VerticalText,
263            CursorIcon::Alias => Alias,
264            CursorIcon::Copy => Copy,
265            CursorIcon::Move => Move,
266            CursorIcon::NoDrop => NoDrop,
267            CursorIcon::NotAllowed => NotAllowed,
268            CursorIcon::Grab => Grab,
269            CursorIcon::Grabbing => Grabbing,
270            CursorIcon::EResize => EResize,
271            CursorIcon::NResize => NResize,
272            CursorIcon::NeResize => NeResize,
273            CursorIcon::NwResize => NwResize,
274            CursorIcon::SResize => SResize,
275            CursorIcon::SeResize => SeResize,
276            CursorIcon::SwResize => SwResize,
277            CursorIcon::WResize => WResize,
278            CursorIcon::EwResize => EwResize,
279            CursorIcon::NsResize => NsResize,
280            CursorIcon::NeswResize => NeswResize,
281            CursorIcon::NwseResize => NwseResize,
282            CursorIcon::ColResize => ColResize,
283            CursorIcon::RowResize => RowResize,
284            CursorIcon::AllScroll => AllScroll,
285            CursorIcon::ZoomIn => ZoomIn,
286            CursorIcon::ZoomOut => ZoomOut,
287            _ => Default,
288        }
289    }
290}
291
292pub(crate) fn monitor_handle_to_info(handle: &MonitorHandle) -> MonitorInfo {
293    let position = handle.position().to_px();
294    let size = handle.size().to_px();
295    MonitorInfo {
296        name: Txt::from_str(&handle.name().unwrap_or_default()),
297        position,
298        size,
299        scale_factor: Factor(handle.scale_factor() as _),
300        video_modes: handle.video_modes().map(glutin_video_mode_to_video_mode).collect(),
301        is_primary: false,
302    }
303}
304
305pub(crate) fn glutin_video_mode_to_video_mode(v: winit::monitor::VideoModeHandle) -> VideoMode {
306    let size = v.size();
307    VideoMode {
308        size: PxSize::new(Px(size.width as i32), Px(size.height as i32)),
309        bit_depth: v.bit_depth(),
310        refresh_rate: v.refresh_rate_millihertz(),
311    }
312}
313
314pub(crate) fn element_state_to_key_state(s: ElementState) -> KeyState {
315    match s {
316        ElementState::Pressed => KeyState::Pressed,
317        ElementState::Released => KeyState::Released,
318    }
319}
320
321pub(crate) fn element_state_to_button_state(s: ElementState) -> ButtonState {
322    match s {
323        ElementState::Pressed => ButtonState::Pressed,
324        ElementState::Released => ButtonState::Released,
325    }
326}
327
328pub(crate) fn winit_mouse_wheel_delta_to_zng(w: winit::event::MouseScrollDelta) -> MouseScrollDelta {
329    match w {
330        winit::event::MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(x, y),
331        winit::event::MouseScrollDelta::PixelDelta(d) => MouseScrollDelta::PixelDelta(d.x as f32, d.y as f32),
332    }
333}
334
335pub(crate) fn winit_touch_phase_to_zng(w: winit::event::TouchPhase) -> TouchPhase {
336    match w {
337        winit::event::TouchPhase::Started => TouchPhase::Start,
338        winit::event::TouchPhase::Moved => TouchPhase::Move,
339        winit::event::TouchPhase::Ended => TouchPhase::End,
340        winit::event::TouchPhase::Cancelled => TouchPhase::Cancel,
341    }
342}
343
344pub(crate) fn winit_force_to_zng(f: winit::event::Force) -> TouchForce {
345    match f {
346        winit::event::Force::Calibrated {
347            force,
348            max_possible_force,
349            altitude_angle,
350        } => TouchForce::Calibrated {
351            force,
352            max_possible_force,
353            altitude_angle,
354        },
355        winit::event::Force::Normalized(f) => TouchForce::Normalized(f),
356    }
357}
358
359pub(crate) fn winit_mouse_button_to_zng(b: winit::event::MouseButton) -> MouseButton {
360    match b {
361        winit::event::MouseButton::Left => MouseButton::Left,
362        winit::event::MouseButton::Right => MouseButton::Right,
363        winit::event::MouseButton::Middle => MouseButton::Middle,
364        winit::event::MouseButton::Back => MouseButton::Back,
365        winit::event::MouseButton::Forward => MouseButton::Forward,
366        winit::event::MouseButton::Other(btn) => MouseButton::Other(btn),
367    }
368}
369
370#[cfg(windows)]
371pub(crate) fn winit_to_hwnd(window: &winit::window::Window) -> isize {
372    use raw_window_handle::HasWindowHandle as _;
373
374    match window.window_handle().unwrap().as_raw() {
375        raw_window_handle::RawWindowHandle::Win32(w) => w.hwnd.get() as _,
376        _ => unreachable!(),
377    }
378}
379
380pub(crate) fn winit_key_location_to_zng(t: winit::keyboard::KeyLocation) -> KeyLocation {
381    match t {
382        winit::keyboard::KeyLocation::Standard => KeyLocation::Standard,
383        winit::keyboard::KeyLocation::Left => KeyLocation::Left,
384        winit::keyboard::KeyLocation::Right => KeyLocation::Right,
385        winit::keyboard::KeyLocation::Numpad => KeyLocation::Numpad,
386    }
387}
388
389use winit::keyboard::{Key as WinitKey, NamedKey as WinitNamedKey};
390pub(crate) fn winit_key_to_key(key: WinitKey) -> Key {
391    match key {
392        WinitKey::Named(k) => match k {
393            WinitNamedKey::Alt => Key::Alt,
394            WinitNamedKey::AltGraph => Key::AltGraph,
395            WinitNamedKey::CapsLock => Key::CapsLock,
396            WinitNamedKey::Control => Key::Ctrl,
397            WinitNamedKey::Fn => Key::Fn,
398            WinitNamedKey::FnLock => Key::FnLock,
399            WinitNamedKey::NumLock => Key::NumLock,
400            WinitNamedKey::ScrollLock => Key::ScrollLock,
401            WinitNamedKey::Shift => Key::Shift,
402            WinitNamedKey::Symbol => Key::Symbol,
403            WinitNamedKey::SymbolLock => Key::SymbolLock,
404            WinitNamedKey::Meta => Key::Meta,
405            WinitNamedKey::Hyper => Key::Hyper,
406            WinitNamedKey::Super => Key::Super,
407            WinitNamedKey::Enter => Key::Enter,
408            WinitNamedKey::Tab => Key::Tab,
409            WinitNamedKey::Space => Key::Space,
410            WinitNamedKey::ArrowDown => Key::ArrowDown,
411            WinitNamedKey::ArrowLeft => Key::ArrowLeft,
412            WinitNamedKey::ArrowRight => Key::ArrowRight,
413            WinitNamedKey::ArrowUp => Key::ArrowUp,
414            WinitNamedKey::End => Key::End,
415            WinitNamedKey::Home => Key::Home,
416            WinitNamedKey::PageDown => Key::PageDown,
417            WinitNamedKey::PageUp => Key::PageUp,
418            WinitNamedKey::Backspace => Key::Backspace,
419            WinitNamedKey::Clear => Key::Clear,
420            WinitNamedKey::Copy => Key::Copy,
421            WinitNamedKey::CrSel => Key::CrSel,
422            WinitNamedKey::Cut => Key::Cut,
423            WinitNamedKey::Delete => Key::Delete,
424            WinitNamedKey::EraseEof => Key::EraseEof,
425            WinitNamedKey::ExSel => Key::ExSel,
426            WinitNamedKey::Insert => Key::Insert,
427            WinitNamedKey::Paste => Key::Paste,
428            WinitNamedKey::Redo => Key::Redo,
429            WinitNamedKey::Undo => Key::Undo,
430            WinitNamedKey::Accept => Key::Accept,
431            WinitNamedKey::Again => Key::Again,
432            WinitNamedKey::Attn => Key::Attn,
433            WinitNamedKey::Cancel => Key::Cancel,
434            WinitNamedKey::ContextMenu => Key::ContextMenu,
435            WinitNamedKey::Escape => Key::Escape,
436            WinitNamedKey::Execute => Key::Execute,
437            WinitNamedKey::Find => Key::Find,
438            WinitNamedKey::Help => Key::Help,
439            WinitNamedKey::Pause => Key::Pause,
440            WinitNamedKey::Play => Key::Play,
441            WinitNamedKey::Props => Key::Props,
442            WinitNamedKey::Select => Key::Select,
443            WinitNamedKey::ZoomIn => Key::ZoomIn,
444            WinitNamedKey::ZoomOut => Key::ZoomOut,
445            WinitNamedKey::BrightnessDown => Key::BrightnessDown,
446            WinitNamedKey::BrightnessUp => Key::BrightnessUp,
447            WinitNamedKey::Eject => Key::Eject,
448            WinitNamedKey::LogOff => Key::LogOff,
449            WinitNamedKey::Power => Key::Power,
450            WinitNamedKey::PowerOff => Key::PowerOff,
451            WinitNamedKey::PrintScreen => Key::PrintScreen,
452            WinitNamedKey::Hibernate => Key::Hibernate,
453            WinitNamedKey::Standby => Key::Standby,
454            WinitNamedKey::WakeUp => Key::WakeUp,
455            WinitNamedKey::AllCandidates => Key::AllCandidates,
456            WinitNamedKey::Alphanumeric => Key::Alphanumeric,
457            WinitNamedKey::CodeInput => Key::CodeInput,
458            WinitNamedKey::Compose => Key::Compose,
459            WinitNamedKey::Convert => Key::Convert,
460            WinitNamedKey::FinalMode => Key::FinalMode,
461            WinitNamedKey::GroupFirst => Key::GroupFirst,
462            WinitNamedKey::GroupLast => Key::GroupLast,
463            WinitNamedKey::GroupNext => Key::GroupNext,
464            WinitNamedKey::GroupPrevious => Key::GroupPrevious,
465            WinitNamedKey::ModeChange => Key::ModeChange,
466            WinitNamedKey::NextCandidate => Key::NextCandidate,
467            WinitNamedKey::NonConvert => Key::NonConvert,
468            WinitNamedKey::PreviousCandidate => Key::PreviousCandidate,
469            WinitNamedKey::Process => Key::Process,
470            WinitNamedKey::SingleCandidate => Key::SingleCandidate,
471            WinitNamedKey::HangulMode => Key::HangulMode,
472            WinitNamedKey::HanjaMode => Key::HanjaMode,
473            WinitNamedKey::JunjaMode => Key::JunjaMode,
474            WinitNamedKey::Eisu => Key::Eisu,
475            WinitNamedKey::Hankaku => Key::Hankaku,
476            WinitNamedKey::Hiragana => Key::Hiragana,
477            WinitNamedKey::HiraganaKatakana => Key::HiraganaKatakana,
478            WinitNamedKey::KanaMode => Key::KanaMode,
479            WinitNamedKey::KanjiMode => Key::KanjiMode,
480            WinitNamedKey::Katakana => Key::Katakana,
481            WinitNamedKey::Romaji => Key::Romaji,
482            WinitNamedKey::Zenkaku => Key::Zenkaku,
483            WinitNamedKey::ZenkakuHankaku => Key::ZenkakuHankaku,
484            WinitNamedKey::Soft1 => Key::Soft1,
485            WinitNamedKey::Soft2 => Key::Soft2,
486            WinitNamedKey::Soft3 => Key::Soft3,
487            WinitNamedKey::Soft4 => Key::Soft4,
488            WinitNamedKey::ChannelDown => Key::ChannelDown,
489            WinitNamedKey::ChannelUp => Key::ChannelUp,
490            WinitNamedKey::Close => Key::Close,
491            WinitNamedKey::MailForward => Key::MailForward,
492            WinitNamedKey::MailReply => Key::MailReply,
493            WinitNamedKey::MailSend => Key::MailSend,
494            WinitNamedKey::MediaClose => Key::MediaClose,
495            WinitNamedKey::MediaFastForward => Key::MediaFastForward,
496            WinitNamedKey::MediaPause => Key::MediaPause,
497            WinitNamedKey::MediaPlay => Key::MediaPlay,
498            WinitNamedKey::MediaPlayPause => Key::MediaPlayPause,
499            WinitNamedKey::MediaRecord => Key::MediaRecord,
500            WinitNamedKey::MediaRewind => Key::MediaRewind,
501            WinitNamedKey::MediaStop => Key::MediaStop,
502            WinitNamedKey::MediaTrackNext => Key::MediaTrackNext,
503            WinitNamedKey::MediaTrackPrevious => Key::MediaTrackPrevious,
504            WinitNamedKey::New => Key::New,
505            WinitNamedKey::Open => Key::Open,
506            WinitNamedKey::Print => Key::Print,
507            WinitNamedKey::Save => Key::Save,
508            WinitNamedKey::SpellCheck => Key::SpellCheck,
509            WinitNamedKey::Key11 => Key::Key11,
510            WinitNamedKey::Key12 => Key::Key12,
511            WinitNamedKey::AudioBalanceLeft => Key::AudioBalanceLeft,
512            WinitNamedKey::AudioBalanceRight => Key::AudioBalanceRight,
513            WinitNamedKey::AudioBassBoostDown => Key::AudioBassBoostDown,
514            WinitNamedKey::AudioBassBoostToggle => Key::AudioBassBoostToggle,
515            WinitNamedKey::AudioBassBoostUp => Key::AudioBassBoostUp,
516            WinitNamedKey::AudioFaderFront => Key::AudioFaderFront,
517            WinitNamedKey::AudioFaderRear => Key::AudioFaderRear,
518            WinitNamedKey::AudioSurroundModeNext => Key::AudioSurroundModeNext,
519            WinitNamedKey::AudioTrebleDown => Key::AudioTrebleDown,
520            WinitNamedKey::AudioTrebleUp => Key::AudioTrebleUp,
521            WinitNamedKey::AudioVolumeDown => Key::AudioVolumeDown,
522            WinitNamedKey::AudioVolumeUp => Key::AudioVolumeUp,
523            WinitNamedKey::AudioVolumeMute => Key::AudioVolumeMute,
524            WinitNamedKey::MicrophoneToggle => Key::MicrophoneToggle,
525            WinitNamedKey::MicrophoneVolumeDown => Key::MicrophoneVolumeDown,
526            WinitNamedKey::MicrophoneVolumeUp => Key::MicrophoneVolumeUp,
527            WinitNamedKey::MicrophoneVolumeMute => Key::MicrophoneVolumeMute,
528            WinitNamedKey::SpeechCorrectionList => Key::SpeechCorrectionList,
529            WinitNamedKey::SpeechInputToggle => Key::SpeechInputToggle,
530            WinitNamedKey::LaunchApplication1 => Key::LaunchApplication1,
531            WinitNamedKey::LaunchApplication2 => Key::LaunchApplication2,
532            WinitNamedKey::LaunchCalendar => Key::LaunchCalendar,
533            WinitNamedKey::LaunchContacts => Key::LaunchContacts,
534            WinitNamedKey::LaunchMail => Key::LaunchMail,
535            WinitNamedKey::LaunchMediaPlayer => Key::LaunchMediaPlayer,
536            WinitNamedKey::LaunchMusicPlayer => Key::LaunchMusicPlayer,
537            WinitNamedKey::LaunchPhone => Key::LaunchPhone,
538            WinitNamedKey::LaunchScreenSaver => Key::LaunchScreenSaver,
539            WinitNamedKey::LaunchSpreadsheet => Key::LaunchSpreadsheet,
540            WinitNamedKey::LaunchWebBrowser => Key::LaunchWebBrowser,
541            WinitNamedKey::LaunchWebCam => Key::LaunchWebCam,
542            WinitNamedKey::LaunchWordProcessor => Key::LaunchWordProcessor,
543            WinitNamedKey::BrowserBack => Key::BrowserBack,
544            WinitNamedKey::BrowserFavorites => Key::BrowserFavorites,
545            WinitNamedKey::BrowserForward => Key::BrowserForward,
546            WinitNamedKey::BrowserHome => Key::BrowserHome,
547            WinitNamedKey::BrowserRefresh => Key::BrowserRefresh,
548            WinitNamedKey::BrowserSearch => Key::BrowserSearch,
549            WinitNamedKey::BrowserStop => Key::BrowserStop,
550            WinitNamedKey::AppSwitch => Key::AppSwitch,
551            WinitNamedKey::Call => Key::Call,
552            WinitNamedKey::Camera => Key::Camera,
553            WinitNamedKey::CameraFocus => Key::CameraFocus,
554            WinitNamedKey::EndCall => Key::EndCall,
555            WinitNamedKey::GoBack => Key::GoBack,
556            WinitNamedKey::GoHome => Key::GoHome,
557            WinitNamedKey::HeadsetHook => Key::HeadsetHook,
558            WinitNamedKey::LastNumberRedial => Key::LastNumberRedial,
559            WinitNamedKey::Notification => Key::Notification,
560            WinitNamedKey::MannerMode => Key::MannerMode,
561            WinitNamedKey::VoiceDial => Key::VoiceDial,
562            WinitNamedKey::TV => Key::TV,
563            WinitNamedKey::TV3DMode => Key::TV3DMode,
564            WinitNamedKey::TVAntennaCable => Key::TVAntennaCable,
565            WinitNamedKey::TVAudioDescription => Key::TVAudioDescription,
566            WinitNamedKey::TVAudioDescriptionMixDown => Key::TVAudioDescriptionMixDown,
567            WinitNamedKey::TVAudioDescriptionMixUp => Key::TVAudioDescriptionMixUp,
568            WinitNamedKey::TVContentsMenu => Key::TVContentsMenu,
569            WinitNamedKey::TVDataService => Key::TVDataService,
570            WinitNamedKey::TVInput => Key::TVInput,
571            WinitNamedKey::TVInputComponent1 => Key::TVInputComponent1,
572            WinitNamedKey::TVInputComponent2 => Key::TVInputComponent2,
573            WinitNamedKey::TVInputComposite1 => Key::TVInputComposite1,
574            WinitNamedKey::TVInputComposite2 => Key::TVInputComposite2,
575            WinitNamedKey::TVInputHDMI1 => Key::TVInputHDMI1,
576            WinitNamedKey::TVInputHDMI2 => Key::TVInputHDMI2,
577            WinitNamedKey::TVInputHDMI3 => Key::TVInputHDMI3,
578            WinitNamedKey::TVInputHDMI4 => Key::TVInputHDMI4,
579            WinitNamedKey::TVInputVGA1 => Key::TVInputVGA1,
580            WinitNamedKey::TVMediaContext => Key::TVMediaContext,
581            WinitNamedKey::TVNetwork => Key::TVNetwork,
582            WinitNamedKey::TVNumberEntry => Key::TVNumberEntry,
583            WinitNamedKey::TVPower => Key::TVPower,
584            WinitNamedKey::TVRadioService => Key::TVRadioService,
585            WinitNamedKey::TVSatellite => Key::TVSatellite,
586            WinitNamedKey::TVSatelliteBS => Key::TVSatelliteBS,
587            WinitNamedKey::TVSatelliteCS => Key::TVSatelliteCS,
588            WinitNamedKey::TVSatelliteToggle => Key::TVSatelliteToggle,
589            WinitNamedKey::TVTerrestrialAnalog => Key::TVTerrestrialAnalog,
590            WinitNamedKey::TVTerrestrialDigital => Key::TVTerrestrialDigital,
591            WinitNamedKey::TVTimer => Key::TVTimer,
592            WinitNamedKey::AVRInput => Key::AVRInput,
593            WinitNamedKey::AVRPower => Key::AVRPower,
594            WinitNamedKey::ColorF0Red => Key::ColorF0Red,
595            WinitNamedKey::ColorF1Green => Key::ColorF1Green,
596            WinitNamedKey::ColorF2Yellow => Key::ColorF2Yellow,
597            WinitNamedKey::ColorF3Blue => Key::ColorF3Blue,
598            WinitNamedKey::ColorF4Grey => Key::ColorF4Grey,
599            WinitNamedKey::ColorF5Brown => Key::ColorF5Brown,
600            WinitNamedKey::ClosedCaptionToggle => Key::ClosedCaptionToggle,
601            WinitNamedKey::Dimmer => Key::Dimmer,
602            WinitNamedKey::DisplaySwap => Key::DisplaySwap,
603            WinitNamedKey::DVR => Key::DVR,
604            WinitNamedKey::Exit => Key::Exit,
605            WinitNamedKey::FavoriteClear0 => Key::FavoriteClear0,
606            WinitNamedKey::FavoriteClear1 => Key::FavoriteClear1,
607            WinitNamedKey::FavoriteClear2 => Key::FavoriteClear2,
608            WinitNamedKey::FavoriteClear3 => Key::FavoriteClear3,
609            WinitNamedKey::FavoriteRecall0 => Key::FavoriteRecall0,
610            WinitNamedKey::FavoriteRecall1 => Key::FavoriteRecall1,
611            WinitNamedKey::FavoriteRecall2 => Key::FavoriteRecall2,
612            WinitNamedKey::FavoriteRecall3 => Key::FavoriteRecall3,
613            WinitNamedKey::FavoriteStore0 => Key::FavoriteStore0,
614            WinitNamedKey::FavoriteStore1 => Key::FavoriteStore1,
615            WinitNamedKey::FavoriteStore2 => Key::FavoriteStore2,
616            WinitNamedKey::FavoriteStore3 => Key::FavoriteStore3,
617            WinitNamedKey::Guide => Key::Guide,
618            WinitNamedKey::GuideNextDay => Key::GuideNextDay,
619            WinitNamedKey::GuidePreviousDay => Key::GuidePreviousDay,
620            WinitNamedKey::Info => Key::Info,
621            WinitNamedKey::InstantReplay => Key::InstantReplay,
622            WinitNamedKey::Link => Key::Link,
623            WinitNamedKey::ListProgram => Key::ListProgram,
624            WinitNamedKey::LiveContent => Key::LiveContent,
625            WinitNamedKey::Lock => Key::Lock,
626            WinitNamedKey::MediaApps => Key::MediaApps,
627            WinitNamedKey::MediaAudioTrack => Key::MediaAudioTrack,
628            WinitNamedKey::MediaLast => Key::MediaLast,
629            WinitNamedKey::MediaSkipBackward => Key::MediaSkipBackward,
630            WinitNamedKey::MediaSkipForward => Key::MediaSkipForward,
631            WinitNamedKey::MediaStepBackward => Key::MediaStepBackward,
632            WinitNamedKey::MediaStepForward => Key::MediaStepForward,
633            WinitNamedKey::MediaTopMenu => Key::MediaTopMenu,
634            WinitNamedKey::NavigateIn => Key::NavigateIn,
635            WinitNamedKey::NavigateNext => Key::NavigateNext,
636            WinitNamedKey::NavigateOut => Key::NavigateOut,
637            WinitNamedKey::NavigatePrevious => Key::NavigatePrevious,
638            WinitNamedKey::NextFavoriteChannel => Key::NextFavoriteChannel,
639            WinitNamedKey::NextUserProfile => Key::NextUserProfile,
640            WinitNamedKey::OnDemand => Key::OnDemand,
641            WinitNamedKey::Pairing => Key::Pairing,
642            WinitNamedKey::PinPDown => Key::PinPDown,
643            WinitNamedKey::PinPMove => Key::PinPMove,
644            WinitNamedKey::PinPToggle => Key::PinPToggle,
645            WinitNamedKey::PinPUp => Key::PinPUp,
646            WinitNamedKey::PlaySpeedDown => Key::PlaySpeedDown,
647            WinitNamedKey::PlaySpeedReset => Key::PlaySpeedReset,
648            WinitNamedKey::PlaySpeedUp => Key::PlaySpeedUp,
649            WinitNamedKey::RandomToggle => Key::RandomToggle,
650            WinitNamedKey::RcLowBattery => Key::RcLowBattery,
651            WinitNamedKey::RecordSpeedNext => Key::RecordSpeedNext,
652            WinitNamedKey::RfBypass => Key::RfBypass,
653            WinitNamedKey::ScanChannelsToggle => Key::ScanChannelsToggle,
654            WinitNamedKey::ScreenModeNext => Key::ScreenModeNext,
655            WinitNamedKey::Settings => Key::Settings,
656            WinitNamedKey::SplitScreenToggle => Key::SplitScreenToggle,
657            WinitNamedKey::STBInput => Key::STBInput,
658            WinitNamedKey::STBPower => Key::STBPower,
659            WinitNamedKey::Subtitle => Key::Subtitle,
660            WinitNamedKey::Teletext => Key::Teletext,
661            WinitNamedKey::VideoModeNext => Key::VideoModeNext,
662            WinitNamedKey::Wink => Key::Wink,
663            WinitNamedKey::ZoomToggle => Key::ZoomToggle,
664            WinitNamedKey::F1 => Key::F1,
665            WinitNamedKey::F2 => Key::F2,
666            WinitNamedKey::F3 => Key::F3,
667            WinitNamedKey::F4 => Key::F4,
668            WinitNamedKey::F5 => Key::F5,
669            WinitNamedKey::F6 => Key::F6,
670            WinitNamedKey::F7 => Key::F7,
671            WinitNamedKey::F8 => Key::F8,
672            WinitNamedKey::F9 => Key::F9,
673            WinitNamedKey::F10 => Key::F10,
674            WinitNamedKey::F11 => Key::F11,
675            WinitNamedKey::F12 => Key::F12,
676            WinitNamedKey::F13 => Key::F13,
677            WinitNamedKey::F14 => Key::F14,
678            WinitNamedKey::F15 => Key::F15,
679            WinitNamedKey::F16 => Key::F16,
680            WinitNamedKey::F17 => Key::F17,
681            WinitNamedKey::F18 => Key::F18,
682            WinitNamedKey::F19 => Key::F19,
683            WinitNamedKey::F20 => Key::F20,
684            WinitNamedKey::F21 => Key::F21,
685            WinitNamedKey::F22 => Key::F22,
686            WinitNamedKey::F23 => Key::F23,
687            WinitNamedKey::F24 => Key::F24,
688            WinitNamedKey::F25 => Key::F25,
689            WinitNamedKey::F26 => Key::F26,
690            WinitNamedKey::F27 => Key::F27,
691            WinitNamedKey::F28 => Key::F28,
692            WinitNamedKey::F29 => Key::F29,
693            WinitNamedKey::F30 => Key::F30,
694            WinitNamedKey::F31 => Key::F31,
695            WinitNamedKey::F32 => Key::F32,
696            WinitNamedKey::F33 => Key::F33,
697            WinitNamedKey::F34 => Key::F34,
698            WinitNamedKey::F35 => Key::F35,
699            k => {
700                tracing::error!("matched unknown key code `{k:?}`");
701                Key::Unidentified
702            }
703        },
704        WinitKey::Character(c) => {
705            let mut chars = c.chars();
706            match (chars.next(), chars.next()) {
707                (Some(c), None) => Key::Char(c),
708                _ => Key::Str(c.as_str().to_owned().into()),
709            }
710        }
711        WinitKey::Unidentified(_) => Key::Unidentified,
712        WinitKey::Dead(k) => Key::Dead(k),
713    }
714}
715
716use winit::keyboard::KeyCode as WinitKeyCode;
717pub(crate) fn winit_key_code_to_key_code(key: WinitKeyCode) -> KeyCode {
718    match key {
719        WinitKeyCode::Backquote => KeyCode::Backquote,
720        WinitKeyCode::Backslash => KeyCode::Backslash,
721        WinitKeyCode::BracketLeft => KeyCode::BracketLeft,
722        WinitKeyCode::BracketRight => KeyCode::BracketRight,
723        WinitKeyCode::Comma => KeyCode::Comma,
724        WinitKeyCode::Digit0 => KeyCode::Digit0,
725        WinitKeyCode::Digit1 => KeyCode::Digit1,
726        WinitKeyCode::Digit2 => KeyCode::Digit2,
727        WinitKeyCode::Digit3 => KeyCode::Digit3,
728        WinitKeyCode::Digit4 => KeyCode::Digit4,
729        WinitKeyCode::Digit5 => KeyCode::Digit5,
730        WinitKeyCode::Digit6 => KeyCode::Digit6,
731        WinitKeyCode::Digit7 => KeyCode::Digit7,
732        WinitKeyCode::Digit8 => KeyCode::Digit8,
733        WinitKeyCode::Digit9 => KeyCode::Digit9,
734        WinitKeyCode::Equal => KeyCode::Equal,
735        WinitKeyCode::IntlBackslash => KeyCode::IntlBackslash,
736        WinitKeyCode::IntlRo => KeyCode::IntlRo,
737        WinitKeyCode::IntlYen => KeyCode::IntlYen,
738        WinitKeyCode::KeyA => KeyCode::KeyA,
739        WinitKeyCode::KeyB => KeyCode::KeyB,
740        WinitKeyCode::KeyC => KeyCode::KeyC,
741        WinitKeyCode::KeyD => KeyCode::KeyD,
742        WinitKeyCode::KeyE => KeyCode::KeyE,
743        WinitKeyCode::KeyF => KeyCode::KeyF,
744        WinitKeyCode::KeyG => KeyCode::KeyG,
745        WinitKeyCode::KeyH => KeyCode::KeyH,
746        WinitKeyCode::KeyI => KeyCode::KeyI,
747        WinitKeyCode::KeyJ => KeyCode::KeyJ,
748        WinitKeyCode::KeyK => KeyCode::KeyK,
749        WinitKeyCode::KeyL => KeyCode::KeyL,
750        WinitKeyCode::KeyM => KeyCode::KeyM,
751        WinitKeyCode::KeyN => KeyCode::KeyN,
752        WinitKeyCode::KeyO => KeyCode::KeyO,
753        WinitKeyCode::KeyP => KeyCode::KeyP,
754        WinitKeyCode::KeyQ => KeyCode::KeyQ,
755        WinitKeyCode::KeyR => KeyCode::KeyR,
756        WinitKeyCode::KeyS => KeyCode::KeyS,
757        WinitKeyCode::KeyT => KeyCode::KeyT,
758        WinitKeyCode::KeyU => KeyCode::KeyU,
759        WinitKeyCode::KeyV => KeyCode::KeyV,
760        WinitKeyCode::KeyW => KeyCode::KeyW,
761        WinitKeyCode::KeyX => KeyCode::KeyX,
762        WinitKeyCode::KeyY => KeyCode::KeyY,
763        WinitKeyCode::KeyZ => KeyCode::KeyZ,
764        WinitKeyCode::Minus => KeyCode::Minus,
765        WinitKeyCode::Period => KeyCode::Period,
766        WinitKeyCode::Quote => KeyCode::Quote,
767        WinitKeyCode::Semicolon => KeyCode::Semicolon,
768        WinitKeyCode::Slash => KeyCode::Slash,
769        WinitKeyCode::AltLeft => KeyCode::AltLeft,
770        WinitKeyCode::AltRight => KeyCode::AltRight,
771        WinitKeyCode::Backspace => KeyCode::Backspace,
772        WinitKeyCode::CapsLock => KeyCode::CapsLock,
773        WinitKeyCode::ContextMenu => KeyCode::ContextMenu,
774        WinitKeyCode::ControlLeft => KeyCode::CtrlLeft,
775        WinitKeyCode::ControlRight => KeyCode::CtrlRight,
776        WinitKeyCode::Enter => KeyCode::Enter,
777        WinitKeyCode::SuperLeft => KeyCode::SuperLeft,
778        WinitKeyCode::SuperRight => KeyCode::SuperRight,
779        WinitKeyCode::ShiftLeft => KeyCode::ShiftLeft,
780        WinitKeyCode::ShiftRight => KeyCode::ShiftRight,
781        WinitKeyCode::Space => KeyCode::Space,
782        WinitKeyCode::Tab => KeyCode::Tab,
783        WinitKeyCode::Convert => KeyCode::Convert,
784        WinitKeyCode::KanaMode => KeyCode::KanaMode,
785        WinitKeyCode::Lang1 => KeyCode::Lang1,
786        WinitKeyCode::Lang2 => KeyCode::Lang2,
787        WinitKeyCode::Lang3 => KeyCode::Lang3,
788        WinitKeyCode::Lang4 => KeyCode::Lang4,
789        WinitKeyCode::Lang5 => KeyCode::Lang5,
790        WinitKeyCode::NonConvert => KeyCode::NonConvert,
791        WinitKeyCode::Delete => KeyCode::Delete,
792        WinitKeyCode::End => KeyCode::End,
793        WinitKeyCode::Help => KeyCode::Help,
794        WinitKeyCode::Home => KeyCode::Home,
795        WinitKeyCode::Insert => KeyCode::Insert,
796        WinitKeyCode::PageDown => KeyCode::PageDown,
797        WinitKeyCode::PageUp => KeyCode::PageUp,
798        WinitKeyCode::ArrowDown => KeyCode::ArrowDown,
799        WinitKeyCode::ArrowLeft => KeyCode::ArrowLeft,
800        WinitKeyCode::ArrowRight => KeyCode::ArrowRight,
801        WinitKeyCode::ArrowUp => KeyCode::ArrowUp,
802        WinitKeyCode::NumLock => KeyCode::NumLock,
803        WinitKeyCode::Numpad0 => KeyCode::Numpad0,
804        WinitKeyCode::Numpad1 => KeyCode::Numpad1,
805        WinitKeyCode::Numpad2 => KeyCode::Numpad2,
806        WinitKeyCode::Numpad3 => KeyCode::Numpad3,
807        WinitKeyCode::Numpad4 => KeyCode::Numpad4,
808        WinitKeyCode::Numpad5 => KeyCode::Numpad5,
809        WinitKeyCode::Numpad6 => KeyCode::Numpad6,
810        WinitKeyCode::Numpad7 => KeyCode::Numpad7,
811        WinitKeyCode::Numpad8 => KeyCode::Numpad8,
812        WinitKeyCode::Numpad9 => KeyCode::Numpad9,
813        WinitKeyCode::NumpadAdd => KeyCode::NumpadAdd,
814        WinitKeyCode::NumpadBackspace => KeyCode::NumpadBackspace,
815        WinitKeyCode::NumpadClear => KeyCode::NumpadClear,
816        WinitKeyCode::NumpadClearEntry => KeyCode::NumpadClearEntry,
817        WinitKeyCode::NumpadComma => KeyCode::NumpadComma,
818        WinitKeyCode::NumpadDecimal => KeyCode::NumpadDecimal,
819        WinitKeyCode::NumpadDivide => KeyCode::NumpadDivide,
820        WinitKeyCode::NumpadEnter => KeyCode::NumpadEnter,
821        WinitKeyCode::NumpadEqual => KeyCode::NumpadEqual,
822        WinitKeyCode::NumpadHash => KeyCode::NumpadHash,
823        WinitKeyCode::NumpadMemoryAdd => KeyCode::NumpadMemoryAdd,
824        WinitKeyCode::NumpadMemoryClear => KeyCode::NumpadMemoryClear,
825        WinitKeyCode::NumpadMemoryRecall => KeyCode::NumpadMemoryRecall,
826        WinitKeyCode::NumpadMemoryStore => KeyCode::NumpadMemoryStore,
827        WinitKeyCode::NumpadMemorySubtract => KeyCode::NumpadMemorySubtract,
828        WinitKeyCode::NumpadMultiply => KeyCode::NumpadMultiply,
829        WinitKeyCode::NumpadParenLeft => KeyCode::NumpadParenLeft,
830        WinitKeyCode::NumpadParenRight => KeyCode::NumpadParenRight,
831        WinitKeyCode::NumpadStar => KeyCode::NumpadStar,
832        WinitKeyCode::NumpadSubtract => KeyCode::NumpadSubtract,
833        WinitKeyCode::Escape => KeyCode::Escape,
834        WinitKeyCode::Fn => KeyCode::Fn,
835        WinitKeyCode::FnLock => KeyCode::FnLock,
836        WinitKeyCode::PrintScreen => KeyCode::PrintScreen,
837        WinitKeyCode::ScrollLock => KeyCode::ScrollLock,
838        WinitKeyCode::Pause => KeyCode::Pause,
839        WinitKeyCode::BrowserBack => KeyCode::BrowserBack,
840        WinitKeyCode::BrowserFavorites => KeyCode::BrowserFavorites,
841        WinitKeyCode::BrowserForward => KeyCode::BrowserForward,
842        WinitKeyCode::BrowserHome => KeyCode::BrowserHome,
843        WinitKeyCode::BrowserRefresh => KeyCode::BrowserRefresh,
844        WinitKeyCode::BrowserSearch => KeyCode::BrowserSearch,
845        WinitKeyCode::BrowserStop => KeyCode::BrowserStop,
846        WinitKeyCode::Eject => KeyCode::Eject,
847        WinitKeyCode::LaunchApp1 => KeyCode::LaunchApp1,
848        WinitKeyCode::LaunchApp2 => KeyCode::LaunchApp2,
849        WinitKeyCode::LaunchMail => KeyCode::LaunchMail,
850        WinitKeyCode::MediaPlayPause => KeyCode::MediaPlayPause,
851        WinitKeyCode::MediaSelect => KeyCode::MediaSelect,
852        WinitKeyCode::MediaStop => KeyCode::MediaStop,
853        WinitKeyCode::MediaTrackNext => KeyCode::MediaTrackNext,
854        WinitKeyCode::MediaTrackPrevious => KeyCode::MediaTrackPrevious,
855        WinitKeyCode::Power => KeyCode::Power,
856        WinitKeyCode::Sleep => KeyCode::Sleep,
857        WinitKeyCode::AudioVolumeDown => KeyCode::AudioVolumeDown,
858        WinitKeyCode::AudioVolumeMute => KeyCode::AudioVolumeMute,
859        WinitKeyCode::AudioVolumeUp => KeyCode::AudioVolumeUp,
860        WinitKeyCode::WakeUp => KeyCode::WakeUp,
861        WinitKeyCode::Meta => KeyCode::Meta,
862        WinitKeyCode::Hyper => KeyCode::Hyper,
863        WinitKeyCode::Turbo => KeyCode::Turbo,
864        WinitKeyCode::Abort => KeyCode::Abort,
865        WinitKeyCode::Resume => KeyCode::Resume,
866        WinitKeyCode::Suspend => KeyCode::Suspend,
867        WinitKeyCode::Again => KeyCode::Again,
868        WinitKeyCode::Copy => KeyCode::Copy,
869        WinitKeyCode::Cut => KeyCode::Cut,
870        WinitKeyCode::Find => KeyCode::Find,
871        WinitKeyCode::Open => KeyCode::Open,
872        WinitKeyCode::Paste => KeyCode::Paste,
873        WinitKeyCode::Props => KeyCode::Props,
874        WinitKeyCode::Select => KeyCode::Select,
875        WinitKeyCode::Undo => KeyCode::Undo,
876        WinitKeyCode::Hiragana => KeyCode::Hiragana,
877        WinitKeyCode::Katakana => KeyCode::Katakana,
878        WinitKeyCode::F1 => KeyCode::F1,
879        WinitKeyCode::F2 => KeyCode::F2,
880        WinitKeyCode::F3 => KeyCode::F3,
881        WinitKeyCode::F4 => KeyCode::F4,
882        WinitKeyCode::F5 => KeyCode::F5,
883        WinitKeyCode::F6 => KeyCode::F6,
884        WinitKeyCode::F7 => KeyCode::F7,
885        WinitKeyCode::F8 => KeyCode::F8,
886        WinitKeyCode::F9 => KeyCode::F9,
887        WinitKeyCode::F10 => KeyCode::F10,
888        WinitKeyCode::F11 => KeyCode::F11,
889        WinitKeyCode::F12 => KeyCode::F12,
890        WinitKeyCode::F13 => KeyCode::F13,
891        WinitKeyCode::F14 => KeyCode::F14,
892        WinitKeyCode::F15 => KeyCode::F15,
893        WinitKeyCode::F16 => KeyCode::F16,
894        WinitKeyCode::F17 => KeyCode::F17,
895        WinitKeyCode::F18 => KeyCode::F18,
896        WinitKeyCode::F19 => KeyCode::F19,
897        WinitKeyCode::F20 => KeyCode::F20,
898        WinitKeyCode::F21 => KeyCode::F21,
899        WinitKeyCode::F22 => KeyCode::F22,
900        WinitKeyCode::F23 => KeyCode::F23,
901        WinitKeyCode::F24 => KeyCode::F24,
902        WinitKeyCode::F25 => KeyCode::F25,
903        WinitKeyCode::F26 => KeyCode::F26,
904        WinitKeyCode::F27 => KeyCode::F27,
905        WinitKeyCode::F28 => KeyCode::F28,
906        WinitKeyCode::F29 => KeyCode::F29,
907        WinitKeyCode::F30 => KeyCode::F30,
908        WinitKeyCode::F31 => KeyCode::F31,
909        WinitKeyCode::F32 => KeyCode::F32,
910        WinitKeyCode::F33 => KeyCode::F33,
911        WinitKeyCode::F34 => KeyCode::F34,
912        WinitKeyCode::F35 => KeyCode::F35,
913        key => {
914            tracing::error!("matched unknown key code `{key:?}`");
915            KeyCode::Unidentified(NativeKeyCode::Unidentified)
916        }
917    }
918}
919
920use winit::keyboard::PhysicalKey as WinitPhysicalKey;
921pub(crate) fn winit_physical_key_to_key_code(key: WinitPhysicalKey) -> KeyCode {
922    match key {
923        WinitPhysicalKey::Code(code) => winit_key_code_to_key_code(code),
924        WinitPhysicalKey::Unidentified(u) => match u {
925            winit::keyboard::NativeKeyCode::Unidentified => KeyCode::Unidentified(NativeKeyCode::Unidentified),
926            winit::keyboard::NativeKeyCode::Android(c) => KeyCode::Unidentified(NativeKeyCode::Android(c)),
927            winit::keyboard::NativeKeyCode::MacOS(c) => KeyCode::Unidentified(NativeKeyCode::MacOS(c)),
928            winit::keyboard::NativeKeyCode::Windows(c) => KeyCode::Unidentified(NativeKeyCode::Windows(c)),
929            winit::keyboard::NativeKeyCode::Xkb(c) => KeyCode::Unidentified(NativeKeyCode::Xkb(c)),
930        },
931    }
932}
933
934thread_local! {
935    static SUPPRESS: Cell<bool> = const { Cell::new(false) };
936    static SUPPRESSED_PANIC: RefCell<Option<SuppressedPanic>> = const { RefCell::new(None) };
937}
938
939/// If `true` our custom panic hook must not log anything.
940#[cfg(ipc)]
941pub(crate) fn suppress_panic() -> bool {
942    SUPPRESS.get()
943}
944#[cfg(ipc)]
945pub(crate) fn set_suppressed_panic(panic: SuppressedPanic) {
946    SUPPRESSED_PANIC.set(Some(panic));
947}
948
949#[derive(Debug)]
950pub(crate) struct SuppressedPanic {
951    pub thread: Txt,
952    pub msg: Txt,
953    pub file: Txt,
954    pub line: u32,
955    pub column: u32,
956    pub backtrace: Backtrace,
957}
958impl SuppressedPanic {
959    #[cfg(ipc)]
960    pub fn from_hook(info: &std::panic::PanicHookInfo, backtrace: Backtrace) -> Self {
961        let current_thread = std::thread::current();
962        let thread = current_thread.name().unwrap_or("<unnamed>");
963        let msg = Self::payload(info.payload());
964
965        let (file, line, column) = if let Some(l) = info.location() {
966            (l.file(), l.line(), l.column())
967        } else {
968            ("<unknown>", 0, 0)
969        };
970        Self {
971            thread: thread.to_txt(),
972            msg,
973            file: file.to_txt(),
974            line,
975            column,
976            backtrace,
977        }
978    }
979
980    pub fn from_catch(p: Box<dyn Any>) -> Self {
981        Self {
982            thread: Txt::from("<unknown>"),
983            msg: Self::payload(&*p),
984            file: Txt::from("<unknown>"),
985            line: 0,
986            column: 0,
987            backtrace: Backtrace::disabled(),
988        }
989    }
990
991    fn payload(p: &dyn Any) -> Txt {
992        match p.downcast_ref::<&'static str>() {
993            Some(s) => s,
994            None => match p.downcast_ref::<String>() {
995                Some(s) => &s[..],
996                None => "Box<dyn Any>",
997            },
998        }
999        .to_txt()
1000    }
1001}
1002impl std::error::Error for SuppressedPanic {}
1003impl fmt::Display for SuppressedPanic {
1004    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1005        write!(
1006            f,
1007            "thread '{}' panicked at {}:{}:{}:\n{}\n{}",
1008            self.thread, self.file, self.line, self.column, self.msg, self.backtrace,
1009        )
1010    }
1011}
1012
1013/// Like [`std::panic::catch_unwind`], but flags [`suppress_panic`] for our custom panic hook.
1014pub(crate) fn catch_suppress<T>(f: impl FnOnce() -> T + std::panic::UnwindSafe) -> Result<T, Box<SuppressedPanic>> {
1015    SUPPRESS.set(true);
1016    let _cleanup = RunOnDrop::new(|| SUPPRESS.set(false));
1017    std::panic::catch_unwind(f).map_err(|e| {
1018        SUPPRESSED_PANIC.with_borrow_mut(|p| match p.take() {
1019            Some(p) => Box::new(p),
1020            None => Box::new(SuppressedPanic::from_catch(e)),
1021        })
1022    })
1023}
1024
1025struct RunOnDrop<F: FnOnce()>(Option<F>);
1026impl<F: FnOnce()> RunOnDrop<F> {
1027    pub fn new(clean: F) -> Self {
1028        RunOnDrop(Some(clean))
1029    }
1030}
1031impl<F: FnOnce()> Drop for RunOnDrop<F> {
1032    fn drop(&mut self) {
1033        if let Some(clean) = self.0.take() {
1034            clean();
1035        }
1036    }
1037}
1038
1039#[cfg(windows)]
1040pub(crate) extern "system" fn minimal_wndproc(
1041    window: windows_sys::Win32::Foundation::HWND,
1042    message: u32,
1043    wparam: windows_sys::Win32::Foundation::WPARAM,
1044    lparam: windows_sys::Win32::Foundation::LPARAM,
1045) -> windows_sys::Win32::Foundation::LRESULT {
1046    unsafe { windows_sys::Win32::UI::WindowsAndMessaging::DefWindowProcW(window, message, wparam, lparam) }
1047}
1048
1049#[cfg(windows)]
1050pub fn get_instance_handle() -> isize {
1051    // HINSTANCE
1052    // Gets the instance handle by taking the address of the
1053    // pseudo-variable created by the Microsoft linker:
1054    // https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483
1055
1056    // This is preferred over GetModuleHandle(NULL) because it also works in DLLs:
1057    // https://stackoverflow.com/questions/21718027/getmodulehandlenull-vs-hinstance
1058
1059    unsafe extern "C" {
1060        static __ImageBase: windows_sys::Win32::System::SystemServices::IMAGE_DOS_HEADER;
1061    }
1062
1063    unsafe { (&__ImageBase) as *const _ as _ }
1064}
1065
1066#[cfg(windows)]
1067pub mod taskbar_com {
1068    // copied from winit
1069
1070    #![expect(non_snake_case)]
1071    #![expect(non_upper_case_globals)]
1072
1073    use std::ffi::c_void;
1074
1075    use windows_sys::{
1076        Win32::Foundation::{BOOL, HWND},
1077        core::{GUID, HRESULT, IUnknown},
1078    };
1079
1080    #[repr(C)]
1081    pub struct IUnknownVtbl {
1082        pub QueryInterface: unsafe extern "system" fn(This: *mut IUnknown, riid: *const GUID, ppvObject: *mut *mut c_void) -> HRESULT,
1083        pub AddRef: unsafe extern "system" fn(This: *mut IUnknown) -> u32,
1084        pub Release: unsafe extern "system" fn(This: *mut IUnknown) -> u32,
1085    }
1086
1087    #[repr(C)]
1088    pub struct ITaskbarListVtbl {
1089        pub parent: IUnknownVtbl,
1090        pub HrInit: unsafe extern "system" fn(This: *mut ITaskbarList) -> HRESULT,
1091        pub AddTab: unsafe extern "system" fn(This: *mut ITaskbarList, hwnd: HWND) -> HRESULT,
1092        pub DeleteTab: unsafe extern "system" fn(This: *mut ITaskbarList, hwnd: HWND) -> HRESULT,
1093        pub ActivateTab: unsafe extern "system" fn(This: *mut ITaskbarList, hwnd: HWND) -> HRESULT,
1094        pub SetActiveAlt: unsafe extern "system" fn(This: *mut ITaskbarList, hwnd: HWND) -> HRESULT,
1095    }
1096
1097    #[repr(C)]
1098    pub struct ITaskbarList {
1099        pub lpVtbl: *const ITaskbarListVtbl,
1100    }
1101
1102    #[repr(C)]
1103    pub struct ITaskbarList2Vtbl {
1104        pub parent: ITaskbarListVtbl,
1105        pub MarkFullscreenWindow: unsafe extern "system" fn(This: *mut ITaskbarList2, hwnd: HWND, fFullscreen: BOOL) -> HRESULT,
1106    }
1107
1108    #[repr(C)]
1109    pub struct ITaskbarList2 {
1110        pub lpVtbl: *const ITaskbarList2Vtbl,
1111    }
1112
1113    pub const CLSID_TaskbarList: GUID = GUID {
1114        data1: 0x56fdf344,
1115        data2: 0xfd6d,
1116        data3: 0x11d0,
1117        data4: [0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90],
1118    };
1119
1120    pub const IID_ITaskbarList2: GUID = GUID {
1121        data1: 0x602d4995,
1122        data2: 0xb13a,
1123        data3: 0x429b,
1124        data4: [0xa6, 0x6e, 0x19, 0x35, 0xe4, 0x4f, 0x43, 0x17],
1125    };
1126}
1127
1128pub(crate) fn wr_workers() -> Arc<rayon::ThreadPool> {
1129    // see: webrender/src/renderer/init.rs#L547
1130    //
1131    // we need the workers instance before renderer init for the extensions, but this
1132    // means that we removed some Webrender profiler instrumentation.
1133    let worker = ThreadPoolBuilder::new().thread_name(|idx| format!("WRWorker#{}", idx)).build();
1134    Arc::new(worker.unwrap())
1135}
1136
1137#[cfg(not(any(windows, target_os = "android")))]
1138pub(crate) fn arboard_to_clip(e: arboard::Error) -> clipboard_api::ClipboardError {
1139    match e {
1140        arboard::Error::ContentNotAvailable => clipboard_api::ClipboardError::NotFound,
1141        arboard::Error::ClipboardNotSupported => clipboard_api::ClipboardError::NotSupported,
1142        e => clipboard_api::ClipboardError::Other(zng_txt::formatx!("{e:?}")),
1143    }
1144}
1145
1146#[cfg(windows)]
1147pub(crate) fn clipboard_win_to_clip(e: clipboard_win::ErrorCode) -> clipboard_api::ClipboardError {
1148    use zng_txt::formatx;
1149
1150    if e.raw_code() == 0 {
1151        // If GetClipboardData fails it returns a NULL, but GetLastError sometimes (always?) returns 0 (ERROR_SUCCESS)
1152        clipboard_api::ClipboardError::NotFound
1153    } else {
1154        clipboard_api::ClipboardError::Other(formatx!("{e:?}"))
1155    }
1156}
1157
1158fn accesskit_to_px(length: f64) -> Px {
1159    Px(length.round() as _)
1160}
1161
1162fn accesskit_point_to_px(p: accesskit::Point) -> PxPoint {
1163    PxPoint::new(accesskit_to_px(p.x), accesskit_to_px(p.y))
1164}
1165
1166pub(crate) fn accesskit_to_event(
1167    window_id: zng_view_api::window::WindowId,
1168    request: accesskit::ActionRequest,
1169) -> Option<zng_view_api::Event> {
1170    use accesskit::Action;
1171    use zng_view_api::access::*;
1172
1173    let target = AccessNodeId(request.target.0);
1174
1175    Some(zng_view_api::Event::AccessCommand {
1176        window: window_id,
1177        target,
1178        command: match request.action {
1179            Action::Click => AccessCmd::Click(true),
1180            Action::ShowContextMenu => AccessCmd::Click(false),
1181            Action::Focus => AccessCmd::Focus(true),
1182            Action::SetSequentialFocusNavigationStartingPoint => AccessCmd::FocusNavOrigin,
1183            Action::Blur => AccessCmd::Focus(false),
1184            Action::Collapse => AccessCmd::SetExpanded(false),
1185            Action::Expand => AccessCmd::SetExpanded(true),
1186            Action::CustomAction => return None,
1187            Action::Decrement => AccessCmd::Increment(-1),
1188            Action::Increment => AccessCmd::Increment(1),
1189            Action::HideTooltip => AccessCmd::SetToolTipVis(false),
1190            Action::ShowTooltip => AccessCmd::SetToolTipVis(true),
1191            Action::ReplaceSelectedText => {
1192                if let Some(accesskit::ActionData::Value(s)) = request.data {
1193                    AccessCmd::ReplaceSelectedText(Txt::from_str(&s))
1194                } else {
1195                    AccessCmd::ReplaceSelectedText(Txt::from_str(""))
1196                }
1197            }
1198            Action::ScrollBackward => AccessCmd::Scroll(ScrollCmd::PageUp),
1199            Action::ScrollForward => AccessCmd::Scroll(ScrollCmd::PageDown),
1200
1201            Action::ScrollDown => AccessCmd::Scroll(ScrollCmd::PageDown),
1202            Action::ScrollLeft => AccessCmd::Scroll(ScrollCmd::PageLeft),
1203            Action::ScrollRight => AccessCmd::Scroll(ScrollCmd::PageRight),
1204            Action::ScrollUp => AccessCmd::Scroll(ScrollCmd::PageUp),
1205            Action::ScrollIntoView => {
1206                if let Some(accesskit::ActionData::ScrollTargetRect(r)) = request.data {
1207                    let r = PxRect::new(
1208                        accesskit_point_to_px(r.origin()),
1209                        PxSize::new(accesskit_to_px(r.width()), accesskit_to_px(r.height())),
1210                    );
1211                    AccessCmd::Scroll(ScrollCmd::ScrollToRect(r))
1212                } else {
1213                    AccessCmd::Scroll(ScrollCmd::ScrollTo)
1214                }
1215            }
1216            Action::ScrollToPoint => {
1217                if let Some(accesskit::ActionData::ScrollToPoint(p)) = request.data {
1218                    AccessCmd::Scroll(ScrollCmd::ScrollToRect(PxRect::new(accesskit_point_to_px(p), PxSize::splat(Px(1)))))
1219                } else {
1220                    return None;
1221                }
1222            }
1223            Action::SetScrollOffset => {
1224                if let Some(accesskit::ActionData::SetScrollOffset(o)) = request.data {
1225                    AccessCmd::Scroll(ScrollCmd::ScrollToRect(PxRect::new(accesskit_point_to_px(o), PxSize::splat(Px(1)))))
1226                } else {
1227                    return None;
1228                }
1229            }
1230            Action::SetTextSelection => {
1231                if let Some(accesskit::ActionData::SetTextSelection(s)) = request.data {
1232                    AccessCmd::SelectText {
1233                        start: (AccessNodeId(s.anchor.node.0), s.anchor.character_index),
1234                        caret: (AccessNodeId(s.focus.node.0), s.focus.character_index),
1235                    }
1236                } else {
1237                    return None;
1238                }
1239            }
1240            Action::SetValue => match request.data {
1241                Some(accesskit::ActionData::Value(s)) => AccessCmd::SetString(Txt::from_str(&s)),
1242                Some(accesskit::ActionData::NumericValue(n)) => AccessCmd::SetNumber(n),
1243                _ => return None,
1244            },
1245        },
1246    })
1247}
1248
1249pub(crate) fn access_tree_update_to_kit(update: zng_view_api::access::AccessTreeUpdate) -> accesskit::TreeUpdate {
1250    let mut nodes = Vec::with_capacity(update.updates.iter().map(|t| t.len()).sum());
1251
1252    for update in update.updates {
1253        access_node_to_kit(update.root(), &mut nodes);
1254    }
1255
1256    accesskit::TreeUpdate {
1257        nodes,
1258        tree: update.full_root.map(|id| accesskit::Tree::new(access_id_to_kit(id))),
1259        focus: access_id_to_kit(update.focused),
1260    }
1261}
1262
1263fn access_node_to_kit(
1264    node: zng_view_api::access::AccessNodeRef,
1265    output: &mut Vec<(accesskit::NodeId, accesskit::Node)>,
1266) -> accesskit::NodeId {
1267    let node_id = access_id_to_kit(node.id);
1268    let node_role = node.role.map(access_role_to_kit).unwrap_or(accesskit::Role::Unknown);
1269    let mut builder = accesskit::Node::new(node_role);
1270
1271    // add bounds and transform
1272    if !node.size.is_empty() {
1273        let mut bounds = accesskit::Rect::new(0.0, 0.0, node.size.width.0 as f64, node.size.height.0 as f64);
1274        if !node.transform.is_identity() {
1275            if let (0, PxTransform::Offset(o)) = (node.children_count(), node.transform) {
1276                let (x, y) = o.cast().to_tuple();
1277                bounds = bounds.with_origin(accesskit::Point::new(x, y));
1278            } else {
1279                let t = node.transform.to_transform().to_2d();
1280                builder.set_transform(accesskit::Affine::new(t.cast().to_array()));
1281            }
1282        }
1283        builder.set_bounds(bounds);
1284    }
1285
1286    // add actions
1287    for cmd in &node.commands {
1288        use zng_view_api::access::AccessCmdName::*;
1289
1290        match cmd {
1291            Click => {
1292                builder.add_action(accesskit::Action::Click);
1293                builder.add_action(accesskit::Action::ShowContextMenu);
1294            }
1295            Focus => {
1296                builder.add_action(accesskit::Action::Focus);
1297                builder.add_action(accesskit::Action::Blur);
1298            }
1299            SetExpanded => {
1300                builder.add_action(accesskit::Action::Expand);
1301                builder.add_action(accesskit::Action::Collapse);
1302            }
1303            Increment => {
1304                builder.add_action(accesskit::Action::Increment);
1305            }
1306            SetToolTipVis => {
1307                builder.add_action(accesskit::Action::ShowTooltip);
1308                builder.add_action(accesskit::Action::HideTooltip);
1309            }
1310            Scroll => {
1311                builder.add_action(accesskit::Action::ScrollBackward);
1312                builder.add_action(accesskit::Action::ScrollUp);
1313                builder.add_action(accesskit::Action::ScrollLeft);
1314                builder.add_action(accesskit::Action::ScrollForward);
1315                builder.add_action(accesskit::Action::ScrollDown);
1316                builder.add_action(accesskit::Action::ScrollRight);
1317                builder.add_action(accesskit::Action::ScrollIntoView);
1318                builder.add_action(accesskit::Action::ScrollToPoint);
1319            }
1320            ReplaceSelectedText => {
1321                builder.add_action(accesskit::Action::ReplaceSelectedText);
1322            }
1323            SelectText => {
1324                builder.add_action(accesskit::Action::SetTextSelection);
1325            }
1326            SetString => {
1327                builder.add_action(accesskit::Action::SetValue);
1328            }
1329            SetNumber => {
1330                builder.add_action(accesskit::Action::SetValue);
1331            }
1332            _ => {}
1333        }
1334    }
1335
1336    // add state
1337    for state in &node.state {
1338        use zng_view_api::access::{self, AccessState::*};
1339
1340        match state {
1341            AutoComplete(s) => {
1342                if *s == access::AutoComplete::BOTH {
1343                    builder.set_auto_complete(accesskit::AutoComplete::Both)
1344                } else if *s == access::AutoComplete::INLINE {
1345                    builder.set_auto_complete(accesskit::AutoComplete::Inline)
1346                } else if *s == access::AutoComplete::LIST {
1347                    builder.set_auto_complete(accesskit::AutoComplete::List)
1348                }
1349            }
1350            Checked(b) => builder.set_toggled(match b {
1351                Some(true) => accesskit::Toggled::True,
1352                Some(false) => accesskit::Toggled::False,
1353                None => accesskit::Toggled::Mixed,
1354            }),
1355            Current(kind) => match kind {
1356                access::CurrentKind::Page => builder.set_aria_current(accesskit::AriaCurrent::Page),
1357                access::CurrentKind::Step => builder.set_aria_current(accesskit::AriaCurrent::Step),
1358                access::CurrentKind::Location => builder.set_aria_current(accesskit::AriaCurrent::Location),
1359                access::CurrentKind::Date => builder.set_aria_current(accesskit::AriaCurrent::Date),
1360                access::CurrentKind::Time => builder.set_aria_current(accesskit::AriaCurrent::Time),
1361                access::CurrentKind::Item => builder.set_aria_current(accesskit::AriaCurrent::True),
1362            },
1363            Disabled => builder.set_disabled(),
1364            ErrorMessage(id) => builder.set_error_message(access_id_to_kit(*id)),
1365            Expanded(b) => builder.set_expanded(*b),
1366            Popup(pop) => match pop {
1367                access::Popup::Menu => builder.set_has_popup(accesskit::HasPopup::Menu),
1368                access::Popup::ListBox => builder.set_has_popup(accesskit::HasPopup::Listbox),
1369                access::Popup::Tree => builder.set_has_popup(accesskit::HasPopup::Tree),
1370                access::Popup::Grid => builder.set_has_popup(accesskit::HasPopup::Grid),
1371                access::Popup::Dialog => builder.set_has_popup(accesskit::HasPopup::Dialog),
1372            },
1373            Invalid(i) => {
1374                if i.contains(access::Invalid::SPELLING) {
1375                    builder.set_invalid(accesskit::Invalid::Spelling)
1376                } else if i.contains(access::Invalid::GRAMMAR) {
1377                    builder.set_invalid(accesskit::Invalid::Grammar)
1378                } else if i.contains(access::Invalid::ANY) {
1379                    builder.set_invalid(accesskit::Invalid::True)
1380                }
1381            }
1382            Label(s) => builder.set_label(s.clone().into_owned().into_boxed_str()),
1383            Level(n) => builder.set_level(n.get() as usize),
1384            Modal => builder.set_modal(),
1385            MultiSelectable => builder.set_multiselectable(),
1386            Orientation(o) => match o {
1387                access::Orientation::Horizontal => builder.set_orientation(accesskit::Orientation::Horizontal),
1388                access::Orientation::Vertical => builder.set_orientation(accesskit::Orientation::Vertical),
1389            },
1390            Placeholder(p) => builder.set_placeholder(p.clone().into_owned().into_boxed_str()),
1391            ReadOnly => builder.set_read_only(),
1392            Required => builder.set_required(),
1393            Selected => builder.set_selected(true),
1394            Sort(o) => match o {
1395                access::SortDirection::Ascending => builder.set_sort_direction(accesskit::SortDirection::Ascending),
1396                access::SortDirection::Descending => builder.set_sort_direction(accesskit::SortDirection::Descending),
1397            },
1398            ValueMax(m) => builder.set_max_numeric_value(*m),
1399            ValueMin(m) => builder.set_min_numeric_value(*m),
1400            Value(v) => builder.set_numeric_value(*v),
1401            ValueText(v) => builder.set_value(v.clone().into_owned().into_boxed_str()),
1402            Live { indicator, atomic, busy } => {
1403                builder.set_live(match indicator {
1404                    access::LiveIndicator::Assertive => accesskit::Live::Assertive,
1405                    access::LiveIndicator::OnlyFocused => accesskit::Live::Off,
1406                    access::LiveIndicator::Polite => accesskit::Live::Polite,
1407                });
1408                if *atomic {
1409                    builder.set_live_atomic();
1410                }
1411                if *busy {
1412                    builder.set_busy();
1413                }
1414            }
1415            ActiveDescendant(id) => builder.set_active_descendant(access_id_to_kit(*id)),
1416            ColCount(c) => builder.set_column_count(*c),
1417            ColIndex(i) => builder.set_column_index(*i),
1418            ColSpan(s) => builder.set_column_span(*s),
1419            Controls(ids) => builder.set_controls(ids.iter().copied().map(access_id_to_kit).collect::<Vec<_>>()),
1420            DescribedBy(ids) => builder.set_described_by(ids.iter().copied().map(access_id_to_kit).collect::<Vec<_>>()),
1421            Details(ids) => builder.set_details(ids.iter().copied().map(access_id_to_kit).collect::<Vec<_>>()),
1422            FlowTo(ids) => builder.set_flow_to(ids.iter().copied().map(access_id_to_kit).collect::<Vec<_>>()),
1423            LabelledBy(ids) => builder.set_labelled_by(ids.iter().copied().map(access_id_to_kit).collect::<Vec<_>>()),
1424            LabelledByChild => {
1425                let labelled_by: Vec<_> = if node.children.is_empty() {
1426                    node.children().map(|c| access_id_to_kit(c.id)).collect()
1427                } else {
1428                    node.children.iter().map(|id| access_id_to_kit(*id)).collect()
1429                };
1430                builder.set_labelled_by(labelled_by);
1431            }
1432            Owns(ids) => {
1433                for id in ids {
1434                    builder.push_child(access_id_to_kit(*id));
1435                }
1436            }
1437            ItemIndex(p) => builder.set_position_in_set(*p),
1438            RowCount(c) => builder.set_row_count(*c),
1439            RowIndex(i) => builder.set_row_index(*i),
1440            RowSpan(s) => builder.set_row_span(*s),
1441            ItemCount(s) => builder.set_size_of_set(*s),
1442            Lang(l) => builder.set_language(l.to_string()),
1443
1444            ScrollHorizontal(x) => {
1445                builder.set_scroll_x(*x as f64);
1446                builder.set_scroll_x_min(0.0);
1447                builder.set_scroll_x_max(1.0);
1448            }
1449            ScrollVertical(y) => {
1450                builder.set_scroll_y(*y as f64);
1451                builder.set_scroll_y_min(0.0);
1452                builder.set_scroll_y_max(1.0);
1453            }
1454            _ => {}
1455        }
1456    }
1457
1458    // add descendants
1459    if node.children.is_empty() {
1460        for child in node.children() {
1461            let child_id = access_node_to_kit(child, output);
1462            builder.push_child(child_id);
1463        }
1464    } else {
1465        for id in &node.children {
1466            builder.push_child(access_id_to_kit(*id));
1467        }
1468        for child in node.children() {
1469            let _ = access_node_to_kit(child, output);
1470        }
1471    }
1472
1473    let node = builder;
1474    output.push((node_id, node));
1475    node_id
1476}
1477
1478fn access_id_to_kit(id: AccessNodeId) -> accesskit::NodeId {
1479    accesskit::NodeId(id.0)
1480}
1481
1482fn access_role_to_kit(role: zng_view_api::access::AccessRole) -> accesskit::Role {
1483    use accesskit::Role;
1484    use zng_view_api::access::AccessRole::*;
1485    match role {
1486        Button => Role::Button,
1487        CheckBox => Role::CheckBox,
1488        GridCell => Role::Cell,
1489        Link => Role::Link,
1490        MenuItem => Role::MenuItem,
1491        MenuItemCheckBox => Role::MenuItemCheckBox,
1492        MenuItemRadio => Role::MenuItemRadio,
1493        Option => Role::ListBoxOption,
1494        ProgressBar => Role::ProgressIndicator,
1495        Radio => Role::RadioButton,
1496        ScrollBar => Role::ScrollBar,
1497        SearchBox => Role::SearchInput,
1498        Slider => Role::Slider,
1499        SpinButton => Role::SpinButton,
1500        Switch => Role::Switch,
1501        Tab => Role::Tab,
1502        TabPanel => Role::TabPanel,
1503        TextInput => Role::TextInput,
1504        TreeItem => Role::TreeItem,
1505        ComboBox => Role::ComboBox,
1506        Grid => Role::Grid,
1507        ListBox => Role::ListBox,
1508        Menu => Role::Menu,
1509        MenuBar => Role::MenuBar,
1510        RadioGroup => Role::RadioGroup,
1511        TabList => Role::TabList,
1512        Tree => Role::Tree,
1513        TreeGrid => Role::TreeGrid,
1514        Application => Role::Application,
1515        Article => Role::Article,
1516        Cell => Role::Cell,
1517        ColumnHeader => Role::ColumnHeader,
1518        Definition => Role::Definition,
1519        Document => Role::Document,
1520        Feed => Role::Feed,
1521        Figure => Role::Figure,
1522        Group => Role::Group,
1523        Heading => Role::Heading,
1524        Image => Role::Image,
1525        List => Role::List,
1526        ListItem => Role::ListItem,
1527        Math => Role::Math,
1528        Note => Role::Note,
1529        Row => Role::Row,
1530        RowGroup => Role::RowGroup,
1531        RowHeader => Role::RowHeader,
1532        Separator => Role::Splitter,
1533        Table => Role::Table,
1534        Term => Role::Term,
1535        ToolBar => Role::Toolbar,
1536        ToolTip => Role::Tooltip,
1537        Banner => Role::Banner,
1538        Complementary => Role::Complementary,
1539        ContentInfo => Role::ContentInfo,
1540        Form => Role::Form,
1541        Main => Role::Main,
1542        Navigation => Role::Navigation,
1543        Region => Role::Region,
1544        Search => Role::Search,
1545        Alert => Role::Alert,
1546        Log => Role::Log,
1547        Marquee => Role::Marquee,
1548        Status => Role::Status,
1549        Timer => Role::Timer,
1550        AlertDialog => Role::AlertDialog,
1551        Dialog => Role::Dialog,
1552        _ => Role::Unknown,
1553    }
1554}
1555
1556pub(crate) fn frame_render_reasons(frame: &FrameRequest) -> wr::RenderReasons {
1557    let mut reasons = wr::RenderReasons::SCENE;
1558
1559    if frame.capture != FrameCapture::None {
1560        reasons |= wr::RenderReasons::SNAPSHOT;
1561    }
1562
1563    reasons
1564}
1565
1566pub(crate) fn frame_update_render_reasons(update: &FrameUpdateRequest) -> wr::RenderReasons {
1567    let mut reasons = wr::RenderReasons::empty();
1568
1569    if update.has_bounds() {
1570        reasons |= wr::RenderReasons::ANIMATED_PROPERTY;
1571    }
1572
1573    if update.capture != FrameCapture::None {
1574        reasons |= wr::RenderReasons::SNAPSHOT;
1575    }
1576
1577    if update.clear_color.is_some() {
1578        reasons |= wr::RenderReasons::CONFIG_CHANGE;
1579    }
1580
1581    reasons
1582}
1583
1584#[must_use = "call unset before drop"]
1585pub(crate) struct WinitEventLoop(*const ActiveEventLoop);
1586impl WinitEventLoop {
1587    pub fn set<'l>(&mut self, winit_loop: &'l ActiveEventLoop) -> WinitEventLoopGuard<'l> {
1588        self.0 = winit_loop;
1589        WinitEventLoopGuard {
1590            defused: false,
1591            _loop_lifetime: PhantomData,
1592        }
1593    }
1594}
1595impl Default for WinitEventLoop {
1596    fn default() -> Self {
1597        Self(std::ptr::null())
1598    }
1599}
1600impl ops::Deref for WinitEventLoop {
1601    type Target = ActiveEventLoop;
1602
1603    fn deref(&self) -> &Self::Target {
1604        assert!(!self.0.is_null(), "winit event loop not active");
1605        // SAFETY: just checked, and can only set pointer with `set`
1606        unsafe { &*self.0 }
1607    }
1608}
1609pub(crate) struct WinitEventLoopGuard<'l> {
1610    defused: bool,
1611    _loop_lifetime: PhantomData<&'l ActiveEventLoop>,
1612}
1613impl WinitEventLoopGuard<'_> {
1614    pub fn unset(&mut self, l: &mut WinitEventLoop) {
1615        self.defused = true;
1616        l.0 = std::ptr::null();
1617    }
1618}
1619impl Drop for WinitEventLoopGuard<'_> {
1620    fn drop(&mut self) {
1621        if !self.defused {
1622            let msg = "unsafe pointer to winit ActiveEventLoop not cleared";
1623            if std::thread::panicking() {
1624                tracing::error!("{msg}");
1625            } else {
1626                panic!("{msg}");
1627            }
1628        }
1629    }
1630}