wita 0.20.0

A window library in Rust for Windows
Documentation
#[cfg(feature = "raw_input")]
use crate::raw_input;
use crate::DEFAULT_DPI;
use crate::{
    api::*,
    context::*,
    device::Cursor,
    error::*,
    event::EventHandler,
    geometry::*,
    ime,
    procedure::{window_proc, UserMessage},
    resource::*,
};
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32WindowHandle};
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, RwLock};
use windows::core::{HSTRING, PCWSTR};
use windows::Win32::{
    Foundation::*, Graphics::Gdi::*, System::LibraryLoader::*, UI::HiDpi::*, UI::Shell::*,
    UI::WindowsAndMessaging::*,
};

#[derive(Clone, PartialEq, Eq)]
pub(crate) struct WindowHandle(HWND);

unsafe impl Send for WindowHandle {}
unsafe impl Sync for WindowHandle {}

/// A window style and the borderless window style.
pub trait Style {
    fn value(&self) -> WINDOW_STYLE;

    fn is_borderless(&self) -> bool {
        self.value() == WS_POPUP
    }
}

/// Represents the borderless window style.
pub struct BorderlessStyle;

impl Style for BorderlessStyle {
    #[inline]
    fn value(&self) -> WINDOW_STYLE {
        WS_POPUP
    }
}

/// Represents a window style.
pub struct WindowStyle(WINDOW_STYLE);

impl WindowStyle {
    #[inline]
    pub fn dialog() -> Self {
        Self(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU)
    }

    #[inline]
    pub fn borderless() -> BorderlessStyle {
        BorderlessStyle
    }

    #[inline]
    pub fn resizable(mut self, resizable: bool) -> Self {
        if resizable {
            self.0 |= WS_THICKFRAME;
        } else {
            self.0 &= !WS_THICKFRAME;
        }
        self
    }

    #[inline]
    pub fn has_minimize_box(mut self, has_minimize_box: bool) -> Self {
        if has_minimize_box {
            self.0 |= WS_MINIMIZEBOX;
        } else {
            self.0 &= !WS_MINIMIZEBOX;
        }
        self
    }

    #[inline]
    pub fn has_maximize_box(mut self, has_maximize_box: bool) -> Self {
        if has_maximize_box {
            self.0 |= WS_MAXIMIZEBOX;
        } else {
            self.0 &= !WS_MAXIMIZEBOX;
        }
        self
    }

    #[inline]
    pub fn is_borderless(&self) -> bool {
        self.value() == WS_POPUP
    }
}

impl Default for WindowStyle {
    #[inline]
    fn default() -> Self {
        Self(
            WS_OVERLAPPED
                | WS_CAPTION
                | WS_SYSMENU
                | WS_THICKFRAME
                | WS_MINIMIZEBOX
                | WS_MAXIMIZEBOX,
        )
    }
}

impl Style for WindowStyle {
    #[inline]
    fn value(&self) -> WINDOW_STYLE {
        self.0
    }
}

const WINDOW_CLASS_NAME: &str = "wita_window_class";

pub(crate) fn register_class<T: EventHandler + 'static>() {
    unsafe {
        let class_name = WINDOW_CLASS_NAME
            .encode_utf16()
            .chain(Some(0))
            .collect::<Vec<_>>();
        let wc = WNDCLASSEXW {
            cbSize: std::mem::size_of::<WNDCLASSEXW>() as _,
            style: CS_VREDRAW | CS_HREDRAW,
            lpfnWndProc: Some(window_proc::<T>),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: GetModuleHandleW(PCWSTR::null()).unwrap(),
            hIcon: HICON(0),
            hCursor: HCURSOR(0),
            hbrBackground: HBRUSH(GetStockObject(WHITE_BRUSH).0),
            lpszMenuName: PCWSTR::null(),
            lpszClassName: PCWSTR(class_name.as_ptr() as _),
            hIconSm: HICON(0),
        };
        if RegisterClassExW(&wc) == 0 {
            panic!("cannot register the window class");
        }
    }
}

/// The object to build a window.
pub struct WindowBuilder<Ti, S> {
    title: Ti,
    position: ScreenPosition,
    inner_size: S,
    visibility: bool,
    style: WINDOW_STYLE,
    enabled_ime: bool,
    visible_ime_composition_window: bool,
    visible_ime_candidate_window: bool,
    parent: Option<Window>,
    children: Vec<Window>,
    accept_drag_files: bool,
    icon: Option<Icon>,
    cursor: Cursor,
    no_redirection_bitmap: bool,
    #[cfg(feature = "raw_input")]
    raw_input_window_state: raw_input::WindowState,
}

