Skip to main content

winit/platform_impl/windows/
event_loop.rs

1#![allow(non_snake_case)]
2
3mod runner;
4
5use std::cell::Cell;
6use std::collections::VecDeque;
7use std::ffi::c_void;
8use std::marker::PhantomData;
9use std::os::windows::io::{AsRawHandle as _, FromRawHandle as _, OwnedHandle, RawHandle};
10use std::rc::Rc;
11use std::sync::atomic::{AtomicU32, Ordering};
12use std::sync::mpsc::{self, Receiver, Sender};
13use std::sync::{Arc, Mutex, MutexGuard};
14use std::time::{Duration, Instant};
15use std::{mem, panic, ptr};
16
17use crate::utils::Lazy;
18
19use windows_sys::Win32::Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE;
20use windows_sys::Win32::Foundation::{
21    GetLastError, FALSE, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_FAILED, WPARAM,
22};
23use windows_sys::Win32::Graphics::Gdi::{
24    GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient,
25    ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE,
26};
27use windows_sys::Win32::System::Ole::RevokeDragDrop;
28use windows_sys::Win32::System::Threading::{
29    CreateWaitableTimerExW, GetCurrentThreadId, SetWaitableTimer,
30    CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, INFINITE, TIMER_ALL_ACCESS,
31};
32use windows_sys::Win32::UI::Controls::{HOVER_DEFAULT, WM_MOUSELEAVE};
33use windows_sys::Win32::UI::Input::Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW};
34use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
35    ReleaseCapture, SetCapture, TrackMouseEvent, TME_LEAVE, TRACKMOUSEEVENT,
36};
37use windows_sys::Win32::UI::Input::Pointer::{
38    POINTER_FLAG_DOWN, POINTER_FLAG_UP, POINTER_FLAG_UPDATE,
39};
40use windows_sys::Win32::UI::Input::Touch::{
41    CloseTouchInputHandle, GetTouchInputInfo, TOUCHEVENTF_DOWN, TOUCHEVENTF_MOVE, TOUCHEVENTF_UP,
42    TOUCHINPUT,
43};
44use windows_sys::Win32::UI::Input::{RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE};
45use windows_sys::Win32::UI::WindowsAndMessaging::{
46    CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect, GetCursorPos,
47    GetMenu, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW,
48    RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos, TranslateMessage,
49    CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT,
50    MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
51    PT_TOUCH, QS_ALLINPUT, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE,
52    SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
53    WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
54    WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
55    WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
56    WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN,
57    WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
58    WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE,
59    WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP,
60    WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE,
61    WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED,
62    WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, WS_EX_LAYERED,
63    WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
64};
65
66use crate::dpi::{PhysicalPosition, PhysicalSize};
67use crate::error::EventLoopError;
68use crate::event::{
69    DeviceEvent, Event, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase, WindowEvent,
70};
71use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed};
72use crate::keyboard::ModifiersState;
73use crate::platform::pump_events::PumpStatus;
74use crate::platform_impl::platform::dark_mode::try_theme;
75use crate::platform_impl::platform::dpi::{become_dpi_aware, dpi_to_scale_factor};
76use crate::platform_impl::platform::drop_handler::FileDropHandler;
77use crate::platform_impl::platform::icon::WinCursor;
78use crate::platform_impl::platform::ime::ImeContext;
79use crate::platform_impl::platform::keyboard::KeyEventBuilder;
80use crate::platform_impl::platform::keyboard_layout::LAYOUT_CACHE;
81use crate::platform_impl::platform::monitor::{self, MonitorHandle};
82use crate::platform_impl::platform::window::InitData;
83use crate::platform_impl::platform::window_state::{
84    CursorFlags, ImeState, WindowFlags, WindowState,
85};
86use crate::platform_impl::platform::{
87    raw_input, util, wrap_device_id, Fullscreen, WindowId, DEVICE_ID,
88};
89use crate::window::{
90    CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId as RootWindowId,
91};
92use runner::{EventLoopRunner, EventLoopRunnerShared};
93
94use super::window::set_skip_taskbar;
95use super::SelectedCursor;
96
97/// some backends like macos uses an uninhabited `Never` type,
98/// on windows, `UserEvent`s are also dispatched through the
99/// WNDPROC callback, and due to the re-entrant nature of the
100/// callback, recursively delivered events must be queued in a
101/// buffer, the current implementation put this queue in
102/// `EventLoopRunner`, which is shared between the event pumping
103/// loop and the callback. because it's hard to decide from the
104/// outside whether a event needs to be buffered, I decided not
105/// use `Event<Never>` for the shared runner state, but use unit
106/// as a placeholder so user events can be buffered as usual,
107/// the real `UserEvent` is pulled from the mpsc channel directly
108/// when the placeholder event is delivered to the event handler
109pub(crate) struct UserEventPlaceholder;
110
111// here below, the generic `EventLoopRunnerShared<T>` is replaced with
112// `EventLoopRunnerShared<UserEventPlaceholder>` so we can get rid
113// of the generic parameter T in types which don't depend on T.
114// this is the approach which requires minimum changes to current
115// backend implementation. it should be considered transitional
116// and should be refactored and cleaned up eventually, I hope.
117
118pub(crate) struct WindowData {
119    pub window_state: Arc<Mutex<WindowState>>,
120    pub event_loop_runner: EventLoopRunnerShared<UserEventPlaceholder>,
121    pub key_event_builder: KeyEventBuilder,
122    pub _file_drop_handler: Option<FileDropHandler>,
123    pub userdata_removed: Cell<bool>,
124    pub recurse_depth: Cell<u32>,
125}
126
127impl WindowData {
128    fn send_event(&self, event: Event<UserEventPlaceholder>) {
129        self.event_loop_runner.send_event(event);
130    }
131
132    fn window_state_lock(&self) -> MutexGuard<'_, WindowState> {
133        self.window_state.lock().unwrap()
134    }
135}
136
137struct ThreadMsgTargetData {
138    event_loop_runner: EventLoopRunnerShared<UserEventPlaceholder>,
139}
140
141impl ThreadMsgTargetData {
142    fn send_event(&self, event: Event<UserEventPlaceholder>) {
143        self.event_loop_runner.send_event(event);
144    }
145}
146
147/// The result of a subclass procedure (the message handling callback)
148#[derive(Clone, Copy)]
149pub(crate) enum ProcResult {
150    DefWindowProc(WPARAM),
151    Value(isize),
152}
153
154pub struct EventLoop<T: 'static> {
155    user_event_sender: Sender<T>,
156    user_event_receiver: Receiver<T>,
157    window_target: RootAEL,
158    msg_hook: Option<Box<dyn FnMut(*const c_void) -> bool + 'static>>,
159    // It is a timer used on timed waits.
160    // It is created lazily in case if we have `ControlFlow::WaitUntil`.
161    // Keep it as a field to avoid recreating it on every `ControlFlow::WaitUntil`.
162    high_resolution_timer: Option<OwnedHandle>,
163}
164
165pub(crate) struct PlatformSpecificEventLoopAttributes {
166    pub(crate) any_thread: bool,
167    pub(crate) dpi_aware: bool,
168    pub(crate) msg_hook: Option<Box<dyn FnMut(*const c_void) -> bool + 'static>>,
169}
170
171impl Default for PlatformSpecificEventLoopAttributes {
172    fn default() -> Self {
173        Self { any_thread: false, dpi_aware: true, msg_hook: None }
174    }
175}
176
177pub struct ActiveEventLoop {
178    thread_id: u32,
179    thread_msg_target: HWND,
180    pub(crate) runner_shared: EventLoopRunnerShared<UserEventPlaceholder>,
181}
182
183impl<T: 'static> EventLoop<T> {
184    pub(crate) fn new(
185        attributes: &mut PlatformSpecificEventLoopAttributes,
186    ) -> Result<Self, EventLoopError> {
187        let thread_id = unsafe { GetCurrentThreadId() };
188
189        if !attributes.any_thread && thread_id != main_thread_id() {
190            panic!(
191                "Initializing the event loop outside of the main thread is a significant \
192                 cross-platform compatibility hazard. If you absolutely need to create an \
193                 EventLoop on a different thread, you can use the \
194                 `EventLoopBuilderExtWindows::any_thread` function."
195            );
196        }
197
198        if attributes.dpi_aware {
199            become_dpi_aware();
200        }
201
202        let thread_msg_target = create_event_target_window();
203
204        let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target));
205
206        let (user_event_sender, user_event_receiver) = mpsc::channel();
207        insert_event_target_window_data(thread_msg_target, runner_shared.clone());
208        raw_input::register_all_mice_and_keyboards_for_raw_input(
209            thread_msg_target,
210            Default::default(),
211        );
212
213        Ok(EventLoop {
214            user_event_sender,
215            user_event_receiver,
216            window_target: RootAEL {
217                p: ActiveEventLoop { thread_id, thread_msg_target, runner_shared },
218                _marker: PhantomData,
219            },
220            msg_hook: attributes.msg_hook.take(),
221            high_resolution_timer: None,
222        })
223    }
224
225    pub fn window_target(&self) -> &RootAEL {
226        &self.window_target
227    }
228
229    pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
230    where
231        F: FnMut(Event<T>, &RootAEL),
232    {
233        self.run_on_demand(event_handler)
234    }
235
236    pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
237    where
238        F: FnMut(Event<T>, &RootAEL),
239    {
240        {
241            let runner = &self.window_target.p.runner_shared;
242
243            let event_loop_windows_ref = &self.window_target;
244            let user_event_receiver = &self.user_event_receiver;
245            // # Safety
246            // We make sure to call runner.clear_event_handler() before
247            // returning
248            unsafe {
249                runner.set_event_handler(move |event| {
250                    // the shared `EventLoopRunner` is not parameterized
251                    // `EventLoopProxy::send_event()` calls `PostMessage`
252                    // to wakeup and dispatch a placeholder `UserEvent`,
253                    // when we received the placeholder event here, the
254                    // real UserEvent(T) should already be put in the
255                    // mpsc channel and ready to be pulled
256                    let event = match event.map_nonuser_event() {
257                        Ok(non_user_event) => non_user_event,
258                        Err(_user_event_placeholder) => Event::UserEvent(
259                            user_event_receiver
260                                .try_recv()
261                                .expect("user event signaled but not received"),
262                        ),
263                    };
264                    event_handler(event, event_loop_windows_ref)
265                });
266            }
267        }
268
269        let exit_code = loop {
270            self.wait_for_messages(None);
271            // wait_for_messages calls user application before and after waiting
272            // so it may have decided to exit.
273            if let Some(code) = self.exit_code() {
274                break code;
275            }
276
277            self.dispatch_peeked_messages();
278
279            if let Some(code) = self.exit_code() {
280                break code;
281            }
282        };
283
284        let runner = &self.window_target.p.runner_shared;
285        runner.loop_destroyed();
286
287        // # Safety
288        // We assume that this will effectively call `runner.clear_event_handler()`
289        // to meet the safety requirements for calling `runner.set_event_handler()` above.
290        runner.reset_runner();
291
292        if exit_code == 0 {
293            Ok(())
294        } else {
295            Err(EventLoopError::ExitFailure(exit_code))
296        }
297    }
298
299    pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut event_handler: F) -> PumpStatus
300    where
301        F: FnMut(Event<T>, &RootAEL),
302    {
303        {
304            let runner = &self.window_target.p.runner_shared;
305            let event_loop_windows_ref = &self.window_target;
306            let user_event_receiver = &self.user_event_receiver;
307
308            // # Safety
309            // We make sure to call runner.clear_event_handler() before
310            // returning
311            //
312            // Note: we're currently assuming nothing can panic and unwind
313            // to leave the runner in an unsound state with an associated
314            // event handler.
315            unsafe {
316                runner.set_event_handler(move |event| {
317                    let event = match event.map_nonuser_event() {
318                        Ok(non_user_event) => non_user_event,
319                        Err(_user_event_placeholder) => Event::UserEvent(
320                            user_event_receiver
321                                .recv()
322                                .expect("user event signaled but not received"),
323                        ),
324                    };
325                    event_handler(event, event_loop_windows_ref)
326                });
327                runner.wakeup();
328            }
329        }
330
331        if self.exit_code().is_none() {
332            self.wait_for_messages(timeout);
333        }
334        // wait_for_messages calls user application before and after waiting
335        // so it may have decided to exit.
336        if self.exit_code().is_none() {
337            self.dispatch_peeked_messages();
338        }
339
340        let runner = &self.window_target.p.runner_shared;
341
342        let status = if let Some(code) = runner.exit_code() {
343            runner.loop_destroyed();
344
345            // Immediately reset the internal state for the loop to allow
346            // the loop to be run more than once.
347            runner.reset_runner();
348            PumpStatus::Exit(code)
349        } else {
350            runner.prepare_wait();
351            PumpStatus::Continue
352        };
353
354        // We wait until we've checked for an exit status before clearing the
355        // application callback, in case we need to dispatch a LoopExiting event
356        //
357        // # Safety
358        // This pairs up with our call to `runner.set_event_handler` and ensures
359        // the application's callback can't be held beyond its lifetime.
360        runner.clear_event_handler();
361
362        status
363    }
364
365    /// Waits until new event messages arrive to be peeked.
366    /// Doesn't peek messages itself.
367    ///
368    /// Parameter timeout is optional. This method would wait for the smaller timeout
369    /// between the argument and a timeout from control flow.
370    fn wait_for_messages(&mut self, timeout: Option<Duration>) {
371        let runner = &self.window_target.p.runner_shared;
372
373        // We aim to be consistent with the MacOS backend which has a RunLoop
374        // observer that will dispatch AboutToWait when about to wait for
375        // events, and NewEvents after the RunLoop wakes up.
376        //
377        // We emulate similar behaviour by treating `MsgWaitForMultipleObjectsEx` as our wait
378        // point and wake up point (when it returns) and we drain all other
379        // pending messages via `PeekMessage` until we come back to "wait" via
380        // `MsgWaitForMultipleObjectsEx`.
381        //
382        runner.prepare_wait();
383        wait_for_messages_impl(&mut self.high_resolution_timer, runner.control_flow(), timeout);
384        // Before we potentially exit, make sure to consistently emit an event for the wake up
385        runner.wakeup();
386    }
387
388    /// Dispatch all queued messages via `PeekMessageW`
389    fn dispatch_peeked_messages(&mut self) {
390        let runner = &self.window_target.p.runner_shared;
391
392        // We generally want to continue dispatching all pending messages
393        // but we also allow dispatching to be interrupted as a means to
394        // ensure the `pump_events` won't indefinitely block an external
395        // event loop if there are too many pending events. This interrupt
396        // flag will be set after dispatching `RedrawRequested` events.
397        runner.interrupt_msg_dispatch.set(false);
398
399        // # Safety
400        // The Windows API has no documented requirement for bitwise
401        // initializing a `MSG` struct (it can be uninitialized memory for the C
402        // API) and there's no API to construct or initialize a `MSG`. This
403        // is the simplest way avoid uninitialized memory in Rust
404        let mut msg: MSG = unsafe { mem::zeroed() };
405
406        loop {
407            unsafe {
408                if PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) == false.into() {
409                    break;
410                }
411
412                let handled = if let Some(callback) = self.msg_hook.as_deref_mut() {
413                    callback(&mut msg as *mut _ as *mut _)
414                } else {
415                    false
416                };
417                if !handled {
418                    TranslateMessage(&msg);
419                    DispatchMessageW(&msg);
420                }
421            }
422
423            if let Err(payload) = runner.take_panic_error() {
424                runner.reset_runner();
425                panic::resume_unwind(payload);
426            }
427
428            if let Some(_code) = runner.exit_code() {
429                break;
430            }
431
432            if runner.interrupt_msg_dispatch.get() {
433                break;
434            }
435        }
436    }
437
438    pub fn create_proxy(&self) -> EventLoopProxy<T> {
439        EventLoopProxy {
440            target_window: self.window_target.p.thread_msg_target,
441            event_send: self.user_event_sender.clone(),
442        }
443    }
444
445    fn exit_code(&self) -> Option<i32> {
446        self.window_target.p.exit_code()
447    }
448}
449
450impl ActiveEventLoop {
451    #[inline(always)]
452    pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor {
453        EventLoopThreadExecutor { thread_id: self.thread_id, target_window: self.thread_msg_target }
454    }
455
456    pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
457        let inner = match WinCursor::new(&source.inner.0) {
458            Ok(cursor) => cursor,
459            Err(err) => {
460                tracing::warn!("Failed to create custom cursor: {err}");
461                WinCursor::Failed
462            },
463        };
464
465        RootCustomCursor { inner }
466    }
467
468    // TODO: Investigate opportunities for caching
469    pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
470        monitor::available_monitors()
471    }
472
473    pub fn primary_monitor(&self) -> Option<MonitorHandle> {
474        let monitor = monitor::primary_monitor();
475        Some(monitor)
476    }
477
478    #[cfg(feature = "rwh_05")]
479    pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
480        rwh_05::RawDisplayHandle::Windows(rwh_05::WindowsDisplayHandle::empty())
481    }
482
483    #[cfg(feature = "rwh_06")]
484    pub fn raw_display_handle_rwh_06(
485        &self,
486    ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
487        Ok(rwh_06::RawDisplayHandle::Windows(rwh_06::WindowsDisplayHandle::new()))
488    }
489
490    pub fn listen_device_events(&self, allowed: DeviceEvents) {
491        raw_input::register_all_mice_and_keyboards_for_raw_input(self.thread_msg_target, allowed);
492    }
493
494    pub fn system_theme(&self) -> Option<Theme> {
495        Some(if super::dark_mode::should_use_dark_mode() { Theme::Dark } else { Theme::Light })
496    }
497
498    pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
499        self.runner_shared.set_control_flow(control_flow)
500    }
501
502    pub(crate) fn control_flow(&self) -> ControlFlow {
503        self.runner_shared.control_flow()
504    }
505
506    pub(crate) fn exit(&self) {
507        self.runner_shared.set_exit_code(0)
508    }
509
510    pub(crate) fn exiting(&self) -> bool {
511        self.runner_shared.exit_code().is_some()
512    }
513
514    pub(crate) fn clear_exit(&self) {
515        self.runner_shared.clear_exit();
516    }
517
518    pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
519        OwnedDisplayHandle
520    }
521
522    fn exit_code(&self) -> Option<i32> {
523        self.runner_shared.exit_code()
524    }
525}
526
527#[derive(Clone)]
528pub(crate) struct OwnedDisplayHandle;
529
530impl OwnedDisplayHandle {
531    #[cfg(feature = "rwh_05")]
532    #[inline]
533    pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
534        rwh_05::WindowsDisplayHandle::empty().into()
535    }
536
537    #[cfg(feature = "rwh_06")]
538    #[inline]
539    pub fn raw_display_handle_rwh_06(
540        &self,
541    ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
542        Ok(rwh_06::WindowsDisplayHandle::new().into())
543    }
544}
545
546/// Returns the id of the main thread.
547///
548/// Windows has no real API to check if the current executing thread is the "main thread", unlike
549/// macOS.
550///
551/// Windows will let us look up the current thread's id, but there's no API that lets us check what
552/// the id of the main thread is. We would somehow need to get the main thread's id before a
553/// developer could spin off any other threads inside of the main entrypoint in order to emulate the
554/// capabilities of other platforms.
555///
556/// We can get the id of the main thread by using CRT initialization. CRT initialization can be used
557/// to setup global state within a program. The OS will call a list of function pointers which
558/// assign values to a static variable. To have get a hold of the main thread id, we need to place
559/// our function pointer inside of the `.CRT$XCU` section so it is called before the main
560/// entrypoint.
561///
562/// Full details of CRT initialization can be found here:
563/// <https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160>
564fn main_thread_id() -> u32 {
565    static mut MAIN_THREAD_ID: u32 = 0;
566
567    // Function pointer used in CRT initialization section to set the above static field's value.
568
569    // Mark as used so this is not removable.
570    #[used]
571    #[allow(non_upper_case_globals)]
572    // Place the function pointer inside of CRT initialization section so it is loaded before
573    // main entrypoint.
574    //
575    // See: https://doc.rust-lang.org/stable/reference/abi.html#the-link_section-attribute
576    #[link_section = ".CRT$XCU"]
577    static INIT_MAIN_THREAD_ID: unsafe extern "C" fn() = {
578        unsafe extern "C" fn initer() {
579            unsafe {
580                MAIN_THREAD_ID = GetCurrentThreadId();
581            }
582        }
583        initer
584    };
585
586    unsafe { MAIN_THREAD_ID }
587}
588
589/// Returns the minimum `Option<Duration>`, taking into account that `None`
590/// equates to an infinite timeout, not a zero timeout (so can't just use
591/// `Option::min`)
592fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
593    a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
594}
595
596// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs
597fn dur2timeout(dur: Duration) -> u32 {
598    // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
599    // timeouts in windows APIs are typically u32 milliseconds. To translate, we
600    // have two pieces to take care of:
601    //
602    // * Nanosecond precision is rounded up
603    // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE (never time out).
604    dur.as_secs()
605        .checked_mul(1000)
606        .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000))
607        .and_then(
608            |ms| {
609                if dur.subsec_nanos() % 1_000_000 > 0 {
610                    ms.checked_add(1)
611                } else {
612                    Some(ms)
613                }
614            },
615        )
616        .map(|ms| if ms > u32::MAX as u64 { INFINITE } else { ms as u32 })
617        .unwrap_or(INFINITE)
618}
619
620impl<T> Drop for EventLoop<T> {
621    fn drop(&mut self) {
622        unsafe {
623            DestroyWindow(self.window_target.p.thread_msg_target);
624        }
625    }
626}
627
628/// Set upper limit for waiting time to avoid overflows.
629/// I chose 50 days as a limit because it is used in dur2timeout.
630const FIFTY_DAYS: Duration = Duration::from_secs(50_u64 * 24 * 60 * 60);
631/// Waitable timers use 100 ns intervals to indicate due time.
632/// <https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setwaitabletimer#parameters>
633/// And there is no point waiting using other ways for such small timings
634/// because they are even less precise (can overshoot by few ms).
635const MIN_WAIT: Duration = Duration::from_nanos(100);
636
637fn create_high_resolution_timer() -> Option<OwnedHandle> {
638    unsafe {
639        let handle: HANDLE = CreateWaitableTimerExW(
640            ptr::null(),
641            ptr::null(),
642            CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
643            TIMER_ALL_ACCESS,
644        );
645        // CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported only after
646        // Win10 1803 but it is already default option for rustc
647        // (std uses it to implement `std::thread::sleep`).
648        if handle == 0 {
649            None
650        } else {
651            Some(OwnedHandle::from_raw_handle(handle as *mut c_void))
652        }
653    }
654}
655
656/// This function should not return error if parameters are valid
657/// but there is no guarantee about that at MSDN docs
658/// so we return result of GetLastError if fail.
659///
660/// ## Safety
661///
662/// timer must be a valid timer handle created by [create_high_resolution_timer].
663/// timeout divided by 100 nanoseconds must be more than 0 and less than i64::MAX.
664unsafe fn set_high_resolution_timer(timer: RawHandle, timeout: Duration) -> Result<(), u32> {
665    const INTERVAL_NS: u32 = MIN_WAIT.subsec_nanos();
666    const INTERVALS_IN_SEC: u64 = (Duration::from_secs(1).as_nanos() / INTERVAL_NS as u128) as u64;
667    let intervals_to_wait: u64 =
668        timeout.as_secs() * INTERVALS_IN_SEC + u64::from(timeout.subsec_nanos() / INTERVAL_NS);
669    debug_assert!(intervals_to_wait < i64::MAX as u64, "Must be called with smaller duration",);
670    // Use negative time to indicate relative time.
671    let due_time: i64 = -(intervals_to_wait as i64);
672    unsafe {
673        let set_result = SetWaitableTimer(timer as HANDLE, &due_time, 0, None, ptr::null(), FALSE);
674        if set_result != FALSE {
675            Ok(())
676        } else {
677            Err(GetLastError())
678        }
679    }
680}
681
682/// Implementation detail of [EventLoop::wait_for_messages].
683///
684/// Does actual system-level waiting and doesn't process any messages itself,
685/// including winits internal notifications about waiting and new messages arrival.
686fn wait_for_messages_impl(
687    high_resolution_timer: &mut Option<OwnedHandle>,
688    control_flow: ControlFlow,
689    timeout: Option<Duration>,
690) {
691    let timeout = {
692        let control_flow_timeout = match control_flow {
693            ControlFlow::Wait => None,
694            ControlFlow::Poll => Some(Duration::ZERO),
695            ControlFlow::WaitUntil(wait_deadline) => {
696                let start = Instant::now();
697                Some(wait_deadline.saturating_duration_since(start))
698            },
699        };
700        let timeout = min_timeout(timeout, control_flow_timeout);
701        if timeout == Some(Duration::ZERO) {
702            // Do not wait if we don't have time.
703            return;
704        }
705        // Now we decided to wait so need to do some clamping
706        // to avoid problems with overflow and calling WinAPI with invalid parameters.
707        timeout
708            .map(|t| t.min(FIFTY_DAYS))
709            // If timeout is less than minimally supported by Windows,
710            // increase it to that minimum. Who want less than microsecond delays anyway?
711            .map(|t| t.max(MIN_WAIT))
712    };
713
714    if timeout.is_some() && high_resolution_timer.is_none() {
715        *high_resolution_timer = create_high_resolution_timer();
716    }
717
718    let high_resolution_timer: Option<RawHandle> =
719        high_resolution_timer.as_ref().map(OwnedHandle::as_raw_handle);
720
721    let use_timer: bool;
722    if let (Some(handle), Some(timeout)) = (high_resolution_timer, timeout) {
723        let res = unsafe {
724            // Safety: handle can be Some only if we succeeded in creating high resolution
725            // timer. We properly clamped timeout so it can be used as argument
726            // to timer.
727            set_high_resolution_timer(handle, timeout)
728        };
729        if let Err(error_code) = res {
730            // We successfully got timer but failed to set it?
731            // Should be some bug in our code.
732            tracing::trace!("Failed to set high resolution timer: last error {}", error_code);
733            use_timer = false;
734        } else {
735            use_timer = true;
736        }
737    } else {
738        use_timer = false;
739    }
740
741    unsafe {
742        // Either:
743        //  1. User wants to wait indefinitely if timeout is not set.
744        //  2. We failed to get and set high resolution timer and we need something instead of it.
745        let wait_duration_ms = timeout.map(dur2timeout).unwrap_or(INFINITE);
746
747        let (num_handles, raw_handles) =
748            if use_timer { (1, [high_resolution_timer.unwrap()]) } else { (0, [ptr::null_mut()]) };
749
750        // We must use `QS_ALLINPUT` to wake on accessibility messages.
751        let result = MsgWaitForMultipleObjectsEx(
752            num_handles,
753            raw_handles.as_ptr() as *const _,
754            wait_duration_ms,
755            QS_ALLINPUT,
756            MWMO_INPUTAVAILABLE,
757        );
758        if result == WAIT_FAILED {
759            // Well, nothing smart to do in such case.
760            // Treat it as spurious wake up.
761            tracing::warn!("Failed to MsgWaitForMultipleObjectsEx: error code {}", GetLastError(),);
762        }
763    }
764}
765
766pub(crate) struct EventLoopThreadExecutor {
767    thread_id: u32,
768    target_window: HWND,
769}
770
771unsafe impl Send for EventLoopThreadExecutor {}
772unsafe impl Sync for EventLoopThreadExecutor {}
773
774impl EventLoopThreadExecutor {
775    /// Check to see if we're in the parent event loop's thread.
776    pub(super) fn in_event_loop_thread(&self) -> bool {
777        let cur_thread_id = unsafe { GetCurrentThreadId() };
778        self.thread_id == cur_thread_id
779    }
780
781    /// Executes a function in the event loop thread. If we're already in the event loop thread,
782    /// we just call the function directly.
783    ///
784    /// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is
785    /// removed automatically if the callback receives a `WM_CLOSE` message for the window.
786    ///
787    /// Note that if you are using this to change some property of a window and updating
788    /// `WindowState` then you should call this within the lock of `WindowState`. Otherwise the
789    /// events may be sent to the other thread in different order to the one in which you set
790    /// `WindowState`, leaving them out of sync.
791    ///
792    /// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent
793    /// to the unstable FnBox.
794    pub(super) fn execute_in_thread<F>(&self, mut function: F)
795    where
796        F: FnMut() + Send + 'static,
797    {
798        unsafe {
799            if self.in_event_loop_thread() {
800                function();
801            } else {
802                // We double-box because the first box is a fat pointer.
803                let boxed2: ThreadExecFn = Box::new(Box::new(function));
804
805                let raw = Box::into_raw(boxed2);
806
807                let res = PostMessageW(self.target_window, EXEC_MSG_ID.get(), raw as usize, 0);
808                assert!(res != false.into(), "PostMessage failed; is the messages queue full?");
809            }
810        }
811    }
812}
813
814type ThreadExecFn = Box<Box<dyn FnMut()>>;
815
816pub struct EventLoopProxy<T: 'static> {
817    target_window: HWND,
818    event_send: Sender<T>,
819}
820unsafe impl<T: Send + 'static> Send for EventLoopProxy<T> {}
821
822impl<T: 'static> Clone for EventLoopProxy<T> {
823    fn clone(&self) -> Self {
824        Self { target_window: self.target_window, event_send: self.event_send.clone() }
825    }
826}
827
828impl<T: 'static> EventLoopProxy<T> {
829    pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
830        self.event_send
831            .send(event)
832            .map(|result| {
833                unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) };
834                result
835            })
836            .map_err(|e| EventLoopClosed(e.0))
837    }
838}
839
840/// A lazily-initialized window message ID.
841pub struct LazyMessageId {
842    /// The ID.
843    id: AtomicU32,
844
845    /// The name of the message.
846    name: &'static str,
847}
848
849/// An invalid custom window ID.
850const INVALID_ID: u32 = 0x0;
851
852impl LazyMessageId {
853    /// Create a new `LazyId`.
854    const fn new(name: &'static str) -> Self {
855        Self { id: AtomicU32::new(INVALID_ID), name }
856    }
857
858    /// Get the message ID.
859    pub fn get(&self) -> u32 {
860        // Load the ID.
861        let id = self.id.load(Ordering::Relaxed);
862
863        if id != INVALID_ID {
864            return id;
865        }
866
867        // Register the message.
868        // SAFETY: We are sure that the pointer is a valid C string ending with '\0'.
869        assert!(self.name.ends_with('\0'));
870        let new_id = unsafe { RegisterWindowMessageA(self.name.as_ptr()) };
871
872        assert_ne!(
873            new_id,
874            0,
875            "RegisterWindowMessageA returned zero for '{}': {}",
876            self.name,
877            std::io::Error::last_os_error()
878        );
879
880        // Store the new ID. Since `RegisterWindowMessageA` returns the same value for any given
881        // string, the target value will always either be a). `INVALID_ID` or b). the
882        // correct ID. Therefore a compare-and-swap operation here (or really any
883        // consideration) is never necessary.
884        self.id.store(new_id, Ordering::Relaxed);
885
886        new_id
887    }
888}
889
890// Message sent by the `EventLoopProxy` when we want to wake up the thread.
891// WPARAM and LPARAM are unused.
892static USER_EVENT_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::WakeupMsg\0");
893// Message sent when we want to execute a closure in the thread.
894// WPARAM contains a Box<Box<dyn FnMut()>> that must be retrieved with `Box::from_raw`,
895// and LPARAM is unused.
896static EXEC_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::ExecMsg\0");
897// Message sent by a `Window` when it wants to be destroyed by the main thread.
898// WPARAM and LPARAM are unused.
899pub(crate) static DESTROY_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::DestroyMsg\0");
900// WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the
901// documentation in the `window_state` module for more information.
902pub(crate) static SET_RETAIN_STATE_ON_SIZE_MSG_ID: LazyMessageId =
903    LazyMessageId::new("Winit::SetRetainMaximized\0");
904static THREAD_EVENT_TARGET_WINDOW_CLASS: Lazy<Vec<u16>> =
905    Lazy::new(|| util::encode_wide("Winit Thread Event Target"));
906/// When the taskbar is created, it registers a message with the "TaskbarCreated" string and then
907/// broadcasts this message to all top-level windows <https://docs.microsoft.com/en-us/windows/win32/shell/taskbar#taskbar-creation-notification>
908pub(crate) static TASKBAR_CREATED: LazyMessageId = LazyMessageId::new("TaskbarCreated\0");
909
910fn create_event_target_window() -> HWND {
911    use windows_sys::Win32::UI::WindowsAndMessaging::{CS_HREDRAW, CS_VREDRAW};
912    unsafe {
913        let class = WNDCLASSEXW {
914            cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
915            style: CS_HREDRAW | CS_VREDRAW,
916            lpfnWndProc: Some(thread_event_target_callback),
917            cbClsExtra: 0,
918            cbWndExtra: 0,
919            hInstance: util::get_instance_handle(),
920            hIcon: 0,
921            hCursor: 0, // must be null in order for cursor state to work properly
922            hbrBackground: 0,
923            lpszMenuName: ptr::null(),
924            lpszClassName: THREAD_EVENT_TARGET_WINDOW_CLASS.as_ptr(),
925            hIconSm: 0,
926        };
927
928        RegisterClassExW(&class);
929    }
930
931    unsafe {
932        // WS_EX_TOOLWINDOW prevents this window from ever showing up in the taskbar, which
933        // we want to avoid. If you remove this style, this window won't show up in the
934        // taskbar *initially*, but it can show up at some later point. This can sometimes
935        // happen on its own after several hours have passed, although this has proven
936        // difficult to reproduce. Alternatively, it can be manually triggered by killing
937        // `explorer.exe` and then starting the process back up.
938        // It is unclear why the bug is triggered by waiting for several hours.
939        let window = CreateWindowExW(
940            WS_EX_NOACTIVATE | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW,
941            THREAD_EVENT_TARGET_WINDOW_CLASS.as_ptr(),
942            ptr::null(),
943            WS_OVERLAPPED,
944            0,
945            0,
946            0,
947            0,
948            0,
949            0,
950            util::get_instance_handle(),
951            ptr::null(),
952        );
953
954        super::set_window_long(
955            window,
956            GWL_STYLE,
957            // The window technically has to be visible to receive WM_PAINT messages (which are
958            // used for delivering events during resizes), but it isn't displayed to
959            // the user because of the LAYERED style.
960            (WS_VISIBLE | WS_POPUP) as isize,
961        );
962        window
963    }
964}
965
966fn insert_event_target_window_data(
967    thread_msg_target: HWND,
968    event_loop_runner: EventLoopRunnerShared<UserEventPlaceholder>,
969) {
970    let userdata = ThreadMsgTargetData { event_loop_runner };
971    let input_ptr = Box::into_raw(Box::new(userdata));
972
973    unsafe { super::set_window_long(thread_msg_target, GWL_USERDATA, input_ptr as isize) };
974}
975
976/// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of
977/// the window.
978unsafe fn capture_mouse(window: HWND, window_state: &mut WindowState) {
979    window_state.mouse.capture_count += 1;
980    unsafe { SetCapture(window) };
981}
982
983/// Release mouse input, stopping windows on this thread from receiving mouse input when the cursor
984/// is outside the window.
985unsafe fn release_mouse(mut window_state: MutexGuard<'_, WindowState>) {
986    window_state.mouse.capture_count = window_state.mouse.capture_count.saturating_sub(1);
987    if window_state.mouse.capture_count == 0 {
988        // ReleaseCapture() causes a WM_CAPTURECHANGED where we lock the window_state.
989        drop(window_state);
990        unsafe { ReleaseCapture() };
991    }
992}
993
994fn normalize_pointer_pressure(pressure: u32) -> Option<Force> {
995    match pressure {
996        1..=1024 => Some(Force::Normalized(pressure as f64 / 1024.0)),
997        _ => None,
998    }
999}
1000
1001/// Emit a `ModifiersChanged` event whenever modifiers have changed.
1002/// Returns the current modifier state
1003fn update_modifiers(window: HWND, userdata: &WindowData) {
1004    use crate::event::WindowEvent::ModifiersChanged;
1005
1006    let modifiers = {
1007        let mut layouts = LAYOUT_CACHE.lock().unwrap();
1008        layouts.get_agnostic_mods()
1009    };
1010
1011    let mut window_state = userdata.window_state.lock().unwrap();
1012    if window_state.modifiers_state != modifiers {
1013        window_state.modifiers_state = modifiers;
1014
1015        // Drop lock
1016        drop(window_state);
1017
1018        userdata.send_event(Event::WindowEvent {
1019            window_id: RootWindowId(WindowId(window)),
1020            event: ModifiersChanged(modifiers.into()),
1021        });
1022    }
1023}
1024
1025unsafe fn gain_active_focus(window: HWND, userdata: &WindowData) {
1026    use crate::event::WindowEvent::Focused;
1027
1028    update_modifiers(window, userdata);
1029
1030    userdata.send_event(Event::WindowEvent {
1031        window_id: RootWindowId(WindowId(window)),
1032        event: Focused(true),
1033    });
1034}
1035
1036unsafe fn lose_active_focus(window: HWND, userdata: &WindowData) {
1037    use crate::event::WindowEvent::{Focused, ModifiersChanged};
1038
1039    userdata.window_state_lock().modifiers_state = ModifiersState::empty();
1040    userdata.send_event(Event::WindowEvent {
1041        window_id: RootWindowId(WindowId(window)),
1042        event: ModifiersChanged(ModifiersState::empty().into()),
1043    });
1044
1045    userdata.send_event(Event::WindowEvent {
1046        window_id: RootWindowId(WindowId(window)),
1047        event: Focused(false),
1048    });
1049}
1050
1051/// Any window whose callback is configured to this function will have its events propagated
1052/// through the events loop of the thread the window was created in.
1053// This is the callback that is called by `DispatchMessage` in the events loop.
1054//
1055// Returning 0 tells the Win32 API that the message has been processed.
1056// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
1057pub(super) unsafe extern "system" fn public_window_callback(
1058    window: HWND,
1059    msg: u32,
1060    wparam: WPARAM,
1061    lparam: LPARAM,
1062) -> LRESULT {
1063    let userdata = unsafe { super::get_window_long(window, GWL_USERDATA) };
1064
1065    let userdata_ptr = match (userdata, msg) {
1066        (0, WM_NCCREATE) => {
1067            let createstruct = unsafe { &mut *(lparam as *mut CREATESTRUCTW) };
1068            let initdata = unsafe { &mut *(createstruct.lpCreateParams as *mut InitData<'_>) };
1069
1070            let result = match unsafe { initdata.on_nccreate(window) } {
1071                Some(userdata) => unsafe {
1072                    super::set_window_long(window, GWL_USERDATA, userdata as _);
1073                    DefWindowProcW(window, msg, wparam, lparam)
1074                },
1075                None => -1, // failed to create the window
1076            };
1077
1078            return result;
1079        },
1080        // Getting here should quite frankly be impossible,
1081        // but we'll make window creation fail here just in case.
1082        (0, WM_CREATE) => return -1,
1083        (_, WM_CREATE) => unsafe {
1084            let createstruct = &mut *(lparam as *mut CREATESTRUCTW);
1085            let initdata = createstruct.lpCreateParams;
1086            let initdata = &mut *(initdata as *mut InitData<'_>);
1087
1088            initdata.on_create();
1089            return DefWindowProcW(window, msg, wparam, lparam);
1090        },
1091        (0, _) => return unsafe { DefWindowProcW(window, msg, wparam, lparam) },
1092        _ => userdata as *mut WindowData,
1093    };
1094
1095    let (result, userdata_removed, recurse_depth) = {
1096        let userdata = unsafe { &*(userdata_ptr) };
1097
1098        userdata.recurse_depth.set(userdata.recurse_depth.get() + 1);
1099
1100        let result = unsafe { public_window_callback_inner(window, msg, wparam, lparam, userdata) };
1101
1102        let userdata_removed = userdata.userdata_removed.get();
1103        let recurse_depth = userdata.recurse_depth.get() - 1;
1104        userdata.recurse_depth.set(recurse_depth);
1105
1106        (result, userdata_removed, recurse_depth)
1107    };
1108
1109    if userdata_removed && recurse_depth == 0 {
1110        drop(unsafe { Box::from_raw(userdata_ptr) });
1111    }
1112
1113    result
1114}
1115
1116unsafe fn public_window_callback_inner(
1117    window: HWND,
1118    msg: u32,
1119    wparam: WPARAM,
1120    lparam: LPARAM,
1121    userdata: &WindowData,
1122) -> LRESULT {
1123    let mut result = ProcResult::DefWindowProc(wparam);
1124
1125    // Send new modifiers before sending key events.
1126    let mods_changed_callback = || match msg {
1127        WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP => {
1128            update_modifiers(window, userdata);
1129            result = ProcResult::Value(0);
1130        },
1131        _ => (),
1132    };
1133    userdata
1134        .event_loop_runner
1135        .catch_unwind(mods_changed_callback)
1136        .unwrap_or_else(|| result = ProcResult::Value(-1));
1137
1138    let keyboard_callback = || {
1139        use crate::event::WindowEvent::KeyboardInput;
1140        let events =
1141            userdata.key_event_builder.process_message(window, msg, wparam, lparam, &mut result);
1142        for event in events {
1143            userdata.send_event(Event::WindowEvent {
1144                window_id: RootWindowId(WindowId(window)),
1145                event: KeyboardInput {
1146                    device_id: DEVICE_ID,
1147                    event: event.event,
1148                    is_synthetic: event.is_synthetic,
1149                },
1150            });
1151        }
1152    };
1153    userdata
1154        .event_loop_runner
1155        .catch_unwind(keyboard_callback)
1156        .unwrap_or_else(|| result = ProcResult::Value(-1));
1157
1158    // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing
1159    // the closure to catch_unwind directly so that the match body indentation wouldn't change and
1160    // the git blame and history would be preserved.
1161    let callback = || match msg {
1162        WM_NCCALCSIZE => {
1163            let window_flags = userdata.window_state_lock().window_flags;
1164            if wparam == 0 || window_flags.contains(WindowFlags::MARKER_DECORATIONS) {
1165                result = ProcResult::DefWindowProc(wparam);
1166                return;
1167            }
1168
1169            let params = unsafe { &mut *(lparam as *mut NCCALCSIZE_PARAMS) };
1170
1171            if util::is_maximized(window) {
1172                // Limit the window size when maximized to the current monitor.
1173                // Otherwise it would include the non-existent decorations.
1174                //
1175                // Use `MonitorFromRect` instead of `MonitorFromWindow` to select
1176                // the correct monitor here.
1177                // See https://github.com/MicrosoftEdge/WebView2Feedback/issues/2549
1178                let monitor = unsafe { MonitorFromRect(&params.rgrc[0], MONITOR_DEFAULTTONULL) };
1179                if let Ok(monitor_info) = monitor::get_monitor_info(monitor) {
1180                    params.rgrc[0] = monitor_info.monitorInfo.rcWork;
1181                }
1182            } else if window_flags.contains(WindowFlags::MARKER_UNDECORATED_SHADOW) {
1183                // Extend the client area to cover the whole non-client area.
1184                // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize#remarks
1185                //
1186                // HACK(msiglreith): To add the drop shadow we slightly tweak the non-client area.
1187                // This leads to a small black 1px border on the top. Adding a margin manually
1188                // on all 4 borders would result in the caption getting drawn by the DWM.
1189                //
1190                // Another option would be to allow the DWM to paint inside the client area.
1191                // Unfortunately this results in janky resize behavior, where the compositor is
1192                // ahead of the window surface. Currently, there seems no option to achieve this
1193                // with the Windows API.
1194                params.rgrc[0].top += 1;
1195                params.rgrc[0].bottom += 1;
1196            }
1197
1198            result = ProcResult::Value(0);
1199        },
1200
1201        WM_ENTERSIZEMOVE => {
1202            userdata
1203                .window_state_lock()
1204                .set_window_flags_in_place(|f| f.insert(WindowFlags::MARKER_IN_SIZE_MOVE));
1205            result = ProcResult::Value(0);
1206        },
1207
1208        WM_EXITSIZEMOVE => {
1209            let mut state = userdata.window_state_lock();
1210            if state.dragging {
1211                state.dragging = false;
1212                unsafe { PostMessageW(window, WM_LBUTTONUP, 0, lparam) };
1213            }
1214
1215            state.set_window_flags_in_place(|f| f.remove(WindowFlags::MARKER_IN_SIZE_MOVE));
1216            result = ProcResult::Value(0);
1217        },
1218
1219        WM_NCLBUTTONDOWN => {
1220            if wparam == HTCAPTION as _ {
1221                // Prevent the user event loop from pausing when left clicking the title bar.
1222                //
1223                // When the user interacts with the title bar, Windows enters the modal event
1224                // loop. Currently, a left click causes a pause for about 500ms. Sending a dummy
1225                // mouse-move event seems to cancel the modal loop early, preventing the pause.
1226                // The application will never see this dummy event.
1227                //
1228                // The mouse coordinates are encoded into the lparam value, however the WM_MOUSEMOVE
1229                // event is not using the same coordinate system of the WM_NCLBUTTONDOWN event.
1230                // One uses client-area coordinates and the other is screen-coordinates. In any
1231                // case, passing the lparam as-is with the dummy event does not seem the cancel
1232                // the modal loop.
1233                //
1234                // However, passing in a value of 0 has been observed to always cancel the pause.
1235                //
1236                // Other notes:
1237                //
1238                // For some unknown reason, the cursor will blink when clicking the title bar.
1239                // Cancelling the modal loop early causes the blink to happen *immediately*.
1240                // Otherwise, the blank happens *after* the pause.
1241                //
1242                // When right-click the title bar, the system window menu is presented to the user,
1243                // and the modal event loop begins. This dummy event does *not* prevent the freeze
1244                // in the main event loop caused by that popup menu.
1245                let lparam = 0;
1246                unsafe { PostMessageW(window, WM_MOUSEMOVE, 0, lparam) };
1247            }
1248            result = ProcResult::DefWindowProc(wparam);
1249        },
1250
1251        WM_CLOSE => {
1252            use crate::event::WindowEvent::CloseRequested;
1253            userdata.send_event(Event::WindowEvent {
1254                window_id: RootWindowId(WindowId(window)),
1255                event: CloseRequested,
1256            });
1257            result = ProcResult::Value(0);
1258        },
1259
1260        WM_DESTROY => {
1261            use crate::event::WindowEvent::Destroyed;
1262            unsafe { RevokeDragDrop(window) };
1263            userdata.send_event(Event::WindowEvent {
1264                window_id: RootWindowId(WindowId(window)),
1265                event: Destroyed,
1266            });
1267            result = ProcResult::Value(0);
1268        },
1269
1270        WM_NCDESTROY => {
1271            unsafe { super::set_window_long(window, GWL_USERDATA, 0) };
1272            userdata.userdata_removed.set(true);
1273            result = ProcResult::Value(0);
1274        },
1275
1276        WM_PAINT => {
1277            userdata.window_state_lock().redraw_requested =
1278                userdata.event_loop_runner.should_buffer();
1279
1280            // We'll buffer only in response to `UpdateWindow`, if win32 decides to redraw the
1281            // window outside the normal flow of the event loop. This way mark event as handled
1282            // and request a normal redraw with `RedrawWindow`.
1283            if !userdata.event_loop_runner.should_buffer() {
1284                userdata.send_event(Event::WindowEvent {
1285                    window_id: RootWindowId(WindowId(window)),
1286                    event: WindowEvent::RedrawRequested,
1287                });
1288            }
1289
1290            // NOTE: calling `RedrawWindow` during `WM_PAINT` does nothing, since to mark
1291            // `WM_PAINT` as handled we should call the `DefWindowProcW`. Call it and check whether
1292            // user asked for redraw during `RedrawRequested` event handling and request it again
1293            // after marking `WM_PAINT` as handled.
1294            result = ProcResult::Value(unsafe { DefWindowProcW(window, msg, wparam, lparam) });
1295            if std::mem::take(&mut userdata.window_state_lock().redraw_requested) {
1296                unsafe { RedrawWindow(window, ptr::null(), 0, RDW_INTERNALPAINT) };
1297            }
1298        },
1299        WM_WINDOWPOSCHANGING => {
1300            let mut window_state = userdata.window_state_lock();
1301            if let Some(ref mut fullscreen) = window_state.fullscreen {
1302                let window_pos = unsafe { &mut *(lparam as *mut WINDOWPOS) };
1303                let new_rect = RECT {
1304                    left: window_pos.x,
1305                    top: window_pos.y,
1306                    right: window_pos.x + window_pos.cx,
1307                    bottom: window_pos.y + window_pos.cy,
1308                };
1309
1310                const NOMOVE_OR_NOSIZE: u32 = SWP_NOMOVE | SWP_NOSIZE;
1311
1312                let new_rect = if window_pos.flags & NOMOVE_OR_NOSIZE != 0 {
1313                    let cur_rect = util::WindowArea::Outer.get_rect(window).expect(
1314                        "Unexpected GetWindowRect failure; please report this error to \
1315                         rust-windowing/winit",
1316                    );
1317
1318                    match window_pos.flags & NOMOVE_OR_NOSIZE {
1319                        NOMOVE_OR_NOSIZE => None,
1320
1321                        SWP_NOMOVE => Some(RECT {
1322                            left: cur_rect.left,
1323                            top: cur_rect.top,
1324                            right: cur_rect.left + window_pos.cx,
1325                            bottom: cur_rect.top + window_pos.cy,
1326                        }),
1327
1328                        SWP_NOSIZE => Some(RECT {
1329                            left: window_pos.x,
1330                            top: window_pos.y,
1331                            right: window_pos.x - cur_rect.left + cur_rect.right,
1332                            bottom: window_pos.y - cur_rect.top + cur_rect.bottom,
1333                        }),
1334
1335                        _ => unreachable!(),
1336                    }
1337                } else {
1338                    Some(new_rect)
1339                };
1340
1341                if let Some(new_rect) = new_rect {
1342                    let new_monitor = unsafe { MonitorFromRect(&new_rect, MONITOR_DEFAULTTONULL) };
1343                    match fullscreen {
1344                        Fullscreen::Borderless(ref mut fullscreen_monitor) => {
1345                            if new_monitor != 0
1346                                && fullscreen_monitor
1347                                    .as_ref()
1348                                    .map(|monitor| new_monitor != monitor.hmonitor())
1349                                    .unwrap_or(true)
1350                            {
1351                                if let Ok(new_monitor_info) = monitor::get_monitor_info(new_monitor)
1352                                {
1353                                    let new_monitor_rect = new_monitor_info.monitorInfo.rcMonitor;
1354                                    window_pos.x = new_monitor_rect.left;
1355                                    window_pos.y = new_monitor_rect.top;
1356                                    window_pos.cx = new_monitor_rect.right - new_monitor_rect.left;
1357                                    window_pos.cy = new_monitor_rect.bottom - new_monitor_rect.top;
1358                                }
1359                                *fullscreen_monitor = Some(MonitorHandle::new(new_monitor));
1360                            }
1361                        },
1362                        Fullscreen::Exclusive(ref video_mode) => {
1363                            let old_monitor = video_mode.monitor.hmonitor();
1364                            if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) {
1365                                let old_monitor_rect = old_monitor_info.monitorInfo.rcMonitor;
1366                                window_pos.x = old_monitor_rect.left;
1367                                window_pos.y = old_monitor_rect.top;
1368                                window_pos.cx = old_monitor_rect.right - old_monitor_rect.left;
1369                                window_pos.cy = old_monitor_rect.bottom - old_monitor_rect.top;
1370                            }
1371                        },
1372                    }
1373                }
1374            }
1375
1376            result = ProcResult::Value(0);
1377        },
1378
1379        // WM_MOVE supplies client area positions, so we send Moved here instead.
1380        WM_WINDOWPOSCHANGED => {
1381            use crate::event::WindowEvent::Moved;
1382
1383            let windowpos = lparam as *const WINDOWPOS;
1384            if unsafe { (*windowpos).flags & SWP_NOMOVE != SWP_NOMOVE } {
1385                let physical_position =
1386                    unsafe { PhysicalPosition::new((*windowpos).x, (*windowpos).y) };
1387                userdata.send_event(Event::WindowEvent {
1388                    window_id: RootWindowId(WindowId(window)),
1389                    event: Moved(physical_position),
1390                });
1391            }
1392
1393            // This is necessary for us to still get sent WM_SIZE.
1394            result = ProcResult::DefWindowProc(wparam);
1395        },
1396
1397        WM_SIZE => {
1398            use crate::event::WindowEvent::Resized;
1399            let w = super::loword(lparam as u32) as u32;
1400            let h = super::hiword(lparam as u32) as u32;
1401
1402            let physical_size = PhysicalSize::new(w, h);
1403            let event = Event::WindowEvent {
1404                window_id: RootWindowId(WindowId(window)),
1405                event: Resized(physical_size),
1406            };
1407
1408            {
1409                let mut w = userdata.window_state_lock();
1410                // See WindowFlags::MARKER_RETAIN_STATE_ON_SIZE docs for info on why this `if` check
1411                // exists.
1412                if !w.window_flags().contains(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE) {
1413                    let maximized = wparam == SIZE_MAXIMIZED as usize;
1414                    w.set_window_flags_in_place(|f| f.set(WindowFlags::MAXIMIZED, maximized));
1415                }
1416            }
1417            userdata.send_event(event);
1418            result = ProcResult::Value(0);
1419        },
1420
1421        WM_SIZING => {
1422            /// Calculate the amount to add to round `value` to the nearest multiple of `increment`.
1423            fn snap_to_nearest_increment_delta(value: i32, increment: i32) -> i32 {
1424                let half_one = increment / 2;
1425                let half_two = increment - half_one;
1426                half_one - (value - half_two) % increment
1427            }
1428
1429            let scale_factor = userdata.window_state_lock().scale_factor;
1430            let Some(inc) = userdata
1431                .window_state_lock()
1432                .resize_increments
1433                .map(|inc| inc.to_physical(scale_factor))
1434                .filter(|inc| inc.width > 0 && inc.height > 0)
1435            else {
1436                result = ProcResult::Value(0);
1437                return;
1438            };
1439
1440            let side = wparam as u32;
1441            // The desired new size of the window, decorations included.
1442            let rect = unsafe { &mut *(lparam as *mut RECT) };
1443
1444            // We need to calculate the dimensions of the window decorations to get the true
1445            // size of the window's contents
1446            let adj_rect = userdata
1447                .window_state_lock()
1448                .window_flags
1449                .adjust_rect(window, *rect)
1450                .unwrap_or(*rect);
1451            let deco_width = rect.left - adj_rect.left + adj_rect.right - rect.right;
1452            let deco_height = rect.top - adj_rect.top + adj_rect.bottom - rect.bottom;
1453
1454            let width = rect.right - rect.left - deco_width;
1455            let height = rect.bottom - rect.top - deco_height;
1456
1457            let mut width_delta = snap_to_nearest_increment_delta(width, inc.width);
1458            let mut height_delta = snap_to_nearest_increment_delta(height, inc.height);
1459
1460            // Windows won't bound check the value of `rect` after we're done here, so we
1461            // have to check manually. If the width/height we snap to would go out of bounds, just
1462            // set it equal to the min/max bound.
1463            let min_size =
1464                userdata.window_state_lock().min_size.map(|size| size.to_physical(scale_factor));
1465            let max_size =
1466                userdata.window_state_lock().max_size.map(|size| size.to_physical(scale_factor));
1467            let final_width = width + width_delta;
1468            let final_height = height + height_delta;
1469            if let Some(min_size) = min_size {
1470                if final_width < min_size.width {
1471                    width_delta += min_size.width - final_width;
1472                }
1473                if final_height < min_size.height {
1474                    height_delta += min_size.height - final_height;
1475                }
1476            }
1477            if let Some(max_size) = max_size {
1478                if final_width > max_size.width {
1479                    width_delta -= final_width - max_size.width;
1480                }
1481                if final_height > max_size.height {
1482                    height_delta -= final_height - max_size.height;
1483                }
1484            }
1485
1486            match side {
1487                WMSZ_LEFT | WMSZ_BOTTOMLEFT | WMSZ_TOPLEFT => {
1488                    rect.left -= width_delta;
1489                },
1490                WMSZ_RIGHT | WMSZ_BOTTOMRIGHT | WMSZ_TOPRIGHT => {
1491                    rect.right += width_delta;
1492                },
1493                _ => {},
1494            }
1495
1496            match side {
1497                WMSZ_TOP | WMSZ_TOPLEFT | WMSZ_TOPRIGHT => {
1498                    rect.top -= height_delta;
1499                },
1500                WMSZ_BOTTOM | WMSZ_BOTTOMLEFT | WMSZ_BOTTOMRIGHT => {
1501                    rect.bottom += height_delta;
1502                },
1503                _ => {},
1504            }
1505
1506            result = ProcResult::DefWindowProc(wparam);
1507        },
1508
1509        WM_MENUCHAR => {
1510            result = ProcResult::Value((MNC_CLOSE << 16) as isize);
1511        },
1512
1513        WM_IME_STARTCOMPOSITION => {
1514            let ime_allowed = userdata.window_state_lock().ime_allowed;
1515            if ime_allowed {
1516                userdata.window_state_lock().ime_state = ImeState::Enabled;
1517
1518                userdata.send_event(Event::WindowEvent {
1519                    window_id: RootWindowId(WindowId(window)),
1520                    event: WindowEvent::Ime(Ime::Enabled),
1521                });
1522            }
1523
1524            result = ProcResult::DefWindowProc(wparam);
1525        },
1526
1527        WM_IME_COMPOSITION => {
1528            let ime_allowed_and_composing = {
1529                let w = userdata.window_state_lock();
1530                w.ime_allowed && w.ime_state != ImeState::Disabled
1531            };
1532            // Windows Hangul IME sends WM_IME_COMPOSITION after WM_IME_ENDCOMPOSITION, so
1533            // check whether composing.
1534            if ime_allowed_and_composing {
1535                let ime_context = unsafe { ImeContext::current(window) };
1536
1537                if lparam == 0 {
1538                    userdata.send_event(Event::WindowEvent {
1539                        window_id: RootWindowId(WindowId(window)),
1540                        event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
1541                    });
1542                }
1543
1544                // Google Japanese Input and ATOK have both flags, so
1545                // first, receive composing result if exist.
1546                if (lparam as u32 & GCS_RESULTSTR) != 0 {
1547                    if let Some(text) = unsafe { ime_context.get_composed_text() } {
1548                        userdata.window_state_lock().ime_state = ImeState::Enabled;
1549
1550                        userdata.send_event(Event::WindowEvent {
1551                            window_id: RootWindowId(WindowId(window)),
1552                            event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
1553                        });
1554                        userdata.send_event(Event::WindowEvent {
1555                            window_id: RootWindowId(WindowId(window)),
1556                            event: WindowEvent::Ime(Ime::Commit(text)),
1557                        });
1558                    }
1559                }
1560
1561                // Next, receive preedit range for next composing if exist.
1562                if (lparam as u32 & GCS_COMPSTR) != 0 {
1563                    if let Some((text, first, last)) =
1564                        unsafe { ime_context.get_composing_text_and_cursor() }
1565                    {
1566                        userdata.window_state_lock().ime_state = ImeState::Preedit;
1567                        let cursor_range = first.map(|f| (f, last.unwrap_or(f)));
1568
1569                        userdata.send_event(Event::WindowEvent {
1570                            window_id: RootWindowId(WindowId(window)),
1571                            event: WindowEvent::Ime(Ime::Preedit(text, cursor_range)),
1572                        });
1573                    }
1574                }
1575            }
1576
1577            // Not calling DefWindowProc to hide composing text drawn by IME.
1578            result = ProcResult::Value(0);
1579        },
1580
1581        WM_IME_ENDCOMPOSITION => {
1582            let ime_allowed_or_composing = {
1583                let w = userdata.window_state_lock();
1584                w.ime_allowed || w.ime_state != ImeState::Disabled
1585            };
1586            if ime_allowed_or_composing {
1587                if userdata.window_state_lock().ime_state == ImeState::Preedit {
1588                    // Windows Hangul IME sends WM_IME_COMPOSITION after WM_IME_ENDCOMPOSITION, so
1589                    // trying receiving composing result and commit if exists.
1590                    let ime_context = unsafe { ImeContext::current(window) };
1591                    if let Some(text) = unsafe { ime_context.get_composed_text() } {
1592                        userdata.send_event(Event::WindowEvent {
1593                            window_id: RootWindowId(WindowId(window)),
1594                            event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
1595                        });
1596                        userdata.send_event(Event::WindowEvent {
1597                            window_id: RootWindowId(WindowId(window)),
1598                            event: WindowEvent::Ime(Ime::Commit(text)),
1599                        });
1600                    }
1601                }
1602
1603                userdata.window_state_lock().ime_state = ImeState::Disabled;
1604
1605                userdata.send_event(Event::WindowEvent {
1606                    window_id: RootWindowId(WindowId(window)),
1607                    event: WindowEvent::Ime(Ime::Disabled),
1608                });
1609            }
1610
1611            result = ProcResult::DefWindowProc(wparam);
1612        },
1613
1614        WM_IME_SETCONTEXT => {
1615            // IME UI visibility flags are in lparam.
1616            let lparam = lparam & !(ISC_SHOWUICOMPOSITIONWINDOW as isize);
1617            result = ProcResult::Value(unsafe { DefWindowProcW(window, msg, wparam, lparam) });
1618        },
1619
1620        // this is necessary for us to maintain minimize/restore state
1621        WM_SYSCOMMAND => {
1622            if wparam == SC_RESTORE as usize {
1623                let mut w = userdata.window_state_lock();
1624                w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, false));
1625            }
1626            if wparam == SC_MINIMIZE as usize {
1627                let mut w = userdata.window_state_lock();
1628                w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, true));
1629            }
1630            // Send `WindowEvent::Minimized` here if we decide to implement one
1631
1632            if wparam == SC_SCREENSAVE as usize {
1633                let window_state = userdata.window_state_lock();
1634                if window_state.fullscreen.is_some() {
1635                    result = ProcResult::Value(0);
1636                    return;
1637                }
1638            }
1639
1640            result = ProcResult::DefWindowProc(wparam);
1641        },
1642
1643        WM_MOUSEMOVE => {
1644            use crate::event::WindowEvent::{CursorEntered, CursorLeft, CursorMoved};
1645
1646            let x = super::get_x_lparam(lparam as u32) as i32;
1647            let y = super::get_y_lparam(lparam as u32) as i32;
1648            let position = PhysicalPosition::new(x as f64, y as f64);
1649
1650            let cursor_moved;
1651            {
1652                let mut w = userdata.window_state_lock();
1653                let mouse_was_inside_window =
1654                    w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW);
1655
1656                match get_pointer_move_kind(window, mouse_was_inside_window, x, y) {
1657                    PointerMoveKind::Enter => {
1658                        w.mouse
1659                            .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, true))
1660                            .ok();
1661
1662                        drop(w);
1663                        userdata.send_event(Event::WindowEvent {
1664                            window_id: RootWindowId(WindowId(window)),
1665                            event: CursorEntered { device_id: DEVICE_ID },
1666                        });
1667
1668                        // Calling TrackMouseEvent in order to receive mouse leave events.
1669                        unsafe {
1670                            TrackMouseEvent(&mut TRACKMOUSEEVENT {
1671                                cbSize: mem::size_of::<TRACKMOUSEEVENT>() as u32,
1672                                dwFlags: TME_LEAVE,
1673                                hwndTrack: window,
1674                                dwHoverTime: HOVER_DEFAULT,
1675                            })
1676                        };
1677                    },
1678                    PointerMoveKind::Leave => {
1679                        w.mouse
1680                            .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false))
1681                            .ok();
1682
1683                        drop(w);
1684                        userdata.send_event(Event::WindowEvent {
1685                            window_id: RootWindowId(WindowId(window)),
1686                            event: CursorLeft { device_id: DEVICE_ID },
1687                        });
1688                    },
1689                    PointerMoveKind::None => drop(w),
1690                }
1691
1692                // handle spurious WM_MOUSEMOVE messages
1693                // see https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42343
1694                // and http://debugandconquer.blogspot.com/2015/08/the-cause-of-spurious-mouse-move.html
1695                let mut w = userdata.window_state_lock();
1696                cursor_moved = w.mouse.last_position != Some(position);
1697                w.mouse.last_position = Some(position);
1698            }
1699
1700            if cursor_moved {
1701                update_modifiers(window, userdata);
1702
1703                userdata.send_event(Event::WindowEvent {
1704                    window_id: RootWindowId(WindowId(window)),
1705                    event: CursorMoved { device_id: DEVICE_ID, position },
1706                });
1707            }
1708
1709            result = ProcResult::Value(0);
1710        },
1711
1712        WM_MOUSELEAVE => {
1713            use crate::event::WindowEvent::CursorLeft;
1714            {
1715                let mut w = userdata.window_state_lock();
1716                w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)).ok();
1717            }
1718
1719            userdata.send_event(Event::WindowEvent {
1720                window_id: RootWindowId(WindowId(window)),
1721                event: CursorLeft { device_id: DEVICE_ID },
1722            });
1723
1724            result = ProcResult::Value(0);
1725        },
1726
1727        WM_MOUSEWHEEL => {
1728            use crate::event::MouseScrollDelta::LineDelta;
1729
1730            let value = (wparam >> 16) as i16;
1731            let value = value as f32 / WHEEL_DELTA as f32;
1732
1733            update_modifiers(window, userdata);
1734
1735            userdata.send_event(Event::WindowEvent {
1736                window_id: RootWindowId(WindowId(window)),
1737                event: WindowEvent::MouseWheel {
1738                    device_id: DEVICE_ID,
1739                    delta: LineDelta(0.0, value),
1740                    phase: TouchPhase::Moved,
1741                },
1742            });
1743
1744            result = ProcResult::Value(0);
1745        },
1746
1747        WM_MOUSEHWHEEL => {
1748            use crate::event::MouseScrollDelta::LineDelta;
1749
1750            let value = (wparam >> 16) as i16;
1751            let value = -value as f32 / WHEEL_DELTA as f32; // NOTE: inverted! See https://github.com/rust-windowing/winit/pull/2105/
1752
1753            update_modifiers(window, userdata);
1754
1755            userdata.send_event(Event::WindowEvent {
1756                window_id: RootWindowId(WindowId(window)),
1757                event: WindowEvent::MouseWheel {
1758                    device_id: DEVICE_ID,
1759                    delta: LineDelta(value, 0.0),
1760                    phase: TouchPhase::Moved,
1761                },
1762            });
1763
1764            result = ProcResult::Value(0);
1765        },
1766
1767        WM_KEYDOWN | WM_SYSKEYDOWN => {
1768            if msg == WM_SYSKEYDOWN {
1769                result = ProcResult::DefWindowProc(wparam);
1770            }
1771        },
1772
1773        WM_KEYUP | WM_SYSKEYUP => {
1774            if msg == WM_SYSKEYUP && unsafe { GetMenu(window) != 0 } {
1775                // let Windows handle event if the window has a native menu, a modal event loop
1776                // is started here on Alt key up.
1777                result = ProcResult::DefWindowProc(wparam);
1778            }
1779        },
1780
1781        WM_LBUTTONDOWN => {
1782            use crate::event::ElementState::Pressed;
1783            use crate::event::MouseButton::Left;
1784            use crate::event::WindowEvent::MouseInput;
1785
1786            unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
1787
1788            update_modifiers(window, userdata);
1789
1790            userdata.send_event(Event::WindowEvent {
1791                window_id: RootWindowId(WindowId(window)),
1792                event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left },
1793            });
1794            result = ProcResult::Value(0);
1795        },
1796
1797        WM_LBUTTONUP => {
1798            use crate::event::ElementState::Released;
1799            use crate::event::MouseButton::Left;
1800            use crate::event::WindowEvent::MouseInput;
1801
1802            unsafe { release_mouse(userdata.window_state_lock()) };
1803
1804            update_modifiers(window, userdata);
1805
1806            userdata.send_event(Event::WindowEvent {
1807                window_id: RootWindowId(WindowId(window)),
1808                event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left },
1809            });
1810            result = ProcResult::Value(0);
1811        },
1812
1813        WM_RBUTTONDOWN => {
1814            use crate::event::ElementState::Pressed;
1815            use crate::event::MouseButton::Right;
1816            use crate::event::WindowEvent::MouseInput;
1817
1818            unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
1819
1820            update_modifiers(window, userdata);
1821
1822            userdata.send_event(Event::WindowEvent {
1823                window_id: RootWindowId(WindowId(window)),
1824                event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right },
1825            });
1826            result = ProcResult::Value(0);
1827        },
1828
1829        WM_RBUTTONUP => {
1830            use crate::event::ElementState::Released;
1831            use crate::event::MouseButton::Right;
1832            use crate::event::WindowEvent::MouseInput;
1833
1834            unsafe { release_mouse(userdata.window_state_lock()) };
1835
1836            update_modifiers(window, userdata);
1837
1838            userdata.send_event(Event::WindowEvent {
1839                window_id: RootWindowId(WindowId(window)),
1840                event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right },
1841            });
1842            result = ProcResult::Value(0);
1843        },
1844
1845        WM_MBUTTONDOWN => {
1846            use crate::event::ElementState::Pressed;
1847            use crate::event::MouseButton::Middle;
1848            use crate::event::WindowEvent::MouseInput;
1849
1850            unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
1851
1852            update_modifiers(window, userdata);
1853
1854            userdata.send_event(Event::WindowEvent {
1855                window_id: RootWindowId(WindowId(window)),
1856                event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle },
1857            });
1858            result = ProcResult::Value(0);
1859        },
1860
1861        WM_MBUTTONUP => {
1862            use crate::event::ElementState::Released;
1863            use crate::event::MouseButton::Middle;
1864            use crate::event::WindowEvent::MouseInput;
1865
1866            unsafe { release_mouse(userdata.window_state_lock()) };
1867
1868            update_modifiers(window, userdata);
1869
1870            userdata.send_event(Event::WindowEvent {
1871                window_id: RootWindowId(WindowId(window)),
1872                event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle },
1873            });
1874            result = ProcResult::Value(0);
1875        },
1876
1877        WM_XBUTTONDOWN => {
1878            use crate::event::ElementState::Pressed;
1879            use crate::event::MouseButton::{Back, Forward, Other};
1880            use crate::event::WindowEvent::MouseInput;
1881            let xbutton = super::get_xbutton_wparam(wparam as u32);
1882
1883            unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
1884
1885            update_modifiers(window, userdata);
1886
1887            userdata.send_event(Event::WindowEvent {
1888                window_id: RootWindowId(WindowId(window)),
1889                event: MouseInput {
1890                    device_id: DEVICE_ID,
1891                    state: Pressed,
1892                    button: match xbutton {
1893                        1 => Back,
1894                        2 => Forward,
1895                        _ => Other(xbutton),
1896                    },
1897                },
1898            });
1899            result = ProcResult::Value(0);
1900        },
1901
1902        WM_XBUTTONUP => {
1903            use crate::event::ElementState::Released;
1904            use crate::event::MouseButton::{Back, Forward, Other};
1905            use crate::event::WindowEvent::MouseInput;
1906            let xbutton = super::get_xbutton_wparam(wparam as u32);
1907
1908            unsafe { release_mouse(userdata.window_state_lock()) };
1909
1910            update_modifiers(window, userdata);
1911
1912            userdata.send_event(Event::WindowEvent {
1913                window_id: RootWindowId(WindowId(window)),
1914                event: MouseInput {
1915                    device_id: DEVICE_ID,
1916                    state: Released,
1917                    button: match xbutton {
1918                        1 => Back,
1919                        2 => Forward,
1920                        _ => Other(xbutton),
1921                    },
1922                },
1923            });
1924            result = ProcResult::Value(0);
1925        },
1926
1927        WM_CAPTURECHANGED => {
1928            // lparam here is a handle to the window which is gaining mouse capture.
1929            // If it is the same as our window, then we're essentially retaining the capture. This
1930            // can happen if `SetCapture` is called on our window when it already has the mouse
1931            // capture.
1932            if lparam != window {
1933                userdata.window_state_lock().mouse.capture_count = 0;
1934            }
1935            result = ProcResult::Value(0);
1936        },
1937
1938        WM_TOUCH => {
1939            let pcount = super::loword(wparam as u32) as usize;
1940            let mut inputs = Vec::with_capacity(pcount);
1941            let htouch = lparam;
1942            if unsafe {
1943                GetTouchInputInfo(
1944                    htouch,
1945                    pcount as u32,
1946                    inputs.as_mut_ptr(),
1947                    mem::size_of::<TOUCHINPUT>() as i32,
1948                ) > 0
1949            } {
1950                unsafe { inputs.set_len(pcount) };
1951                for input in &inputs {
1952                    let mut location = POINT { x: input.x / 100, y: input.y / 100 };
1953
1954                    if unsafe { ScreenToClient(window, &mut location) } == false.into() {
1955                        continue;
1956                    }
1957
1958                    let x = location.x as f64 + (input.x % 100) as f64 / 100f64;
1959                    let y = location.y as f64 + (input.y % 100) as f64 / 100f64;
1960                    let location = PhysicalPosition::new(x, y);
1961                    userdata.send_event(Event::WindowEvent {
1962                        window_id: RootWindowId(WindowId(window)),
1963                        event: WindowEvent::Touch(Touch {
1964                            phase: if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) {
1965                                TouchPhase::Started
1966                            } else if util::has_flag(input.dwFlags, TOUCHEVENTF_UP) {
1967                                TouchPhase::Ended
1968                            } else if util::has_flag(input.dwFlags, TOUCHEVENTF_MOVE) {
1969                                TouchPhase::Moved
1970                            } else {
1971                                continue;
1972                            },
1973                            location,
1974                            force: None, // WM_TOUCH doesn't support pressure information
1975                            id: input.dwID as u64,
1976                            device_id: DEVICE_ID,
1977                        }),
1978                    });
1979                }
1980            }
1981            unsafe { CloseTouchInputHandle(htouch) };
1982            result = ProcResult::Value(0);
1983        },
1984
1985        WM_POINTERDOWN | WM_POINTERUPDATE | WM_POINTERUP => {
1986            if let (
1987                Some(GetPointerFrameInfoHistory),
1988                Some(SkipPointerFrameMessages),
1989                Some(GetPointerDeviceRects),
1990            ) = (
1991                *util::GET_POINTER_FRAME_INFO_HISTORY,
1992                *util::SKIP_POINTER_FRAME_MESSAGES,
1993                *util::GET_POINTER_DEVICE_RECTS,
1994            ) {
1995                let pointer_id = super::loword(wparam as u32) as u32;
1996                let mut entries_count = 0u32;
1997                let mut pointers_count = 0u32;
1998                if unsafe {
1999                    GetPointerFrameInfoHistory(
2000                        pointer_id,
2001                        &mut entries_count,
2002                        &mut pointers_count,
2003                        ptr::null_mut(),
2004                    )
2005                } == false.into()
2006                {
2007                    result = ProcResult::Value(0);
2008                    return;
2009                }
2010
2011                let pointer_info_count = (entries_count * pointers_count) as usize;
2012                let mut pointer_infos = Vec::with_capacity(pointer_info_count);
2013                if unsafe {
2014                    GetPointerFrameInfoHistory(
2015                        pointer_id,
2016                        &mut entries_count,
2017                        &mut pointers_count,
2018                        pointer_infos.as_mut_ptr(),
2019                    )
2020                } == false.into()
2021                {
2022                    result = ProcResult::Value(0);
2023                    return;
2024                }
2025                unsafe { pointer_infos.set_len(pointer_info_count) };
2026
2027                // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getpointerframeinfohistory
2028                // The information retrieved appears in reverse chronological order, with the most
2029                // recent entry in the first row of the returned array
2030                for pointer_info in pointer_infos.iter().rev() {
2031                    let mut device_rect = mem::MaybeUninit::uninit();
2032                    let mut display_rect = mem::MaybeUninit::uninit();
2033
2034                    if unsafe {
2035                        GetPointerDeviceRects(
2036                            pointer_info.sourceDevice,
2037                            device_rect.as_mut_ptr(),
2038                            display_rect.as_mut_ptr(),
2039                        )
2040                    } == false.into()
2041                    {
2042                        continue;
2043                    }
2044
2045                    let device_rect = unsafe { device_rect.assume_init() };
2046                    let display_rect = unsafe { display_rect.assume_init() };
2047
2048                    // For the most precise himetric to pixel conversion we calculate the ratio
2049                    // between the resolution of the display device (pixel) and
2050                    // the touch device (himetric).
2051                    let himetric_to_pixel_ratio_x = (display_rect.right - display_rect.left) as f64
2052                        / (device_rect.right - device_rect.left) as f64;
2053                    let himetric_to_pixel_ratio_y = (display_rect.bottom - display_rect.top) as f64
2054                        / (device_rect.bottom - device_rect.top) as f64;
2055
2056                    // ptHimetricLocation's origin is 0,0 even on multi-monitor setups.
2057                    // On multi-monitor setups we need to translate the himetric location to the
2058                    // rect of the display device it's attached to.
2059                    let x = display_rect.left as f64
2060                        + pointer_info.ptHimetricLocation.x as f64 * himetric_to_pixel_ratio_x;
2061                    let y = display_rect.top as f64
2062                        + pointer_info.ptHimetricLocation.y as f64 * himetric_to_pixel_ratio_y;
2063
2064                    let mut location = POINT { x: x.floor() as i32, y: y.floor() as i32 };
2065
2066                    if unsafe { ScreenToClient(window, &mut location) } == false.into() {
2067                        continue;
2068                    }
2069
2070                    let force = match pointer_info.pointerType {
2071                        PT_TOUCH => {
2072                            let mut touch_info = mem::MaybeUninit::uninit();
2073                            util::GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
2074                                match unsafe {
2075                                    GetPointerTouchInfo(
2076                                        pointer_info.pointerId,
2077                                        touch_info.as_mut_ptr(),
2078                                    )
2079                                } {
2080                                    0 => None,
2081                                    _ => normalize_pointer_pressure(unsafe {
2082                                        touch_info.assume_init().pressure
2083                                    }),
2084                                }
2085                            })
2086                        },
2087                        PT_PEN => {
2088                            let mut pen_info = mem::MaybeUninit::uninit();
2089                            util::GET_POINTER_PEN_INFO.and_then(|GetPointerPenInfo| {
2090                                match unsafe {
2091                                    GetPointerPenInfo(pointer_info.pointerId, pen_info.as_mut_ptr())
2092                                } {
2093                                    0 => None,
2094                                    _ => normalize_pointer_pressure(unsafe {
2095                                        pen_info.assume_init().pressure
2096                                    }),
2097                                }
2098                            })
2099                        },
2100                        _ => None,
2101                    };
2102
2103                    let x = location.x as f64 + x.fract();
2104                    let y = location.y as f64 + y.fract();
2105                    let location = PhysicalPosition::new(x, y);
2106                    userdata.send_event(Event::WindowEvent {
2107                        window_id: RootWindowId(WindowId(window)),
2108                        event: WindowEvent::Touch(Touch {
2109                            phase: if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) {
2110                                TouchPhase::Started
2111                            } else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UP) {
2112                                TouchPhase::Ended
2113                            } else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UPDATE)
2114                            {
2115                                TouchPhase::Moved
2116                            } else {
2117                                continue;
2118                            },
2119                            location,
2120                            force,
2121                            id: pointer_info.pointerId as u64,
2122                            device_id: DEVICE_ID,
2123                        }),
2124                    });
2125                }
2126
2127                unsafe { SkipPointerFrameMessages(pointer_id) };
2128            }
2129            result = ProcResult::Value(0);
2130        },
2131
2132        WM_NCACTIVATE => {
2133            let is_active = wparam != false.into();
2134            let active_focus_changed = userdata.window_state_lock().set_active(is_active);
2135            if active_focus_changed {
2136                if is_active {
2137                    unsafe { gain_active_focus(window, userdata) };
2138                } else {
2139                    unsafe { lose_active_focus(window, userdata) };
2140                }
2141            }
2142            result = ProcResult::DefWindowProc(wparam);
2143        },
2144
2145        WM_SETFOCUS => {
2146            let active_focus_changed = userdata.window_state_lock().set_focused(true);
2147            if active_focus_changed {
2148                unsafe { gain_active_focus(window, userdata) };
2149            }
2150            result = ProcResult::Value(0);
2151        },
2152
2153        WM_KILLFOCUS => {
2154            let active_focus_changed = userdata.window_state_lock().set_focused(false);
2155            if active_focus_changed {
2156                unsafe { lose_active_focus(window, userdata) };
2157            }
2158            result = ProcResult::Value(0);
2159        },
2160
2161        WM_SETCURSOR => {
2162            let set_cursor_to = {
2163                let window_state = userdata.window_state_lock();
2164                // The return value for the preceding `WM_NCHITTEST` message is conveniently
2165                // provided through the low-order word of lParam. We use that here since
2166                // `WM_MOUSEMOVE` seems to come after `WM_SETCURSOR` for a given cursor movement.
2167                let in_client_area = super::loword(lparam as u32) as u32 == HTCLIENT;
2168                if in_client_area {
2169                    Some(window_state.mouse.selected_cursor.clone())
2170                } else {
2171                    None
2172                }
2173            };
2174
2175            match set_cursor_to {
2176                Some(selected_cursor) => {
2177                    let hcursor = match selected_cursor {
2178                        SelectedCursor::Named(cursor_icon) => unsafe {
2179                            LoadCursorW(0, util::to_windows_cursor(cursor_icon))
2180                        },
2181                        SelectedCursor::Custom(cursor) => cursor.as_raw_handle(),
2182                    };
2183                    unsafe { SetCursor(hcursor) };
2184                    result = ProcResult::Value(0);
2185                },
2186                None => result = ProcResult::DefWindowProc(wparam),
2187            }
2188        },
2189
2190        WM_GETMINMAXINFO => {
2191            let mmi = lparam as *mut MINMAXINFO;
2192
2193            let window_state = userdata.window_state_lock();
2194            let window_flags = window_state.window_flags;
2195
2196            if window_state.min_size.is_some() || window_state.max_size.is_some() {
2197                if let Some(min_size) = window_state.min_size {
2198                    let min_size = min_size.to_physical(window_state.scale_factor);
2199                    let (width, height): (u32, u32) =
2200                        window_flags.adjust_size(window, min_size).into();
2201                    unsafe { (*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 } };
2202                }
2203                if let Some(max_size) = window_state.max_size {
2204                    let max_size = max_size.to_physical(window_state.scale_factor);
2205                    let (width, height): (u32, u32) =
2206                        window_flags.adjust_size(window, max_size).into();
2207                    unsafe { (*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 } };
2208                }
2209            }
2210
2211            result = ProcResult::Value(0);
2212        },
2213
2214        // Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change
2215        // DPI, therefore all applications are closed while DPI is changing.
2216        WM_DPICHANGED => {
2217            use crate::event::WindowEvent::ScaleFactorChanged;
2218
2219            // This message actually provides two DPI values - x and y. However MSDN says that
2220            // "you only need to use either the X-axis or the Y-axis value when scaling your
2221            // application since they are the same".
2222            // https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx
2223            let new_dpi_x = super::loword(wparam as u32) as u32;
2224            let new_scale_factor = dpi_to_scale_factor(new_dpi_x);
2225            let old_scale_factor: f64;
2226
2227            let (allow_resize, window_flags) = {
2228                let mut window_state = userdata.window_state_lock();
2229                old_scale_factor = window_state.scale_factor;
2230                window_state.scale_factor = new_scale_factor;
2231
2232                if new_scale_factor == old_scale_factor {
2233                    result = ProcResult::Value(0);
2234                    return;
2235                }
2236
2237                let allow_resize = window_state.fullscreen.is_none()
2238                    && !window_state.window_flags().contains(WindowFlags::MAXIMIZED);
2239
2240                (allow_resize, window_state.window_flags)
2241            };
2242
2243            // New size as suggested by Windows.
2244            let suggested_rect = unsafe { *(lparam as *const RECT) };
2245
2246            // The window rect provided is the window's outer size, not it's inner size. However,
2247            // win32 doesn't provide an `UnadjustWindowRectEx` function to get the client rect from
2248            // the outer rect, so we instead adjust the window rect to get the decoration margins
2249            // and remove them from the outer size.
2250            let margin_left: i32;
2251            let margin_top: i32;
2252            // let margin_right: i32;
2253            // let margin_bottom: i32;
2254            {
2255                let adjusted_rect =
2256                    window_flags.adjust_rect(window, suggested_rect).unwrap_or(suggested_rect);
2257                margin_left = suggested_rect.left - adjusted_rect.left;
2258                margin_top = suggested_rect.top - adjusted_rect.top;
2259                // margin_right = adjusted_rect.right - suggested_rect.right;
2260                // margin_bottom = adjusted_rect.bottom - suggested_rect.bottom;
2261            }
2262
2263            let old_physical_inner_rect = util::WindowArea::Inner
2264                .get_rect(window)
2265                .expect("failed to query (old) inner window area");
2266            let old_physical_inner_size = PhysicalSize::new(
2267                (old_physical_inner_rect.right - old_physical_inner_rect.left) as u32,
2268                (old_physical_inner_rect.bottom - old_physical_inner_rect.top) as u32,
2269            );
2270
2271            // `allow_resize` prevents us from re-applying DPI adjustment to the restored size after
2272            // exiting fullscreen (the restored size is already DPI adjusted).
2273            let new_physical_inner_size = match allow_resize {
2274                // We calculate our own size because the default suggested rect doesn't do a great
2275                // job of preserving the window's logical size.
2276                true => old_physical_inner_size
2277                    .to_logical::<f64>(old_scale_factor)
2278                    .to_physical::<u32>(new_scale_factor),
2279                false => old_physical_inner_size,
2280            };
2281
2282            let new_inner_size = Arc::new(Mutex::new(new_physical_inner_size));
2283            userdata.send_event(Event::WindowEvent {
2284                window_id: RootWindowId(WindowId(window)),
2285                event: ScaleFactorChanged {
2286                    scale_factor: new_scale_factor,
2287                    inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
2288                },
2289            });
2290
2291            let new_physical_inner_size = *new_inner_size.lock().unwrap();
2292            drop(new_inner_size);
2293
2294            let dragging_window: bool;
2295
2296            {
2297                let window_state = userdata.window_state_lock();
2298                dragging_window =
2299                    window_state.window_flags().contains(WindowFlags::MARKER_IN_SIZE_MOVE);
2300                // Unset maximized if we're changing the window's size.
2301                if new_physical_inner_size != old_physical_inner_size {
2302                    WindowState::set_window_flags(window_state, window, |f| {
2303                        f.set(WindowFlags::MAXIMIZED, false)
2304                    });
2305                }
2306            }
2307
2308            let new_outer_rect: RECT;
2309            {
2310                let suggested_ul =
2311                    (suggested_rect.left + margin_left, suggested_rect.top + margin_top);
2312
2313                let mut conservative_rect = RECT {
2314                    left: suggested_ul.0,
2315                    top: suggested_ul.1,
2316                    right: suggested_ul.0 + new_physical_inner_size.width as i32,
2317                    bottom: suggested_ul.1 + new_physical_inner_size.height as i32,
2318                };
2319
2320                conservative_rect = window_flags
2321                    .adjust_rect(window, conservative_rect)
2322                    .unwrap_or(conservative_rect);
2323
2324                // If we're dragging the window, offset the window so that the cursor's
2325                // relative horizontal position in the title bar is preserved.
2326                if dragging_window {
2327                    let bias = {
2328                        let cursor_pos = {
2329                            let mut pos = unsafe { mem::zeroed() };
2330                            unsafe { GetCursorPos(&mut pos) };
2331                            pos
2332                        };
2333                        let suggested_cursor_horizontal_ratio = (cursor_pos.x - suggested_rect.left)
2334                            as f64
2335                            / (suggested_rect.right - suggested_rect.left) as f64;
2336
2337                        (cursor_pos.x
2338                            - (suggested_cursor_horizontal_ratio
2339                                * (conservative_rect.right - conservative_rect.left) as f64)
2340                                as i32)
2341                            - conservative_rect.left
2342                    };
2343                    conservative_rect.left += bias;
2344                    conservative_rect.right += bias;
2345                }
2346
2347                // Check to see if the new window rect is on the monitor with the new DPI factor.
2348                // If it isn't, offset the window so that it is.
2349                let new_dpi_monitor = unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONULL) };
2350                let conservative_rect_monitor =
2351                    unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) };
2352                new_outer_rect = if conservative_rect_monitor == new_dpi_monitor {
2353                    conservative_rect
2354                } else {
2355                    let get_monitor_rect = |monitor| {
2356                        let mut monitor_info = MONITORINFO {
2357                            cbSize: mem::size_of::<MONITORINFO>() as _,
2358                            ..unsafe { mem::zeroed() }
2359                        };
2360                        unsafe { GetMonitorInfoW(monitor, &mut monitor_info) };
2361                        monitor_info.rcMonitor
2362                    };
2363                    let wrong_monitor = conservative_rect_monitor;
2364                    let wrong_monitor_rect = get_monitor_rect(wrong_monitor);
2365                    let new_monitor_rect = get_monitor_rect(new_dpi_monitor);
2366
2367                    // The direction to nudge the window in to get the window onto the monitor with
2368                    // the new DPI factor. We calculate this by seeing which monitor edges are
2369                    // shared and nudging away from the wrong monitor based on those.
2370                    #[allow(clippy::bool_to_int_with_if)]
2371                    let delta_nudge_to_dpi_monitor = (
2372                        if wrong_monitor_rect.left == new_monitor_rect.right {
2373                            -1
2374                        } else if wrong_monitor_rect.right == new_monitor_rect.left {
2375                            1
2376                        } else {
2377                            0
2378                        },
2379                        if wrong_monitor_rect.bottom == new_monitor_rect.top {
2380                            1
2381                        } else if wrong_monitor_rect.top == new_monitor_rect.bottom {
2382                            -1
2383                        } else {
2384                            0
2385                        },
2386                    );
2387
2388                    let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left
2389                        + new_monitor_rect.bottom
2390                        - new_monitor_rect.top;
2391                    for _ in 0..abort_after_iterations {
2392                        conservative_rect.left += delta_nudge_to_dpi_monitor.0;
2393                        conservative_rect.right += delta_nudge_to_dpi_monitor.0;
2394                        conservative_rect.top += delta_nudge_to_dpi_monitor.1;
2395                        conservative_rect.bottom += delta_nudge_to_dpi_monitor.1;
2396
2397                        if unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) }
2398                            == new_dpi_monitor
2399                        {
2400                            break;
2401                        }
2402                    }
2403
2404                    conservative_rect
2405                };
2406            }
2407
2408            unsafe {
2409                SetWindowPos(
2410                    window,
2411                    0,
2412                    new_outer_rect.left,
2413                    new_outer_rect.top,
2414                    new_outer_rect.right - new_outer_rect.left,
2415                    new_outer_rect.bottom - new_outer_rect.top,
2416                    SWP_NOZORDER | SWP_NOACTIVATE,
2417                )
2418            };
2419
2420            result = ProcResult::Value(0);
2421        },
2422
2423        WM_SETTINGCHANGE => {
2424            use crate::event::WindowEvent::ThemeChanged;
2425
2426            let preferred_theme = userdata.window_state_lock().preferred_theme;
2427
2428            if preferred_theme.is_none() {
2429                let new_theme = try_theme(window, preferred_theme);
2430                let mut window_state = userdata.window_state_lock();
2431
2432                if window_state.current_theme != new_theme {
2433                    window_state.current_theme = new_theme;
2434                    drop(window_state);
2435                    userdata.send_event(Event::WindowEvent {
2436                        window_id: RootWindowId(WindowId(window)),
2437                        event: ThemeChanged(new_theme),
2438                    });
2439                }
2440            }
2441            result = ProcResult::DefWindowProc(wparam);
2442        },
2443
2444        _ => {
2445            if msg == DESTROY_MSG_ID.get() {
2446                unsafe { DestroyWindow(window) };
2447                result = ProcResult::Value(0);
2448            } else if msg == SET_RETAIN_STATE_ON_SIZE_MSG_ID.get() {
2449                let mut window_state = userdata.window_state_lock();
2450                window_state.set_window_flags_in_place(|f| {
2451                    f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0)
2452                });
2453                result = ProcResult::Value(0);
2454            } else if msg == TASKBAR_CREATED.get() {
2455                let window_state = userdata.window_state_lock();
2456                unsafe { set_skip_taskbar(window, window_state.skip_taskbar) };
2457                result = ProcResult::DefWindowProc(wparam);
2458            } else {
2459                result = ProcResult::DefWindowProc(wparam);
2460            }
2461        },
2462    };
2463
2464    userdata
2465        .event_loop_runner
2466        .catch_unwind(callback)
2467        .unwrap_or_else(|| result = ProcResult::Value(-1));
2468
2469    match result {
2470        ProcResult::DefWindowProc(wparam) => unsafe { DefWindowProcW(window, msg, wparam, lparam) },
2471        ProcResult::Value(val) => val,
2472    }
2473}
2474
2475unsafe extern "system" fn thread_event_target_callback(
2476    window: HWND,
2477    msg: u32,
2478    wparam: WPARAM,
2479    lparam: LPARAM,
2480) -> LRESULT {
2481    let userdata_ptr =
2482        unsafe { super::get_window_long(window, GWL_USERDATA) } as *mut ThreadMsgTargetData;
2483    if userdata_ptr.is_null() {
2484        // `userdata_ptr` will always be null for the first `WM_GETMINMAXINFO`, as well as
2485        // `WM_NCCREATE` and `WM_CREATE`.
2486        return unsafe { DefWindowProcW(window, msg, wparam, lparam) };
2487    }
2488    let userdata = unsafe { Box::from_raw(userdata_ptr) };
2489
2490    if msg != WM_PAINT {
2491        unsafe { RedrawWindow(window, ptr::null(), 0, RDW_INTERNALPAINT) };
2492    }
2493
2494    let mut userdata_removed = false;
2495
2496    // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing
2497    // the closure to catch_unwind directly so that the match body indentation wouldn't change and
2498    // the git blame and history would be preserved.
2499    let callback = || match msg {
2500        WM_NCDESTROY => {
2501            unsafe { super::set_window_long(window, GWL_USERDATA, 0) };
2502            userdata_removed = true;
2503            0
2504        },
2505        WM_PAINT => unsafe {
2506            ValidateRect(window, ptr::null());
2507            // Default WM_PAINT behaviour. This makes sure modals and popups are shown immediately
2508            // when opening them.
2509            DefWindowProcW(window, msg, wparam, lparam)
2510        },
2511
2512        WM_INPUT_DEVICE_CHANGE => {
2513            let event = match wparam as u32 {
2514                GIDC_ARRIVAL => DeviceEvent::Added,
2515                GIDC_REMOVAL => DeviceEvent::Removed,
2516                _ => unreachable!(),
2517            };
2518
2519            userdata
2520                .send_event(Event::DeviceEvent { device_id: wrap_device_id(lparam as u32), event });
2521
2522            0
2523        },
2524
2525        WM_INPUT => {
2526            if let Some(data) = raw_input::get_raw_input_data(lparam as _) {
2527                unsafe { handle_raw_input(&userdata, data) };
2528            }
2529
2530            unsafe { DefWindowProcW(window, msg, wparam, lparam) }
2531        },
2532
2533        _ if msg == USER_EVENT_MSG_ID.get() => {
2534            // synthesis a placeholder UserEvent, so that if the callback is
2535            // re-entered it can be buffered for later delivery. the real
2536            // user event is still in the mpsc channel and will be pulled
2537            // once the placeholder event is delivered to the wrapper
2538            // `event_handler`
2539            userdata.send_event(Event::UserEvent(UserEventPlaceholder));
2540            0
2541        },
2542        _ if msg == EXEC_MSG_ID.get() => {
2543            let mut function: ThreadExecFn = unsafe { Box::from_raw(wparam as *mut _) };
2544            function();
2545            0
2546        },
2547        _ => unsafe { DefWindowProcW(window, msg, wparam, lparam) },
2548    };
2549
2550    let result = userdata.event_loop_runner.catch_unwind(callback).unwrap_or(-1);
2551    if userdata_removed {
2552        drop(userdata);
2553    } else {
2554        Box::leak(userdata);
2555    }
2556    result
2557}
2558
2559unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) {
2560    use crate::event::DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel};
2561    use crate::event::ElementState::{Pressed, Released};
2562    use crate::event::MouseScrollDelta::LineDelta;
2563
2564    let device_id = wrap_device_id(data.header.hDevice as _);
2565
2566    if data.header.dwType == RIM_TYPEMOUSE {
2567        let mouse = unsafe { data.data.mouse };
2568
2569        if util::has_flag(mouse.usFlags as u32, MOUSE_MOVE_RELATIVE) {
2570            let x = mouse.lLastX as f64;
2571            let y = mouse.lLastY as f64;
2572
2573            if x != 0.0 {
2574                userdata.send_event(Event::DeviceEvent {
2575                    device_id,
2576                    event: Motion { axis: 0, value: x },
2577                });
2578            }
2579
2580            if y != 0.0 {
2581                userdata.send_event(Event::DeviceEvent {
2582                    device_id,
2583                    event: Motion { axis: 1, value: y },
2584                });
2585            }
2586
2587            if x != 0.0 || y != 0.0 {
2588                userdata.send_event(Event::DeviceEvent {
2589                    device_id,
2590                    event: MouseMotion { delta: (x, y) },
2591                });
2592            }
2593        }
2594
2595        let button_flags = unsafe { mouse.Anonymous.Anonymous.usButtonFlags };
2596        if util::has_flag(button_flags as u32, RI_MOUSE_WHEEL) {
2597            let button_data = unsafe { mouse.Anonymous.Anonymous.usButtonData } as i16;
2598            let delta = button_data as f32 / WHEEL_DELTA as f32;
2599            userdata.send_event(Event::DeviceEvent {
2600                device_id,
2601                event: MouseWheel { delta: LineDelta(0.0, delta) },
2602            });
2603        }
2604        if util::has_flag(button_flags as u32, RI_MOUSE_HWHEEL) {
2605            let button_data = unsafe { mouse.Anonymous.Anonymous.usButtonData } as i16;
2606            let delta = -button_data as f32 / WHEEL_DELTA as f32;
2607            userdata.send_event(Event::DeviceEvent {
2608                device_id,
2609                event: MouseWheel { delta: LineDelta(delta, 0.0) },
2610            });
2611        }
2612
2613        let button_state = raw_input::get_raw_mouse_button_state(button_flags as u32);
2614        for (button, state) in button_state.iter().enumerate() {
2615            if let Some(state) = *state {
2616                userdata.send_event(Event::DeviceEvent {
2617                    device_id,
2618                    event: Button { button: button as _, state },
2619                });
2620            }
2621        }
2622    } else if data.header.dwType == RIM_TYPEKEYBOARD {
2623        let keyboard = unsafe { data.data.keyboard };
2624
2625        let pressed = keyboard.Message == WM_KEYDOWN || keyboard.Message == WM_SYSKEYDOWN;
2626        let released = keyboard.Message == WM_KEYUP || keyboard.Message == WM_SYSKEYUP;
2627
2628        if !pressed && !released {
2629            return;
2630        }
2631
2632        if let Some(physical_key) = raw_input::get_keyboard_physical_key(keyboard) {
2633            let state = if pressed { Pressed } else { Released };
2634
2635            userdata.send_event(Event::DeviceEvent {
2636                device_id,
2637                event: Key(RawKeyEvent { physical_key, state }),
2638            });
2639        }
2640    }
2641}
2642
2643enum PointerMoveKind {
2644    /// Pointer entered to the window.
2645    Enter,
2646    /// Pointer leaved the window client area.
2647    Leave,
2648    /// Pointer is inside the window or `GetClientRect` failed.
2649    None,
2650}
2651
2652fn get_pointer_move_kind(
2653    window: HWND,
2654    mouse_was_inside_window: bool,
2655    x: i32,
2656    y: i32,
2657) -> PointerMoveKind {
2658    let rect: RECT = unsafe {
2659        let mut rect: RECT = mem::zeroed();
2660        if GetClientRect(window, &mut rect) == false.into() {
2661            return PointerMoveKind::None; // exit early if GetClientRect failed
2662        }
2663        rect
2664    };
2665
2666    let x = (rect.left..rect.right).contains(&x);
2667    let y = (rect.top..rect.bottom).contains(&y);
2668
2669    if !mouse_was_inside_window && x && y {
2670        PointerMoveKind::Enter
2671    } else if mouse_was_inside_window && !(x && y) {
2672        PointerMoveKind::Leave
2673    } else {
2674        PointerMoveKind::None
2675    }
2676}