async_winit/platform/windows.rs
1/*
2
3`async-winit` is free software: you can redistribute it and/or modify it under the terms of one of
4the following licenses:
5
6* GNU Lesser General Public License as published by the Free Software Foundation, either
7 version 3 of the License, or (at your option) any later version.
8* Mozilla Public License as published by the Mozilla Foundation, version 2.
9
10`async-winit` is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
11the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
12Public License and the Patron License for more details.
13
14You should have received a copy of the GNU Lesser General Public License and the Mozilla
15Public License along with `async-winit`. If not, see <https://www.gnu.org/licenses/>.
16
17*/
18
19// This file is partially derived from `winit`, which was originally created by Pierre Krieger and
20// contributers. It was originally released under the MIT license.
21
22//! Windows-specific functionality.
23
24#[doc(inline)]
25pub use winit::platform::windows::{
26 IconExtWindows, MonitorHandleExtWindows, HINSTANCE, HMENU, HMONITOR, HWND,
27};
28
29use super::__private as sealed;
30use crate::event_loop::EventLoopBuilder;
31use crate::window::{Icon, Window, WindowBuilder};
32use crate::ThreadSafety;
33
34use std::os::raw::c_void;
35
36use winit::platform::windows::{
37 EventLoopBuilderExtWindows as _, WindowBuilderExtWindows as _, WindowExtWindows as _,
38};
39
40/// Additional methods on `EventLoop` that are specific to Windows.
41pub trait EventLoopBuilderExtWindows: sealed::EventLoopBuilderPrivate {
42 /// Whether to allow the event loop to be created off of the main thread.
43 ///
44 /// By default, the window is only allowed to be created on the main
45 /// thread, to make platform compatibility easier.
46 ///
47 /// # `Window` caveats
48 ///
49 /// Note that any `Window` created on the new thread will be destroyed when the thread
50 /// terminates. Attempting to use a `Window` after its parent thread terminates has
51 /// unspecified, although explicitly not undefined, behavior.
52 fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
53
54 /// Whether to enable process-wide DPI awareness.
55 ///
56 /// By default, `winit` will attempt to enable process-wide DPI awareness. If
57 /// that's undesirable, you can disable it with this function.
58 ///
59 /// # Example
60 ///
61 /// Disable process-wide DPI awareness.
62 ///
63 /// ```
64 /// use winit::event_loop::EventLoopBuilder;
65 /// #[cfg(target_os = "windows")]
66 /// use winit::platform::windows::EventLoopBuilderExtWindows;
67 ///
68 /// let mut builder = EventLoopBuilder::new();
69 /// #[cfg(target_os = "windows")]
70 /// builder.with_dpi_aware(false);
71 /// # if false { // We can't test this part
72 /// let event_loop = builder.build();
73 /// # }
74 /// ```
75 fn with_dpi_aware(&mut self, dpi_aware: bool) -> &mut Self;
76
77 /// A callback to be executed before dispatching a win32 message to the window procedure.
78 /// Return true to disable winit's internal message dispatching.
79 fn with_msg_hook<F>(&mut self, callback: F) -> &mut Self
80 where
81 F: FnMut(*const c_void) -> bool + 'static;
82}
83
84impl EventLoopBuilderExtWindows for EventLoopBuilder {
85 fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
86 self.inner.with_any_thread(any_thread);
87 self
88 }
89
90 fn with_dpi_aware(&mut self, dpi_aware: bool) -> &mut Self {
91 self.inner.with_dpi_aware(dpi_aware);
92 self
93 }
94
95 fn with_msg_hook<F>(&mut self, callback: F) -> &mut Self
96 where
97 F: FnMut(*const c_void) -> bool + 'static,
98 {
99 self.inner.with_msg_hook(callback);
100 self
101 }
102}
103
104/// Additional methods on `Window` that are specific to Windows.
105pub trait WindowExtWindows: sealed::WindowPrivate {
106 /// Returns the HINSTANCE of the window
107 fn hinstance(&self) -> HINSTANCE;
108 /// Returns the native handle that is used by this window.
109 ///
110 /// The pointer will become invalid when the native window was destroyed.
111 fn hwnd(&self) -> HWND;
112
113 /// Enables or disables mouse and keyboard input to the specified window.
114 ///
115 /// A window must be enabled before it can be activated.
116 /// If an application has create a modal dialog box by disabling its owner window
117 /// (as described in [`WindowBuilderExtWindows::with_owner_window`]), the application must enable
118 /// the owner window before destroying the dialog box.
119 /// Otherwise, another window will receive the keyboard focus and be activated.
120 ///
121 /// If a child window is disabled, it is ignored when the system tries to determine which
122 /// window should receive mouse messages.
123 ///
124 /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks>
125 /// and <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#disabled-windows>
126 fn set_enable(&self, enabled: bool);
127
128 /// This sets `ICON_BIG`. A good ceiling here is 256x256.
129 fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
130
131 /// Whether to show or hide the window icon in the taskbar.
132 fn set_skip_taskbar(&self, skip: bool);
133
134 /// Shows or hides the background drop shadow for undecorated windows.
135 ///
136 /// Enabling the shadow causes a thin 1px line to appear on the top of the window.
137 fn set_undecorated_shadow(&self, shadow: bool);
138}
139
140impl<TS: ThreadSafety> WindowExtWindows for Window<TS> {
141 fn hwnd(&self) -> HWND {
142 self.window().hwnd()
143 }
144
145 fn hinstance(&self) -> HINSTANCE {
146 self.window().hinstance()
147 }
148
149 fn set_enable(&self, enabled: bool) {
150 self.window().set_enable(enabled);
151 }
152
153 fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
154 self.window().set_taskbar_icon(taskbar_icon);
155 }
156
157 fn set_skip_taskbar(&self, skip: bool) {
158 self.window().set_skip_taskbar(skip);
159 }
160
161 fn set_undecorated_shadow(&self, shadow: bool) {
162 self.window().set_undecorated_shadow(shadow);
163 }
164}
165
166/// Additional methods on `WindowBuilder` that are specific to Windows.
167pub trait WindowBuilderExtWindows: sealed::WindowBuilderPrivate {
168 /// Set an owner to the window to be created. Can be used to create a dialog box, for example.
169 /// This only works when `WindowBuilder::with_parent_window` isn't called or set to `None`.
170 /// Can be used in combination with [`WindowExtWindows::set_enable(false)`](WindowExtWindows::set_enable)
171 /// on the owner window to create a modal dialog box.
172 ///
173 /// From MSDN:
174 /// - An owned window is always above its owner in the z-order.
175 /// - The system automatically destroys an owned window when its owner is destroyed.
176 /// - An owned window is hidden when its owner is minimized.
177 ///
178 /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
179 fn with_owner_window(self, parent: HWND) -> WindowBuilder;
180
181 /// Sets a menu on the window to be created.
182 ///
183 /// Parent and menu are mutually exclusive; a child window cannot have a menu!
184 ///
185 /// The menu must have been manually created beforehand with `CreateMenu` or similar.
186 ///
187 /// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look.
188 /// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect.
189 fn with_menu(self, menu: HMENU) -> WindowBuilder;
190
191 /// This sets `ICON_BIG`. A good ceiling here is 256x256.
192 fn with_taskbar_icon(self, taskbar_icon: Option<Icon>) -> WindowBuilder;
193
194 /// This sets `WS_EX_NOREDIRECTIONBITMAP`.
195 fn with_no_redirection_bitmap(self, flag: bool) -> WindowBuilder;
196
197 /// Enables or disables drag and drop support (enabled by default). Will interfere with other crates
198 /// that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` instead of
199 /// `COINIT_APARTMENTTHREADED`) on the same thread. Note that winit may still attempt to initialize
200 /// COM API regardless of this option. Currently only fullscreen mode does that, but there may be more in the future.
201 /// If you need COM API with `COINIT_MULTITHREADED` you must initialize it before calling any winit functions.
202 /// See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information.
203 fn with_drag_and_drop(self, flag: bool) -> WindowBuilder;
204
205 /// Whether show or hide the window icon in the taskbar.
206 fn with_skip_taskbar(self, skip: bool) -> WindowBuilder;
207
208 /// Shows or hides the background drop shadow for undecorated windows.
209 ///
210 /// The shadow is hidden by default.
211 /// Enabling the shadow causes a thin 1px line to appear on the top of the window.
212 fn with_undecorated_shadow(self, shadow: bool) -> WindowBuilder;
213}
214
215#[derive(Default)]
216pub(crate) struct PlatformSpecific {
217 owner_window: Option<HWND>,
218 menu: Option<HMENU>,
219 taskbar_icon: Option<Icon>,
220 no_redirection_bitmap: Option<bool>,
221 drag_and_drop: Option<bool>,
222 skip_taskbar: Option<bool>,
223 undecorated_shadow: Option<bool>,
224}
225
226impl PlatformSpecific {
227 pub(crate) fn apply_to(
228 self,
229 mut wb: winit::window::WindowBuilder,
230 ) -> winit::window::WindowBuilder {
231 if let Some(owner_window) = self.owner_window {
232 wb = wb.with_owner_window(owner_window);
233 }
234
235 if let Some(menu) = self.menu {
236 wb = wb.with_menu(menu);
237 }
238
239 if let Some(taskbar_icon) = self.taskbar_icon {
240 wb = wb.with_taskbar_icon(Some(taskbar_icon));
241 }
242
243 if let Some(no_redirection_bitmap) = self.no_redirection_bitmap {
244 wb = wb.with_no_redirection_bitmap(no_redirection_bitmap);
245 }
246
247 if let Some(drag_and_drop) = self.drag_and_drop {
248 wb = wb.with_drag_and_drop(drag_and_drop);
249 }
250
251 if let Some(skip_taskbar) = self.skip_taskbar {
252 wb = wb.with_skip_taskbar(skip_taskbar);
253 }
254
255 if let Some(undecorated_shadow) = self.undecorated_shadow {
256 wb = wb.with_undecorated_shadow(undecorated_shadow);
257 }
258
259 wb
260 }
261}