impl WindowBuilder<(), ()> {
    #[allow(clippy::new_ret_no_self)]
    pub fn new() -> WindowBuilder<&'static str, LogicalSize<u32>> {
        WindowBuilder {
            title: "",
            position: ScreenPosition::new(0, 0),
            inner_size: LogicalSize::new(640, 480),
            style: WindowStyle::default().value(),
            visibility: true,
            enabled_ime: false,
            visible_ime_composition_window: false,
            visible_ime_candidate_window: true,
            parent: None,
            children: Vec::new(),
            accept_drag_files: false,
            icon: None,
            cursor: Cursor::default(),
            no_redirection_bitmap: false,
            #[cfg(feature = "raw_input")]
            raw_input_window_state: raw_input::WindowState::Foreground,
        }
    }
}

impl<Ti, S> WindowBuilder<Ti, S> {
    #[inline]
    pub fn title<T>(self, title: T) -> WindowBuilder<T, S> {
        WindowBuilder {
            title,
            position: self.position,
            inner_size: self.inner_size,
            style: self.style,
            visibility: self.visibility,
            enabled_ime: self.enabled_ime,
            visible_ime_composition_window: self.visible_ime_composition_window,
            visible_ime_candidate_window: self.visible_ime_candidate_window,
            parent: self.parent,
            children: self.children,
            accept_drag_files: self.accept_drag_files,
            icon: self.icon,
            cursor: self.cursor,
            no_redirection_bitmap: self.no_redirection_bitmap,
            #[cfg(feature = "raw_input")]
            raw_input_window_state: self.raw_input_window_state,
        }
    }

    #[inline]
    pub fn position(mut self, position: impl Into<ScreenPosition>) -> Self {
        self.position = position.into();
        self
    }

    #[inline]
    pub fn inner_size<T>(self, inner_size: T) -> WindowBuilder<Ti, T> {
        WindowBuilder {
            title: self.title,
            position: self.position,
            inner_size,
            style: self.style,
            visibility: self.visibility,
            enabled_ime: self.enabled_ime,
            visible_ime_composition_window: self.visible_ime_composition_window,
            visible_ime_candidate_window: self.visible_ime_candidate_window,
            parent: self.parent,
            children: self.children,
            accept_drag_files: self.accept_drag_files,
            icon: self.icon,
            cursor: self.cursor,
            no_redirection_bitmap: self.no_redirection_bitmap,
            #[cfg(feature = "raw_input")]
            raw_input_window_state: self.raw_input_window_state,
        }
    }

    #[inline]
    pub fn style(mut self, style: impl Style) -> WindowBuilder<Ti, S> {
        self.style = style.value();
        self
    }

    #[inline]
    pub fn visible(mut self, visibility: bool) -> WindowBuilder<Ti, S> {
        self.visibility = visibility;
        self
    }

    #[inline]
    pub fn visible_ime_composition_window(
        mut self,
        show_ime_composition_window: bool,
    ) -> WindowBuilder<Ti, S> {
        self.visible_ime_composition_window = show_ime_composition_window;
        self
    }

    #[inline]
    pub fn visible_ime_candidate_window(
        mut self,
        show_ime_cadidate_window: bool,
    ) -> WindowBuilder<Ti, S> {
        self.visible_ime_candidate_window = show_ime_cadidate_window;
        self
    }

    #[inline]
    pub fn parent(mut self, parent: &Window) -> WindowBuilder<Ti, S> {
        self.parent = Some(parent.clone());
        self
    }

    #[inline]
    pub fn child(mut self, child: &Window) -> WindowBuilder<Ti, S> {
        self.children.push(child.clone());
        self
    }

    #[inline]
    pub fn children(mut self, children: &[&Window]) -> WindowBuilder<Ti, S> {
        for &c in children {
            self.children.push(c.clone());
        }
        self
    }

    #[inline]
    pub fn accept_drag_files(mut self, enabled: bool) -> WindowBuilder<Ti, S> {
        self.accept_drag_files = enabled;
        self
    }

    #[inline]
    pub fn icon(mut self, icon: Icon) -> WindowBuilder<Ti, S> {
        self.icon = Some(icon);
        self
    }

