winit/platform_impl/windows/
event_loop.rs

1#![allow(non_snake_case)]
2
3mod runner;
4
5use parking_lot::Mutex;
6use std::{
7    cell::Cell,
8    collections::VecDeque,
9    marker::PhantomData,
10    mem, panic, ptr,
11    rc::Rc,
12    sync::{
13        mpsc::{self, Receiver, Sender},
14        Arc,
15    },
16    thread,
17    time::{Duration, Instant},
18};
19use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR};
20
21use winapi::{
22    shared::{
23        minwindef::{BOOL, DWORD, HIWORD, INT, LOWORD, LPARAM, LRESULT, UINT, WORD, WPARAM},
24        windef::{HWND, POINT, RECT},
25        windowsx, winerror,
26    },
27    um::{
28        commctrl, libloaderapi, ole2, processthreadsapi, winbase,
29        winnt::{HANDLE, LONG, LPCSTR, SHORT},
30        winuser,
31    },
32};
33
34use crate::{
35    dpi::{PhysicalPosition, PhysicalSize},
36    event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent},
37    event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
38    monitor::MonitorHandle as RootMonitorHandle,
39    platform_impl::platform::{
40        dark_mode::try_theme,
41        dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling},
42        drop_handler::FileDropHandler,
43        event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey},
44        monitor::{self, MonitorHandle},
45        raw_input, util,
46        window_state::{CursorFlags, WindowFlags, WindowState},
47        wrap_device_id, WindowId, DEVICE_ID,
48    },
49    window::{Fullscreen, WindowId as RootWindowId},
50};
51use runner::{EventLoopRunner, EventLoopRunnerShared};
52
53type GetPointerFrameInfoHistory = unsafe extern "system" fn(
54    pointerId: UINT,
55    entriesCount: *mut UINT,
56    pointerCount: *mut UINT,
57    pointerInfo: *mut winuser::POINTER_INFO,
58) -> BOOL;
59
60type SkipPointerFrameMessages = unsafe extern "system" fn(pointerId: UINT) -> BOOL;
61type GetPointerDeviceRects = unsafe extern "system" fn(
62    device: HANDLE,
63    pointerDeviceRect: *mut RECT,
64    displayRect: *mut RECT,
65) -> BOOL;
66
67type GetPointerTouchInfo =
68    unsafe extern "system" fn(pointerId: UINT, touchInfo: *mut winuser::POINTER_TOUCH_INFO) -> BOOL;
69
70type GetPointerPenInfo =
71    unsafe extern "system" fn(pointId: UINT, penInfo: *mut winuser::POINTER_PEN_INFO) -> BOOL;
72
73lazy_static! {
74    static ref GET_POINTER_FRAME_INFO_HISTORY: Option<GetPointerFrameInfoHistory> =
75        get_function!("user32.dll", GetPointerFrameInfoHistory);
76    static ref SKIP_POINTER_FRAME_MESSAGES: Option<SkipPointerFrameMessages> =
77        get_function!("user32.dll", SkipPointerFrameMessages);
78    static ref GET_POINTER_DEVICE_RECTS: Option<GetPointerDeviceRects> =
79        get_function!("user32.dll", GetPointerDeviceRects);
80    static ref GET_POINTER_TOUCH_INFO: Option<GetPointerTouchInfo> =
81        get_function!("user32.dll", GetPointerTouchInfo);
82    static ref GET_POINTER_PEN_INFO: Option<GetPointerPenInfo> =
83        get_function!("user32.dll", GetPointerPenInfo);
84}
85
86pub(crate) struct SubclassInput<T: 'static> {
87    pub window_state: Arc<Mutex<WindowState>>,
88    pub event_loop_runner: EventLoopRunnerShared<T>,
89    pub file_drop_handler: Option<FileDropHandler>,
90    pub subclass_removed: Cell<bool>,
91    pub recurse_depth: Cell<u32>,
92}
93
94impl<T> SubclassInput<T> {
95    unsafe fn send_event(&self, event: Event<'_, T>) {
96        self.event_loop_runner.send_event(event);
97    }
98}
99
100struct ThreadMsgTargetSubclassInput<T: 'static> {
101    event_loop_runner: EventLoopRunnerShared<T>,
102    user_event_receiver: Receiver<T>,
103}
104
105impl<T> ThreadMsgTargetSubclassInput<T> {
106    unsafe fn send_event(&self, event: Event<'_, T>) {
107        self.event_loop_runner.send_event(event);
108    }
109}
110
111pub struct EventLoop<T: 'static> {
112    thread_msg_sender: Sender<T>,
113    window_target: RootELW<T>,
114}
115
116pub struct EventLoopWindowTarget<T: 'static> {
117    thread_id: DWORD,
118    thread_msg_target: HWND,
119    pub(crate) runner_shared: EventLoopRunnerShared<T>,
120}
121
122macro_rules! main_thread_check {
123    ($fn_name:literal) => {{
124        let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
125        if thread_id != main_thread_id() {
126            panic!(concat!(
127                "Initializing the event loop outside of the main thread is a significant \
128                 cross-platform compatibility hazard. If you really, absolutely need to create an \
129                 EventLoop on a different thread, please use the `EventLoopExtWindows::",
130                $fn_name,
131                "` function."
132            ));
133        }
134    }};
135}
136
137impl<T: 'static> EventLoop<T> {
138    pub fn new() -> EventLoop<T> {
139        main_thread_check!("new_any_thread");
140
141        Self::new_any_thread()
142    }
143
144    pub fn new_any_thread() -> EventLoop<T> {
145        become_dpi_aware();
146        Self::new_dpi_unaware_any_thread()
147    }
148
149    pub fn new_dpi_unaware() -> EventLoop<T> {
150        main_thread_check!("new_dpi_unaware_any_thread");
151
152        Self::new_dpi_unaware_any_thread()
153    }
154
155    pub fn new_dpi_unaware_any_thread() -> EventLoop<T> {
156        let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
157
158        let thread_msg_target = create_event_target_window();
159
160        let send_thread_msg_target = thread_msg_target as usize;
161        thread::spawn(move || wait_thread(thread_id, send_thread_msg_target as HWND));
162        let wait_thread_id = get_wait_thread_id();
163
164        let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target, wait_thread_id));
165
166        let thread_msg_sender =
167            subclass_event_target_window(thread_msg_target, runner_shared.clone());
168        raw_input::register_all_mice_and_keyboards_for_raw_input(thread_msg_target);
169
170        EventLoop {
171            thread_msg_sender,
172            window_target: RootELW {
173                p: EventLoopWindowTarget {
174                    thread_id,
175                    thread_msg_target,
176                    runner_shared,
177                },
178                _marker: PhantomData,
179            },
180        }
181    }
182
183    pub fn window_target(&self) -> &RootELW<T> {
184        &self.window_target
185    }
186
187    pub fn run<F>(mut self, event_handler: F) -> !
188    where
189        F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
190    {
191        self.run_return(event_handler);
192        ::std::process::exit(0);
193    }
194
195    pub fn run_return<F>(&mut self, mut event_handler: F)
196    where
197        F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
198    {
199        let event_loop_windows_ref = &self.window_target;
200
201        unsafe {
202            self.window_target
203                .p
204                .runner_shared
205                .set_event_handler(move |event, control_flow| {
206                    event_handler(event, event_loop_windows_ref, control_flow)
207                });
208        }
209
210        let runner = &self.window_target.p.runner_shared;
211
212        unsafe {
213            let mut msg = mem::zeroed();
214
215            runner.poll();
216            'main: loop {
217                if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
218                    break 'main;
219                }
220                winuser::TranslateMessage(&mut msg);
221                winuser::DispatchMessageW(&mut msg);
222
223                if let Err(payload) = runner.take_panic_error() {
224                    runner.reset_runner();
225                    panic::resume_unwind(payload);
226                }
227
228                if runner.control_flow() == ControlFlow::Exit && !runner.handling_events() {
229                    break 'main;
230                }
231            }
232        }
233
234        unsafe {
235            runner.loop_destroyed();
236        }
237        runner.reset_runner();
238    }
239
240    pub fn create_proxy(&self) -> EventLoopProxy<T> {
241        EventLoopProxy {
242            target_window: self.window_target.p.thread_msg_target,
243            event_send: self.thread_msg_sender.clone(),
244        }
245    }
246}
247
248impl<T> EventLoopWindowTarget<T> {
249    #[inline(always)]
250    pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor {
251        EventLoopThreadExecutor {
252            thread_id: self.thread_id,
253            target_window: self.thread_msg_target,
254        }
255    }
256
257    // TODO: Investigate opportunities for caching
258    pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
259        monitor::available_monitors()
260    }
261
262    pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
263        let monitor = monitor::primary_monitor();
264        Some(RootMonitorHandle { inner: monitor })
265    }
266}
267
268fn main_thread_id() -> DWORD {
269    static mut MAIN_THREAD_ID: DWORD = 0;
270    #[used]
271    #[allow(non_upper_case_globals)]
272    #[link_section = ".CRT$XCU"]
273    static INIT_MAIN_THREAD_ID: unsafe fn() = {
274        unsafe fn initer() {
275            MAIN_THREAD_ID = processthreadsapi::GetCurrentThreadId();
276        }
277        initer
278    };
279
280    unsafe { MAIN_THREAD_ID }
281}
282
283fn get_wait_thread_id() -> DWORD {
284    unsafe {
285        let mut msg = mem::zeroed();
286        let result = winuser::GetMessageW(
287            &mut msg,
288            -1 as _,
289            *SEND_WAIT_THREAD_ID_MSG_ID,
290            *SEND_WAIT_THREAD_ID_MSG_ID,
291        );
292        assert_eq!(
293            msg.message, *SEND_WAIT_THREAD_ID_MSG_ID,
294            "this shouldn't be possible. please open an issue with Winit. error code: {}",
295            result
296        );
297        msg.lParam as DWORD
298    }
299}
300
301fn wait_thread(parent_thread_id: DWORD, msg_window_id: HWND) {
302    unsafe {
303        let mut msg: winuser::MSG;
304
305        let cur_thread_id = processthreadsapi::GetCurrentThreadId();
306        winuser::PostThreadMessageW(
307            parent_thread_id,
308            *SEND_WAIT_THREAD_ID_MSG_ID,
309            0,
310            cur_thread_id as LPARAM,
311        );
312
313        let mut wait_until_opt = None;
314        'main: loop {
315            // Zeroing out the message ensures that the `WaitUntilInstantBox` doesn't get
316            // double-freed if `MsgWaitForMultipleObjectsEx` returns early and there aren't
317            // additional messages to process.
318            msg = mem::zeroed();
319
320            if wait_until_opt.is_some() {
321                if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, winuser::PM_REMOVE) {
322                    winuser::TranslateMessage(&mut msg);
323                    winuser::DispatchMessageW(&mut msg);
324                }
325            } else {
326                if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
327                    break 'main;
328                } else {
329                    winuser::TranslateMessage(&mut msg);
330                    winuser::DispatchMessageW(&mut msg);
331                }
332            }
333
334            if msg.message == *WAIT_UNTIL_MSG_ID {
335                wait_until_opt = Some(*WaitUntilInstantBox::from_raw(msg.lParam as *mut _));
336            } else if msg.message == *CANCEL_WAIT_UNTIL_MSG_ID {
337                wait_until_opt = None;
338            }
339
340            if let Some(wait_until) = wait_until_opt {
341                let now = Instant::now();
342                if now < wait_until {
343                    // MsgWaitForMultipleObjects tends to overshoot just a little bit. We subtract
344                    // 1 millisecond from the requested time and spinlock for the remainder to
345                    // compensate for that.
346                    let resume_reason = winuser::MsgWaitForMultipleObjectsEx(
347                        0,
348                        ptr::null(),
349                        dur2timeout(wait_until - now).saturating_sub(1),
350                        winuser::QS_ALLEVENTS,
351                        winuser::MWMO_INPUTAVAILABLE,
352                    );
353                    if resume_reason == winerror::WAIT_TIMEOUT {
354                        winuser::PostMessageW(msg_window_id, *PROCESS_NEW_EVENTS_MSG_ID, 0, 0);
355                        wait_until_opt = None;
356                    }
357                } else {
358                    winuser::PostMessageW(msg_window_id, *PROCESS_NEW_EVENTS_MSG_ID, 0, 0);
359                    wait_until_opt = None;
360                }
361            }
362        }
363    }
364}
365
366// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs
367fn dur2timeout(dur: Duration) -> DWORD {
368    // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
369    // timeouts in windows APIs are typically u32 milliseconds. To translate, we
370    // have two pieces to take care of:
371    //
372    // * Nanosecond precision is rounded up
373    // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE
374    //   (never time out).
375    dur.as_secs()
376        .checked_mul(1000)
377        .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000))
378        .and_then(|ms| {
379            ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 {
380                1
381            } else {
382                0
383            })
384        })
385        .map(|ms| {
386            if ms > DWORD::max_value() as u64 {
387                winbase::INFINITE
388            } else {
389                ms as DWORD
390            }
391        })
392        .unwrap_or(winbase::INFINITE)
393}
394
395impl<T> Drop for EventLoop<T> {
396    fn drop(&mut self) {
397        unsafe {
398            winuser::DestroyWindow(self.window_target.p.thread_msg_target);
399        }
400    }
401}
402
403pub(crate) struct EventLoopThreadExecutor {
404    thread_id: DWORD,
405    target_window: HWND,
406}
407
408unsafe impl Send for EventLoopThreadExecutor {}
409unsafe impl Sync for EventLoopThreadExecutor {}
410
411impl EventLoopThreadExecutor {
412    /// Check to see if we're in the parent event loop's thread.
413    pub(super) fn in_event_loop_thread(&self) -> bool {
414        let cur_thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
415        self.thread_id == cur_thread_id
416    }
417
418    /// Executes a function in the event loop thread. If we're already in the event loop thread,
419    /// we just call the function directly.
420    ///
421    /// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is
422    /// removed automatically if the callback receives a `WM_CLOSE` message for the window.
423    ///
424    /// Note that if you are using this to change some property of a window and updating
425    /// `WindowState` then you should call this within the lock of `WindowState`. Otherwise the
426    /// events may be sent to the other thread in different order to the one in which you set
427    /// `WindowState`, leaving them out of sync.
428    ///
429    /// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent
430    /// to the unstable FnBox.
431    pub(super) fn execute_in_thread<F>(&self, mut function: F)
432    where
433        F: FnMut() + Send + 'static,
434    {
435        unsafe {
436            if self.in_event_loop_thread() {
437                function();
438            } else {
439                // We double-box because the first box is a fat pointer.
440                let boxed = Box::new(function) as Box<dyn FnMut()>;
441                let boxed2: ThreadExecFn = Box::new(boxed);
442
443                let raw = Box::into_raw(boxed2);
444
445                let res = winuser::PostMessageW(
446                    self.target_window,
447                    *EXEC_MSG_ID,
448                    raw as *mut () as usize as WPARAM,
449                    0,
450                );
451                assert!(res != 0, "PostMessage failed ; is the messages queue full?");
452            }
453        }
454    }
455}
456
457type ThreadExecFn = Box<Box<dyn FnMut()>>;
458
459pub struct EventLoopProxy<T: 'static> {
460    target_window: HWND,
461    event_send: Sender<T>,
462}
463unsafe impl<T: Send + 'static> Send for EventLoopProxy<T> {}
464
465impl<T: 'static> Clone for EventLoopProxy<T> {
466    fn clone(&self) -> Self {
467        Self {
468            target_window: self.target_window,
469            event_send: self.event_send.clone(),
470        }
471    }
472}
473
474impl<T: 'static> EventLoopProxy<T> {
475    pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
476        unsafe {
477            if winuser::PostMessageW(self.target_window, *USER_EVENT_MSG_ID, 0, 0) != 0 {
478                self.event_send.send(event).ok();
479                Ok(())
480            } else {
481                Err(EventLoopClosed(event))
482            }
483        }
484    }
485}
486
487type WaitUntilInstantBox = Box<Instant>;
488
489lazy_static! {
490    // Message sent by the `EventLoopProxy` when we want to wake up the thread.
491    // WPARAM and LPARAM are unused.
492    static ref USER_EVENT_MSG_ID: u32 = {
493        unsafe {
494            winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as LPCSTR)
495        }
496    };
497    // Message sent when we want to execute a closure in the thread.
498    // WPARAM contains a Box<Box<dyn FnMut()>> that must be retrieved with `Box::from_raw`,
499    // and LPARAM is unused.
500    static ref EXEC_MSG_ID: u32 = {
501        unsafe {
502            winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8)
503        }
504    };
505    static ref PROCESS_NEW_EVENTS_MSG_ID: u32 = {
506        unsafe {
507            winuser::RegisterWindowMessageA("Winit::ProcessNewEvents\0".as_ptr() as *const i8)
508        }
509    };
510    /// lparam is the wait thread's message id.
511    static ref SEND_WAIT_THREAD_ID_MSG_ID: u32 = {
512        unsafe {
513            winuser::RegisterWindowMessageA("Winit::SendWaitThreadId\0".as_ptr() as *const i8)
514        }
515    };
516    /// lparam points to a `Box<Instant>` signifying the time `PROCESS_NEW_EVENTS_MSG_ID` should
517    /// be sent.
518    static ref WAIT_UNTIL_MSG_ID: u32 = {
519        unsafe {
520            winuser::RegisterWindowMessageA("Winit::WaitUntil\0".as_ptr() as *const i8)
521        }
522    };
523    static ref CANCEL_WAIT_UNTIL_MSG_ID: u32 = {
524        unsafe {
525            winuser::RegisterWindowMessageA("Winit::CancelWaitUntil\0".as_ptr() as *const i8)
526        }
527    };
528    // Message sent by a `Window` when it wants to be destroyed by the main thread.
529    // WPARAM and LPARAM are unused.
530    pub static ref DESTROY_MSG_ID: u32 = {
531        unsafe {
532            winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as LPCSTR)
533        }
534    };
535    // WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the
536    // documentation in the `window_state` module for more information.
537    pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe {
538        winuser::RegisterWindowMessageA("Winit::SetRetainMaximized\0".as_ptr() as LPCSTR)
539    };
540    static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec<u16> = unsafe {
541        use std::ffi::OsStr;
542        use std::os::windows::ffi::OsStrExt;
543
544        let class_name: Vec<_> = OsStr::new("Winit Thread Event Target")
545            .encode_wide()
546            .chain(Some(0).into_iter())
547            .collect();
548
549        let class = winuser::WNDCLASSEXW {
550            cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
551            style: 0,
552            lpfnWndProc: Some(winuser::DefWindowProcW),
553            cbClsExtra: 0,
554            cbWndExtra: 0,
555            hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
556            hIcon: ptr::null_mut(),
557            hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
558            hbrBackground: ptr::null_mut(),
559            lpszMenuName: ptr::null(),
560            lpszClassName: class_name.as_ptr(),
561            hIconSm: ptr::null_mut(),
562        };
563
564        winuser::RegisterClassExW(&class);
565
566        class_name
567    };
568}
569
570fn create_event_target_window() -> HWND {
571    unsafe {
572        let window = winuser::CreateWindowExW(
573            winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED,
574            THREAD_EVENT_TARGET_WINDOW_CLASS.as_ptr(),
575            ptr::null_mut(),
576            0,
577            0,
578            0,
579            0,
580            0,
581            ptr::null_mut(),
582            ptr::null_mut(),
583            libloaderapi::GetModuleHandleW(ptr::null()),
584            ptr::null_mut(),
585        );
586        winuser::SetWindowLongPtrW(
587            window,
588            winuser::GWL_STYLE,
589            // The window technically has to be visible to receive WM_PAINT messages (which are used
590            // for delivering events during resizes), but it isn't displayed to the user because of
591            // the LAYERED style.
592            (winuser::WS_VISIBLE | winuser::WS_POPUP) as _,
593        );
594        window
595    }
596}
597
598fn subclass_event_target_window<T>(
599    window: HWND,
600    event_loop_runner: EventLoopRunnerShared<T>,
601) -> Sender<T> {
602    unsafe {
603        let (tx, rx) = mpsc::channel();
604
605        let subclass_input = ThreadMsgTargetSubclassInput {
606            event_loop_runner,
607            user_event_receiver: rx,
608        };
609        let input_ptr = Box::into_raw(Box::new(subclass_input));
610        let subclass_result = commctrl::SetWindowSubclass(
611            window,
612            Some(thread_event_target_callback::<T>),
613            THREAD_EVENT_TARGET_SUBCLASS_ID,
614            input_ptr as DWORD_PTR,
615        );
616        assert_eq!(subclass_result, 1);
617
618        tx
619    }
620}
621
622fn remove_event_target_window_subclass<T: 'static>(window: HWND) {
623    let removal_result = unsafe {
624        commctrl::RemoveWindowSubclass(
625            window,
626            Some(thread_event_target_callback::<T>),
627            THREAD_EVENT_TARGET_SUBCLASS_ID,
628        )
629    };
630    assert_eq!(removal_result, 1);
631}
632
633/// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of
634/// the window.
635unsafe fn capture_mouse(window: HWND, window_state: &mut WindowState) {
636    window_state.mouse.capture_count += 1;
637    winuser::SetCapture(window);
638}
639
640/// Release mouse input, stopping windows on this thread from receiving mouse input when the cursor
641/// is outside the window.
642unsafe fn release_mouse(mut window_state: parking_lot::MutexGuard<'_, WindowState>) {
643    window_state.mouse.capture_count = window_state.mouse.capture_count.saturating_sub(1);
644    if window_state.mouse.capture_count == 0 {
645        // ReleaseCapture() causes a WM_CAPTURECHANGED where we lock the window_state.
646        drop(window_state);
647        winuser::ReleaseCapture();
648    }
649}
650
651const WINDOW_SUBCLASS_ID: UINT_PTR = 0;
652const THREAD_EVENT_TARGET_SUBCLASS_ID: UINT_PTR = 1;
653pub(crate) fn subclass_window<T>(window: HWND, subclass_input: SubclassInput<T>) {
654    subclass_input.event_loop_runner.register_window(window);
655    let input_ptr = Box::into_raw(Box::new(subclass_input));
656    let subclass_result = unsafe {
657        commctrl::SetWindowSubclass(
658            window,
659            Some(public_window_callback::<T>),
660            WINDOW_SUBCLASS_ID,
661            input_ptr as DWORD_PTR,
662        )
663    };
664    assert_eq!(subclass_result, 1);
665}
666
667fn remove_window_subclass<T: 'static>(window: HWND) {
668    let removal_result = unsafe {
669        commctrl::RemoveWindowSubclass(
670            window,
671            Some(public_window_callback::<T>),
672            WINDOW_SUBCLASS_ID,
673        )
674    };
675    assert_eq!(removal_result, 1);
676}
677
678fn normalize_pointer_pressure(pressure: u32) -> Option<Force> {
679    match pressure {
680        1..=1024 => Some(Force::Normalized(pressure as f64 / 1024.0)),
681        _ => None,
682    }
683}
684
685/// Flush redraw events for Winit's windows.
686///
687/// Winit's API guarantees that all redraw events will be clustered together and dispatched all at
688/// once, but the standard Windows message loop doesn't always exhibit that behavior. If multiple
689/// windows have had redraws scheduled, but an input event is pushed to the message queue between
690/// the `WM_PAINT` call for the first window and the `WM_PAINT` call for the second window, Windows
691/// will dispatch the input event immediately instead of flushing all the redraw events. This
692/// function explicitly pulls all of Winit's redraw events out of the event queue so that they
693/// always all get processed in one fell swoop.
694///
695/// Returns `true` if this invocation flushed all the redraw events. If this function is re-entrant,
696/// it won't flush the redraw events and will return `false`.
697#[must_use]
698unsafe fn flush_paint_messages<T: 'static>(
699    except: Option<HWND>,
700    runner: &EventLoopRunner<T>,
701) -> bool {
702    if !runner.redrawing() {
703        runner.main_events_cleared();
704        let mut msg = mem::zeroed();
705        runner.owned_windows(|redraw_window| {
706            if Some(redraw_window) == except {
707                return;
708            }
709
710            if 0 == winuser::PeekMessageW(
711                &mut msg,
712                redraw_window,
713                winuser::WM_PAINT,
714                winuser::WM_PAINT,
715                winuser::PM_REMOVE | winuser::PM_QS_PAINT,
716            ) {
717                return;
718            }
719
720            winuser::TranslateMessage(&mut msg);
721            winuser::DispatchMessageW(&mut msg);
722        });
723        true
724    } else {
725        false
726    }
727}
728
729unsafe fn process_control_flow<T: 'static>(runner: &EventLoopRunner<T>) {
730    match runner.control_flow() {
731        ControlFlow::Poll => {
732            winuser::PostMessageW(runner.thread_msg_target(), *PROCESS_NEW_EVENTS_MSG_ID, 0, 0);
733        }
734        ControlFlow::Wait => (),
735        ControlFlow::WaitUntil(until) => {
736            winuser::PostThreadMessageW(
737                runner.wait_thread_id(),
738                *WAIT_UNTIL_MSG_ID,
739                0,
740                Box::into_raw(WaitUntilInstantBox::new(until)) as LPARAM,
741            );
742        }
743        ControlFlow::Exit => (),
744    }
745}
746
747/// Emit a `ModifiersChanged` event whenever modifiers have changed.
748fn update_modifiers<T>(window: HWND, subclass_input: &SubclassInput<T>) {
749    use crate::event::WindowEvent::ModifiersChanged;
750
751    let modifiers = event::get_key_mods();
752    let mut window_state = subclass_input.window_state.lock();
753    if window_state.modifiers_state != modifiers {
754        window_state.modifiers_state = modifiers;
755
756        // Drop lock
757        drop(window_state);
758
759        unsafe {
760            subclass_input.send_event(Event::WindowEvent {
761                window_id: RootWindowId(WindowId(window)),
762                event: ModifiersChanged(modifiers),
763            });
764        }
765    }
766}
767
768/// Any window whose callback is configured to this function will have its events propagated
769/// through the events loop of the thread the window was created in.
770//
771// This is the callback that is called by `DispatchMessage` in the events loop.
772//
773// Returning 0 tells the Win32 API that the message has been processed.
774// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
775unsafe extern "system" fn public_window_callback<T: 'static>(
776    window: HWND,
777    msg: UINT,
778    wparam: WPARAM,
779    lparam: LPARAM,
780    uidsubclass: UINT_PTR,
781    subclass_input_ptr: DWORD_PTR,
782) -> LRESULT {
783    let subclass_input_ptr = subclass_input_ptr as *mut SubclassInput<T>;
784    let (result, subclass_removed, recurse_depth) = {
785        let subclass_input = &*subclass_input_ptr;
786        subclass_input
787            .recurse_depth
788            .set(subclass_input.recurse_depth.get() + 1);
789
790        let result =
791            public_window_callback_inner(window, msg, wparam, lparam, uidsubclass, subclass_input);
792
793        let subclass_removed = subclass_input.subclass_removed.get();
794        let recurse_depth = subclass_input.recurse_depth.get() - 1;
795        subclass_input.recurse_depth.set(recurse_depth);
796
797        (result, subclass_removed, recurse_depth)
798    };
799
800    if subclass_removed && recurse_depth == 0 {
801        Box::from_raw(subclass_input_ptr);
802    }
803
804    result
805}
806
807unsafe fn public_window_callback_inner<T: 'static>(
808    window: HWND,
809    msg: UINT,
810    wparam: WPARAM,
811    lparam: LPARAM,
812    _: UINT_PTR,
813    subclass_input: &SubclassInput<T>,
814) -> LRESULT {
815    winuser::RedrawWindow(
816        subclass_input.event_loop_runner.thread_msg_target(),
817        ptr::null(),
818        ptr::null_mut(),
819        winuser::RDW_INTERNALPAINT,
820    );
821
822    // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing
823    // the closure to catch_unwind directly so that the match body indendation wouldn't change and
824    // the git blame and history would be preserved.
825    let callback = || match msg {
826        winuser::WM_ENTERSIZEMOVE => {
827            subclass_input
828                .window_state
829                .lock()
830                .set_window_flags_in_place(|f| f.insert(WindowFlags::MARKER_IN_SIZE_MOVE));
831            0
832        }
833
834        winuser::WM_EXITSIZEMOVE => {
835            subclass_input
836                .window_state
837                .lock()
838                .set_window_flags_in_place(|f| f.remove(WindowFlags::MARKER_IN_SIZE_MOVE));
839            0
840        }
841
842        winuser::WM_NCCREATE => {
843            enable_non_client_dpi_scaling(window);
844            commctrl::DefSubclassProc(window, msg, wparam, lparam)
845        }
846        winuser::WM_NCLBUTTONDOWN => {
847            if wparam == winuser::HTCAPTION as _ {
848                winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, lparam);
849            }
850            commctrl::DefSubclassProc(window, msg, wparam, lparam)
851        }
852
853        winuser::WM_CLOSE => {
854            use crate::event::WindowEvent::CloseRequested;
855            subclass_input.send_event(Event::WindowEvent {
856                window_id: RootWindowId(WindowId(window)),
857                event: CloseRequested,
858            });
859            0
860        }
861
862        winuser::WM_DESTROY => {
863            use crate::event::WindowEvent::Destroyed;
864            ole2::RevokeDragDrop(window);
865            subclass_input.send_event(Event::WindowEvent {
866                window_id: RootWindowId(WindowId(window)),
867                event: Destroyed,
868            });
869            subclass_input.event_loop_runner.remove_window(window);
870            0
871        }
872
873        winuser::WM_NCDESTROY => {
874            remove_window_subclass::<T>(window);
875            subclass_input.subclass_removed.set(true);
876            0
877        }
878
879        winuser::WM_PAINT => {
880            if subclass_input.event_loop_runner.should_buffer() {
881                // this branch can happen in response to `UpdateWindow`, if win32 decides to
882                // redraw the window outside the normal flow of the event loop.
883                winuser::RedrawWindow(
884                    window,
885                    ptr::null(),
886                    ptr::null_mut(),
887                    winuser::RDW_INTERNALPAINT,
888                );
889            } else {
890                let managing_redraw =
891                    flush_paint_messages(Some(window), &subclass_input.event_loop_runner);
892                subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window))));
893                if managing_redraw {
894                    subclass_input.event_loop_runner.redraw_events_cleared();
895                    process_control_flow(&subclass_input.event_loop_runner);
896                }
897            }
898
899            commctrl::DefSubclassProc(window, msg, wparam, lparam)
900        }
901
902        winuser::WM_WINDOWPOSCHANGING => {
903            let mut window_state = subclass_input.window_state.lock();
904            if let Some(ref mut fullscreen) = window_state.fullscreen {
905                let window_pos = &mut *(lparam as *mut winuser::WINDOWPOS);
906                let new_rect = RECT {
907                    left: window_pos.x,
908                    top: window_pos.y,
909                    right: window_pos.x + window_pos.cx,
910                    bottom: window_pos.y + window_pos.cy,
911                };
912                let new_monitor =
913                    winuser::MonitorFromRect(&new_rect, winuser::MONITOR_DEFAULTTONULL);
914                match fullscreen {
915                    Fullscreen::Borderless(ref mut fullscreen_monitor) => {
916                        if new_monitor != ptr::null_mut()
917                            && fullscreen_monitor
918                                .as_ref()
919                                .map(|monitor| new_monitor != monitor.inner.hmonitor())
920                                .unwrap_or(true)
921                        {
922                            if let Ok(new_monitor_info) = monitor::get_monitor_info(new_monitor) {
923                                let new_monitor_rect = new_monitor_info.rcMonitor;
924                                window_pos.x = new_monitor_rect.left;
925                                window_pos.y = new_monitor_rect.top;
926                                window_pos.cx = new_monitor_rect.right - new_monitor_rect.left;
927                                window_pos.cy = new_monitor_rect.bottom - new_monitor_rect.top;
928                            }
929                            *fullscreen_monitor = Some(crate::monitor::MonitorHandle {
930                                inner: MonitorHandle::new(new_monitor),
931                            });
932                        }
933                    }
934                    Fullscreen::Exclusive(ref video_mode) => {
935                        let old_monitor = video_mode.video_mode.monitor.hmonitor();
936                        if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) {
937                            let old_monitor_rect = old_monitor_info.rcMonitor;
938                            window_pos.x = old_monitor_rect.left;
939                            window_pos.y = old_monitor_rect.top;
940                            window_pos.cx = old_monitor_rect.right - old_monitor_rect.left;
941                            window_pos.cy = old_monitor_rect.bottom - old_monitor_rect.top;
942                        }
943                    }
944                }
945            }
946
947            0
948        }
949
950        // WM_MOVE supplies client area positions, so we send Moved here instead.
951        winuser::WM_WINDOWPOSCHANGED => {
952            use crate::event::WindowEvent::Moved;
953
954            let windowpos = lparam as *const winuser::WINDOWPOS;
955            if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE {
956                let physical_position =
957                    PhysicalPosition::new((*windowpos).x as i32, (*windowpos).y as i32);
958                subclass_input.send_event(Event::WindowEvent {
959                    window_id: RootWindowId(WindowId(window)),
960                    event: Moved(physical_position),
961                });
962            }
963
964            // This is necessary for us to still get sent WM_SIZE.
965            commctrl::DefSubclassProc(window, msg, wparam, lparam)
966        }
967
968        winuser::WM_SIZE => {
969            use crate::event::WindowEvent::Resized;
970            let w = LOWORD(lparam as DWORD) as u32;
971            let h = HIWORD(lparam as DWORD) as u32;
972
973            let physical_size = PhysicalSize::new(w, h);
974            let event = Event::WindowEvent {
975                window_id: RootWindowId(WindowId(window)),
976                event: Resized(physical_size),
977            };
978
979            {
980                let mut w = subclass_input.window_state.lock();
981                // See WindowFlags::MARKER_RETAIN_STATE_ON_SIZE docs for info on why this `if` check exists.
982                if !w
983                    .window_flags()
984                    .contains(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE)
985                {
986                    let maximized = wparam == winuser::SIZE_MAXIMIZED;
987                    w.set_window_flags_in_place(|f| f.set(WindowFlags::MAXIMIZED, maximized));
988                }
989            }
990
991            subclass_input.send_event(event);
992            0
993        }
994
995        winuser::WM_CHAR | winuser::WM_SYSCHAR => {
996            use crate::event::WindowEvent::ReceivedCharacter;
997            use std::char;
998            let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF;
999            let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF;
1000
1001            if is_high_surrogate {
1002                subclass_input.window_state.lock().high_surrogate = Some(wparam as u16);
1003            } else if is_low_surrogate {
1004                let high_surrogate = subclass_input.window_state.lock().high_surrogate.take();
1005
1006                if let Some(high_surrogate) = high_surrogate {
1007                    let pair = [high_surrogate, wparam as u16];
1008                    if let Some(Ok(chr)) = char::decode_utf16(pair.iter().copied()).next() {
1009                        subclass_input.send_event(Event::WindowEvent {
1010                            window_id: RootWindowId(WindowId(window)),
1011                            event: ReceivedCharacter(chr),
1012                        });
1013                    }
1014                }
1015            } else {
1016                subclass_input.window_state.lock().high_surrogate = None;
1017
1018                if let Some(chr) = char::from_u32(wparam as u32) {
1019                    subclass_input.send_event(Event::WindowEvent {
1020                        window_id: RootWindowId(WindowId(window)),
1021                        event: ReceivedCharacter(chr),
1022                    });
1023                }
1024            }
1025            0
1026        }
1027
1028        // this is necessary for us to maintain minimize/restore state
1029        winuser::WM_SYSCOMMAND => {
1030            if wparam == winuser::SC_RESTORE {
1031                let mut w = subclass_input.window_state.lock();
1032                w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, false));
1033            }
1034            if wparam == winuser::SC_MINIMIZE {
1035                let mut w = subclass_input.window_state.lock();
1036                w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, true));
1037            }
1038            // Send `WindowEvent::Minimized` here if we decide to implement one
1039
1040            if wparam == winuser::SC_SCREENSAVE {
1041                let window_state = subclass_input.window_state.lock();
1042                if window_state.fullscreen.is_some() {
1043                    return 0;
1044                }
1045            }
1046
1047            winuser::DefWindowProcW(window, msg, wparam, lparam)
1048        }
1049
1050        winuser::WM_MOUSEMOVE => {
1051            use crate::event::WindowEvent::{CursorEntered, CursorMoved};
1052            let mouse_was_outside_window = {
1053                let mut w = subclass_input.window_state.lock();
1054
1055                let was_outside_window = !w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW);
1056                w.mouse
1057                    .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, true))
1058                    .ok();
1059                was_outside_window
1060            };
1061
1062            if mouse_was_outside_window {
1063                subclass_input.send_event(Event::WindowEvent {
1064                    window_id: RootWindowId(WindowId(window)),
1065                    event: CursorEntered {
1066                        device_id: DEVICE_ID,
1067                    },
1068                });
1069
1070                // Calling TrackMouseEvent in order to receive mouse leave events.
1071                winuser::TrackMouseEvent(&mut winuser::TRACKMOUSEEVENT {
1072                    cbSize: mem::size_of::<winuser::TRACKMOUSEEVENT>() as DWORD,
1073                    dwFlags: winuser::TME_LEAVE,
1074                    hwndTrack: window,
1075                    dwHoverTime: winuser::HOVER_DEFAULT,
1076                });
1077            }
1078
1079            let x = windowsx::GET_X_LPARAM(lparam) as f64;
1080            let y = windowsx::GET_Y_LPARAM(lparam) as f64;
1081            let position = PhysicalPosition::new(x, y);
1082            let cursor_moved;
1083            {
1084                // handle spurious WM_MOUSEMOVE messages
1085                // see https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42343
1086                // and http://debugandconquer.blogspot.com/2015/08/the-cause-of-spurious-mouse-move.html
1087                let mut w = subclass_input.window_state.lock();
1088                cursor_moved = w.mouse.last_position != Some(position);
1089                w.mouse.last_position = Some(position);
1090            }
1091            if cursor_moved {
1092                update_modifiers(window, subclass_input);
1093
1094                subclass_input.send_event(Event::WindowEvent {
1095                    window_id: RootWindowId(WindowId(window)),
1096                    event: CursorMoved {
1097                        device_id: DEVICE_ID,
1098                        position,
1099                        modifiers: event::get_key_mods(),
1100                    },
1101                });
1102            }
1103
1104            0
1105        }
1106
1107        winuser::WM_MOUSELEAVE => {
1108            use crate::event::WindowEvent::CursorLeft;
1109            {
1110                let mut w = subclass_input.window_state.lock();
1111                w.mouse
1112                    .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false))
1113                    .ok();
1114            }
1115
1116            subclass_input.send_event(Event::WindowEvent {
1117                window_id: RootWindowId(WindowId(window)),
1118                event: CursorLeft {
1119                    device_id: DEVICE_ID,
1120                },
1121            });
1122
1123            0
1124        }
1125
1126        winuser::WM_MOUSEWHEEL => {
1127            use crate::event::MouseScrollDelta::LineDelta;
1128
1129            let value = (wparam >> 16) as i16;
1130            let value = value as i32;
1131            let value = value as f32 / winuser::WHEEL_DELTA as f32;
1132
1133            update_modifiers(window, subclass_input);
1134
1135            subclass_input.send_event(Event::WindowEvent {
1136                window_id: RootWindowId(WindowId(window)),
1137                event: WindowEvent::MouseWheel {
1138                    device_id: DEVICE_ID,
1139                    delta: LineDelta(0.0, value),
1140                    phase: TouchPhase::Moved,
1141                    modifiers: event::get_key_mods(),
1142                },
1143            });
1144
1145            0
1146        }
1147
1148        winuser::WM_MOUSEHWHEEL => {
1149            use crate::event::MouseScrollDelta::LineDelta;
1150
1151            let value = (wparam >> 16) as i16;
1152            let value = value as i32;
1153            let value = value as f32 / winuser::WHEEL_DELTA as f32;
1154
1155            update_modifiers(window, subclass_input);
1156
1157            subclass_input.send_event(Event::WindowEvent {
1158                window_id: RootWindowId(WindowId(window)),
1159                event: WindowEvent::MouseWheel {
1160                    device_id: DEVICE_ID,
1161                    delta: LineDelta(value, 0.0),
1162                    phase: TouchPhase::Moved,
1163                    modifiers: event::get_key_mods(),
1164                },
1165            });
1166
1167            0
1168        }
1169
1170        winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => {
1171            use crate::event::{ElementState::Pressed, VirtualKeyCode};
1172            if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 {
1173                commctrl::DefSubclassProc(window, msg, wparam, lparam)
1174            } else {
1175                if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
1176                    update_modifiers(window, subclass_input);
1177
1178                    #[allow(deprecated)]
1179                    subclass_input.send_event(Event::WindowEvent {
1180                        window_id: RootWindowId(WindowId(window)),
1181                        event: WindowEvent::KeyboardInput {
1182                            device_id: DEVICE_ID,
1183                            input: KeyboardInput {
1184                                state: Pressed,
1185                                scancode,
1186                                virtual_keycode: vkey,
1187                                modifiers: event::get_key_mods(),
1188                            },
1189                            is_synthetic: false,
1190                        },
1191                    });
1192                    // Windows doesn't emit a delete character by default, but in order to make it
1193                    // consistent with the other platforms we'll emit a delete character here.
1194                    if vkey == Some(VirtualKeyCode::Delete) {
1195                        subclass_input.send_event(Event::WindowEvent {
1196                            window_id: RootWindowId(WindowId(window)),
1197                            event: WindowEvent::ReceivedCharacter('\u{7F}'),
1198                        });
1199                    }
1200                }
1201                0
1202            }
1203        }
1204
1205        winuser::WM_KEYUP | winuser::WM_SYSKEYUP => {
1206            use crate::event::ElementState::Released;
1207            if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
1208                update_modifiers(window, subclass_input);
1209
1210                #[allow(deprecated)]
1211                subclass_input.send_event(Event::WindowEvent {
1212                    window_id: RootWindowId(WindowId(window)),
1213                    event: WindowEvent::KeyboardInput {
1214                        device_id: DEVICE_ID,
1215                        input: KeyboardInput {
1216                            state: Released,
1217                            scancode,
1218                            virtual_keycode: vkey,
1219                            modifiers: event::get_key_mods(),
1220                        },
1221                        is_synthetic: false,
1222                    },
1223                });
1224            }
1225            0
1226        }
1227
1228        winuser::WM_LBUTTONDOWN => {
1229            use crate::event::{ElementState::Pressed, MouseButton::Left, WindowEvent::MouseInput};
1230
1231            capture_mouse(window, &mut *subclass_input.window_state.lock());
1232
1233            update_modifiers(window, subclass_input);
1234
1235            subclass_input.send_event(Event::WindowEvent {
1236                window_id: RootWindowId(WindowId(window)),
1237                event: MouseInput {
1238                    device_id: DEVICE_ID,
1239                    state: Pressed,
1240                    button: Left,
1241                    modifiers: event::get_key_mods(),
1242                },
1243            });
1244            0
1245        }
1246
1247        winuser::WM_LBUTTONUP => {
1248            use crate::event::{
1249                ElementState::Released, MouseButton::Left, WindowEvent::MouseInput,
1250            };
1251
1252            release_mouse(subclass_input.window_state.lock());
1253
1254            update_modifiers(window, subclass_input);
1255
1256            subclass_input.send_event(Event::WindowEvent {
1257                window_id: RootWindowId(WindowId(window)),
1258                event: MouseInput {
1259                    device_id: DEVICE_ID,
1260                    state: Released,
1261                    button: Left,
1262                    modifiers: event::get_key_mods(),
1263                },
1264            });
1265            0
1266        }
1267
1268        winuser::WM_RBUTTONDOWN => {
1269            use crate::event::{
1270                ElementState::Pressed, MouseButton::Right, WindowEvent::MouseInput,
1271            };
1272
1273            capture_mouse(window, &mut *subclass_input.window_state.lock());
1274
1275            update_modifiers(window, subclass_input);
1276
1277            subclass_input.send_event(Event::WindowEvent {
1278                window_id: RootWindowId(WindowId(window)),
1279                event: MouseInput {
1280                    device_id: DEVICE_ID,
1281                    state: Pressed,
1282                    button: Right,
1283                    modifiers: event::get_key_mods(),
1284                },
1285            });
1286            0
1287        }
1288
1289        winuser::WM_RBUTTONUP => {
1290            use crate::event::{
1291                ElementState::Released, MouseButton::Right, WindowEvent::MouseInput,
1292            };
1293
1294            release_mouse(subclass_input.window_state.lock());
1295
1296            update_modifiers(window, subclass_input);
1297
1298            subclass_input.send_event(Event::WindowEvent {
1299                window_id: RootWindowId(WindowId(window)),
1300                event: MouseInput {
1301                    device_id: DEVICE_ID,
1302                    state: Released,
1303                    button: Right,
1304                    modifiers: event::get_key_mods(),
1305                },
1306            });
1307            0
1308        }
1309
1310        winuser::WM_MBUTTONDOWN => {
1311            use crate::event::{
1312                ElementState::Pressed, MouseButton::Middle, WindowEvent::MouseInput,
1313            };
1314
1315            capture_mouse(window, &mut *subclass_input.window_state.lock());
1316
1317            update_modifiers(window, subclass_input);
1318
1319            subclass_input.send_event(Event::WindowEvent {
1320                window_id: RootWindowId(WindowId(window)),
1321                event: MouseInput {
1322                    device_id: DEVICE_ID,
1323                    state: Pressed,
1324                    button: Middle,
1325                    modifiers: event::get_key_mods(),
1326                },
1327            });
1328            0
1329        }
1330
1331        winuser::WM_MBUTTONUP => {
1332            use crate::event::{
1333                ElementState::Released, MouseButton::Middle, WindowEvent::MouseInput,
1334            };
1335
1336            release_mouse(subclass_input.window_state.lock());
1337
1338            update_modifiers(window, subclass_input);
1339
1340            subclass_input.send_event(Event::WindowEvent {
1341                window_id: RootWindowId(WindowId(window)),
1342                event: MouseInput {
1343                    device_id: DEVICE_ID,
1344                    state: Released,
1345                    button: Middle,
1346                    modifiers: event::get_key_mods(),
1347                },
1348            });
1349            0
1350        }
1351
1352        winuser::WM_XBUTTONDOWN => {
1353            use crate::event::{
1354                ElementState::Pressed, MouseButton::Other, WindowEvent::MouseInput,
1355            };
1356            let xbutton = winuser::GET_XBUTTON_WPARAM(wparam);
1357
1358            capture_mouse(window, &mut *subclass_input.window_state.lock());
1359
1360            update_modifiers(window, subclass_input);
1361
1362            subclass_input.send_event(Event::WindowEvent {
1363                window_id: RootWindowId(WindowId(window)),
1364                event: MouseInput {
1365                    device_id: DEVICE_ID,
1366                    state: Pressed,
1367                    button: Other(xbutton),
1368                    modifiers: event::get_key_mods(),
1369                },
1370            });
1371            0
1372        }
1373
1374        winuser::WM_XBUTTONUP => {
1375            use crate::event::{
1376                ElementState::Released, MouseButton::Other, WindowEvent::MouseInput,
1377            };
1378            let xbutton = winuser::GET_XBUTTON_WPARAM(wparam);
1379
1380            release_mouse(subclass_input.window_state.lock());
1381
1382            update_modifiers(window, subclass_input);
1383
1384            subclass_input.send_event(Event::WindowEvent {
1385                window_id: RootWindowId(WindowId(window)),
1386                event: MouseInput {
1387                    device_id: DEVICE_ID,
1388                    state: Released,
1389                    button: Other(xbutton),
1390                    modifiers: event::get_key_mods(),
1391                },
1392            });
1393            0
1394        }
1395
1396        winuser::WM_CAPTURECHANGED => {
1397            // lparam here is a handle to the window which is gaining mouse capture.
1398            // If it is the same as our window, then we're essentially retaining the capture. This
1399            // can happen if `SetCapture` is called on our window when it already has the mouse
1400            // capture.
1401            if lparam != window as isize {
1402                subclass_input.window_state.lock().mouse.capture_count = 0;
1403            }
1404            0
1405        }
1406
1407        winuser::WM_TOUCH => {
1408            let pcount = LOWORD(wparam as DWORD) as usize;
1409            let mut inputs = Vec::with_capacity(pcount);
1410            inputs.set_len(pcount);
1411            let htouch = lparam as winuser::HTOUCHINPUT;
1412            if winuser::GetTouchInputInfo(
1413                htouch,
1414                pcount as UINT,
1415                inputs.as_mut_ptr(),
1416                mem::size_of::<winuser::TOUCHINPUT>() as INT,
1417            ) > 0
1418            {
1419                for input in &inputs {
1420                    let mut location = POINT {
1421                        x: input.x / 100,
1422                        y: input.y / 100,
1423                    };
1424
1425                    if winuser::ScreenToClient(window, &mut location as *mut _) == 0 {
1426                        continue;
1427                    }
1428
1429                    let x = location.x as f64 + (input.x % 100) as f64 / 100f64;
1430                    let y = location.y as f64 + (input.y % 100) as f64 / 100f64;
1431                    let location = PhysicalPosition::new(x, y);
1432                    subclass_input.send_event(Event::WindowEvent {
1433                        window_id: RootWindowId(WindowId(window)),
1434                        event: WindowEvent::Touch(Touch {
1435                            phase: if input.dwFlags & winuser::TOUCHEVENTF_DOWN != 0 {
1436                                TouchPhase::Started
1437                            } else if input.dwFlags & winuser::TOUCHEVENTF_UP != 0 {
1438                                TouchPhase::Ended
1439                            } else if input.dwFlags & winuser::TOUCHEVENTF_MOVE != 0 {
1440                                TouchPhase::Moved
1441                            } else {
1442                                continue;
1443                            },
1444                            location,
1445                            force: None, // WM_TOUCH doesn't support pressure information
1446                            id: input.dwID as u64,
1447                            device_id: DEVICE_ID,
1448                        }),
1449                    });
1450                }
1451            }
1452            winuser::CloseTouchInputHandle(htouch);
1453            0
1454        }
1455
1456        winuser::WM_POINTERDOWN | winuser::WM_POINTERUPDATE | winuser::WM_POINTERUP => {
1457            if let (
1458                Some(GetPointerFrameInfoHistory),
1459                Some(SkipPointerFrameMessages),
1460                Some(GetPointerDeviceRects),
1461            ) = (
1462                *GET_POINTER_FRAME_INFO_HISTORY,
1463                *SKIP_POINTER_FRAME_MESSAGES,
1464                *GET_POINTER_DEVICE_RECTS,
1465            ) {
1466                let pointer_id = LOWORD(wparam as DWORD) as UINT;
1467                let mut entries_count = 0 as UINT;
1468                let mut pointers_count = 0 as UINT;
1469                if GetPointerFrameInfoHistory(
1470                    pointer_id,
1471                    &mut entries_count as *mut _,
1472                    &mut pointers_count as *mut _,
1473                    std::ptr::null_mut(),
1474                ) == 0
1475                {
1476                    return 0;
1477                }
1478
1479                let pointer_info_count = (entries_count * pointers_count) as usize;
1480                let mut pointer_infos = Vec::with_capacity(pointer_info_count);
1481                pointer_infos.set_len(pointer_info_count);
1482                if GetPointerFrameInfoHistory(
1483                    pointer_id,
1484                    &mut entries_count as *mut _,
1485                    &mut pointers_count as *mut _,
1486                    pointer_infos.as_mut_ptr(),
1487                ) == 0
1488                {
1489                    return 0;
1490                }
1491
1492                // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getpointerframeinfohistory
1493                // The information retrieved appears in reverse chronological order, with the most recent entry in the first
1494                // row of the returned array
1495                for pointer_info in pointer_infos.iter().rev() {
1496                    let mut device_rect = mem::MaybeUninit::uninit();
1497                    let mut display_rect = mem::MaybeUninit::uninit();
1498
1499                    if (GetPointerDeviceRects(
1500                        pointer_info.sourceDevice,
1501                        device_rect.as_mut_ptr(),
1502                        display_rect.as_mut_ptr(),
1503                    )) == 0
1504                    {
1505                        continue;
1506                    }
1507
1508                    let device_rect = device_rect.assume_init();
1509                    let display_rect = display_rect.assume_init();
1510
1511                    // For the most precise himetric to pixel conversion we calculate the ratio between the resolution
1512                    // of the display device (pixel) and the touch device (himetric).
1513                    let himetric_to_pixel_ratio_x = (display_rect.right - display_rect.left) as f64
1514                        / (device_rect.right - device_rect.left) as f64;
1515                    let himetric_to_pixel_ratio_y = (display_rect.bottom - display_rect.top) as f64
1516                        / (device_rect.bottom - device_rect.top) as f64;
1517
1518                    // ptHimetricLocation's origin is 0,0 even on multi-monitor setups.
1519                    // On multi-monitor setups we need to translate the himetric location to the rect of the
1520                    // display device it's attached to.
1521                    let x = display_rect.left as f64
1522                        + pointer_info.ptHimetricLocation.x as f64 * himetric_to_pixel_ratio_x;
1523                    let y = display_rect.top as f64
1524                        + pointer_info.ptHimetricLocation.y as f64 * himetric_to_pixel_ratio_y;
1525
1526                    let mut location = POINT {
1527                        x: x.floor() as i32,
1528                        y: y.floor() as i32,
1529                    };
1530
1531                    if winuser::ScreenToClient(window, &mut location as *mut _) == 0 {
1532                        continue;
1533                    }
1534
1535                    let force = match pointer_info.pointerType {
1536                        winuser::PT_TOUCH => {
1537                            let mut touch_info = mem::MaybeUninit::uninit();
1538                            GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
1539                                match GetPointerTouchInfo(
1540                                    pointer_info.pointerId,
1541                                    touch_info.as_mut_ptr(),
1542                                ) {
1543                                    0 => None,
1544                                    _ => normalize_pointer_pressure(
1545                                        touch_info.assume_init().pressure,
1546                                    ),
1547                                }
1548                            })
1549                        }
1550                        winuser::PT_PEN => {
1551                            let mut pen_info = mem::MaybeUninit::uninit();
1552                            GET_POINTER_PEN_INFO.and_then(|GetPointerPenInfo| {
1553                                match GetPointerPenInfo(
1554                                    pointer_info.pointerId,
1555                                    pen_info.as_mut_ptr(),
1556                                ) {
1557                                    0 => None,
1558                                    _ => {
1559                                        normalize_pointer_pressure(pen_info.assume_init().pressure)
1560                                    }
1561                                }
1562                            })
1563                        }
1564                        _ => None,
1565                    };
1566
1567                    let x = location.x as f64 + x.fract();
1568                    let y = location.y as f64 + y.fract();
1569                    let location = PhysicalPosition::new(x, y);
1570                    subclass_input.send_event(Event::WindowEvent {
1571                        window_id: RootWindowId(WindowId(window)),
1572                        event: WindowEvent::Touch(Touch {
1573                            phase: if pointer_info.pointerFlags & winuser::POINTER_FLAG_DOWN != 0 {
1574                                TouchPhase::Started
1575                            } else if pointer_info.pointerFlags & winuser::POINTER_FLAG_UP != 0 {
1576                                TouchPhase::Ended
1577                            } else if pointer_info.pointerFlags & winuser::POINTER_FLAG_UPDATE != 0
1578                            {
1579                                TouchPhase::Moved
1580                            } else {
1581                                continue;
1582                            },
1583                            location,
1584                            force,
1585                            id: pointer_info.pointerId as u64,
1586                            device_id: DEVICE_ID,
1587                        }),
1588                    });
1589                }
1590
1591                SkipPointerFrameMessages(pointer_id);
1592            }
1593            0
1594        }
1595
1596        winuser::WM_SETFOCUS => {
1597            use crate::event::{ElementState::Released, WindowEvent::Focused};
1598            for windows_keycode in event::get_pressed_keys() {
1599                let scancode =
1600                    winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC);
1601                let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode);
1602
1603                update_modifiers(window, subclass_input);
1604
1605                #[allow(deprecated)]
1606                subclass_input.send_event(Event::WindowEvent {
1607                    window_id: RootWindowId(WindowId(window)),
1608                    event: WindowEvent::KeyboardInput {
1609                        device_id: DEVICE_ID,
1610                        input: KeyboardInput {
1611                            scancode,
1612                            virtual_keycode,
1613                            state: Released,
1614                            modifiers: event::get_key_mods(),
1615                        },
1616                        is_synthetic: true,
1617                    },
1618                })
1619            }
1620
1621            subclass_input.send_event(Event::WindowEvent {
1622                window_id: RootWindowId(WindowId(window)),
1623                event: Focused(true),
1624            });
1625
1626            0
1627        }
1628
1629        winuser::WM_KILLFOCUS => {
1630            use crate::event::{
1631                ElementState::Released,
1632                ModifiersState,
1633                WindowEvent::{Focused, ModifiersChanged},
1634            };
1635            for windows_keycode in event::get_pressed_keys() {
1636                let scancode =
1637                    winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC);
1638                let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode);
1639
1640                #[allow(deprecated)]
1641                subclass_input.send_event(Event::WindowEvent {
1642                    window_id: RootWindowId(WindowId(window)),
1643                    event: WindowEvent::KeyboardInput {
1644                        device_id: DEVICE_ID,
1645                        input: KeyboardInput {
1646                            scancode,
1647                            virtual_keycode,
1648                            state: Released,
1649                            modifiers: event::get_key_mods(),
1650                        },
1651                        is_synthetic: true,
1652                    },
1653                })
1654            }
1655
1656            subclass_input.window_state.lock().modifiers_state = ModifiersState::empty();
1657            subclass_input.send_event(Event::WindowEvent {
1658                window_id: RootWindowId(WindowId(window)),
1659                event: ModifiersChanged(ModifiersState::empty()),
1660            });
1661
1662            subclass_input.send_event(Event::WindowEvent {
1663                window_id: RootWindowId(WindowId(window)),
1664                event: Focused(false),
1665            });
1666            0
1667        }
1668
1669        winuser::WM_SETCURSOR => {
1670            let set_cursor_to = {
1671                let window_state = subclass_input.window_state.lock();
1672                // The return value for the preceding `WM_NCHITTEST` message is conveniently
1673                // provided through the low-order word of lParam. We use that here since
1674                // `WM_MOUSEMOVE` seems to come after `WM_SETCURSOR` for a given cursor movement.
1675                let in_client_area = LOWORD(lparam as DWORD) == winuser::HTCLIENT as WORD;
1676                if in_client_area {
1677                    Some(window_state.mouse.cursor)
1678                } else {
1679                    None
1680                }
1681            };
1682
1683            match set_cursor_to {
1684                Some(cursor) => {
1685                    let cursor = winuser::LoadCursorW(ptr::null_mut(), cursor.to_windows_cursor());
1686                    winuser::SetCursor(cursor);
1687                    0
1688                }
1689                None => winuser::DefWindowProcW(window, msg, wparam, lparam),
1690            }
1691        }
1692
1693        winuser::WM_DROPFILES => {
1694            // See `FileDropHandler` for implementation.
1695            0
1696        }
1697
1698        winuser::WM_GETMINMAXINFO => {
1699            let mmi = lparam as *mut winuser::MINMAXINFO;
1700
1701            let window_state = subclass_input.window_state.lock();
1702
1703            if window_state.min_size.is_some() || window_state.max_size.is_some() {
1704                if let Some(min_size) = window_state.min_size {
1705                    let min_size = min_size.to_physical(window_state.scale_factor);
1706                    let (width, height): (u32, u32) = util::adjust_size(window, min_size).into();
1707                    (*mmi).ptMinTrackSize = POINT {
1708                        x: width as i32,
1709                        y: height as i32,
1710                    };
1711                }
1712                if let Some(max_size) = window_state.max_size {
1713                    let max_size = max_size.to_physical(window_state.scale_factor);
1714                    let (width, height): (u32, u32) = util::adjust_size(window, max_size).into();
1715                    (*mmi).ptMaxTrackSize = POINT {
1716                        x: width as i32,
1717                        y: height as i32,
1718                    };
1719                }
1720            }
1721
1722            0
1723        }
1724
1725        // Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change
1726        // DPI, therefore all applications are closed while DPI is changing.
1727        winuser::WM_DPICHANGED => {
1728            use crate::event::WindowEvent::ScaleFactorChanged;
1729
1730            // This message actually provides two DPI values - x and y. However MSDN says that
1731            // "you only need to use either the X-axis or the Y-axis value when scaling your
1732            // application since they are the same".
1733            // https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx
1734            let new_dpi_x = u32::from(LOWORD(wparam as DWORD));
1735            let new_scale_factor = dpi_to_scale_factor(new_dpi_x);
1736            let old_scale_factor: f64;
1737
1738            let allow_resize = {
1739                let mut window_state = subclass_input.window_state.lock();
1740                old_scale_factor = window_state.scale_factor;
1741                window_state.scale_factor = new_scale_factor;
1742
1743                if new_scale_factor == old_scale_factor {
1744                    return 0;
1745                }
1746
1747                window_state.fullscreen.is_none()
1748                    && !window_state.window_flags().contains(WindowFlags::MAXIMIZED)
1749            };
1750
1751            let style = winuser::GetWindowLongW(window, winuser::GWL_STYLE) as _;
1752            let style_ex = winuser::GetWindowLongW(window, winuser::GWL_EXSTYLE) as _;
1753
1754            // New size as suggested by Windows.
1755            let suggested_rect = *(lparam as *const RECT);
1756
1757            // The window rect provided is the window's outer size, not it's inner size. However,
1758            // win32 doesn't provide an `UnadjustWindowRectEx` function to get the client rect from
1759            // the outer rect, so we instead adjust the window rect to get the decoration margins
1760            // and remove them from the outer size.
1761            let margin_left: i32;
1762            let margin_top: i32;
1763            // let margin_right: i32;
1764            // let margin_bottom: i32;
1765            {
1766                let adjusted_rect =
1767                    util::adjust_window_rect_with_styles(window, style, style_ex, suggested_rect)
1768                        .unwrap_or(suggested_rect);
1769                margin_left = suggested_rect.left - adjusted_rect.left;
1770                margin_top = suggested_rect.top - adjusted_rect.top;
1771                // margin_right = adjusted_rect.right - suggested_rect.right;
1772                // margin_bottom = adjusted_rect.bottom - suggested_rect.bottom;
1773            }
1774
1775            let old_physical_inner_rect = {
1776                let mut old_physical_inner_rect = mem::zeroed();
1777                winuser::GetClientRect(window, &mut old_physical_inner_rect);
1778                let mut origin = mem::zeroed();
1779                winuser::ClientToScreen(window, &mut origin);
1780
1781                old_physical_inner_rect.left += origin.x;
1782                old_physical_inner_rect.right += origin.x;
1783                old_physical_inner_rect.top += origin.y;
1784                old_physical_inner_rect.bottom += origin.y;
1785
1786                old_physical_inner_rect
1787            };
1788            let old_physical_inner_size = PhysicalSize::new(
1789                (old_physical_inner_rect.right - old_physical_inner_rect.left) as u32,
1790                (old_physical_inner_rect.bottom - old_physical_inner_rect.top) as u32,
1791            );
1792
1793            // `allow_resize` prevents us from re-applying DPI adjustment to the restored size after
1794            // exiting fullscreen (the restored size is already DPI adjusted).
1795            let mut new_physical_inner_size = match allow_resize {
1796                // We calculate our own size because the default suggested rect doesn't do a great job
1797                // of preserving the window's logical size.
1798                true => old_physical_inner_size
1799                    .to_logical::<f64>(old_scale_factor)
1800                    .to_physical::<u32>(new_scale_factor),
1801                false => old_physical_inner_size,
1802            };
1803
1804            let _ = subclass_input.send_event(Event::WindowEvent {
1805                window_id: RootWindowId(WindowId(window)),
1806                event: ScaleFactorChanged {
1807                    scale_factor: new_scale_factor,
1808                    new_inner_size: &mut new_physical_inner_size,
1809                },
1810            });
1811
1812            let dragging_window: bool;
1813
1814            {
1815                let window_state = subclass_input.window_state.lock();
1816                dragging_window = window_state
1817                    .window_flags()
1818                    .contains(WindowFlags::MARKER_IN_SIZE_MOVE);
1819                // Unset maximized if we're changing the window's size.
1820                if new_physical_inner_size != old_physical_inner_size {
1821                    WindowState::set_window_flags(window_state, window, |f| {
1822                        f.set(WindowFlags::MAXIMIZED, false)
1823                    });
1824                }
1825            }
1826
1827            let new_outer_rect: RECT;
1828            {
1829                let suggested_ul = (
1830                    suggested_rect.left + margin_left,
1831                    suggested_rect.top + margin_top,
1832                );
1833
1834                let mut conservative_rect = RECT {
1835                    left: suggested_ul.0,
1836                    top: suggested_ul.1,
1837                    right: suggested_ul.0 + new_physical_inner_size.width as LONG,
1838                    bottom: suggested_ul.1 + new_physical_inner_size.height as LONG,
1839                };
1840
1841                conservative_rect = util::adjust_window_rect_with_styles(
1842                    window,
1843                    style,
1844                    style_ex,
1845                    conservative_rect,
1846                )
1847                .unwrap_or(conservative_rect);
1848
1849                // If we're dragging the window, offset the window so that the cursor's
1850                // relative horizontal position in the title bar is preserved.
1851                if dragging_window {
1852                    let bias = {
1853                        let cursor_pos = {
1854                            let mut pos = mem::zeroed();
1855                            winuser::GetCursorPos(&mut pos);
1856                            pos
1857                        };
1858                        let suggested_cursor_horizontal_ratio = (cursor_pos.x - suggested_rect.left)
1859                            as f64
1860                            / (suggested_rect.right - suggested_rect.left) as f64;
1861
1862                        (cursor_pos.x
1863                            - (suggested_cursor_horizontal_ratio
1864                                * (conservative_rect.right - conservative_rect.left) as f64)
1865                                as LONG)
1866                            - conservative_rect.left
1867                    };
1868                    conservative_rect.left += bias;
1869                    conservative_rect.right += bias;
1870                }
1871
1872                // Check to see if the new window rect is on the monitor with the new DPI factor.
1873                // If it isn't, offset the window so that it is.
1874                let new_dpi_monitor = winuser::MonitorFromWindow(window, 0);
1875                let conservative_rect_monitor = winuser::MonitorFromRect(&conservative_rect, 0);
1876                new_outer_rect = if conservative_rect_monitor == new_dpi_monitor {
1877                    conservative_rect
1878                } else {
1879                    let get_monitor_rect = |monitor| {
1880                        let mut monitor_info = winuser::MONITORINFO {
1881                            cbSize: mem::size_of::<winuser::MONITORINFO>() as _,
1882                            ..mem::zeroed()
1883                        };
1884                        winuser::GetMonitorInfoW(monitor, &mut monitor_info);
1885                        monitor_info.rcMonitor
1886                    };
1887                    let wrong_monitor = conservative_rect_monitor;
1888                    let wrong_monitor_rect = get_monitor_rect(wrong_monitor);
1889                    let new_monitor_rect = get_monitor_rect(new_dpi_monitor);
1890
1891                    // The direction to nudge the window in to get the window onto the monitor with
1892                    // the new DPI factor. We calculate this by seeing which monitor edges are
1893                    // shared and nudging away from the wrong monitor based on those.
1894                    let delta_nudge_to_dpi_monitor = (
1895                        if wrong_monitor_rect.left == new_monitor_rect.right {
1896                            -1
1897                        } else if wrong_monitor_rect.right == new_monitor_rect.left {
1898                            1
1899                        } else {
1900                            0
1901                        },
1902                        if wrong_monitor_rect.bottom == new_monitor_rect.top {
1903                            1
1904                        } else if wrong_monitor_rect.top == new_monitor_rect.bottom {
1905                            -1
1906                        } else {
1907                            0
1908                        },
1909                    );
1910
1911                    let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left
1912                        + new_monitor_rect.bottom
1913                        - new_monitor_rect.top;
1914                    for _ in 0..abort_after_iterations {
1915                        conservative_rect.left += delta_nudge_to_dpi_monitor.0;
1916                        conservative_rect.right += delta_nudge_to_dpi_monitor.0;
1917                        conservative_rect.top += delta_nudge_to_dpi_monitor.1;
1918                        conservative_rect.bottom += delta_nudge_to_dpi_monitor.1;
1919
1920                        if winuser::MonitorFromRect(&conservative_rect, 0) == new_dpi_monitor {
1921                            break;
1922                        }
1923                    }
1924
1925                    conservative_rect
1926                };
1927            }
1928
1929            winuser::SetWindowPos(
1930                window,
1931                ptr::null_mut(),
1932                new_outer_rect.left,
1933                new_outer_rect.top,
1934                new_outer_rect.right - new_outer_rect.left,
1935                new_outer_rect.bottom - new_outer_rect.top,
1936                winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE,
1937            );
1938
1939            0
1940        }
1941
1942        winuser::WM_SETTINGCHANGE => {
1943            use crate::event::WindowEvent::ThemeChanged;
1944
1945            let preferred_theme = subclass_input.window_state.lock().preferred_theme;
1946
1947            if preferred_theme == None {
1948                let new_theme = try_theme(window, preferred_theme);
1949                let mut window_state = subclass_input.window_state.lock();
1950
1951                if window_state.current_theme != new_theme {
1952                    window_state.current_theme = new_theme;
1953                    mem::drop(window_state);
1954                    subclass_input.send_event(Event::WindowEvent {
1955                        window_id: RootWindowId(WindowId(window)),
1956                        event: ThemeChanged(new_theme),
1957                    });
1958                }
1959            }
1960
1961            commctrl::DefSubclassProc(window, msg, wparam, lparam)
1962        }
1963
1964        _ => {
1965            if msg == *DESTROY_MSG_ID {
1966                winuser::DestroyWindow(window);
1967                0
1968            } else if msg == *SET_RETAIN_STATE_ON_SIZE_MSG_ID {
1969                let mut window_state = subclass_input.window_state.lock();
1970                window_state.set_window_flags_in_place(|f| {
1971                    f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0)
1972                });
1973                0
1974            } else {
1975                commctrl::DefSubclassProc(window, msg, wparam, lparam)
1976            }
1977        }
1978    };
1979
1980    subclass_input
1981        .event_loop_runner
1982        .catch_unwind(callback)
1983        .unwrap_or(-1)
1984}
1985
1986unsafe extern "system" fn thread_event_target_callback<T: 'static>(
1987    window: HWND,
1988    msg: UINT,
1989    wparam: WPARAM,
1990    lparam: LPARAM,
1991    _: UINT_PTR,
1992    subclass_input_ptr: DWORD_PTR,
1993) -> LRESULT {
1994    let subclass_input = Box::from_raw(subclass_input_ptr as *mut ThreadMsgTargetSubclassInput<T>);
1995
1996    if msg != winuser::WM_PAINT {
1997        winuser::RedrawWindow(
1998            window,
1999            ptr::null(),
2000            ptr::null_mut(),
2001            winuser::RDW_INTERNALPAINT,
2002        );
2003    }
2004
2005    let mut subclass_removed = false;
2006
2007    // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing
2008    // the closure to catch_unwind directly so that the match body indendation wouldn't change and
2009    // the git blame and history would be preserved.
2010    let callback = || match msg {
2011        winuser::WM_NCDESTROY => {
2012            remove_event_target_window_subclass::<T>(window);
2013            subclass_removed = true;
2014            0
2015        }
2016        // Because WM_PAINT comes after all other messages, we use it during modal loops to detect
2017        // when the event queue has been emptied. See `process_event` for more details.
2018        winuser::WM_PAINT => {
2019            winuser::ValidateRect(window, ptr::null());
2020            // If the WM_PAINT handler in `public_window_callback` has already flushed the redraw
2021            // events, `handling_events` will return false and we won't emit a second
2022            // `RedrawEventsCleared` event.
2023            if subclass_input.event_loop_runner.handling_events() {
2024                if subclass_input.event_loop_runner.should_buffer() {
2025                    // This branch can be triggered when a nested win32 event loop is triggered
2026                    // inside of the `event_handler` callback.
2027                    winuser::RedrawWindow(
2028                        window,
2029                        ptr::null(),
2030                        ptr::null_mut(),
2031                        winuser::RDW_INTERNALPAINT,
2032                    );
2033                } else {
2034                    // This WM_PAINT handler will never be re-entrant because `flush_paint_messages`
2035                    // doesn't call WM_PAINT for the thread event target (i.e. this window).
2036                    assert!(flush_paint_messages(
2037                        None,
2038                        &subclass_input.event_loop_runner
2039                    ));
2040                    subclass_input.event_loop_runner.redraw_events_cleared();
2041                    process_control_flow(&subclass_input.event_loop_runner);
2042                }
2043            }
2044
2045            // Default WM_PAINT behaviour. This makes sure modals and popups are shown immediatly when opening them.
2046            commctrl::DefSubclassProc(window, msg, wparam, lparam)
2047        }
2048
2049        winuser::WM_INPUT_DEVICE_CHANGE => {
2050            let event = match wparam as _ {
2051                winuser::GIDC_ARRIVAL => DeviceEvent::Added,
2052                winuser::GIDC_REMOVAL => DeviceEvent::Removed,
2053                _ => unreachable!(),
2054            };
2055
2056            subclass_input.send_event(Event::DeviceEvent {
2057                device_id: wrap_device_id(lparam as _),
2058                event,
2059            });
2060
2061            0
2062        }
2063
2064        winuser::WM_INPUT => {
2065            use crate::event::{
2066                DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel},
2067                ElementState::{Pressed, Released},
2068                MouseScrollDelta::LineDelta,
2069            };
2070
2071            if let Some(data) = raw_input::get_raw_input_data(lparam as _) {
2072                let device_id = wrap_device_id(data.header.hDevice as _);
2073
2074                if data.header.dwType == winuser::RIM_TYPEMOUSE {
2075                    let mouse = data.data.mouse();
2076
2077                    if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) {
2078                        let x = mouse.lLastX as f64;
2079                        let y = mouse.lLastY as f64;
2080
2081                        if x != 0.0 {
2082                            subclass_input.send_event(Event::DeviceEvent {
2083                                device_id,
2084                                event: Motion { axis: 0, value: x },
2085                            });
2086                        }
2087
2088                        if y != 0.0 {
2089                            subclass_input.send_event(Event::DeviceEvent {
2090                                device_id,
2091                                event: Motion { axis: 1, value: y },
2092                            });
2093                        }
2094
2095                        if x != 0.0 || y != 0.0 {
2096                            subclass_input.send_event(Event::DeviceEvent {
2097                                device_id,
2098                                event: MouseMotion { delta: (x, y) },
2099                            });
2100                        }
2101                    }
2102
2103                    if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) {
2104                        let delta =
2105                            mouse.usButtonData as SHORT as f32 / winuser::WHEEL_DELTA as f32;
2106                        subclass_input.send_event(Event::DeviceEvent {
2107                            device_id,
2108                            event: MouseWheel {
2109                                delta: LineDelta(0.0, delta),
2110                            },
2111                        });
2112                    }
2113
2114                    let button_state = raw_input::get_raw_mouse_button_state(mouse.usButtonFlags);
2115                    // Left, middle, and right, respectively.
2116                    for (index, state) in button_state.iter().enumerate() {
2117                        if let Some(state) = *state {
2118                            // This gives us consistency with X11, since there doesn't
2119                            // seem to be anything else reasonable to do for a mouse
2120                            // button ID.
2121                            let button = (index + 1) as _;
2122                            subclass_input.send_event(Event::DeviceEvent {
2123                                device_id,
2124                                event: Button { button, state },
2125                            });
2126                        }
2127                    }
2128                } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD {
2129                    let keyboard = data.data.keyboard();
2130
2131                    let pressed = keyboard.Message == winuser::WM_KEYDOWN
2132                        || keyboard.Message == winuser::WM_SYSKEYDOWN;
2133                    let released = keyboard.Message == winuser::WM_KEYUP
2134                        || keyboard.Message == winuser::WM_SYSKEYUP;
2135
2136                    if pressed || released {
2137                        let state = if pressed { Pressed } else { Released };
2138
2139                        let scancode = keyboard.MakeCode as _;
2140                        let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _)
2141                            | util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _);
2142
2143                        if let Some((vkey, scancode)) =
2144                            handle_extended_keys(keyboard.VKey as _, scancode, extended)
2145                        {
2146                            let virtual_keycode = vkey_to_winit_vkey(vkey);
2147
2148                            #[allow(deprecated)]
2149                            subclass_input.send_event(Event::DeviceEvent {
2150                                device_id,
2151                                event: Key(KeyboardInput {
2152                                    scancode,
2153                                    state,
2154                                    virtual_keycode,
2155                                    modifiers: event::get_key_mods(),
2156                                }),
2157                            });
2158                        }
2159                    }
2160                }
2161            }
2162
2163            commctrl::DefSubclassProc(window, msg, wparam, lparam)
2164        }
2165
2166        _ if msg == *USER_EVENT_MSG_ID => {
2167            if let Ok(event) = subclass_input.user_event_receiver.recv() {
2168                subclass_input.send_event(Event::UserEvent(event));
2169            }
2170            0
2171        }
2172        _ if msg == *EXEC_MSG_ID => {
2173            let mut function: ThreadExecFn = Box::from_raw(wparam as usize as *mut _);
2174            function();
2175            0
2176        }
2177        _ if msg == *PROCESS_NEW_EVENTS_MSG_ID => {
2178            winuser::PostThreadMessageW(
2179                subclass_input.event_loop_runner.wait_thread_id(),
2180                *CANCEL_WAIT_UNTIL_MSG_ID,
2181                0,
2182                0,
2183            );
2184
2185            // if the control_flow is WaitUntil, make sure the given moment has actually passed
2186            // before emitting NewEvents
2187            if let ControlFlow::WaitUntil(wait_until) =
2188                subclass_input.event_loop_runner.control_flow()
2189            {
2190                let mut msg = mem::zeroed();
2191                while Instant::now() < wait_until {
2192                    if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) {
2193                        // This works around a "feature" in PeekMessageW. If the message PeekMessageW
2194                        // gets is a WM_PAINT message that had RDW_INTERNALPAINT set (i.e. doesn't
2195                        // have an update region), PeekMessageW will remove that window from the
2196                        // redraw queue even though we told it not to remove messages from the
2197                        // queue. We fix it by re-dispatching an internal paint message to that
2198                        // window.
2199                        if msg.message == winuser::WM_PAINT {
2200                            let mut rect = mem::zeroed();
2201                            if 0 == winuser::GetUpdateRect(msg.hwnd, &mut rect, 0) {
2202                                winuser::RedrawWindow(
2203                                    msg.hwnd,
2204                                    ptr::null(),
2205                                    ptr::null_mut(),
2206                                    winuser::RDW_INTERNALPAINT,
2207                                );
2208                            }
2209                        }
2210
2211                        break;
2212                    }
2213                }
2214            }
2215            subclass_input.event_loop_runner.poll();
2216            0
2217        }
2218        _ => commctrl::DefSubclassProc(window, msg, wparam, lparam),
2219    };
2220
2221    let result = subclass_input
2222        .event_loop_runner
2223        .catch_unwind(callback)
2224        .unwrap_or(-1);
2225    if subclass_removed {
2226        mem::drop(subclass_input);
2227    } else {
2228        Box::into_raw(subclass_input);
2229    }
2230    result
2231}