//! # Winit Win32 / Windows backend //! //! The supported OS version is Windows 7 or higher, though Windows 10 is //! tested regularly. #![cfg(target_os = "windows")] // FIXME(madsmtm): Allow compiling on all platforms. #[macro_use] mod util; mod dark_mode; mod definitions; mod dpi; mod drop_handler; mod event_loop; mod icon; mod ime; mod keyboard; mod keyboard_layout; mod monitor; mod raw_input; mod window; mod window_state; use std::borrow::Borrow; use std::ffi::c_void; use std::ops::Deref; use std::path::Path; use std::sync::Arc; use ::dpi::PhysicalSize; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use windows_sys::Win32::Foundation::HANDLE; use winit_core::event::DeviceId; use winit_core::icon::{BadIcon, Icon}; use winit_core::window::{PlatformWindowAttributes, Window as CoreWindow}; pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes}; use self::icon::{RaiiIcon, SelectedCursor}; pub use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey}; pub use self::monitor::{MonitorHandle, VideoModeHandle}; pub use self::window::Window; /// Window Handle type used by Win32 API pub type HWND = *mut c_void; /// Menu Handle type used by Win32 API pub type HMENU = *mut c_void; /// Monitor Handle type used by Win32 API pub type HMONITOR = *mut c_void; /// Describes a system-drawn backdrop material of a window. /// /// For a detailed explanation, see [`DWM_SYSTEMBACKDROP_TYPE docs`]. /// /// [`DWM_SYSTEMBACKDROP_TYPE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum BackdropType { /// Corresponds to `DWMSBT_AUTO`. /// /// Usually draws a default backdrop effect on the title bar. #[default] Auto = 0, /// Corresponds to `DWMSBT_NONE`. None = 1, /// Corresponds to `DWMSBT_MAINWINDOW`. /// /// Draws the Mica backdrop material. MainWindow = 2, /// Corresponds to `DWMSBT_TRANSIENTWINDOW`. /// /// Draws the Background Acrylic backdrop material. TransientWindow = 3, /// Corresponds to `DWMSBT_TABBEDWINDOW`. /// /// Draws the Alt Mica backdrop material. TabbedWindow = 4, } /// Describes a color used by Windows #[repr(transparent)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Color(u32); impl Color { // Special constant only valid for the window border and therefore modeled using Option // for user facing code const NONE: Color = Color(0xfffffffe); /// Use the system's default color pub const SYSTEM_DEFAULT: Color = Color(0xffffffff); /// Create a new color from the given RGB values pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self { Self((r as u32) | ((g as u32) << 8) | ((b as u32) << 16)) } } impl Default for Color { fn default() -> Self { Self::SYSTEM_DEFAULT } } /// Describes how the corners of a window should look like. /// /// For a detailed explanation, see [`DWM_WINDOW_CORNER_PREFERENCE docs`]. /// /// [`DWM_WINDOW_CORNER_PREFERENCE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference #[repr(i32)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum CornerPreference { /// Corresponds to `DWMWCP_DEFAULT`. /// /// Let the system decide when to round window corners. #[default] Default = 0, /// Corresponds to `DWMWCP_DONOTROUND`. /// /// Never round window corners. DoNotRound = 1, /// Corresponds to `DWMWCP_ROUND`. /// /// Round the corners, if appropriate. Round = 2, /// Corresponds to `DWMWCP_ROUNDSMALL`. /// /// Round the corners if appropriate, with a small radius. RoundSmall = 3, } /// A wrapper around a [`Window`] that ignores thread-specific window handle limitations. /// /// See [`WindowBorrowExtWindows::any_thread`] for more information. #[derive(Clone, Debug)] pub struct AnyThread(W); impl AnyThread { /// Get a reference to the inner window. #[inline] pub fn get_ref(&self) -> &dyn CoreWindow { &self.0 } } impl Deref for AnyThread { type Target = W; fn deref(&self) -> &Self::Target { &self.0 } } impl rwh_06::HasWindowHandle for AnyThread { fn window_handle(&self) -> Result, rwh_06::HandleError> { // SAFETY: The top level user has asserted this is only used safely. unsafe { self.get_ref().window_handle_any_thread() } } } /// Additional methods on `EventLoop` that are specific to Windows. pub trait EventLoopBuilderExtWindows { /// Whether to allow the event loop to be created off of the main thread. /// /// By default, the window is only allowed to be created on the main /// thread, to make platform compatibility easier. /// /// # `Window` caveats /// /// Note that any `Window` created on the new thread will be destroyed when the thread /// terminates. Attempting to use a `Window` after its parent thread terminates has /// unspecified, although explicitly not undefined, behavior. fn with_any_thread(&mut self, any_thread: bool) -> &mut Self; /// Whether to enable process-wide DPI awareness. /// /// By default, `winit` will attempt to enable process-wide DPI awareness. If /// that's undesirable, you can disable it with this function. /// /// # Example /// /// Disable process-wide DPI awareness. /// /// ``` /// use winit::event_loop::EventLoop; /// #[cfg(target_os = "windows")] /// use winit::platform::windows::EventLoopBuilderExtWindows; /// /// let mut builder = EventLoop::builder(); /// #[cfg(target_os = "windows")] /// builder.with_dpi_aware(false); /// # if false { // We can't test this part /// let event_loop = builder.build(); /// # } /// ``` fn with_dpi_aware(&mut self, dpi_aware: bool) -> &mut Self; /// A callback to be executed before dispatching a win32 message to the window procedure. /// Return true to disable winit's internal message dispatching. /// /// # Example /// /// ``` /// # use windows_sys::Win32::UI::WindowsAndMessaging::{ACCEL, CreateAcceleratorTableW, TranslateAcceleratorW, DispatchMessageW, TranslateMessage, MSG}; /// use winit::event_loop::EventLoop; /// #[cfg(target_os = "windows")] /// use winit::platform::windows::EventLoopBuilderExtWindows; /// /// let mut builder = EventLoop::builder(); /// #[cfg(target_os = "windows")] /// builder.with_msg_hook(|msg|{ /// let msg = msg as *const MSG; /// # let accels: Vec = Vec::new(); /// let translated = unsafe { /// TranslateAcceleratorW( /// (*msg).hwnd, /// CreateAcceleratorTableW(accels.as_ptr() as _, 1), /// msg, /// ) == 1 /// }; /// translated /// }); /// ``` fn with_msg_hook(&mut self, callback: F) -> &mut Self where F: FnMut(*const c_void) -> bool + 'static; } /// Additional methods on `Window` that are specific to Windows. pub trait WindowExtWindows { /// Enables or disables mouse and keyboard input to the specified window. /// /// A window must be enabled before it can be activated. /// If an application has create a modal dialog box by disabling its owner window /// (as described in [`WindowAttributesWindows::with_owner_window`]), the application must /// enable the owner window before destroying the dialog box. /// Otherwise, another window will receive the keyboard focus and be activated. /// /// If a child window is disabled, it is ignored when the system tries to determine which /// window should receive mouse messages. /// /// For more information, see /// and fn set_enable(&self, enabled: bool); /// This sets `ICON_BIG`. A good ceiling here is 256x256. fn set_taskbar_icon(&self, taskbar_icon: Option); /// Whether to show or hide the window icon in the taskbar. fn set_skip_taskbar(&self, skip: bool); /// Shows or hides the background drop shadow for undecorated windows. /// /// Enabling the shadow causes a thin 1px line to appear on the top of the window. fn set_undecorated_shadow(&self, shadow: bool); /// Sets system-drawn backdrop type. /// /// Requires Windows 11 build 22523+. fn set_system_backdrop(&self, backdrop_type: BackdropType); /// Sets the color of the window border. /// /// Supported starting with Windows 11 Build 22000. fn set_border_color(&self, color: Option); /// Sets the background color of the title bar. /// /// Supported starting with Windows 11 Build 22000. fn set_title_background_color(&self, color: Option); /// Sets the color of the window title. /// /// Supported starting with Windows 11 Build 22000. fn set_title_text_color(&self, color: Color); /// Sets the preferred style of the window corners. /// /// Supported starting with Windows 11 Build 22000. fn set_corner_preference(&self, preference: CornerPreference); /// Sets if the reported [`winit_core::event::WindowEvent::MouseWheel`] event /// should account for scroll speed system settings. /// /// The default scroll speed on Windows is 3 lines/characters per scroll, /// this will be 1 if you set it to false. /// /// The default is `true`. fn set_use_system_scroll_speed(&self, should_use: bool); /// Get the raw window handle for this [`Window`] without checking for thread affinity. /// /// Window handles in Win32 have a property called "thread affinity" that ties them to their /// origin thread. Some operations can only happen on the window's origin thread, while others /// can be called from any thread. For example, [`SetWindowSubclass`] is not thread safe while /// [`GetDC`] is thread safe. /// /// In Rust terms, the window handle is `Send` sometimes but `!Send` other times. /// /// Therefore, in order to avoid confusing threading errors, [`Window`] only returns the /// window handle when the [`window_handle`] function is called from the thread that created /// the window. In other cases, it returns an [`Unavailable`] error. /// /// However in some cases you may already know that you are using the window handle for /// operations that are guaranteed to be thread-safe. In which case this function aims /// to provide an escape hatch so these functions are still accessible from other threads. /// /// # Safety /// /// It is the responsibility of the user to only pass the window handle into thread-safe /// Win32 APIs. /// /// [`SetWindowSubclass`]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-setwindowsubclass /// [`GetDC`]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc /// [`Window`]: crate::window::Window /// [`window_handle`]: https://docs.rs/raw-window-handle/latest/raw_window_handle/trait.HasWindowHandle.html#tymethod.window_handle /// [`Unavailable`]: https://docs.rs/raw-window-handle/latest/raw_window_handle/enum.HandleError.html#variant.Unavailable /// /// ## Example /// /// ```no_run /// # use winit::window::Window; /// # fn scope(window: Box) { /// use std::thread; /// /// use winit::platform::windows::WindowExtWindows; /// use winit::raw_window_handle::HasWindowHandle; /// /// // We can get the window handle on the current thread. /// let handle = window.window_handle().unwrap(); /// /// // However, on another thread, we can't! /// thread::spawn(move || { /// assert!(window.window_handle().is_err()); /// /// // We can use this function as an escape hatch. /// let handle = unsafe { window.window_handle_any_thread().unwrap() }; /// }); /// # } /// ``` unsafe fn window_handle_any_thread( &self, ) -> Result, rwh_06::HandleError>; } impl WindowExtWindows for dyn CoreWindow + '_ { #[inline] fn set_enable(&self, enabled: bool) { let window = self.cast_ref::().unwrap(); window.set_enable(enabled) } #[inline] fn set_taskbar_icon(&self, taskbar_icon: Option) { let window = self.cast_ref::().unwrap(); window.set_taskbar_icon(taskbar_icon) } #[inline] fn set_skip_taskbar(&self, skip: bool) { let window = self.cast_ref::().unwrap(); window.set_skip_taskbar(skip) } #[inline] fn set_undecorated_shadow(&self, shadow: bool) { let window = self.cast_ref::().unwrap(); window.set_undecorated_shadow(shadow) } #[inline] fn set_system_backdrop(&self, backdrop_type: BackdropType) { let window = self.cast_ref::().unwrap(); window.set_system_backdrop(backdrop_type) } #[inline] fn set_border_color(&self, color: Option) { let window = self.cast_ref::().unwrap(); window.set_border_color(color.unwrap_or(Color::NONE)) } #[inline] fn set_title_background_color(&self, color: Option) { // The windows docs don't mention NONE as a valid options but it works in practice and is // useful to circumvent the Windows option "Show accent color on title bars and // window borders" let window = self.cast_ref::().unwrap(); window.set_title_background_color(color.unwrap_or(Color::NONE)) } #[inline] fn set_title_text_color(&self, color: Color) { let window = self.cast_ref::().unwrap(); window.set_title_text_color(color) } #[inline] fn set_corner_preference(&self, preference: CornerPreference) { let window = self.cast_ref::().unwrap(); window.set_corner_preference(preference) } fn set_use_system_scroll_speed(&self, should_use: bool) { let window = self.cast_ref::().unwrap(); window.set_use_system_scroll_speed(should_use) } unsafe fn window_handle_any_thread( &self, ) -> Result, rwh_06::HandleError> { let window = self.cast_ref::().unwrap(); unsafe { let handle = window.rwh_06_no_thread_check()?; // SAFETY: The handle is valid in this context. Ok(rwh_06::WindowHandle::borrow_raw(handle)) } } } /// Additional methods for anything that dereference to [`Window`]. /// /// [`Window`]: crate::window::Window pub trait WindowBorrowExtWindows: Borrow + Sized { /// Create an object that allows accessing the inner window handle in a thread-unsafe way. /// /// It is possible to call [`window_handle_any_thread`] to get around Windows's thread /// affinity limitations. However, it may be desired to pass the [`Window`] into something /// that requires the [`HasWindowHandle`] trait, while ignoring thread affinity limitations. /// /// This function wraps anything that implements `Borrow` into a structure that /// uses the inner window handle as a mean of implementing [`HasWindowHandle`]. It wraps /// `Window`, `&Window`, `Arc`, and other reference types. /// /// # Safety /// /// It is the responsibility of the user to only pass the window handle into thread-safe /// Win32 APIs. /// /// [`Window`]: crate::window::Window /// [`HasWindowHandle`]: rwh_06::HasWindowHandle /// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread unsafe fn any_thread(self) -> AnyThread where Self: CoreWindow, { AnyThread(self) } } impl + Sized> WindowBorrowExtWindows for W {} #[derive(Clone, Debug)] pub struct WindowAttributesWindows { pub(crate) owner: Option, pub(crate) menu: Option, pub(crate) taskbar_icon: Option, pub(crate) no_redirection_bitmap: bool, pub(crate) drag_and_drop: bool, pub(crate) skip_taskbar: bool, pub(crate) class_name: String, pub(crate) decoration_shadow: bool, pub(crate) backdrop_type: BackdropType, pub(crate) clip_children: bool, pub(crate) border_color: Option, pub(crate) title_background_color: Option, pub(crate) title_text_color: Option, pub(crate) corner_preference: Option, pub(crate) use_system_wheel_speed: bool, } impl Default for WindowAttributesWindows { fn default() -> Self { Self { owner: None, menu: None, taskbar_icon: None, no_redirection_bitmap: false, drag_and_drop: true, skip_taskbar: false, class_name: "Window Class".to_string(), decoration_shadow: false, backdrop_type: BackdropType::default(), clip_children: true, border_color: None, title_background_color: None, title_text_color: None, corner_preference: None, use_system_wheel_speed: true, } } } unsafe impl Send for WindowAttributesWindows {} unsafe impl Sync for WindowAttributesWindows {} impl WindowAttributesWindows { /// Set an owner to the window to be created. Can be used to create a dialog box, for example. /// This only works when [`WindowAttributes::with_parent_window`] isn't called or set to `None`. /// Can be used in combination with /// [`WindowExtWindows::set_enable(false)`][WindowExtWindows::set_enable] on the owner /// window to create a modal dialog box. /// /// From MSDN: /// - An owned window is always above its owner in the z-order. /// - The system automatically destroys an owned window when its owner is destroyed. /// - An owned window is hidden when its owner is minimized. /// /// For more information, see /// /// [`WindowAttributes::with_parent_window`]: winit_core::window::WindowAttributes::with_parent_window pub fn with_owner_window(mut self, parent: HWND) -> Self { self.owner = Some(parent); self } /// Sets a menu on the window to be created. /// /// Parent and menu are mutually exclusive; a child window cannot have a menu! /// /// The menu must have been manually created beforehand with [`CreateMenu`] or similar. /// /// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how /// the menus look. If you use this, it is recommended that you combine it with /// `with_theme(Some(Theme::Light))` to avoid a jarring effect. /// /// [`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu" pub fn with_menu(mut self, menu: HMENU) -> Self { self.menu = Some(menu); self } /// This sets `ICON_BIG`. A good ceiling here is 256x256. pub fn with_taskbar_icon(mut self, taskbar_icon: Option) -> Self { self.taskbar_icon = taskbar_icon; self } /// This sets `WS_EX_NOREDIRECTIONBITMAP`. pub fn with_no_redirection_bitmap(mut self, flag: bool) -> Self { self.no_redirection_bitmap = flag; self } /// Enables or disables drag and drop support (enabled by default). Will interfere with other /// crates that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` /// instead of `COINIT_APARTMENTTHREADED`) on the same thread. Note that winit may still /// attempt to initialize COM API regardless of this option. Currently only fullscreen mode /// does that, but there may be more in the future. If you need COM API with /// `COINIT_MULTITHREADED` you must initialize it before calling any winit functions. See for more information. pub fn with_drag_and_drop(mut self, flag: bool) -> Self { self.drag_and_drop = flag; self } /// Whether show or hide the window icon in the taskbar. pub fn with_skip_taskbar(mut self, skip: bool) -> Self { self.skip_taskbar = skip; self } /// Customize the window class name. pub fn with_class_name>(mut self, class_name: S) -> Self { self.class_name = class_name.into(); self } /// Shows or hides the background drop shadow for undecorated windows. /// /// The shadow is hidden by default. /// Enabling the shadow causes a thin 1px line to appear on the top of the window. pub fn with_undecorated_shadow(mut self, shadow: bool) -> Self { self.decoration_shadow = shadow; self } /// Sets system-drawn backdrop type. /// /// Requires Windows 11 build 22523+. pub fn with_system_backdrop(mut self, backdrop_type: BackdropType) -> Self { self.backdrop_type = backdrop_type; self } /// This sets or removes `WS_CLIPCHILDREN` style. pub fn with_clip_children(mut self, flag: bool) -> Self { self.clip_children = flag; self } /// Sets the color of the window border. /// /// Supported starting with Windows 11 Build 22000. pub fn with_border_color(mut self, color: Option) -> Self { self.border_color = Some(color.unwrap_or(Color::NONE)); self } /// Sets the background color of the title bar. /// /// Supported starting with Windows 11 Build 22000. pub fn with_title_background_color(mut self, color: Option) -> Self { self.title_background_color = Some(color.unwrap_or(Color::NONE)); self } /// Sets the color of the window title. /// /// Supported starting with Windows 11 Build 22000. pub fn with_title_text_color(mut self, color: Color) -> Self { self.title_text_color = Some(color); self } /// Sets the preferred style of the window corners. /// /// Supported starting with Windows 11 Build 22000. pub fn with_corner_preference(mut self, corners: CornerPreference) -> Self { self.corner_preference = Some(corners); self } /// Sets if the reported [`winit_core::event::WindowEvent::MouseWheel`] event /// should account for scroll speed system settings. /// /// The default scroll speed on Windows is 3 lines/characters per scroll, /// this will be 1 if you set it to false. /// /// The default is `true`. pub fn with_use_system_scroll_speed(mut self, should_use: bool) -> Self { self.use_system_wheel_speed = should_use; self } } impl PlatformWindowAttributes for WindowAttributesWindows { fn box_clone(&self) -> Box { Box::from(self.clone()) } } /// Additional methods on `DeviceId` that are specific to Windows. pub trait DeviceIdExtWindows { /// Returns an identifier that persistently refers to this specific device. /// /// Will return `None` if the device is no longer available. fn persistent_identifier(&self) -> Option; } impl DeviceIdExtWindows for DeviceId { fn persistent_identifier(&self) -> Option { let raw_id = self.into_raw(); if raw_id != 0 { raw_input::get_raw_input_device_name(raw_id as HANDLE) } else { None } } } /// Windows specific `Icon`. /// /// Windows icons can be created from files, or from the [`embedded resources`](https://learn.microsoft.com/en-us/windows/win32/menurc/about-resource-files). /// /// The `ICON` resource definition statement use the following syntax: /// ```rc /// nameID ICON filename /// ``` /// `nameID` is a unique name or a 16-bit unsigned integer value identifying the resource, /// `filename` is the name of the file that contains the resource. /// /// More information about the `ICON` resource can be found at [`Microsoft Learn`](https://learn.microsoft.com/en-us/windows/win32/menurc/icon-resource) portal. #[derive(Clone, PartialEq, Eq, Hash)] pub struct WinIcon { pub(crate) inner: Arc, } impl WinIcon { /// Create an icon from a file path. /// /// Specify `size` to load a specific icon size from the file, or `None` to load the default /// icon size from the file. /// /// In cases where the specified size does not exist in the file, Windows may perform scaling /// to get an icon of the desired size. pub fn from_path>( path: P, size: Option>, ) -> Result { Self::from_path_impl(path, size) } /// Create an icon from a resource embedded in this executable or library by its ordinal id. /// /// The valid `ordinal` values range from 1 to [`u16::MAX`] (inclusive). The value `0` is an /// invalid ordinal id, but it can be used with [`from_resource_name`] as `"0"`. /// /// [`from_resource_name`]: Self::from_resource_name /// /// Specify `size` to load a specific icon size from the file, or `None` to load the default /// icon size from the file. /// /// In cases where the specified size does not exist in the file, Windows may perform scaling /// to get an icon of the desired size. pub fn from_resource( resource_id: u16, size: Option>, ) -> Result { Self::from_resource_impl(resource_id, size) } /// Create an icon from a resource embedded in this executable or library by its name. /// /// Specify `size` to load a specific icon size from the file, or `None` to load the default /// icon size from the file. /// /// In cases where the specified size does not exist in the file, Windows may perform scaling /// to get an icon of the desired size. /// /// # Notes /// /// Consider the following resource definition statements: /// ```rc /// app ICON "app.ico" /// 1 ICON "a.ico" /// 0027 ICON "custom.ico" /// 0 ICON "alt.ico" /// ``` /// /// Due to some internal implementation details of the resource embedding/loading process on /// Windows platform, strings that can be interpreted as 16-bit unsigned integers (`"1"`, /// `"002"`, etc.) cannot be used as valid resource names, and instead should be passed into /// [`from_resource`]: /// /// [`from_resource`]: Self::from_resource /// /// ```rust,no_run /// use winit::platform::windows::WinIcon; /// /// assert!(WinIcon::from_resource_name("app", None).is_ok()); /// assert!(WinIcon::from_resource(1, None).is_ok()); /// assert!(WinIcon::from_resource(27, None).is_ok()); /// assert!(WinIcon::from_resource_name("27", None).is_err()); /// assert!(WinIcon::from_resource_name("0027", None).is_err()); /// ``` /// /// While `0` cannot be used as an ordinal id (see [`from_resource`]), it can be used as a /// name: /// /// [`from_resource`]: IconExtWindows::from_resource /// /// ```rust,no_run /// # use winit::platform::windows::WinIcon; /// # use winit::icon::Icon; /// assert!(WinIcon::from_resource_name("0", None).is_ok()); /// assert!(WinIcon::from_resource(0, None).is_err()); /// ``` pub fn from_resource_name( resource_name: &str, size: Option>, ) -> Result { Self::from_resource_name_impl(resource_name, size) } } impl From for Icon { fn from(value: WinIcon) -> Self { Self(Arc::new(value)) } }