    #[inline]
    pub fn cursor(mut self, cursor: Cursor) -> WindowBuilder<Ti, S> {
        self.cursor = cursor;
        self
    }

    #[inline]
    pub fn ime(mut self, enable: bool) -> WindowBuilder<Ti, S> {
        self.enabled_ime = enable;
        self
    }

    #[inline]
    pub fn no_redirection_bitmap(mut self, enable: bool) -> WindowBuilder<Ti, S> {
        self.no_redirection_bitmap = enable;
        self
    }

    #[cfg(feature = "raw_input")]
    #[inline]
    pub fn raw_input_window_state(mut self, state: raw_input::WindowState) -> WindowBuilder<Ti, S> {
        self.raw_input_window_state = state;
        self
    }
}

impl<Ti, S> WindowBuilder<Ti, S>
where
    Ti: AsRef<str>,
    S: ToPhysicalSize<u32>,
{
    pub fn build(self) -> Result<Window, ApiError> {
        if is_context_null() {
            panic!("The window can be created after run");
        }
        unsafe {
            let dpi = get_dpi_from_point(self.position);
            let inner_size = self.inner_size.to_physical(dpi);
            let rc = adjust_window_rect(inner_size, self.style, WINDOW_EX_STYLE(0), dpi);
            let hinst = GetModuleHandleW(PCWSTR::null()).unwrap();
            let hwnd = CreateWindowExW(
                if self.no_redirection_bitmap {
                    WS_EX_NOREDIRECTIONBITMAP
                } else {
                    WINDOW_EX_STYLE(0)
                },
                &HSTRING::from(WINDOW_CLASS_NAME),
                &HSTRING::from(self.title.as_ref()),
                self.style,
                self.position.x,
                self.position.y,
                (rc.right - rc.left) as i32,
                (rc.bottom - rc.top) as i32,
                HWND(0),
                HMENU(0),
                hinst,
                None,
            );
            if hwnd == HWND(0) {
                return Err(ApiError::new());
            }
            let window = LocalWindow::new(
                hwnd,
                WindowState {
                    title: self.title.as_ref().to_string(),
                    style: self.style,
                    set_position: (self.position.x, self.position.y),
                    set_inner_size: inner_size,
                    enabled_ime: self.enabled_ime,
                    visible_ime_composition_window: self.visible_ime_composition_window,
                    visible_ime_candidate_window: self.visible_ime_candidate_window,
                    ime_position: PhysicalPosition::new(0, 0),
                    children: self.children,
                    closed: false,
                    cursor: self.cursor,
                },
            );
            self.cursor.set();
            let handle = window.handle.clone();
            if let Some(parent) = self.parent {
                let mut state = parent.state.write().unwrap();
                state.children.push(handle.clone());
            }
            if self.visibility {
                window.handle.show();
            }
            if self.accept_drag_files {
                DragAcceptFiles(hwnd, true);
            }
            if let Some(icon) = self.icon {
                let big = load_icon(&icon, hinst);
                let small = load_small_icon(&icon, hinst);
                SendMessageW(
                    HWND(handle.raw_handle() as _),
                    WM_SETICON,
                    WPARAM(ICON_BIG as _),
                    LPARAM(big.0 as _),
                );
                SendMessageW(
                    HWND(handle.raw_handle() as _),
                    WM_SETICON,
                    WPARAM(ICON_SMALL as _),
                    LPARAM(small.0 as _),
                );
            }
            if self.enabled_ime {
                window.handle.ime(self.enabled_ime);
            }
            #[cfg(feature = "raw_input")]
            raw_input::register_devices(&window.handle, self.raw_input_window_state);
            push_window(hwnd, window);
            Ok(handle)
        }
    }
}

/// The object to build a window into the parent window.
pub struct InnerWindowBuilder<W = (), P = (), S = ()> {
    parent: W,
    position: P,
    size: S,
    visibility: bool,
    visible_ime_composition_window: bool,
    visible_ime_candidate_window: bool,
    accept_drag_files: bool,
    cursor: Cursor,
    #[cfg(feature = "raw_input")]
    raw_input_window_state: raw_input::WindowState,
}

