Skip to main content

azul_core/
window.rs

1//! Window configuration types, input state, and platform-specific options.
2//!
3//! This module defines the core types used by the windowing system:
4//!
5//! - **Window configuration**: [`WindowSize`], [`WindowFlags`], [`WindowPosition`],
6//!   [`RendererOptions`], [`PlatformSpecificOptions`]
7//! - **Input state**: [`KeyboardState`], [`MouseState`], [`TouchState`], [`CursorPosition`]
8//! - **Monitor/display info**: [`Monitor`], [`MonitorId`], [`VideoMode`]
9//! - **Virtual key codes**: [`VirtualKeyCode`], [`ScanCode`]
10//! - **Window icons**: [`WindowIcon`], [`TaskBarIcon`]
11//! - **Platform options**: [`WindowsWindowOptions`], [`LinuxWindowOptions`],
12//!   [`MacWindowOptions`], [`WasmWindowOptions`]
13//!
14//! These types are consumed by the platform shell backends in
15//! `dll/src/desktop/shell2/{windows,macos,linux}/` and by
16//! `layout/src/window_state.rs` for state management.
17
18#[cfg(not(feature = "std"))]
19use alloc::string::{String, ToString};
20use alloc::{
21    boxed::Box,
22    collections::{btree_map::BTreeMap, btree_set::BTreeSet},
23    vec::Vec,
24};
25use core::{
26    cmp::Ordering,
27    ffi::c_void,
28    hash::{Hash, Hasher},
29    ops,
30    sync::atomic::{AtomicI64, AtomicUsize, Ordering as AtomicOrdering},
31};
32
33use azul_css::{
34    css::CssPath,
35    props::{
36        basic::{ColorU, FloatValue, LayoutPoint, LayoutRect, LayoutSize},
37        property::CssProperty,
38    },
39    AzString, LayoutDebugMessage, OptionF32, OptionI32, OptionString, OptionU32, U8Vec,
40};
41use rust_fontconfig::FcFontCache;
42
43use crate::{
44    callbacks::{LayoutCallback, LayoutCallbackType, Update},
45    dom::{DomId, DomNodeId, NodeHierarchy},
46    geom::{
47        LogicalPosition, LogicalRect, LogicalSize, OptionLogicalSize, PhysicalPositionI32,
48        PhysicalSize,
49    },
50    gl::OptionGlContextPtr,
51    hit_test::{ExternalScrollId, OverflowingScrollNode},
52    id::{NodeDataContainer, NodeId},
53    refany::OptionRefAny,
54    resources::{
55        DpiScaleFactor, Epoch, GlTextureCache, IdNamespace, ImageCache, ImageMask, ImageRef,
56        RendererResources, ResourceUpdate,
57    },
58    selection::SelectionState,
59    styled_dom::NodeHierarchyItemId,
60    task::{Instant, ThreadId, TimerId},
61    FastBTreeSet, OrderedMap,
62};
63
64pub const DEFAULT_TITLE: &str = "Azul App";
65
66static LAST_WINDOW_ID: AtomicI64 = AtomicI64::new(0);
67
68/// Unique identifier for a window, auto-assigned via atomic counter.
69#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
70#[repr(transparent)]
71pub struct WindowId {
72    pub id: i64,
73}
74
75impl Default for WindowId {
76    fn default() -> Self {
77        Self::new()
78    }
79}
80
81impl WindowId {
82    pub fn new() -> Self {
83        Self {
84            id: LAST_WINDOW_ID.fetch_add(1, AtomicOrdering::SeqCst),
85        }
86    }
87}
88
89static LAST_ICON_KEY: AtomicUsize = AtomicUsize::new(0);
90
91/// Key that is used for checking whether a window icon has changed -
92/// this way azul doesn't need to diff the actual bytes, just the icon key.
93/// Use `IconKey::new()` to generate a new, unique key
94#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
95#[repr(C)]
96pub struct IconKey {
97    icon_id: usize,
98}
99
100impl Default for IconKey {
101    fn default() -> Self {
102        Self::new()
103    }
104}
105
106impl IconKey {
107    pub fn new() -> Self {
108        Self {
109            icon_id: LAST_ICON_KEY.fetch_add(1, AtomicOrdering::SeqCst),
110        }
111    }
112}
113
114#[repr(C)]
115#[derive(PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash)]
116pub struct RendererOptions {
117    pub vsync: Vsync,
118    pub srgb: Srgb,
119    pub hw_accel: HwAcceleration,
120}
121
122impl_option!(
123    RendererOptions,
124    OptionRendererOptions,
125    [PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash]
126);
127
128impl Default for RendererOptions {
129    fn default() -> Self {
130        Self {
131            vsync: Vsync::Enabled,
132            srgb: Srgb::Disabled,
133            // DontCare defers the choice to AZ_BACKEND / the desktop default,
134            // which is now CPU (software) rendering on all platforms — matching
135            // what the headless e2e tests render. GPU is re-selectable via
136            // AZ_BACKEND=gpu / AZ_BACKEND=auto or HwAcceleration::Enabled.
137            hw_accel: HwAcceleration::DontCare,
138        }
139    }
140}
141
142impl RendererOptions {
143    #[must_use]
144    pub const fn new(vsync: Vsync, srgb: Srgb, hw_accel: HwAcceleration) -> Self {
145        Self {
146            vsync,
147            srgb,
148            hw_accel,
149        }
150    }
151}
152
153#[repr(C)]
154#[derive(PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash)]
155pub enum Vsync {
156    Enabled,
157    Disabled,
158    DontCare,
159}
160
161impl Vsync {
162    #[must_use]
163    pub const fn is_enabled(&self) -> bool {
164        matches!(self, Self::Enabled)
165    }
166}
167
168#[repr(C)]
169#[derive(PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash)]
170pub enum Srgb {
171    Enabled,
172    Disabled,
173    DontCare,
174}
175impl Srgb {
176    #[must_use]
177    pub const fn is_enabled(&self) -> bool {
178        matches!(self, Self::Enabled)
179    }
180}
181
182#[repr(C)]
183#[derive(PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash)]
184pub enum HwAcceleration {
185    Enabled,
186    Disabled,
187    DontCare,
188}
189impl HwAcceleration {
190    #[must_use]
191    pub const fn is_enabled(&self) -> bool {
192        matches!(self, Self::Enabled)
193    }
194}
195
196#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
197#[repr(C, u8)]
198pub enum RawWindowHandle {
199    IOS(IOSHandle),
200    MacOS(MacOSHandle),
201    Xlib(XlibHandle),
202    Xcb(XcbHandle),
203    Wayland(WaylandHandle),
204    Windows(WindowsHandle),
205    Web(WebHandle),
206    Android(AndroidHandle),
207    Unsupported,
208}
209
210// SAFETY: RawWindowHandle contains raw pointers that are only used as opaque
211// identifiers for platform window handles. The handle values are not
212// dereferenced across threads; they are passed to platform APIs on the main thread.
213unsafe impl Send for RawWindowHandle {}
214
215#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
216#[repr(C)]
217pub struct IOSHandle {
218    pub ui_window: *mut c_void,
219    pub ui_view: *mut c_void,
220    pub ui_view_controller: *mut c_void,
221}
222
223#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
224#[repr(C)]
225pub struct MacOSHandle {
226    pub ns_window: *mut c_void,
227    pub ns_view: *mut c_void,
228}
229
230#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
231#[repr(C)]
232pub struct XlibHandle {
233    /// An Xlib Window
234    pub window: u64,
235    pub display: *mut c_void,
236}
237
238#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
239#[repr(C)]
240pub struct XcbHandle {
241    /// An X11 `xcb_window_t`.
242    pub window: u32,
243    /// A pointer to an X server `xcb_connection_t`.
244    pub connection: *mut c_void,
245}
246
247#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
248#[repr(C)]
249pub struct WaylandHandle {
250    /// A pointer to a `wl_surface`
251    pub surface: *mut c_void,
252    /// A pointer to a `wl_display`.
253    pub display: *mut c_void,
254}
255
256#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
257#[repr(C)]
258pub struct WindowsHandle {
259    /// A Win32 HWND handle.
260    pub hwnd: *mut c_void,
261    /// The HINSTANCE associated with this type's HWND.
262    pub hinstance: *mut c_void,
263}
264
265#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
266#[repr(C)]
267pub struct WebHandle {
268    /// An ID value inserted into the data attributes of the canvas element as 'raw-handle'
269    ///
270    /// When accessing from JS, the attribute will automatically be called rawHandle. Each canvas
271    /// created by the windowing system should be assigned their own unique ID.
272    /// 0 should be reserved for invalid / null IDs.
273    pub id: u32,
274}
275
276#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
277#[repr(C)]
278pub struct AndroidHandle {
279    /// A pointer to an `ANativeWindow`.
280    pub a_native_window: *mut c_void,
281}
282
283#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
284#[repr(C)]
285#[derive(Default)]
286pub enum MouseCursorType {
287    #[default]
288    Default,
289    Crosshair,
290    Hand,
291    Arrow,
292    Move,
293    Text,
294    Wait,
295    Help,
296    Progress,
297    NotAllowed,
298    ContextMenu,
299    Cell,
300    VerticalText,
301    Alias,
302    Copy,
303    NoDrop,
304    Grab,
305    Grabbing,
306    AllScroll,
307    ZoomIn,
308    ZoomOut,
309    EResize,
310    NResize,
311    NeResize,
312    NwResize,
313    SResize,
314    SeResize,
315    SwResize,
316    WResize,
317    EwResize,
318    NsResize,
319    NeswResize,
320    NwseResize,
321    ColResize,
322    RowResize,
323}
324
325/// Hardware-dependent keyboard scan code.
326pub type ScanCode = u32;
327
328/// Which lock keys are currently engaged.
329///
330/// Not modifiers: a modifier is held, a lock is toggled, and an app that wants
331/// to warn "Caps Lock is on" in a password field needs the toggle state, which
332/// no key event carries.
333#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
334#[repr(C)]
335pub struct KeyLocks {
336    pub caps_lock: bool,
337    pub num_lock: bool,
338    pub scroll_lock: bool,
339}
340
341/// A key identified by its PHYSICAL POSITION, independent of layout.
342///
343/// The companion to [`VirtualKeyCode`], which is the LOGICAL key — what the
344/// user's layout says that position produces. Every modern stack splits these
345/// because they answer different questions: `KeyboardEvent.code` vs `.key` on
346/// the web, `PhysicalKey` vs `Key` in winit.
347///
348/// `ScanCode` already carried the physical key as a raw `u32`, but a raw
349/// scancode is platform-specific and unnameable — an app cannot write
350/// `if scancode == 17` and mean anything portable. This is the same
351/// information in a form that can be matched on.
352///
353/// Names follow the W3C UI Events `code` values, which name each position by
354/// what it produces on US ANSI. `KeyW` is "the key where W sits on a US
355/// board" — on AZERTY the user sees Z there, and a game binding "forward"
356/// wants that position regardless.
357#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
358#[repr(C)]
359pub enum PhysicalKey {
360    /// The platform reported a position this enum does not name.
361    Unidentified,
362    // Letters — POSITIONAL. `KeyW` is the key where W sits on US ANSI,
363    // wherever the user's layout maps it: on AZERTY that key produces Z, and
364    // a first-person game binding "forward" wants the position, not the
365    // letter, which is the whole reason this enum exists alongside
366    // `VirtualKeyCode`.
367    KeyA, KeyB, KeyC, KeyD, KeyE, KeyF, KeyG, KeyH, KeyI, KeyJ, KeyK, KeyL, KeyM,
368    KeyN, KeyO, KeyP, KeyQ, KeyR, KeyS, KeyT, KeyU, KeyV, KeyW, KeyX, KeyY, KeyZ,
369    // Number row.
370    Digit0, Digit1, Digit2, Digit3, Digit4, Digit5, Digit6, Digit7, Digit8, Digit9,
371    // Punctuation, by position on the US ANSI board.
372    Backquote, Minus, Equal, BracketLeft, BracketRight, Backslash,
373    Semicolon, Quote, Comma, Period, Slash,
374    // Whitespace and editing.
375    Enter, Tab, Space, Backspace, Escape, CapsLock,
376    // Modifiers, LEFT and RIGHT distinguished — which `VirtualKeyCode` can do
377    // but `KeyModifiers` deliberately cannot, since a shortcut cares that
378    // Shift is down and a game may care which one.
379    ShiftLeft, ShiftRight, ControlLeft, ControlRight,
380    AltLeft, AltRight, MetaLeft, MetaRight, ContextMenu,
381    // Navigation.
382    Insert, Delete, Home, End, PageUp, PageDown,
383    ArrowUp, ArrowDown, ArrowLeft, ArrowRight,
384    // Function row.
385    F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
386    F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24,
387    // System.
388    PrintScreen, ScrollLock, Pause,
389    // Numpad — always positional, and distinct from the number row even when
390    // both produce the same character.
391    NumLock, NumpadDivide, NumpadMultiply, NumpadSubtract, NumpadAdd,
392    NumpadEnter, NumpadDecimal, NumpadComma, NumpadEqual,
393    Numpad0, Numpad1, Numpad2, Numpad3, Numpad4,
394    Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
395    // ISO/JIS keys that ANSI boards do not have.
396    IntlBackslash, IntlRo, IntlYen, Lang1, Lang2, Convert, NonConvert, KanaMode,
397}
398
399/// Determines which keys are pressed currently (modifiers, etc.)
400#[derive(Default, Debug, Clone, PartialEq, Eq)]
401#[repr(C)]
402pub struct KeyboardState {
403    /// Currently pressed virtual keycode - **DO NOT USE THIS FOR TEXT INPUT**.
404    ///
405    /// For text input, use the `text_input` parameter in callbacks.
406    /// For example entering `à` will fire a `VirtualKeyCode::Grave`, then `VirtualKeyCode::A`,
407    /// so to correctly combine characters, the framework handles text composition internally.
408    pub current_virtual_keycode: OptionVirtualKeyCode,
409    /// Currently pressed virtual keycodes (READONLY) - it can happen that more than one key is
410    /// pressed
411    ///
412    /// This is essentially an "extension" of `current_scancodes` - `current_keys` stores the
413    /// characters, but what if the pressed key is not a character (such as `ArrowRight` or
414    /// `PgUp`)?
415    ///
416    /// Note that this can have an overlap, so pressing "a" on the keyboard will insert
417    /// both a `VirtualKeyCode::A` into `current_virtual_keycodes` and text input will be handled
418    /// by the framework automatically for contenteditable nodes.
419    pub pressed_virtual_keycodes: VirtualKeyCodeVec,
420    /// Same as `current_virtual_keycodes`, but the scancode identifies the physical key pressed,
421    /// independent of the keyboard layout. The scancode does not change if the user adjusts the
422    /// host's keyboard map. Use when the physical location of the key is more important than
423    /// the key's host GUI semantics, such as for movement controls in a first-person game
424    /// (German keyboard: Z key, UK keyboard: Y key, etc.)
425    pub pressed_scancodes: ScanCodeVec,
426    /// Which modifiers are held right now. (READONLY)
427    ///
428    /// Derivable from `pressed_virtual_keycodes` by scanning for the six
429    /// modifier keycodes, which is what every caller was doing. Carrying it
430    /// directly makes the common check cheap and makes `ModifiersChanged`
431    /// possible to emit at all.
432    pub modifiers: crate::events::KeyModifiers,
433    /// Which lock keys are engaged. (READONLY)
434    ///
435    /// NOT derivable from the pressed set: a lock is a toggle, so it stays on
436    /// after the key is released and no key event describes it. It has to be
437    /// read from the OS.
438    pub locks: KeyLocks,
439    /// Whether the current key event is an auto-repeat rather than a fresh
440    /// press. (READONLY)
441    ///
442    /// A text field wants repeats; a jump button does not, and without this
443    /// the two are indistinguishable.
444    pub is_repeat: bool,
445    /// Physical position of the currently pressed key. (READONLY)
446    pub current_physical_key: OptionPhysicalKey,
447}
448
449impl KeyboardState {
450    #[must_use]
451    pub fn shift_down(&self) -> bool {
452        self.is_key_down(VirtualKeyCode::LShift) || self.is_key_down(VirtualKeyCode::RShift)
453    }
454    #[must_use]
455    pub fn ctrl_down(&self) -> bool {
456        self.is_key_down(VirtualKeyCode::LControl) || self.is_key_down(VirtualKeyCode::RControl)
457    }
458    #[must_use]
459    pub fn alt_down(&self) -> bool {
460        self.is_key_down(VirtualKeyCode::LAlt) || self.is_key_down(VirtualKeyCode::RAlt)
461    }
462    #[must_use]
463    pub fn super_down(&self) -> bool {
464        self.is_key_down(VirtualKeyCode::LWin) || self.is_key_down(VirtualKeyCode::RWin)
465    }
466    /// The platform's PRIMARY shortcut modifier: Cmd (super) on macOS, Ctrl
467    /// everywhere else (MWA-A2). Every standard editing shortcut
468    /// (copy / cut / paste / select-all / undo / redo) keys off this —
469    /// hardcoding `ctrl_down()` made Cmd+C/X/V/A/Z dead on macOS, where Cmd
470    /// arrives as LWin/super.
471    #[must_use]
472    pub fn primary_down(&self) -> bool {
473        if cfg!(target_os = "macos") {
474            self.super_down()
475        } else {
476            self.ctrl_down()
477        }
478    }
479    #[must_use]
480    pub fn is_key_down(&self, key: VirtualKeyCode) -> bool {
481        self.pressed_virtual_keycodes.iter().any(|k| *k == key)
482    }
483
484    /// The modifier set implied by the keys currently held.
485    ///
486    /// `modifiers` is a stored field because callbacks read it directly and
487    /// `ModifiersChanged` diffs against it, but its VALUE is a pure function
488    /// of the pressed set — so it must never be assigned independently, only
489    /// recomputed from here.
490    #[must_use]
491    pub fn derived_modifiers(&self) -> crate::events::KeyModifiers {
492        crate::events::KeyModifiers {
493            shift: self.shift_down(),
494            ctrl: self.ctrl_down(),
495            alt: self.alt_down(),
496            meta: self.super_down(),
497        }
498    }
499
500    /// Bring `modifiers` back in step with the pressed set.
501    ///
502    /// Call this after ANY change to `pressed_virtual_keycodes`. Nothing did,
503    /// on any backend, so `modifiers` sat at its default: every callback that
504    /// read it saw "no modifiers held" no matter what was down, and
505    /// `ModifiersChanged` could never fire because the diff compared two
506    /// identical defaults. The derived accessors (`shift_down()` and friends)
507    /// were right the whole time, which is why shortcuts still worked and the
508    /// gap stayed invisible.
509    pub fn sync_modifiers(&mut self) {
510        self.modifiers = self.derived_modifiers();
511    }
512
513    /// Returns `true` iff every entry of `chord` is currently active in this
514    /// keyboard state. Used by accelerator/keymap registrations to evaluate
515    /// shortcuts like `[Ctrl, Shift, Key(VirtualKeyCode::S)]`.
516    ///
517    /// An empty chord matches trivially.
518    #[must_use]
519    pub fn matches_accelerator(&self, chord: &[AcceleratorKey]) -> bool {
520        chord.iter().all(|a| a.matches(self))
521    }
522}
523
524impl_option!(
525    KeyboardState,
526    OptionKeyboardState,
527    copy = false,
528    [Debug, Clone, PartialEq, Eq]
529);
530
531// char is not ABI-stable, use u32 instead
532impl_option!(
533    u32,
534    OptionChar,
535    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
536);
537impl_option!(
538    VirtualKeyCode,
539    OptionVirtualKeyCode,
540    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
541);
542impl_option!(
543    PhysicalKey,
544    OptionPhysicalKey,
545    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
546);
547
548impl_vec!(
549    VirtualKeyCode,
550    VirtualKeyCodeVec,
551    VirtualKeyCodeVecDestructor,
552    VirtualKeyCodeVecDestructorType,
553    VirtualKeyCodeVecSlice,
554    OptionVirtualKeyCode
555);
556impl_vec_debug!(VirtualKeyCode, VirtualKeyCodeVec);
557impl_vec_partialord!(VirtualKeyCode, VirtualKeyCodeVec);
558impl_vec_ord!(VirtualKeyCode, VirtualKeyCodeVec);
559impl_vec_clone!(
560    VirtualKeyCode,
561    VirtualKeyCodeVec,
562    VirtualKeyCodeVecDestructor
563);
564impl_vec_partialeq!(VirtualKeyCode, VirtualKeyCodeVec);
565impl_vec_eq!(VirtualKeyCode, VirtualKeyCodeVec);
566impl_vec_hash!(VirtualKeyCode, VirtualKeyCodeVec);
567impl_vec_mut!(VirtualKeyCode, VirtualKeyCodeVec);
568
569impl_vec_as_hashmap!(VirtualKeyCode, VirtualKeyCodeVec);
570
571impl_vec!(
572    ScanCode,
573    ScanCodeVec,
574    ScanCodeVecDestructor,
575    ScanCodeVecDestructorType,
576    ScanCodeVecSlice,
577    OptionU32
578);
579impl_vec_debug!(ScanCode, ScanCodeVec);
580impl_vec_partialord!(ScanCode, ScanCodeVec);
581impl_vec_ord!(ScanCode, ScanCodeVec);
582impl_vec_clone!(ScanCode, ScanCodeVec, ScanCodeVecDestructor);
583impl_vec_partialeq!(ScanCode, ScanCodeVec);
584impl_vec_eq!(ScanCode, ScanCodeVec);
585impl_vec_hash!(ScanCode, ScanCodeVec);
586impl_vec_mut!(ScanCode, ScanCodeVec);
587
588impl_vec_as_hashmap!(ScanCode, ScanCodeVec);
589
590/// Mouse position, cursor type, user scroll input, etc.
591#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq)]
592#[repr(C)]
593pub struct MouseState {
594    // Field order is by DECREASING ALIGNMENT, not by topic. `#[repr(C)]` lays
595    // these out literally, so the u64 has to lead and the five bools have to
596    // trail — the previous grouping cost ~8 bytes of padding per instance,
597    // and this struct is copied every mouse move.
598    /// Which physical pointing device is driving, or `0` when the platform
599    /// does not say. (READONLY)
600    pub pointer_device_id: u64,
601    /// Where is the mouse cursor currently? Set to `None` if the window is not focused.
602    /// (READWRITE)
603    pub cursor_position: CursorPosition,
604    /// Current mouse cursor type, set to `None` if the cursor is hidden. (READWRITE)
605    pub mouse_cursor_type: OptionMouseCursorType,
606    /// What kind of device is currently driving the pointer. (READONLY)
607    ///
608    /// On `MouseState` rather than on the event because it is a property of
609    /// the DEVICE, not of one motion — an app can ask "is this a trackpad?"
610    /// from any callback, not only from inside a pointer handler.
611    pub pointer_source: crate::events::PointerSource,
612    /// Is the mouse cursor locked to the current window (important for applications like games)?
613    /// (READWRITE)
614    pub is_cursor_locked: bool,
615    /// Is the left mouse button down? (READONLY)
616    pub left_down: bool,
617    /// Is the right mouse button down? (READONLY)
618    pub right_down: bool,
619    /// Is the middle mouse button down? (READONLY)
620    pub middle_down: bool,
621    /// Bitmask of the thumb buttons currently held. (READONLY)
622    ///
623    /// A bitmask rather than two bools because the set is open-ended: a mouse
624    /// can report buttons past forward, and the shells already carry them as
625    /// `MouseButton::Other(n)`. Bit 0 is back, bit 1 is forward — see
626    /// `MOUSE_OTHER_MASK_BACK` / `MOUSE_OTHER_MASK_FORWARD`; use
627    /// [`MouseState::back_down`] and [`MouseState::forward_down`] rather than
628    /// testing bits by hand.
629    pub other_down: u8,
630}
631
632
633impl MouseState {
634    /// Whether the thumb "back" button is held.
635    #[must_use]
636    pub const fn back_down(&self) -> bool {
637        self.other_down & crate::events::MOUSE_OTHER_MASK_BACK != 0
638    }
639
640    /// Whether the thumb "forward" button is held.
641    #[must_use]
642    pub const fn forward_down(&self) -> bool {
643        self.other_down & crate::events::MOUSE_OTHER_MASK_FORWARD != 0
644    }
645
646    #[must_use]
647    pub const fn matches(&self, context: &ContextMenuMouseButton) -> bool {
648        use self::ContextMenuMouseButton::{Left, Middle, Right};
649        match context {
650            Left => self.left_down,
651            Right => self.right_down,
652            Middle => self.middle_down,
653        }
654    }
655}
656
657impl_option!(
658    MouseState,
659    OptionMouseState,
660    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd]
661);
662
663impl_option!(
664    MouseCursorType,
665    OptionMouseCursorType,
666    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
667);
668
669impl Default for MouseState {
670    fn default() -> Self {
671        Self {
672            mouse_cursor_type: Some(MouseCursorType::Default).into(),
673            cursor_position: CursorPosition::default(),
674            is_cursor_locked: false,
675            left_down: false,
676            right_down: false,
677            middle_down: false,
678            other_down: 0,
679            pointer_source: crate::events::PointerSource::Unknown,
680            pointer_device_id: 0,
681        }
682    }
683}
684
685/// The seat id of the PRIMARY pointer - the one `FullWindowState::mouse_state`
686/// describes. Every platform has exactly this seat; most have only it.
687pub const PRIMARY_POINTER_SEAT: u64 = 0;
688
689/// One ADDITIONAL pointer seat (9b-ii): an independent cursor with its own
690/// position, buttons and hover, next to the primary one.
691///
692/// A SEAT is not a DEVICE. `MouseState::pointer_device_id` names the physical
693/// hardware that last drove a cursor; a seat is the cursor itself. The
694/// distinction is the whole design: on Windows, macOS, Android and iOS the OS
695/// merges every mouse into ONE cursor, so two mice are two devices driving one
696/// seat - a click from the second mouse after a move from the first lands
697/// where the first left the cursor, because that IS where the cursor is.
698/// Keying state by device would have split that one cursor into two entries
699/// with different positions, and dispatched the click somewhere the user
700/// could not see. Only X11 (MPX master pointers) and Wayland (one `wl_seat`
701/// per user) can present a second cursor, and only there does a second entry
702/// exist.
703///
704/// The primary seat is NOT in `pointer_seats`; it stays `mouse_state` so
705/// every existing reader of "the mouse" keeps meaning what it meant.
706#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)]
707#[repr(C)]
708pub struct PointerSeat {
709    /// The platform's identity for this cursor - an X11 master pointer id,
710    /// a Wayland seat's global name. Never [`PRIMARY_POINTER_SEAT`].
711    pub seat_id: u64,
712    /// The seat's own cursor state.
713    pub state: MouseState,
714}
715
716impl_option!(
717    PointerSeat,
718    OptionPointerSeat,
719    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd]
720);
721
722impl_vec!(
723    PointerSeat,
724    PointerSeatVec,
725    PointerSeatVecDestructor,
726    PointerSeatVecDestructorType,
727    PointerSeatVecSlice,
728    OptionPointerSeat
729);
730impl_vec_debug!(PointerSeat, PointerSeatVec);
731impl_vec_clone!(PointerSeat, PointerSeatVec, PointerSeatVecDestructor);
732impl_vec_partialeq!(PointerSeat, PointerSeatVec);
733impl_vec_mut!(PointerSeat, PointerSeatVec);
734
735/// One NON-primary keyboard seat's state (9b-ii-a-i): the per-seat twin of
736/// [`PointerSeat`]. X11 MPX pairs each master keyboard with a master
737/// pointer, and a Wayland `wl_seat` carries both; the seat id is the same
738/// number [`PointerSeat::seat_id`] uses for that pairing, so a keystroke and
739/// a click from the same person carry the same seat. The primary seat's
740/// keyboard is `FullWindowState::keyboard_state`, never listed here.
741///
742/// FOCUS IS SHARED: azul has one focused node, so every seat's keys reach
743/// it (MPX's per-keyboard focus is not modelled - 9b-ii-a-i-d).
744#[derive(Debug, Clone, PartialEq, Eq)]
745#[repr(C)]
746pub struct KeyboardSeat {
747    /// The seat's identity - the same id as the seat's pointer. Never
748    /// [`PRIMARY_POINTER_SEAT`].
749    pub seat_id: u64,
750    /// The seat's own keyboard state.
751    pub state: KeyboardState,
752}
753
754impl_option!(
755    KeyboardSeat,
756    OptionKeyboardSeat,
757    copy = false,
758    [Debug, Clone, PartialEq, Eq]
759);
760
761impl_vec!(
762    KeyboardSeat,
763    KeyboardSeatVec,
764    KeyboardSeatVecDestructor,
765    KeyboardSeatVecDestructorType,
766    KeyboardSeatVecSlice,
767    OptionKeyboardSeat
768);
769impl_vec_debug!(KeyboardSeat, KeyboardSeatVec);
770impl_vec_clone!(KeyboardSeat, KeyboardSeatVec, KeyboardSeatVecDestructor);
771impl_vec_partialeq!(KeyboardSeat, KeyboardSeatVec);
772impl_vec_mut!(KeyboardSeat, KeyboardSeatVec);
773
774#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
775#[repr(C)]
776pub struct VirtualKeyCodeCombo {
777    pub keys: VirtualKeyCodeVec,
778}
779
780impl_option!(
781    VirtualKeyCodeCombo,
782    OptionVirtualKeyCodeCombo,
783    copy = false,
784    [Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord]
785);
786
787#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
788#[repr(C)]
789#[derive(Default)]
790pub enum ContextMenuMouseButton {
791    #[default]
792    Right,
793    Middle,
794    Left,
795}
796
797impl MouseState {
798    /// Returns whether any mouse button (left, right or center) is currently held down
799    #[must_use]
800    pub const fn mouse_down(&self) -> bool {
801        self.right_down || self.left_down || self.middle_down
802    }
803
804    /// Snapshot the button-down flags as a `MouseButtonState` for drag tracking.
805    #[must_use]
806    pub const fn button_state(&self) -> crate::events::MouseButtonState {
807        crate::events::MouseButtonState {
808            left_down: self.left_down,
809            right_down: self.right_down,
810            middle_down: self.middle_down,
811        }
812    }
813}
814
815impl From<&MouseState> for crate::events::MouseButtonState {
816    fn from(s: &MouseState) -> Self {
817        s.button_state()
818    }
819}
820
821impl crate::events::MouseButtonState {
822    /// Returns true if any of the tracked buttons is held down.
823    #[must_use]
824    pub const fn any_down(&self) -> bool {
825        self.left_down || self.right_down || self.middle_down
826    }
827}
828
829/// Result of dispatching a scroll delta into the system scroll-handling pipeline.
830///
831/// Returned by [`process_system_scroll`]. Higher layers can use the
832/// [`ScrollResult::remaining_delta`] to forward un-consumed scroll to a parent
833/// container, and [`ScrollResult::hit_scrollbar`] to distinguish scrollbar-drag
834/// scrolling from wheel-on-content scrolling for hit-testing purposes.
835#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd)]
836#[repr(C)]
837pub struct ScrollResult {
838    /// Number of scrollable nodes whose offset was updated by this dispatch.
839    pub scrolled_nodes: usize,
840    /// Delta that could not be consumed (overscroll). May be forwarded to a parent.
841    pub remaining_delta: LogicalPosition,
842    /// `true` if the dispatch hit a native scrollbar (drag), `false` for wheel/touch.
843    pub hit_scrollbar: bool,
844}
845
846/// Dispatch a system scroll event and return a [`ScrollResult`] describing what
847/// happened.
848///
849/// This is the entry point used by headless integration tests and embedders that
850/// drive scroll programmatically. The richer per-document scroll handling lives
851/// in `LayoutWindow::process_scroll`; this helper packages a delta into a
852/// `ScrollResult` for return to callers so the result type is observable from
853/// the public API.
854#[must_use]
855pub fn process_system_scroll(delta: LogicalPosition, hit_scrollbar: bool) -> ScrollResult {
856    let consumed = delta.x != 0.0 || delta.y != 0.0;
857    ScrollResult {
858        scrolled_nodes: usize::from(consumed),
859        remaining_delta: LogicalPosition::zero(),
860        hit_scrollbar,
861    }
862}
863
864#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)]
865#[repr(C, u8)]
866#[derive(Default)]
867pub enum CursorPosition {
868    OutOfWindow(LogicalPosition),
869    #[default]
870    Uninitialized,
871    InWindow(LogicalPosition),
872}
873
874impl CursorPosition {
875    #[must_use]
876    pub const fn get_position(&self) -> Option<LogicalPosition> {
877        match self {
878            Self::InWindow(logical_pos) => Some(*logical_pos),
879            Self::OutOfWindow(_) | Self::Uninitialized => None,
880        }
881    }
882
883    #[must_use]
884    pub const fn is_inside_window(&self) -> bool {
885        self.get_position().is_some()
886    }
887}
888
889/// Toggles webrender debug flags (will make stuff appear on
890/// the screen that you might not want to - used for debugging purposes)
891///
892/// Every field here maps onto a `webrender::DebugFlags` bit except
893/// `show_hit_test_areas`, which is azul's own overlay. Populate it from the
894/// environment with [`DebugState::from_az_overlay_env`] — see that function for
895/// the verb list and why the hit-test overlay is no longer `debug_assertions`-only.
896#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
897#[repr(C)]
898pub struct DebugState {
899    /// Paint a translucent red rectangle over every hit-test area.
900    ///
901    /// azul's own overlay, not a webrender flag: the compositor draws it while
902    /// emitting `DisplayListItem::HitTestArea`, so it shows exactly the regions
903    /// the hit tester will actually consider — which is the question you have
904    /// when a click does nothing, or lands on the wrong node.
905    pub show_hit_test_areas: bool,
906    pub profiler_dbg: bool,
907    pub render_target_dbg: bool,
908    pub texture_cache_dbg: bool,
909    pub gpu_time_queries: bool,
910    pub gpu_sample_queries: bool,
911    pub disable_batching: bool,
912    pub epochs: bool,
913    pub echo_driver_messages: bool,
914    pub show_overdraw: bool,
915    pub gpu_cache_dbg: bool,
916    pub texture_cache_dbg_clear_evicted: bool,
917    pub picture_caching_dbg: bool,
918    pub primitive_dbg: bool,
919    pub zoom_dbg: bool,
920    pub small_screen: bool,
921    pub disable_opaque_pass: bool,
922    pub disable_alpha_pass: bool,
923    pub disable_clip_masks: bool,
924    pub disable_text_prims: bool,
925    pub disable_gradient_prims: bool,
926    pub obscure_images: bool,
927    pub glyph_flashing: bool,
928    pub smart_profiler: bool,
929    pub invalidation_dbg: bool,
930    pub tile_cache_logging_dbg: bool,
931    pub profiler_capture: bool,
932    pub force_picture_invalidation: bool,
933}
934
935impl DebugState {
936    /// Build a `DebugState` from the `AZ_OVERLAY` environment variable.
937    ///
938    /// `AZ_OVERLAY` is a comma-separated list of verbs, e.g.
939    ///
940    /// ```text
941    /// AZ_OVERLAY=hit-test
942    /// AZ_OVERLAY=hit-test,overdraw,profiler
943    /// AZ_OVERLAY=list          # print the verbs and exit-code nothing
944    /// ```
945    ///
946    /// WHY THIS EXISTS: the hit-test overlay used to be `#[cfg(debug_assertions)]`
947    /// in the compositor, so a debug build painted every hit-test area red and a
948    /// release build painted none, with no way to ask for either. That is a
949    /// debug/release divergence in VISUAL OUTPUT — running hello-world showed a
950    /// red window and the reasonable first guess was "this linked the wrong
951    /// DLL". It was not. An overlay you cannot turn on when you need it, and
952    /// cannot turn off when you do not, is worse than no overlay.
953    ///
954    /// Available in RELEASE builds too, deliberately: the moment you need to see
955    /// hit-test regions or overdraw is usually on the build a user is running.
956    ///
957    /// Unknown verbs are reported and ignored rather than fatal — a typo in a
958    /// debugging aid must not stop the app you are trying to debug.
959    /// Reading the environment needs std; on `no_std` there is no environment to
960    /// read, so the overlay is simply off. `from_overlay_spec` stays available
961    /// everywhere, so a `no_std` embedder can still enable overlays explicitly.
962    #[cfg(feature = "std")]
963    #[must_use]
964    pub fn from_az_overlay_env() -> Self {
965        std::env::var("AZ_OVERLAY")
966            .map_or_else(|_| Self::default(), |v| Self::from_overlay_spec(v.as_str()))
967    }
968
969    /// `no_std`: there is no environment, so no overlay.
970    #[cfg(not(feature = "std"))]
971    #[must_use]
972    pub fn from_az_overlay_env() -> Self {
973        Self::default()
974    }
975
976    /// The parser behind [`DebugState::from_az_overlay_env`], separated so it is
977    /// testable without touching the process environment.
978    #[must_use]
979    pub fn from_overlay_spec(spec: &str) -> Self {
980        let mut s = Self::default();
981        for raw in spec.split(',') {
982            let verb = raw.trim().to_ascii_lowercase();
983            if verb.is_empty() {
984                continue;
985            }
986            match verb.as_str() {
987                // azul's own overlay.
988                "hit-test" | "hittest" => s.show_hit_test_areas = true,
989                // webrender flags, named for what they SHOW rather than for the
990                // flag constant, because the constant names are not obvious.
991                "profiler" => s.profiler_dbg = true,
992                "smart-profiler" => s.smart_profiler = true,
993                "overdraw" => s.show_overdraw = true,
994                "render-targets" => s.render_target_dbg = true,
995                "texture-cache" => s.texture_cache_dbg = true,
996                "gpu-cache" => s.gpu_cache_dbg = true,
997                "picture-caching" => s.picture_caching_dbg = true,
998                "primitives" => s.primitive_dbg = true,
999                "invalidation" => s.invalidation_dbg = true,
1000                "epochs" => s.epochs = true,
1001                "zoom" => s.zoom_dbg = true,
1002                "glyph-flashing" => s.glyph_flashing = true,
1003                "obscure-images" => s.obscure_images = true,
1004                "gpu-time" => s.gpu_time_queries = true,
1005                "gpu-samples" => s.gpu_sample_queries = true,
1006                "echo-driver" => s.echo_driver_messages = true,
1007                // Diagnostic switches that DISABLE a stage — for bisecting which
1008                // stage is responsible for a visual artefact.
1009                "no-batching" => s.disable_batching = true,
1010                "no-opaque-pass" => s.disable_opaque_pass = true,
1011                "no-alpha-pass" => s.disable_alpha_pass = true,
1012                "no-clip-masks" => s.disable_clip_masks = true,
1013                "no-text" => s.disable_text_prims = true,
1014                "no-gradients" => s.disable_gradient_prims = true,
1015                "all" => {
1016                    s.show_hit_test_areas = true;
1017                    s.profiler_dbg = true;
1018                    s.show_overdraw = true;
1019                    s.primitive_dbg = true;
1020                }
1021                other => {
1022                    // Not fatal: a typo in a debugging aid must not stop the app.
1023                    #[cfg(feature = "std")]
1024                    eprintln!(
1025                        "[azul] AZ_OVERLAY: unknown verb {other:?}. Known: hit-test, profiler, \
1026                         smart-profiler, overdraw, render-targets, texture-cache, gpu-cache, \
1027                         picture-caching, primitives, invalidation, epochs, zoom, glyph-flashing, \
1028                         obscure-images, gpu-time, gpu-samples, echo-driver, no-batching, \
1029                         no-opaque-pass, no-alpha-pass, no-clip-masks, no-text, no-gradients, all"
1030                    );
1031                }
1032            }
1033        }
1034        s
1035    }
1036}
1037
1038#[derive(Debug, Default, Clone, PartialEq)]
1039#[repr(C)]
1040pub struct TouchState {
1041    /// Number of active touch points (kept in sync with `touch_points.len()`).
1042    pub num_touches: usize,
1043    /// Currently active touch points (one entry per finger / stylus).
1044    /// Backends update this on touch start / move / end events.
1045    pub touch_points: TouchPointVec,
1046    /// Intermediate samples the OS captured BETWEEN this frame and the last,
1047    /// oldest first. APPENDED for ABI stability.
1048    ///
1049    /// A digitizer samples far faster than the display refreshes — 120 Hz or
1050    /// 240 Hz against 60 — and the OS delivers only the newest position per
1051    /// frame, because that is what a button or a scroll view wants. A drawing
1052    /// app wants all of them: a fast stroke rendered from one point per frame
1053    /// is a polyline with visible corners, and the samples that would have
1054    /// rounded it were captured and discarded.
1055    ///
1056    /// Empty when the platform does not report them, or when nothing moved
1057    /// between frames. The current `touch_points` entry is NOT repeated here.
1058    pub coalesced_points: TouchPointVec,
1059    /// Where the OS predicts the touch is about to go, oldest first.
1060    /// APPENDED for ABI stability.
1061    ///
1062    /// These are EXTRAPOLATIONS, not measurements, and they are wrong as
1063    /// often as the user changes direction. They exist to hide latency: a
1064    /// stroke drawn through them appears to keep up with the finger, and the
1065    /// app discards and redraws them next frame when the real samples arrive.
1066    /// Never persist them — committing a predicted point to a document means
1067    /// committing a guess.
1068    pub predicted_points: TouchPointVec,
1069}
1070
1071/// What is making a touch contact.
1072///
1073/// Mirrors Android's `MotionEvent.TOOL_TYPE_*`, which is the richest of the
1074/// platform vocabularies; Windows and Wayland report a subset. `Palm` matters
1075/// even though nothing can be done with it directly: a digitizer that can
1076/// classify a palm is telling the app to IGNORE that contact, which is the
1077/// whole of palm rejection.
1078#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1079#[repr(C)]
1080pub enum TouchToolType {
1081    /// The platform did not say.
1082    Unknown,
1083    /// A fingertip.
1084    Finger,
1085    /// A stylus tip.
1086    Stylus,
1087    /// The inverted end of a stylus.
1088    Eraser,
1089    /// A resting palm — the digitizer classified this contact as accidental.
1090    Palm,
1091    /// A mouse, reported through the touch stream (Android `SOURCE_MOUSE`
1092    /// contacts, some digitizer pucks).
1093    Mouse,
1094}
1095
1096/// Single touch point (finger, stylus, etc.)
1097#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1098#[repr(C)]
1099pub struct TouchPoint {
1100    /// Unique identifier for this touch point (persists across move events)
1101    pub id: u64,
1102    /// Which SEAT's touchscreen this contact is on (9b-ii-a-i-c): a second
1103    /// seat's fingers have their own id space (Wayland `wl_touch` ids are per
1104    /// seat, X11 touch events carry their master pointer), so a contact is
1105    /// identified by (`seat_id`, `id`). `PRIMARY_POINTER_SEAT` for the one
1106    /// touchscreen every other platform has.
1107    pub seat_id: u64,
1108    /// Current position of the touch point in logical coordinates
1109    pub position: LogicalPosition,
1110    /// Force/pressure of the touch (0.0 = no pressure, 1.0 = maximum pressure)
1111    /// Set to 0.5 if pressure is not available
1112    pub force: f32,
1113    /// Major axis of the contact ellipse, in logical px. `0.0` = not reported.
1114    ///
1115    /// A contact is an area, not a point, and every platform says so: Wayland
1116    /// `wl_touch.shape`, Windows `POINTER_TOUCH_INFO.rcContact`, Android
1117    /// `AXIS_TOUCH_MAJOR`. Without it there is no palm rejection, no
1118    /// brush-size-from-fingertip, and no way to size a hit target to a thumb
1119    /// rather than to a mouse cursor.
1120    pub major: f32,
1121    /// Minor axis of the contact ellipse, in logical px. `0.0` = not reported.
1122    pub minor: f32,
1123    /// Rotation of the contact ellipse, radians clockwise from the x-axis.
1124    /// `0.0` when unreported OR when a circular contact makes it meaningless —
1125    /// check `major`/`minor` before trusting it.
1126    pub orientation_rad: f32,
1127    /// What is touching. `Unknown` where the platform does not classify.
1128    pub tool_type: TouchToolType,
1129}
1130
1131/// The one `u64` a touch contact is tracked under in the hover manager and
1132/// the gesture sessions (9b-ii-a-i-c): the raw id for the primary seat, so
1133/// every existing key stays what it was, and a seat-namespaced value for
1134/// any other seat, so two seats' finger `0` never share a session.
1135#[must_use]
1136pub const fn touch_point_key(seat_id: u64, id: u64) -> u64 {
1137    if seat_id == PRIMARY_POINTER_SEAT {
1138        id
1139    } else {
1140        0x8000_0000_0000_0000 | (seat_id.rotate_left(32) ^ id)
1141    }
1142}
1143
1144impl_option!(
1145    TouchPoint,
1146    OptionTouchPoint,
1147    [Debug, Copy, Clone, PartialEq, PartialOrd]
1148);
1149
1150impl_vec!(
1151    TouchPoint,
1152    TouchPointVec,
1153    TouchPointVecDestructor,
1154    TouchPointVecDestructorType,
1155    TouchPointVecSlice,
1156    OptionTouchPoint
1157);
1158impl_vec_debug!(TouchPoint, TouchPointVec);
1159impl_vec_clone!(TouchPoint, TouchPointVec, TouchPointVecDestructor);
1160impl_vec_partialeq!(TouchPoint, TouchPointVec);
1161
1162/// State, size, etc of the window, for comparing to the last frame
1163#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Ord, Eq)]
1164#[repr(C)]
1165#[derive(Default)]
1166pub enum WindowTheme {
1167    DarkMode,
1168    #[default]
1169    LightMode,
1170}
1171
1172impl_option!(
1173    WindowTheme,
1174    OptionWindowTheme,
1175    [Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash]
1176);
1177
1178/// Identifies a specific monitor/display
1179///
1180/// Contains both an index (for fast current-session lookup) and a stable hash
1181/// (for persistence across app restarts and monitor reconfigurations).
1182///
1183/// - `index`: Runtime index (0-based), may change if monitors are added/removed
1184/// - `hash`: Stable identifier based on monitor properties (name, size, position)
1185///
1186/// Applications can serialize `hash` to remember which monitor a window was on,
1187/// then search for matching hash on next launch, falling back to index or PRIMARY.
1188#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
1189#[repr(C)]
1190pub struct MonitorId {
1191    /// Runtime index of the monitor (may change between sessions)
1192    pub index: usize,
1193    /// Stable hash of monitor properties (for persistence)
1194    pub hash: u64,
1195}
1196
1197impl MonitorId {
1198    /// Primary/default monitor (index 0, hash 0)
1199    pub const PRIMARY: Self = Self { index: 0, hash: 0 };
1200
1201    /// Create a `MonitorId` from index only (hash will be 0)
1202    #[must_use]
1203    pub const fn new(index: usize) -> Self {
1204        Self { index, hash: 0 }
1205    }
1206
1207    /// Create a `MonitorId` from index and hash
1208    #[must_use]
1209    pub const fn from_index_and_hash(index: usize, hash: u64) -> Self {
1210        Self { index, hash }
1211    }
1212
1213    /// Create a stable monitor ID from monitor properties
1214    ///
1215    /// Uses FNV-1a hash of: name + position + size
1216    /// This ensures the hash is stable across app restarts as long as
1217    /// the monitor configuration doesn't change significantly
1218    #[must_use]
1219    pub fn from_properties(
1220        index: usize,
1221        name: &str,
1222        position: LayoutPoint,
1223        size: LayoutSize,
1224    ) -> Self {
1225        use core::hash::{Hash, Hasher};
1226
1227        // FNV-1a hash (simple, fast, good distribution)
1228        struct FnvHasher(u64);
1229
1230        impl Hasher for FnvHasher {
1231            fn write(&mut self, bytes: &[u8]) {
1232                const FNV_PRIME: u64 = 0x0100_0000_01b3;
1233                for &byte in bytes {
1234                    self.0 ^= u64::from(byte);
1235                    self.0 = self.0.wrapping_mul(FNV_PRIME);
1236                }
1237            }
1238
1239            fn finish(&self) -> u64 {
1240                self.0
1241            }
1242        }
1243
1244        const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
1245        let mut hasher = FnvHasher(FNV_OFFSET_BASIS);
1246
1247        // Hash the monitor properties
1248        name.hash(&mut hasher);
1249        (position.x as i64).hash(&mut hasher);
1250        (position.y as i64).hash(&mut hasher);
1251        (size.width as i64).hash(&mut hasher);
1252        (size.height as i64).hash(&mut hasher);
1253
1254        Self {
1255            index,
1256            hash: hasher.finish(),
1257        }
1258    }
1259}
1260
1261impl_option!(
1262    MonitorId,
1263    OptionMonitorId,
1264    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
1265);
1266
1267/// Complete information about a monitor/display
1268#[derive(Debug, PartialEq, PartialOrd, Clone)]
1269#[repr(C)]
1270pub struct Monitor {
1271    /// Unique identifier for this monitor (stable across frames)
1272    pub monitor_id: MonitorId,
1273    /// Human-readable name (e.g., "\\.\DISPLAY1", "HDMI-1", "Built-in Retina Display")
1274    pub monitor_name: OptionString,
1275    /// Physical size of the monitor in logical pixels
1276    pub size: LayoutSize,
1277    /// Position of the monitor in the virtual screen coordinate system
1278    pub position: LayoutPoint,
1279    /// DPI scale factor (1.0 = 96 DPI, 2.0 = 192 DPI for Retina)
1280    pub scale_factor: f64,
1281    /// Work area (monitor bounds minus taskbars/panels) in logical pixels
1282    pub work_area: LayoutRect,
1283    /// Available video modes for this monitor
1284    pub video_modes: VideoModeVec,
1285    /// Whether this is the primary/main monitor
1286    pub is_primary_monitor: bool,
1287}
1288
1289impl_option!(
1290    Monitor,
1291    OptionMonitor,
1292    copy = false,
1293    [Debug, PartialEq, PartialOrd, Clone]
1294);
1295
1296impl_vec!(
1297    Monitor,
1298    MonitorVec,
1299    MonitorVecDestructor,
1300    MonitorVecDestructorType,
1301    MonitorVecSlice,
1302    OptionMonitor
1303);
1304impl_vec_debug!(Monitor, MonitorVec);
1305impl_vec_clone!(Monitor, MonitorVec, MonitorVecDestructor);
1306impl_vec_partialeq!(Monitor, MonitorVec);
1307impl_vec_partialord!(Monitor, MonitorVec);
1308
1309impl Hash for Monitor {
1310    fn hash<H>(&self, state: &mut H)
1311    where
1312        H: Hasher,
1313    {
1314        self.monitor_id.hash(state);
1315    }
1316}
1317
1318impl Default for Monitor {
1319    fn default() -> Self {
1320        Self {
1321            monitor_id: MonitorId::PRIMARY,
1322            monitor_name: OptionString::None,
1323            size: LayoutSize::zero(),
1324            position: LayoutPoint::zero(),
1325            scale_factor: 1.0,
1326            work_area: LayoutRect::zero(),
1327            video_modes: Vec::new().into(),
1328            is_primary_monitor: false,
1329        }
1330    }
1331}
1332#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1333#[repr(C)]
1334pub struct VideoMode {
1335    pub size: LayoutSize,
1336    pub bit_depth: u16,
1337    pub refresh_rate: u16,
1338}
1339
1340impl_option!(
1341    VideoMode,
1342    OptionVideoMode,
1343    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
1344);
1345
1346impl_vec!(
1347    VideoMode,
1348    VideoModeVec,
1349    VideoModeVecDestructor,
1350    VideoModeVecDestructorType,
1351    VideoModeVecSlice,
1352    OptionVideoMode
1353);
1354impl_vec_clone!(VideoMode, VideoModeVec, VideoModeVecDestructor);
1355impl_vec_debug!(VideoMode, VideoModeVec);
1356impl_vec_partialeq!(VideoMode, VideoModeVec);
1357impl_vec_partialord!(VideoMode, VideoModeVec);
1358
1359/// Position of the window on screen
1360#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1361#[repr(C, u8)]
1362#[derive(Default)]
1363pub enum WindowPosition {
1364    #[default]
1365    Uninitialized,
1366    /// Absolute position on the virtual screen (physical px). The default for
1367    /// top-level windows.
1368    Initialized(PhysicalPositionI32),
1369    /// Offset (physical px) from the PARENT window's top-left corner. Used by
1370    /// child windows (menus, dropdowns, popups) together with
1371    /// `WindowCreateOptions.parent_window_id`: the backend resolves the final
1372    /// screen position as `parent_top_left + offset`. This is robust where
1373    /// absolute screen coordinates aren't available — notably Wayland, whose
1374    /// `xdg_popup` / subsurface protocol positions relative to the parent. Falls
1375    /// back to absolute (`offset` from origin) if there is no parent.
1376    RelativeToParentWindow(PhysicalPositionI32),
1377}
1378#[allow(variant_size_differences)]
1379// repr(C,u8) FFI enum: boxing the large variant would change the C ABI (api.json bindings); size disparity accepted
1380#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1381#[repr(C, u8)]
1382/// IME composition window rectangle (cursor position + height)
1383#[derive(Default)]
1384pub enum ImePosition {
1385    #[default]
1386    Uninitialized,
1387    Initialized(LogicalRect),
1388}
1389
1390#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1391#[repr(C)]
1392pub struct WindowFlags {
1393    /// Is the window currently maximized, minimized or fullscreen
1394    pub frame: WindowFrame,
1395    /// Window decoration style (title bar, native controls)
1396    pub decorations: WindowDecorations,
1397    /// Compositor blur/transparency effect material
1398    pub background_material: WindowBackgroundMaterial,
1399    /// Window type classification (Normal, Menu, Tooltip, Dialog)
1400    pub window_type: WindowType,
1401    /// User clicked the close button (set by `WindowDelegate`, checked by event loop)
1402    /// The `close_callback` can set this to false to prevent closing
1403    pub close_requested: bool,
1404    /// Is the window currently visible?
1405    pub is_visible: bool,
1406    /// Is the window always on top?
1407    pub is_always_on_top: bool,
1408    /// Whether the window is resizable
1409    pub is_resizable: bool,
1410    /// Whether the window has focus or not (mutating this will request user attention)
1411    pub has_focus: bool,
1412    /// Is smooth scrolling enabled for this window?
1413    pub smooth_scroll_enabled: bool,
1414    /// Is automatic TAB switching supported?
1415    pub autotab_enabled: bool,
1416    /// Enable client-side decorations (custom titlebar with CSD)
1417    /// Only effective when decorations == `WindowDecorations::None`
1418    pub has_decorations: bool,
1419    /// Use native menus (Win32 HMENU, macOS `NSMenu`) instead of Azul window-based menus
1420    /// Default: true on Windows/macOS, false on Linux
1421    pub use_native_menus: bool,
1422    /// Use native context menus instead of Azul window-based context menus
1423    /// Default: true on Windows/macOS, false on Linux
1424    pub use_native_context_menus: bool,
1425    /// Keep window above all others (even from other applications)
1426    /// Platform-specific: Uses `SetWindowPos(HWND_TOPMOST)` on Windows, [`NSWindow` setLevel:] on
1427    /// macOS, _`NET_WM_STATE_ABOVE` on X11, `zwlr_layer_shell` on Wayland
1428    pub is_top_level: bool,
1429    /// Prevent system from sleeping while window is open
1430    /// Platform-specific: Uses `SetThreadExecutionState` on Windows, `IOPMAssertionCreateWithName` on
1431    /// macOS, org.freedesktop.ScreenSaver.Inhibit on Linux
1432    pub prevent_system_sleep: bool,
1433    /// Desired fullscreen-transition style.
1434    ///
1435    /// On macOS this controls whether entering/leaving fullscreen plays the
1436    /// system animation (`Slow*`) or transitions immediately (`Fast*`). On
1437    /// other platforms `Slow*` and `Fast*` behave identically.
1438    ///
1439    /// The actual current frame state still lives in [`WindowFlags::frame`]; this
1440    /// field only describes how the next transition should be performed.
1441    pub fullscreen_mode: FullScreenMode,
1442    /// Draw UNDER the notch, the home indicator and the status bar (10c-v).
1443    ///
1444    /// `false` (the default, and the browser's `viewport-fit=auto`): the
1445    /// engine insets the root layout by the platform's safe-area insets, so
1446    /// nothing lands under a bar and the window's clear colour shows there.
1447    /// `true` (`viewport-fit=cover`): the root fills the whole surface and the
1448    /// app places its own content with `get_safe_area_insets()` - what a
1449    /// full-screen video, a map or a photo viewer wants. The on-screen
1450    /// keyboard is never part of this: it is a transient occlusion the app
1451    /// reads from `get_safe_area_insets().keyboard`.
1452    pub extend_into_safe_area: bool,
1453}
1454
1455impl_option!(
1456    WindowFlags,
1457    OptionWindowFlags,
1458    copy = false,
1459    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
1460);
1461
1462/// Window type classification for behavior control
1463#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1464#[repr(C)]
1465pub enum WindowType {
1466    /// Normal application window
1467    Normal,
1468    /// Menu popup window (always-on-top, frameless, auto-closes on focus loss)
1469    Menu,
1470    /// Tooltip window (always-on-top, no interaction)
1471    Tooltip,
1472    /// Dialog window (blocks parent window)
1473    Dialog,
1474}
1475
1476impl Default for WindowType {
1477    fn default() -> Self {
1478        Self::Normal
1479    }
1480}
1481
1482/// Window frame state (normal, minimized, maximized, fullscreen)
1483#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1484#[repr(C)]
1485pub enum WindowFrame {
1486    Normal,
1487    Minimized,
1488    Maximized,
1489    Fullscreen,
1490}
1491
1492/// Window decoration style
1493#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1494#[repr(C)]
1495pub enum WindowDecorations {
1496    /// Full decorations: title bar with controls
1497    Normal,
1498    /// No title text but controls visible (extended frame).
1499    /// The application must draw its own title text.
1500    NoTitle,
1501    /// Like `NoTitle`, but the framework auto-injects a `Titlebar`
1502    /// at the top of the user's DOM after calling the layout callback.
1503    ///
1504    /// The injected titlebar reads `TitlebarMetrics` from `SystemStyle` for
1505    /// correct padding around the OS-drawn window control buttons, uses the
1506    /// system title font, and carries the `__azul-native-titlebar` class for
1507    /// automatic window-drag activation.
1508    NoTitleAutoInject,
1509    /// No controls visible but title bar area present
1510    NoControls,
1511    /// No decorations at all (borderless)
1512    None,
1513}
1514
1515impl Default for WindowDecorations {
1516    fn default() -> Self {
1517        Self::Normal
1518    }
1519}
1520
1521/// Compositor blur/transparency effects for window background.
1522///
1523/// Anything but `Opaque` gives the window PER-PIXEL alpha on the CPU path:
1524/// the frame is cleared to transparent and whatever the content leaves at
1525/// alpha 0 shows the desktop through (a `border-radius` on the body makes
1526/// real rounded corners; a clip mask on the body makes any shape). The
1527/// window's INPUT shape follows that alpha as well - clicks on fully
1528/// transparent pixels fall through to whatever is behind, on every backend
1529/// (macOS does this by itself for a non-opaque window; X11 gets an `XShape`,
1530/// Wayland an input region, Windows a window region). This is partial
1531/// (per-pixel) transparency, not whole-window opacity; X11 without an ARGB
1532/// visual falls back to `_NET_WM_WINDOW_OPACITY`, which is whole-window.
1533#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1534#[repr(C)]
1535pub enum WindowBackgroundMaterial {
1536    /// No transparency or blur
1537    Opaque,
1538    /// Transparent without blur
1539    Transparent,
1540    /// macOS: Sidebar material, Windows: Acrylic light
1541    Sidebar,
1542    /// macOS: Menu material, Windows: Acrylic
1543    Menu,
1544    /// macOS: HUD material, Windows: Acrylic dark
1545    HUD,
1546    /// macOS: Titlebar material, Windows: Mica
1547    Titlebar,
1548    /// Windows: Mica Alt material
1549    MicaAlt,
1550}
1551
1552impl Default for WindowBackgroundMaterial {
1553    fn default() -> Self {
1554        Self::Opaque
1555    }
1556}
1557
1558impl Default for WindowFlags {
1559    fn default() -> Self {
1560        Self {
1561            frame: WindowFrame::Normal,
1562            decorations: WindowDecorations::Normal,
1563            background_material: WindowBackgroundMaterial::Opaque,
1564            window_type: WindowType::Normal,
1565            close_requested: false,
1566            is_visible: true,
1567            is_always_on_top: false,
1568            is_resizable: true,
1569            has_focus: true,
1570            smooth_scroll_enabled: true,
1571            autotab_enabled: true,
1572            has_decorations: false,
1573            // Native menus are the default on platforms that support them (Windows/macOS)
1574            // The platform layer will override this appropriately
1575            use_native_menus: cfg!(any(target_os = "windows", target_os = "macos")),
1576            use_native_context_menus: cfg!(any(target_os = "windows", target_os = "macos")),
1577            is_top_level: false,
1578            prevent_system_sleep: false,
1579            fullscreen_mode: FullScreenMode::FastFullScreen,
1580            extend_into_safe_area: false,
1581        }
1582    }
1583}
1584
1585impl WindowFlags {
1586    /// Check if window is a menu popup
1587    #[inline]
1588    #[must_use]
1589    pub fn is_menu_window(&self) -> bool {
1590        self.window_type == WindowType::Menu
1591    }
1592
1593    /// Check if window is a tooltip
1594    #[inline]
1595    #[must_use]
1596    pub fn is_tooltip_window(&self) -> bool {
1597        self.window_type == WindowType::Tooltip
1598    }
1599
1600    /// Check if window is a dialog
1601    #[inline]
1602    #[must_use]
1603    pub fn is_dialog_window(&self) -> bool {
1604        self.window_type == WindowType::Dialog
1605    }
1606
1607    /// Check if window currently has focus
1608    #[inline]
1609    #[must_use]
1610    pub const fn window_has_focus(&self) -> bool {
1611        self.has_focus
1612    }
1613
1614    /// Check if close was requested via callback
1615    #[inline]
1616    #[must_use]
1617    pub const fn is_close_requested(&self) -> bool {
1618        self.close_requested
1619    }
1620
1621    /// Check if window has client-side decorations enabled
1622    #[inline]
1623    #[must_use]
1624    pub const fn has_csd(&self) -> bool {
1625        self.has_decorations
1626    }
1627
1628    /// Check if native menus should be used
1629    #[inline]
1630    #[must_use]
1631    pub const fn use_native_menus(&self) -> bool {
1632        self.use_native_menus
1633    }
1634
1635    /// Check if native context menus should be used
1636    #[inline]
1637    #[must_use]
1638    pub const fn use_native_context_menus(&self) -> bool {
1639        self.use_native_context_menus
1640    }
1641}
1642
1643/// Platform-specific window configuration options (Windows, Linux, macOS, WASM)
1644#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
1645#[repr(C)]
1646pub struct PlatformSpecificOptions {
1647    pub windows_options: WindowsWindowOptions,
1648    pub linux_options: LinuxWindowOptions,
1649    pub mac_options: MacWindowOptions,
1650    pub wasm_options: WasmWindowOptions,
1651}
1652
1653// SAFETY: PlatformSpecificOptions contains raw pointers (X11Visual) that are
1654// opaque platform handles, not dereferenced across threads.
1655unsafe impl Sync for PlatformSpecificOptions {}
1656#[allow(clippy::non_send_fields_in_send_ty)] // opaque platform handles, not dereferenced across threads (see note above)
1657unsafe impl Send for PlatformSpecificOptions {}
1658
1659#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
1660#[repr(C)]
1661pub struct WindowsWindowOptions {
1662    /// STARTUP ONLY: Whether the window should allow drag + drop operations (default: true)
1663    pub allow_drag_and_drop: bool,
1664    /// STARTUP ONLY: Sets `WS_EX_NOREDIRECTIONBITMAP`
1665    pub no_redirection_bitmap: bool,
1666    /// STARTUP ONLY: Window icon (decoded bytes), appears at the top right corner of the window
1667    pub window_icon: OptionWindowIcon,
1668    /// READWRITE: Taskbar icon (decoded bytes), usually 256x256x4 bytes large (`ICON_BIG`).
1669    ///
1670    /// Can be changed in callbacks / at runtime.
1671    pub taskbar_icon: OptionTaskBarIcon,
1672    // NOTE: the old Windows-specific `parent_window: OptionHwndHandle` field was
1673    // removed in favor of the cross-platform `WindowCreateOptions.parent_window_id`
1674    // (+ `WindowPosition::RelativeToParentWindow`), which every backend resolves
1675    // through its window registry. One parenting model for all platforms.
1676}
1677
1678impl Default for WindowsWindowOptions {
1679    fn default() -> Self {
1680        Self {
1681            allow_drag_and_drop: true,
1682            no_redirection_bitmap: false,
1683            window_icon: OptionWindowIcon::None,
1684            taskbar_icon: OptionTaskBarIcon::None,
1685        }
1686    }
1687}
1688
1689/// X window type. Maps directly to
1690/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
1691#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1692#[repr(C)]
1693#[derive(Default)]
1694pub enum XWindowType {
1695    /// A desktop feature. This can include a single window containing desktop icons with the same
1696    /// dimensions as the screen, allowing the desktop environment to have full control of the
1697    /// desktop, without the need for proxying root window clicks.
1698    Desktop,
1699    /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all
1700    /// other windows.
1701    Dock,
1702    /// Toolbar windows. "Torn off" from the main application.
1703    Toolbar,
1704    /// Pinnable menu windows. "Torn off" from the main application.
1705    Menu,
1706    /// A small persistent utility window, such as a palette or toolbox.
1707    Utility,
1708    /// The window is a splash screen displayed as an application is starting up.
1709    Splash,
1710    /// This is a dialog window.
1711    Dialog,
1712    /// A dropdown menu that usually appears when the user clicks on an item in a menu bar.
1713    /// This property is typically used on override-redirect windows.
1714    DropdownMenu,
1715    /// A popup menu that usually appears when the user right clicks on an object.
1716    /// This property is typically used on override-redirect windows.
1717    PopupMenu,
1718    /// A tooltip window. Usually used to show additional information when hovering over an object
1719    /// with the cursor. This property is typically used on override-redirect windows.
1720    Tooltip,
1721    /// The window is a notification.
1722    /// This property is typically used on override-redirect windows.
1723    Notification,
1724    /// This should be used on the windows that are popped up by combo boxes.
1725    /// This property is typically used on override-redirect windows.
1726    Combo,
1727    /// This indicates the the window is being dragged.
1728    /// This property is typically used on override-redirect windows.
1729    Dnd,
1730    /// This is a normal, top-level window.
1731    #[default]
1732    Normal,
1733}
1734
1735impl_option!(
1736    XWindowType,
1737    OptionXWindowType,
1738    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
1739);
1740
1741#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
1742#[repr(C)]
1743#[derive(Default)]
1744pub enum UserAttentionType {
1745    #[default]
1746    None,
1747    Critical,
1748    Informational,
1749}
1750
1751/// State for tracking hover and interaction with Linux window decoration elements (CSD).
1752#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
1753#[repr(C)]
1754pub struct LinuxDecorationsState {
1755    pub is_dragging_titlebar: bool,
1756    pub close_button_hover: bool,
1757    pub maximize_button_hover: bool,
1758    pub minimize_button_hover: bool,
1759}
1760
1761impl_option!(
1762    LinuxDecorationsState,
1763    OptionLinuxDecorationsState,
1764    [Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash]
1765);
1766
1767#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
1768#[repr(C)]
1769pub struct LinuxWindowOptions {
1770    pub wayland_theme: OptionWaylandTheme,
1771    pub window_icon: OptionWindowIcon,
1772    /// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only
1773    /// relevant on X11. Can only be set at window creation, can't be changed in callbacks.
1774    pub x11_gtk_theme_variant: OptionString,
1775    /// Build window with a given application ID. It should match the `.desktop` file distributed
1776    /// with your program. Only relevant on Wayland.
1777    /// Can only be set at window creation, can't be changed in callbacks.
1778    ///
1779    /// For details about application ID conventions, see the
1780    /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
1781    pub wayland_app_id: OptionString,
1782    /// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on
1783    /// X11. Can only be set at window creation, can't be changed in callbacks.
1784    pub x11_wm_classes: StringPairVec,
1785    /// Build window with `_NET_WM_WINDOW_TYPE` hint; defaults to `Normal`. Only relevant on X11.
1786    /// Can only be set at window creation, can't be changed in callbacks.
1787    pub x11_window_types: XWindowTypeVec,
1788    /// (Unimplemented) - Can only be set at window creation, can't be changed in callbacks.
1789    pub x11_visual: OptionX11Visual,
1790    /// Build window with resize increment hint. Only implemented on X11.
1791    /// Can only be set at window creation, can't be changed in callbacks.
1792    pub x11_resize_increments: OptionLogicalSize,
1793    /// Build window with base size hint. Only implemented on X11.
1794    /// Can only be set at window creation, can't be changed in callbacks.
1795    pub x11_base_size: OptionLogicalSize,
1796    /// (Unimplemented) - Can only be set at window creation, can't be changed in callbacks.
1797    pub x11_screen: OptionI32,
1798    pub request_user_attention: UserAttentionType,
1799    /// X11-specific: Client-side decoration state (drag position, button hover, etc.)
1800    pub x11_decorations_state: OptionLinuxDecorationsState,
1801    /// Build window with override-redirect flag; defaults to false. Only relevant on X11.
1802    /// Can only be set at window creation, can't be changed in callbacks.
1803    pub x11_override_redirect: bool,
1804}
1805
1806pub type X11Visual = *const c_void;
1807impl_option!(
1808    X11Visual,
1809    OptionX11Visual,
1810    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
1811);
1812
1813/// A key-value pair of strings, used for X11 `WM_CLASS` and other platform properties
1814#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1815#[repr(C)]
1816pub struct AzStringPair {
1817    pub key: AzString,
1818    pub value: AzString,
1819}
1820
1821impl_option!(
1822    AzStringPair,
1823    OptionStringPair,
1824    copy = false,
1825    [Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
1826);
1827
1828impl_vec!(
1829    AzStringPair,
1830    StringPairVec,
1831    StringPairVecDestructor,
1832    StringPairVecDestructorType,
1833    StringPairVecSlice,
1834    OptionStringPair
1835);
1836impl_vec_mut!(AzStringPair, StringPairVec);
1837impl_vec_debug!(AzStringPair, StringPairVec);
1838impl_vec_partialord!(AzStringPair, StringPairVec);
1839impl_vec_ord!(AzStringPair, StringPairVec);
1840impl_vec_clone!(AzStringPair, StringPairVec, StringPairVecDestructor);
1841impl_vec_partialeq!(AzStringPair, StringPairVec);
1842impl_vec_eq!(AzStringPair, StringPairVec);
1843impl_vec_hash!(AzStringPair, StringPairVec);
1844
1845impl_option!(
1846    StringPairVec,
1847    OptionStringPairVec,
1848    copy = false,
1849    [Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash]
1850);
1851
1852impl StringPairVec {
1853    #[must_use]
1854    pub fn get_key(&self, search_key: &str) -> Option<&AzString> {
1855        self.as_ref().iter().find_map(|v| {
1856            if v.key.as_str() == search_key {
1857                Some(&v.value)
1858            } else {
1859                None
1860            }
1861        })
1862    }
1863    pub fn get_key_mut(&mut self, search_key: &str) -> Option<&mut AzStringPair> {
1864        self.as_mut()
1865            .iter_mut()
1866            .find(|v| v.key.as_str() == search_key)
1867    }
1868    pub fn insert_kv<I: Into<AzString>>(&mut self, key: I, value: I) {
1869        let key = key.into();
1870        let value = value.into();
1871        match self.get_key_mut(key.as_str()) {
1872            None => {}
1873            Some(s) => {
1874                s.value = value;
1875                return;
1876            }
1877        }
1878        self.push(AzStringPair { key, value });
1879    }
1880}
1881
1882impl_vec!(
1883    XWindowType,
1884    XWindowTypeVec,
1885    XWindowTypeVecDestructor,
1886    XWindowTypeVecDestructorType,
1887    XWindowTypeVecSlice,
1888    OptionXWindowType
1889);
1890impl_vec_debug!(XWindowType, XWindowTypeVec);
1891impl_vec_partialord!(XWindowType, XWindowTypeVec);
1892impl_vec_ord!(XWindowType, XWindowTypeVec);
1893impl_vec_clone!(XWindowType, XWindowTypeVec, XWindowTypeVecDestructor);
1894impl_vec_partialeq!(XWindowType, XWindowTypeVec);
1895impl_vec_eq!(XWindowType, XWindowTypeVec);
1896impl_vec_hash!(XWindowType, XWindowTypeVec);
1897
1898impl_option!(
1899    WaylandTheme,
1900    OptionWaylandTheme,
1901    copy = false,
1902    [Debug, Clone, PartialEq, PartialOrd]
1903);
1904
1905/// macOS-specific window options (reserved for future use)
1906#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1907#[repr(C)]
1908// `_`-prefixed fields are C-ABI/api.json names; cannot rename.
1909#[allow(clippy::pub_underscore_fields)]
1910pub struct MacWindowOptions {
1911    // empty for now, single field must be present for ABI compat - always set to 0
1912    pub _reserved: u8,
1913}
1914
1915/// WASM/web-specific window options (reserved for future use)
1916#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1917#[repr(C)]
1918// `_`-prefixed fields are C-ABI/api.json names; cannot rename.
1919#[allow(clippy::pub_underscore_fields)]
1920pub struct WasmWindowOptions {
1921    // empty for now, single field must be present for ABI compat - always set to 0
1922    pub _reserved: u8,
1923}
1924
1925#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1926#[repr(C)]
1927#[derive(Default)]
1928pub enum FullScreenMode {
1929    /// - macOS: If the window is in windowed mode, transitions it slowly to fullscreen mode
1930    /// - other: Does the same as `FastFullScreen`.
1931    SlowFullScreen,
1932    /// Window should immediately go into fullscreen mode (on macOS this is not the default
1933    /// behaviour).
1934    #[default]
1935    FastFullScreen,
1936    /// - macOS: If the window is in fullscreen mode, transitions slowly back to windowed state.
1937    /// - other: Does the same as `FastWindowed`.
1938    SlowWindowed,
1939    /// If the window is in fullscreen mode, will immediately go back to windowed mode (on macOS
1940    /// this is not the default behaviour).
1941    FastWindowed,
1942}
1943
1944// Translation type because in winit 24.0 the WinitWaylandTheme is a trait instead
1945// of a struct, which makes things more complicated
1946#[derive(Debug, Clone, PartialEq, PartialOrd)]
1947#[repr(C)]
1948pub struct WaylandTheme {
1949    pub title_bar_active_background_color: ColorU,
1950    pub title_bar_active_separator_color: ColorU,
1951    pub title_bar_active_text_color: ColorU,
1952    pub title_bar_inactive_background_color: ColorU,
1953    pub title_bar_inactive_separator_color: ColorU,
1954    pub title_bar_inactive_text_color: ColorU,
1955    pub maximize_idle_foreground_inactive_color: ColorU,
1956    pub minimize_idle_foreground_inactive_color: ColorU,
1957    pub close_idle_foreground_inactive_color: ColorU,
1958    pub maximize_hovered_foreground_inactive_color: ColorU,
1959    pub minimize_hovered_foreground_inactive_color: ColorU,
1960    pub close_hovered_foreground_inactive_color: ColorU,
1961    pub maximize_disabled_foreground_inactive_color: ColorU,
1962    pub minimize_disabled_foreground_inactive_color: ColorU,
1963    pub close_disabled_foreground_inactive_color: ColorU,
1964    pub maximize_idle_background_inactive_color: ColorU,
1965    pub minimize_idle_background_inactive_color: ColorU,
1966    pub close_idle_background_inactive_color: ColorU,
1967    pub maximize_hovered_background_inactive_color: ColorU,
1968    pub minimize_hovered_background_inactive_color: ColorU,
1969    pub close_hovered_background_inactive_color: ColorU,
1970    pub maximize_disabled_background_inactive_color: ColorU,
1971    pub minimize_disabled_background_inactive_color: ColorU,
1972    pub close_disabled_background_inactive_color: ColorU,
1973    pub maximize_idle_foreground_active_color: ColorU,
1974    pub minimize_idle_foreground_active_color: ColorU,
1975    pub close_idle_foreground_active_color: ColorU,
1976    pub maximize_hovered_foreground_active_color: ColorU,
1977    pub minimize_hovered_foreground_active_color: ColorU,
1978    pub close_hovered_foreground_active_color: ColorU,
1979    pub maximize_disabled_foreground_active_color: ColorU,
1980    pub minimize_disabled_foreground_active_color: ColorU,
1981    pub close_disabled_foreground_active_color: ColorU,
1982    pub maximize_idle_background_active_color: ColorU,
1983    pub minimize_idle_background_active_color: ColorU,
1984    pub close_idle_background_active_color: ColorU,
1985    pub maximize_hovered_background_active_color: ColorU,
1986    pub minimize_hovered_background_active_color: ColorU,
1987    pub close_hovered_background_active_color: ColorU,
1988    pub maximize_disabled_background_active_color: ColorU,
1989    pub minimize_disabled_background_active_color: ColorU,
1990    pub close_disabled_background_active_color: ColorU,
1991    pub title_bar_font: AzString,
1992    pub title_bar_font_size: f32,
1993}
1994
1995/// The global CSS viewport breakpoints for `@media`-style conditions.
1996///
1997/// The dynamic-selector system evaluates against these, and they are one of
1998/// the three signals the resize fast path checks: crossing any of these
1999/// (on either axis) re-invokes the
2000/// app's `layout()`; staying between them re-flows the existing DOM.
2001///
2002/// Lived in `azul-dll`'s shell (`shell2::common::CSS_BREAKPOINTS`, still
2003/// re-exported there) until the headless E2E runner needed the same resize
2004/// decision — the list is engine policy, not shell policy.
2005pub const CSS_BREAKPOINTS: &[f32] = &[320.0, 480.0, 640.0, 768.0, 1024.0, 1280.0, 1440.0, 1920.0];
2006
2007#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)]
2008#[repr(C)]
2009pub struct WindowSize {
2010    /// Width and height of the window, in logical
2011    /// units (may not correspond to the physical on-screen size)
2012    pub dimensions: LogicalSize,
2013    /// Actual DPI value (default: 96)
2014    pub dpi: u32,
2015    /// Minimum dimensions of the window
2016    pub min_dimensions: OptionLogicalSize,
2017    /// Maximum dimensions of the window
2018    pub max_dimensions: OptionLogicalSize,
2019}
2020
2021impl WindowSize {
2022    #[allow(clippy::cast_possible_truncation)] // bounded DPI/dimension/number conversion
2023    #[must_use]
2024    pub fn get_layout_size(&self) -> LayoutSize {
2025        LayoutSize::new(
2026            libm::roundf(self.dimensions.width) as isize,
2027            libm::roundf(self.dimensions.height) as isize,
2028        )
2029    }
2030
2031    /// Get the actual logical size
2032    #[must_use]
2033    pub const fn get_logical_size(&self) -> LogicalSize {
2034        self.dimensions
2035    }
2036
2037    #[must_use]
2038    pub fn get_physical_size(&self) -> PhysicalSize<u32> {
2039        self.dimensions
2040            .to_physical(self.get_hidpi_factor().inner.get())
2041    }
2042
2043    #[allow(clippy::cast_precision_loss)] // bounded DPI/dimension/number conversion
2044    #[must_use]
2045    pub fn get_hidpi_factor(&self) -> DpiScaleFactor {
2046        // Guard against `dpi == 0` (uninitialized / misreporting platform),
2047        // which would yield a 0.0 scale factor and later divide-by-zero when
2048        // converting physical <-> logical sizes (`to_logical` divides by this).
2049        // Fall back to the standard 96 DPI (scale 1.0).
2050        let dpi = if self.dpi == 0 { 96 } else { self.dpi };
2051        DpiScaleFactor {
2052            inner: FloatValue::new(dpi as f32 / 96.0),
2053        }
2054    }
2055}
2056
2057impl Default for WindowSize {
2058    fn default() -> Self {
2059        Self {
2060            dimensions: LogicalSize::new(640.0, 480.0),
2061            dpi: 96,
2062            min_dimensions: None.into(),
2063            max_dimensions: None.into(),
2064        }
2065    }
2066}
2067
2068#[repr(C)]
2069#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
2070pub enum RendererType {
2071    /// Force hardware rendering
2072    Hardware,
2073    /// Force software rendering
2074    Software,
2075}
2076
2077impl_option!(
2078    RendererType,
2079    OptionRendererType,
2080    [Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash]
2081);
2082
2083#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
2084pub enum UpdateFocusWarning {
2085    FocusInvalidDomId(DomId),
2086    FocusInvalidNodeId(NodeHierarchyItemId),
2087    CouldNotFindFocusNode(CssPath),
2088}
2089
2090impl ::core::fmt::Display for UpdateFocusWarning {
2091    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
2092        use self::UpdateFocusWarning::{
2093            CouldNotFindFocusNode, FocusInvalidDomId, FocusInvalidNodeId,
2094        };
2095        match self {
2096            FocusInvalidDomId(dom_id) => write!(f, "Focusing on DOM with invalid ID: {dom_id:?}"),
2097            FocusInvalidNodeId(node_id) => {
2098                write!(f, "Focusing on node with invalid ID: {node_id}")
2099            }
2100            CouldNotFindFocusNode(css_path) => {
2101                write!(f, "Could not find focus node for path: {css_path}")
2102            }
2103        }
2104    }
2105}
2106
2107/// Utility function for easier creation of a keymap - i.e. `[vec![Ctrl, S], my_function]`
2108#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2109#[repr(C, u8)]
2110pub enum AcceleratorKey {
2111    Ctrl,
2112    Alt,
2113    Shift,
2114    Key(VirtualKeyCode),
2115}
2116
2117impl AcceleratorKey {
2118    /// Checks if the current keyboard state contains the given char or modifier,
2119    /// i.e. if the keyboard state currently has the shift key pressed and the
2120    /// accelerator key is `Shift`, evaluates to true, otherwise to false.
2121    #[must_use]
2122    pub fn matches(&self, keyboard_state: &KeyboardState) -> bool {
2123        use self::AcceleratorKey::{Alt, Ctrl, Key, Shift};
2124        match self {
2125            Ctrl => keyboard_state.ctrl_down(),
2126            Alt => keyboard_state.alt_down(),
2127            Shift => keyboard_state.shift_down(),
2128            Key(k) => keyboard_state.is_key_down(*k),
2129        }
2130    }
2131}
2132
2133/// Symbolic name for a keyboard key, does NOT take the keyboard locale into account
2134#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2135#[repr(C)]
2136pub enum VirtualKeyCode {
2137    Key1,
2138    Key2,
2139    Key3,
2140    Key4,
2141    Key5,
2142    Key6,
2143    Key7,
2144    Key8,
2145    Key9,
2146    Key0,
2147    A,
2148    B,
2149    C,
2150    D,
2151    E,
2152    F,
2153    G,
2154    H,
2155    I,
2156    J,
2157    K,
2158    L,
2159    M,
2160    N,
2161    O,
2162    P,
2163    Q,
2164    R,
2165    S,
2166    T,
2167    U,
2168    V,
2169    W,
2170    X,
2171    Y,
2172    Z,
2173    Escape,
2174    F1,
2175    F2,
2176    F3,
2177    F4,
2178    F5,
2179    F6,
2180    F7,
2181    F8,
2182    F9,
2183    F10,
2184    F11,
2185    F12,
2186    F13,
2187    F14,
2188    F15,
2189    F16,
2190    F17,
2191    F18,
2192    F19,
2193    F20,
2194    F21,
2195    F22,
2196    F23,
2197    F24,
2198    Snapshot,
2199    Scroll,
2200    Pause,
2201    Insert,
2202    Home,
2203    Delete,
2204    End,
2205    PageDown,
2206    PageUp,
2207    Left,
2208    Up,
2209    Right,
2210    Down,
2211    Back,
2212    Return,
2213    Space,
2214    Compose,
2215    Caret,
2216    Numlock,
2217    Numpad0,
2218    Numpad1,
2219    Numpad2,
2220    Numpad3,
2221    Numpad4,
2222    Numpad5,
2223    Numpad6,
2224    Numpad7,
2225    Numpad8,
2226    Numpad9,
2227    NumpadAdd,
2228    NumpadDivide,
2229    NumpadDecimal,
2230    NumpadComma,
2231    NumpadEnter,
2232    NumpadEquals,
2233    NumpadMultiply,
2234    NumpadSubtract,
2235    AbntC1,
2236    AbntC2,
2237    Apostrophe,
2238    Apps,
2239    Asterisk,
2240    At,
2241    Ax,
2242    Backslash,
2243    Calculator,
2244    Capital,
2245    Colon,
2246    Comma,
2247    Convert,
2248    Equals,
2249    Grave,
2250    Kana,
2251    Kanji,
2252    LAlt,
2253    LBracket,
2254    LControl,
2255    LShift,
2256    LWin,
2257    Mail,
2258    MediaSelect,
2259    MediaStop,
2260    Minus,
2261    Mute,
2262    MyComputer,
2263    NavigateForward,
2264    NavigateBackward,
2265    NextTrack,
2266    NoConvert,
2267    OEM102,
2268    Period,
2269    PlayPause,
2270    Plus,
2271    Power,
2272    PrevTrack,
2273    RAlt,
2274    RBracket,
2275    RControl,
2276    RShift,
2277    RWin,
2278    Semicolon,
2279    Slash,
2280    Sleep,
2281    Stop,
2282    Sysrq,
2283    Tab,
2284    Underline,
2285    Unlabeled,
2286    VolumeDown,
2287    VolumeUp,
2288    Wake,
2289    WebBack,
2290    WebFavorites,
2291    WebForward,
2292    WebHome,
2293    WebRefresh,
2294    WebSearch,
2295    WebStop,
2296    Yen,
2297    Copy,
2298    Paste,
2299    Cut,
2300}
2301
2302impl VirtualKeyCode {
2303    /// Reconstructs a `VirtualKeyCode` from its `as u32` discriminant.
2304    ///
2305    /// This enum is a fieldless `#[repr(C)]` enum with no explicit discriminants,
2306    /// so the discriminants are assigned sequentially in declaration order and
2307    /// `VariantN as u32` round-trips through this table. Used to recover the key
2308    /// of a keyboard *event* from its `key_code` (which is stored as
2309    /// `VirtualKeyCode as u32`) instead of reading live keyboard state.
2310    #[must_use]
2311    #[allow(clippy::too_many_lines)] // exhaustive keycode match table
2312    pub const fn from_u32(v: u32) -> Option<Self> {
2313        match v {
2314            0 => Some(Self::Key1),
2315            1 => Some(Self::Key2),
2316            2 => Some(Self::Key3),
2317            3 => Some(Self::Key4),
2318            4 => Some(Self::Key5),
2319            5 => Some(Self::Key6),
2320            6 => Some(Self::Key7),
2321            7 => Some(Self::Key8),
2322            8 => Some(Self::Key9),
2323            9 => Some(Self::Key0),
2324            10 => Some(Self::A),
2325            11 => Some(Self::B),
2326            12 => Some(Self::C),
2327            13 => Some(Self::D),
2328            14 => Some(Self::E),
2329            15 => Some(Self::F),
2330            16 => Some(Self::G),
2331            17 => Some(Self::H),
2332            18 => Some(Self::I),
2333            19 => Some(Self::J),
2334            20 => Some(Self::K),
2335            21 => Some(Self::L),
2336            22 => Some(Self::M),
2337            23 => Some(Self::N),
2338            24 => Some(Self::O),
2339            25 => Some(Self::P),
2340            26 => Some(Self::Q),
2341            27 => Some(Self::R),
2342            28 => Some(Self::S),
2343            29 => Some(Self::T),
2344            30 => Some(Self::U),
2345            31 => Some(Self::V),
2346            32 => Some(Self::W),
2347            33 => Some(Self::X),
2348            34 => Some(Self::Y),
2349            35 => Some(Self::Z),
2350            36 => Some(Self::Escape),
2351            37 => Some(Self::F1),
2352            38 => Some(Self::F2),
2353            39 => Some(Self::F3),
2354            40 => Some(Self::F4),
2355            41 => Some(Self::F5),
2356            42 => Some(Self::F6),
2357            43 => Some(Self::F7),
2358            44 => Some(Self::F8),
2359            45 => Some(Self::F9),
2360            46 => Some(Self::F10),
2361            47 => Some(Self::F11),
2362            48 => Some(Self::F12),
2363            49 => Some(Self::F13),
2364            50 => Some(Self::F14),
2365            51 => Some(Self::F15),
2366            52 => Some(Self::F16),
2367            53 => Some(Self::F17),
2368            54 => Some(Self::F18),
2369            55 => Some(Self::F19),
2370            56 => Some(Self::F20),
2371            57 => Some(Self::F21),
2372            58 => Some(Self::F22),
2373            59 => Some(Self::F23),
2374            60 => Some(Self::F24),
2375            61 => Some(Self::Snapshot),
2376            62 => Some(Self::Scroll),
2377            63 => Some(Self::Pause),
2378            64 => Some(Self::Insert),
2379            65 => Some(Self::Home),
2380            66 => Some(Self::Delete),
2381            67 => Some(Self::End),
2382            68 => Some(Self::PageDown),
2383            69 => Some(Self::PageUp),
2384            70 => Some(Self::Left),
2385            71 => Some(Self::Up),
2386            72 => Some(Self::Right),
2387            73 => Some(Self::Down),
2388            74 => Some(Self::Back),
2389            75 => Some(Self::Return),
2390            76 => Some(Self::Space),
2391            77 => Some(Self::Compose),
2392            78 => Some(Self::Caret),
2393            79 => Some(Self::Numlock),
2394            80 => Some(Self::Numpad0),
2395            81 => Some(Self::Numpad1),
2396            82 => Some(Self::Numpad2),
2397            83 => Some(Self::Numpad3),
2398            84 => Some(Self::Numpad4),
2399            85 => Some(Self::Numpad5),
2400            86 => Some(Self::Numpad6),
2401            87 => Some(Self::Numpad7),
2402            88 => Some(Self::Numpad8),
2403            89 => Some(Self::Numpad9),
2404            90 => Some(Self::NumpadAdd),
2405            91 => Some(Self::NumpadDivide),
2406            92 => Some(Self::NumpadDecimal),
2407            93 => Some(Self::NumpadComma),
2408            94 => Some(Self::NumpadEnter),
2409            95 => Some(Self::NumpadEquals),
2410            96 => Some(Self::NumpadMultiply),
2411            97 => Some(Self::NumpadSubtract),
2412            98 => Some(Self::AbntC1),
2413            99 => Some(Self::AbntC2),
2414            100 => Some(Self::Apostrophe),
2415            101 => Some(Self::Apps),
2416            102 => Some(Self::Asterisk),
2417            103 => Some(Self::At),
2418            104 => Some(Self::Ax),
2419            105 => Some(Self::Backslash),
2420            106 => Some(Self::Calculator),
2421            107 => Some(Self::Capital),
2422            108 => Some(Self::Colon),
2423            109 => Some(Self::Comma),
2424            110 => Some(Self::Convert),
2425            111 => Some(Self::Equals),
2426            112 => Some(Self::Grave),
2427            113 => Some(Self::Kana),
2428            114 => Some(Self::Kanji),
2429            115 => Some(Self::LAlt),
2430            116 => Some(Self::LBracket),
2431            117 => Some(Self::LControl),
2432            118 => Some(Self::LShift),
2433            119 => Some(Self::LWin),
2434            120 => Some(Self::Mail),
2435            121 => Some(Self::MediaSelect),
2436            122 => Some(Self::MediaStop),
2437            123 => Some(Self::Minus),
2438            124 => Some(Self::Mute),
2439            125 => Some(Self::MyComputer),
2440            126 => Some(Self::NavigateForward),
2441            127 => Some(Self::NavigateBackward),
2442            128 => Some(Self::NextTrack),
2443            129 => Some(Self::NoConvert),
2444            130 => Some(Self::OEM102),
2445            131 => Some(Self::Period),
2446            132 => Some(Self::PlayPause),
2447            133 => Some(Self::Plus),
2448            134 => Some(Self::Power),
2449            135 => Some(Self::PrevTrack),
2450            136 => Some(Self::RAlt),
2451            137 => Some(Self::RBracket),
2452            138 => Some(Self::RControl),
2453            139 => Some(Self::RShift),
2454            140 => Some(Self::RWin),
2455            141 => Some(Self::Semicolon),
2456            142 => Some(Self::Slash),
2457            143 => Some(Self::Sleep),
2458            144 => Some(Self::Stop),
2459            145 => Some(Self::Sysrq),
2460            146 => Some(Self::Tab),
2461            147 => Some(Self::Underline),
2462            148 => Some(Self::Unlabeled),
2463            149 => Some(Self::VolumeDown),
2464            150 => Some(Self::VolumeUp),
2465            151 => Some(Self::Wake),
2466            152 => Some(Self::WebBack),
2467            153 => Some(Self::WebFavorites),
2468            154 => Some(Self::WebForward),
2469            155 => Some(Self::WebHome),
2470            156 => Some(Self::WebRefresh),
2471            157 => Some(Self::WebSearch),
2472            158 => Some(Self::WebStop),
2473            159 => Some(Self::Yen),
2474            160 => Some(Self::Copy),
2475            161 => Some(Self::Paste),
2476            162 => Some(Self::Cut),
2477            _ => None,
2478        }
2479    }
2480
2481    #[must_use]
2482    pub const fn get_lowercase(&self) -> Option<char> {
2483        use self::VirtualKeyCode::{
2484            Asterisk, At, Caret, Key0, Key1, Key2, Key3, Key4, Key5, Key6, Key7, Key8, Key9, Minus,
2485            Numpad0, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8,
2486            Numpad9, Period, Semicolon, Slash, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
2487            R, S, T, U, V, W, X, Y, Z,
2488        };
2489        match self {
2490            A => Some('a'),
2491            B => Some('b'),
2492            C => Some('c'),
2493            D => Some('d'),
2494            E => Some('e'),
2495            F => Some('f'),
2496            G => Some('g'),
2497            H => Some('h'),
2498            I => Some('i'),
2499            J => Some('j'),
2500            K => Some('k'),
2501            L => Some('l'),
2502            M => Some('m'),
2503            N => Some('n'),
2504            O => Some('o'),
2505            P => Some('p'),
2506            Q => Some('q'),
2507            R => Some('r'),
2508            S => Some('s'),
2509            T => Some('t'),
2510            U => Some('u'),
2511            V => Some('v'),
2512            W => Some('w'),
2513            X => Some('x'),
2514            Y => Some('y'),
2515            Z => Some('z'),
2516            Key0 | Numpad0 => Some('0'),
2517            Key1 | Numpad1 => Some('1'),
2518            Key2 | Numpad2 => Some('2'),
2519            Key3 | Numpad3 => Some('3'),
2520            Key4 | Numpad4 => Some('4'),
2521            Key5 | Numpad5 => Some('5'),
2522            Key6 | Numpad6 => Some('6'),
2523            Key7 | Numpad7 => Some('7'),
2524            Key8 | Numpad8 => Some('8'),
2525            Key9 | Numpad9 => Some('9'),
2526            Minus => Some('-'),
2527            Asterisk => Some('*'),
2528            At => Some('@'),
2529            Period => Some('.'),
2530            Semicolon => Some(';'),
2531            Slash => Some('/'),
2532            Caret => Some('^'),
2533            _ => None,
2534        }
2535    }
2536}
2537
2538/// 16x16x4 bytes icon
2539#[derive(Debug, Clone)]
2540#[repr(C)]
2541pub struct SmallWindowIconBytes {
2542    pub key: IconKey,
2543    pub rgba_bytes: U8Vec,
2544}
2545
2546/// 32x32x4 bytes icon
2547#[derive(Debug, Clone)]
2548#[repr(C)]
2549pub struct LargeWindowIconBytes {
2550    pub key: IconKey,
2551    pub rgba_bytes: U8Vec,
2552}
2553
2554// Window icon that usually appears in the top-left corner of the window
2555#[derive(Debug, Clone)]
2556#[repr(C, u8)]
2557pub enum WindowIcon {
2558    Small(SmallWindowIconBytes),
2559    /// 32x32x4 bytes icon
2560    Large(LargeWindowIconBytes),
2561}
2562
2563impl_option!(
2564    WindowIcon,
2565    OptionWindowIcon,
2566    copy = false,
2567    [Debug, Clone, PartialOrd, PartialEq, Eq, Hash, Ord]
2568);
2569
2570impl WindowIcon {
2571    #[must_use]
2572    pub const fn get_key(&self) -> IconKey {
2573        match &self {
2574            Self::Small(SmallWindowIconBytes { key, .. })
2575            | Self::Large(LargeWindowIconBytes { key, .. }) => *key,
2576        }
2577    }
2578}
2579// -- Only compare the IconKey (for WindowIcon and TaskBarIcon)
2580
2581impl PartialEq for WindowIcon {
2582    fn eq(&self, rhs: &Self) -> bool {
2583        self.get_key() == rhs.get_key()
2584    }
2585}
2586
2587impl PartialOrd for WindowIcon {
2588    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
2589        Some((self.get_key()).cmp(&rhs.get_key()))
2590    }
2591}
2592
2593impl Eq for WindowIcon {}
2594
2595impl Ord for WindowIcon {
2596    fn cmp(&self, rhs: &Self) -> Ordering {
2597        (self.get_key()).cmp(&rhs.get_key())
2598    }
2599}
2600
2601impl Hash for WindowIcon {
2602    fn hash<H>(&self, state: &mut H)
2603    where
2604        H: Hasher,
2605    {
2606        self.get_key().hash(state);
2607    }
2608}
2609
2610/// 256x256x4 bytes window icon
2611#[derive(Debug, Clone)]
2612#[repr(C)]
2613pub struct TaskBarIcon {
2614    pub key: IconKey,
2615    pub rgba_bytes: U8Vec,
2616}
2617
2618impl_option!(
2619    TaskBarIcon,
2620    OptionTaskBarIcon,
2621    copy = false,
2622    [Debug, Clone, PartialOrd, PartialEq, Eq, Hash, Ord]
2623);
2624
2625impl PartialEq for TaskBarIcon {
2626    fn eq(&self, rhs: &Self) -> bool {
2627        self.key == rhs.key
2628    }
2629}
2630
2631impl PartialOrd for TaskBarIcon {
2632    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
2633        Some((self.key).cmp(&rhs.key))
2634    }
2635}
2636
2637impl Eq for TaskBarIcon {}
2638
2639impl Ord for TaskBarIcon {
2640    fn cmp(&self, rhs: &Self) -> Ordering {
2641        (self.key).cmp(&rhs.key)
2642    }
2643}
2644
2645impl Hash for TaskBarIcon {
2646    fn hash<H>(&self, state: &mut H)
2647    where
2648        H: Hasher,
2649    {
2650        self.key.hash(state);
2651    }
2652}
2653
2654/// A built-in system dialog the engine presents on the app's behalf.
2655///
2656/// Invoked via `CallbackInfo::invoke_system_dialog`. These dialogs are
2657/// rendered by azul itself in a new window that is ALWAYS CPU-rendered — a
2658/// dialog reporting a problem (possibly a GPU problem) must not depend on
2659/// the GPU working.
2660#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2661#[repr(C)]
2662pub enum SysDialogType {
2663    /// "Report a problem": a message box (user text + optional screenshot of
2664    /// the current window + optional system information) mailed to
2665    /// `AppConfig.report_problem`, or saved to disk when no address is set.
2666    ReportProblem,
2667    /// "Check for updates": runs the update check on a background thread,
2668    /// shows the release's Markdown changelog, and — only where the install
2669    /// permits self-update and the user consents — downloads and applies it.
2670    /// Package-managed installs get a "update via your package manager" note.
2671    UpdateVersion,
2672    /// "Data collection": the telemetry consent dialog. Lists EVERY
2673    /// instrument the app can record with per-metric checkmarks, the four
2674    /// signal switches (crashes / logs / metrics / app state on crash), and
2675    /// "remember for all azul apps" (writes the machine-wide shared config).
2676    TelemetryConsent,
2677    /// "Graphics check": shows what the engine's GPU probe found (vendor /
2678    /// renderer / verdict) and, when GPU rendering is unusable, per-platform
2679    /// driver guidance - for apps that NEED working video acceleration.
2680    GpuCheck,
2681}
2682
2683#[cfg(test)]
2684#[path = "window_test.rs"]
2685mod window_test;