impl InnerWindowBuilder<(), (), ()> {
    #[allow(clippy::new_ret_no_self)]
    #[inline]
    pub fn new() -> InnerWindowBuilder<(), LogicalPosition<f32>, ()> {
        InnerWindowBuilder {
            parent: (),
            position: LogicalPosition::new(0.0, 0.0),
            size: (),
            visibility: true,
            visible_ime_composition_window: false,
            visible_ime_candidate_window: true,
            accept_drag_files: false,
            cursor: Cursor::Arrow,
            #[cfg(feature = "raw_input")]
            raw_input_window_state: raw_input::WindowState::Foreground,
        }
    }
}

impl<W, P, S> InnerWindowBuilder<W, P, S> {
    #[inline]
    pub fn parent(self, parent: &Window) -> InnerWindowBuilder<Window, P, S> {
        InnerWindowBuilder {
            parent: parent.clone(),
            position: self.position,
            size: self.size,
            visibility: self.visibility,
            visible_ime_composition_window: self.visible_ime_composition_window,
            visible_ime_candidate_window: self.visible_ime_candidate_window,
            accept_drag_files: self.accept_drag_files,
            cursor: self.cursor,
            #[cfg(feature = "raw_input")]
            raw_input_window_state: self.raw_input_window_state,
        }
    }

    #[inline]
    pub fn position<T>(self, position: T) -> InnerWindowBuilder<W, T, S> {
        InnerWindowBuilder {
            parent: self.parent,
            position,
            size: self.size,
            visibility: self.visibility,
            visible_ime_composition_window: self.visible_ime_composition_window,
            visible_ime_candidate_window: self.visible_ime_candidate_window,
            accept_drag_files: self.accept_drag_files,
            cursor: self.cursor,
            #[cfg(feature = "raw_input")]
            raw_input_window_state: self.raw_input_window_state,
        }
    }

    #[inline]
    pub fn size<T>(self, size: T) -> InnerWindowBuilder<W, P, T> {
        InnerWindowBuilder {
            parent: self.parent,
            position: self.position,
            size,
            visibility: self.visibility,
            visible_ime_composition_window: self.visible_ime_composition_window,
            visible_ime_candidate_window: self.visible_ime_candidate_window,
            accept_drag_files: self.accept_drag_files,
            cursor: self.cursor,
            #[cfg(feature = "raw_input")]
            raw_input_window_state: self.raw_input_window_state,
        }
    }

    #[inline]
    pub fn visible(mut self, visibility: bool) -> Self {
        self.visibility = visibility;
        self
    }

    #[inline]
    pub fn accept_drag_files(mut self) -> Self {
        self.accept_drag_files = true;
        self
    }
}

impl<P, S> InnerWindowBuilder<Window, P, S>
where
    P: ToPhysicalPosition<i32>,
    S: ToPhysicalSize<u32>,
{
    pub fn build(self) -> Result<Window, ApiError> {
        unsafe {
            let dpi = self.parent.dpi();
            let position = self.position.to_physical(dpi as i32);
            let size = self.size.to_physical(dpi);
            let rc = adjust_window_rect(size, WS_CHILD, WINDOW_EX_STYLE(0), dpi);
            let hinst = GetModuleHandleW(PCWSTR::null()).unwrap();
            let hwnd = CreateWindowExW(
                WINDOW_EX_STYLE(0),
                &HSTRING::from(WINDOW_CLASS_NAME),
                PCWSTR::null(),
                WS_CHILD,
                position.x,
                position.y,
                (rc.right - rc.left) as i32,
                (rc.bottom - rc.top) as i32,
                HWND(self.parent.raw_handle() as _),
                HMENU(0),
                hinst,
                None,
            );
            if hwnd == HWND(0) {
                return Err(ApiError::new());
            }
            let window = LocalWindow::new(
                hwnd,
                WindowState {
                    title: String::new(),
                    style: WS_CHILD,
                    set_position: (position.x, position.y),
                    set_inner_size: size,
                    enabled_ime: self.parent.is_enabled_ime(),
                    visible_ime_composition_window: self.visible_ime_composition_window,
                    visible_ime_candidate_window: self.visible_ime_candidate_window,
                    ime_position: PhysicalPosition::new(0, 0),
                    children: vec![],
                    cursor: self.cursor,
                    closed: false,
                },
            );
            let handle = window.handle.clone();
            if self.visibility {
                window.handle.show();
            }
            if self.accept_drag_files {
                DragAcceptFiles(hwnd, true);
            }
            #[cfg(feature = "raw_input")]
            raw_input::register_devices(&window.handle, self.raw_input_window_state);
            push_window(hwnd, window);
            Ok(handle)
        }
    }
}

pub(crate) struct WindowState {
    pub title: String,
    pub style: WINDOW_STYLE,
    pub set_position: (i32, i32),
    pub set_inner_size: PhysicalSize<u32>,
    pub enabled_ime: bool,
    pub visible_ime_composition_window: bool,
    pub visible_ime_candidate_window: bool,
    pub ime_position: PhysicalPosition<i32>,
    pub children: Vec<Window>,
    pub closed: bool,
    pub cursor: Cursor,
}

#[derive(Clone)]
pub(crate) struct LocalWindow {
    pub handle: Window,
    pub ime_context: Rc<RefCell<ime::ImmContext>>,
}

impl LocalWindow {
    pub(crate) fn new(hwnd: HWND, state: WindowState) -> Self {
        Self {
            handle: Window {
                hwnd: WindowHandle(hwnd),
                state: Arc::new(RwLock::new(state)),
            },
            ime_context: Rc::new(RefCell::new(ime::ImmContext::new(hwnd))),
        }
    }
}

/// Represents a window.
#[derive(Clone)]
pub struct Window {
    pub(crate) hwnd: WindowHandle,
    pub(crate) state: Arc<RwLock<WindowState>>,
}

impl Window {
    #[inline]
    pub fn builder() -> WindowBuilder<&'static str, Size<u32, Logical>> {
        WindowBuilder::new()
    }

    #[inline]
    pub fn inner_builder() -> InnerWindowBuilder<(), LogicalPosition<f32>, ()> {
        InnerWindowBuilder::new()
    }

    #[inline]
    pub fn title(&self) -> String {
        let state = self.state.read().unwrap();
        state.title.clone()
    }

    #[inline]
    pub fn set_title(&self, title: impl AsRef<str>) {
        let mut state = self.state.write().unwrap();
        state.title = title.as_ref().to_string();
        unsafe {
            PostMessageW(
                self.hwnd.0,
                WM_USER,
                WPARAM(UserMessage::SetTitle as _),
                LPARAM(0),
            );
        }
    }

    #[inline]
    pub fn position(&self) -> ScreenPosition {
        unsafe {
            let mut rc = RECT::default();
            GetWindowRect(self.hwnd.0, &mut rc);
            ScreenPosition::new(rc.left, rc.top)
        }
    }

    #[inline]
    pub fn set_position(&self, position: ScreenPosition) {
        unsafe {
            let mut state = self.state.write().unwrap();
            state.set_position = (position.x, position.y);
            PostMessageW(
                self.hwnd.0,
                WM_USER,
                WPARAM(UserMessage::SetPosition as _),
                LPARAM(0),
            );
        }
    }

    #[inline]
    pub fn inner_size(&self) -> PhysicalSize<u32> {
        unsafe {
            let mut rc = RECT::default();
            GetClientRect(self.hwnd.0, &mut rc);
            PhysicalSize::new((rc.right - rc.left) as u32, (rc.bottom - rc.top) as u32)
        }
    }

    #[inline]
    pub fn set_inner_size(&self, size: impl ToPhysicalSize<u32>) {
        unsafe {
            let mut state = self.state.write().unwrap();
            state.set_inner_size = size.to_physical(self.dpi());
            PostMessageW(
                self.hwnd.0,
                WM_USER,
                WPARAM(UserMessage::SetInnerSize as _),
                LPARAM(0),
            );
        }
    }

    #[inline]
    pub fn dpi(&self) -> u32 {
        unsafe { GetDpiForWindow(self.hwnd.0) }
    }

    #[inline]
    pub fn scale_factor(&self) -> f32 {
        unsafe { GetDpiForWindow(self.hwnd.0) as f32 / DEFAULT_DPI as f32 }
    }

    #[inline]
    pub fn show(&self) {
        unsafe {
            if self.is_minimized() {
                self.restore();
            }
            ShowWindowAsync(self.hwnd.0, SW_SHOW);
        }
    }

    #[inline]
    pub fn hide(&self) {
        unsafe {
            ShowWindowAsync(self.hwnd.0, SW_HIDE);
        }
    }

    #[inline]
    pub fn minimize(&self) {
        unsafe {
            ShowWindowAsync(self.hwnd.0, SW_MINIMIZE);
        }
    }

    #[inline]
    pub fn maximize(&self) {
        unsafe {
            ShowWindowAsync(self.hwnd.0, SW_MAXIMIZE);
        }
    }

    #[inline]
    pub fn restore(&self) {
        unsafe {
            ShowWindowAsync(self.hwnd.0, SW_RESTORE);
        }
    }

    #[inline]
    pub fn is_minimized(&self) -> bool {
        unsafe { IsIconic(self.hwnd.0).as_bool() }
    }

    #[inline]
    pub fn is_maximized(&self) -> bool {
        unsafe { IsZoomed(self.hwnd.0).as_bool() }
    }

    #[inline]
    pub fn redraw(&self) {
        unsafe {
            RedrawWindow(self.hwnd.0, None, HRGN(0), RDW_INTERNALPAINT);
        }
    }

    #[inline]
    pub fn is_closed(&self) -> bool {
        let state = self.state.read().unwrap();
        state.closed
    }

    #[inline]
    pub fn close(&self) {
        unsafe {
            if !self.is_closed() {
                PostMessageW(self.hwnd.0, WM_CLOSE, WPARAM(0), LPARAM(0));
            }
        }
    }

    #[inline]
    pub fn ime_position(&self) -> PhysicalPosition<i32> {
        let state = self.state.read().unwrap();
        PhysicalPosition::new(state.ime_position.x, state.ime_position.y)
    }

    #[inline]
    pub fn ime(&self, enable: bool) {
        unsafe {
            if enable {
                PostMessageW(
                    self.hwnd.0,
                    WM_USER,
                    WPARAM(UserMessage::EnableIme as _),
                    LPARAM(0),
                );
            } else {
                PostMessageW(
                    self.hwnd.0,
                    WM_USER,
                    WPARAM(UserMessage::DisableIme as _),
                    LPARAM(0),
                );
            }
        }
        let mut state = self.state.write().unwrap();
        state.enabled_ime = enable;
    }

    #[inline]
    pub fn set_ime_position(&self, position: impl ToPhysicalPosition<i32>) {
        let mut state = self.state.write().unwrap();
        let position = position.to_physical(self.dpi() as i32);
        state.ime_position.x = position.x;
        state.ime_position.y = position.y;
        let imc = ime::Imc::get(self.hwnd.0);
        if state.visible_ime_composition_window {
            imc.set_composition_window_position(state.ime_position);
        }
        if state.visible_ime_candidate_window {
            imc.set_candidate_window_position(
                state.ime_position,
                state.visible_ime_composition_window,
            );
        }
    }

    #[inline]
    pub fn is_enabled_ime(&self) -> bool {
        let state = self.state.read().unwrap();
        state.enabled_ime
    }

    #[inline]
    pub fn style(&self) -> WindowStyle {
        let state = self.state.read().unwrap();
        WindowStyle(state.style)
    }

    #[inline]
    pub fn set_style(&self, style: impl Style) {
        unsafe {
            let mut state = self.state.write().unwrap();
            state.style = style.value();
            PostMessageW(
                self.hwnd.0,
                WM_USER,
                WPARAM(UserMessage::SetStyle as _),
                LPARAM(0),
            );
        }
    }

    #[inline]
    pub fn accept_drag_files(&self, enabled: bool) {
        unsafe {
            PostMessageW(
                self.hwnd.0,
                WM_USER,
                WPARAM(UserMessage::AcceptDragFiles as _),
                LPARAM(if enabled { 1 } else { 0 }),
            );
        }
    }

    #[inline]
    pub fn set_cursor(&self, cursor: Cursor) {
        let mut state = self.state.write().unwrap();
        state.cursor = cursor;
        cursor.set();
    }

    #[inline]
    pub fn raw_handle(&self) -> *mut std::ffi::c_void {
        self.hwnd.0 .0 as _
    }
}

impl PartialEq for Window {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.hwnd == other.hwnd
    }
}

impl Eq for Window {}

unsafe impl HasRawWindowHandle for Window {
    #[inline]
    fn raw_window_handle(&self) -> RawWindowHandle {
        let mut handle = Win32WindowHandle::empty();
        handle.hinstance =
            unsafe { GetModuleHandleW(PCWSTR::null()).unwrap().0 as *mut std::ffi::c_void };
        handle.hwnd = self.hwnd.0 .0 as *mut std::ffi::c_void;
        RawWindowHandle::Win32(handle)
    }
}