Minor stylistic cleanup in Windows platform (#35503)

Max Brunsfeld created

This PR doesn't change any logic, it just cleans up some naming and
style in the windows platform layer.

* Rename `WindowsWindowStatePtr` to `WindowsWindowInner`, since it isn't
a pointer type.
* Move window event handler methods into an impl on this type, so that
all of the `state_ptr: &Rc<WindowsWindowInner>` parameters can just be
replaced with `&self`.
* In window creation, use a `match` instead of a conditional followed by
an unwrap

There's a lot of whitespace in the diff, so view it with `w=1`.

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/events.rs   | 1634 ++++++++++-----------
crates/gpui/src/platform/windows/platform.rs |    6 
crates/gpui/src/platform/windows/window.rs   |  127 
3 files changed, 820 insertions(+), 947 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/events.rs 🔗

@@ -28,997 +28,863 @@ pub(crate) const WM_GPUI_FORCE_UPDATE_WINDOW: u32 = WM_USER + 5;
 const SIZE_MOVE_LOOP_TIMER_ID: usize = 1;
 const AUTO_HIDE_TASKBAR_THICKNESS_PX: i32 = 1;
 
-pub(crate) fn handle_msg(
-    handle: HWND,
-    msg: u32,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> LRESULT {
-    let handled = match msg {
-        WM_ACTIVATE => handle_activate_msg(wparam, state_ptr),
-        WM_CREATE => handle_create_msg(handle, state_ptr),
-        WM_DEVICECHANGE => handle_device_change_msg(handle, wparam, state_ptr),
-        WM_MOVE => handle_move_msg(handle, lparam, state_ptr),
-        WM_SIZE => handle_size_msg(wparam, lparam, state_ptr),
-        WM_GETMINMAXINFO => handle_get_min_max_info_msg(lparam, state_ptr),
-        WM_ENTERSIZEMOVE | WM_ENTERMENULOOP => handle_size_move_loop(handle),
-        WM_EXITSIZEMOVE | WM_EXITMENULOOP => handle_size_move_loop_exit(handle),
-        WM_TIMER => handle_timer_msg(handle, wparam, state_ptr),
-        WM_NCCALCSIZE => handle_calc_client_size(handle, wparam, lparam, state_ptr),
-        WM_DPICHANGED => handle_dpi_changed_msg(handle, wparam, lparam, state_ptr),
-        WM_DISPLAYCHANGE => handle_display_change_msg(handle, state_ptr),
-        WM_NCHITTEST => handle_hit_test_msg(handle, msg, wparam, lparam, state_ptr),
-        WM_PAINT => handle_paint_msg(handle, state_ptr),
-        WM_CLOSE => handle_close_msg(state_ptr),
-        WM_DESTROY => handle_destroy_msg(handle, state_ptr),
-        WM_MOUSEMOVE => handle_mouse_move_msg(handle, lparam, wparam, state_ptr),
-        WM_MOUSELEAVE | WM_NCMOUSELEAVE => handle_mouse_leave_msg(state_ptr),
-        WM_NCMOUSEMOVE => handle_nc_mouse_move_msg(handle, lparam, state_ptr),
-        WM_NCLBUTTONDOWN => {
-            handle_nc_mouse_down_msg(handle, MouseButton::Left, wparam, lparam, state_ptr)
+impl WindowsWindowInner {
+    pub(crate) fn handle_msg(
+        self: &Rc<Self>,
+        handle: HWND,
+        msg: u32,
+        wparam: WPARAM,
+        lparam: LPARAM,
+    ) -> LRESULT {
+        let handled = match msg {
+            WM_ACTIVATE => self.handle_activate_msg(wparam),
+            WM_CREATE => self.handle_create_msg(handle),
+            WM_DEVICECHANGE => self.handle_device_change_msg(handle, wparam),
+            WM_MOVE => self.handle_move_msg(handle, lparam),
+            WM_SIZE => self.handle_size_msg(wparam, lparam),
+            WM_GETMINMAXINFO => self.handle_get_min_max_info_msg(lparam),
+            WM_ENTERSIZEMOVE | WM_ENTERMENULOOP => self.handle_size_move_loop(handle),
+            WM_EXITSIZEMOVE | WM_EXITMENULOOP => self.handle_size_move_loop_exit(handle),
+            WM_TIMER => self.handle_timer_msg(handle, wparam),
+            WM_NCCALCSIZE => self.handle_calc_client_size(handle, wparam, lparam),
+            WM_DPICHANGED => self.handle_dpi_changed_msg(handle, wparam, lparam),
+            WM_DISPLAYCHANGE => self.handle_display_change_msg(handle),
+            WM_NCHITTEST => self.handle_hit_test_msg(handle, msg, wparam, lparam),
+            WM_PAINT => self.handle_paint_msg(handle),
+            WM_CLOSE => self.handle_close_msg(),
+            WM_DESTROY => self.handle_destroy_msg(handle),
+            WM_MOUSEMOVE => self.handle_mouse_move_msg(handle, lparam, wparam),
+            WM_MOUSELEAVE | WM_NCMOUSELEAVE => self.handle_mouse_leave_msg(),
+            WM_NCMOUSEMOVE => self.handle_nc_mouse_move_msg(handle, lparam),
+            WM_NCLBUTTONDOWN => {
+                self.handle_nc_mouse_down_msg(handle, MouseButton::Left, wparam, lparam)
+            }
+            WM_NCRBUTTONDOWN => {
+                self.handle_nc_mouse_down_msg(handle, MouseButton::Right, wparam, lparam)
+            }
+            WM_NCMBUTTONDOWN => {
+                self.handle_nc_mouse_down_msg(handle, MouseButton::Middle, wparam, lparam)
+            }
+            WM_NCLBUTTONUP => {
+                self.handle_nc_mouse_up_msg(handle, MouseButton::Left, wparam, lparam)
+            }
+            WM_NCRBUTTONUP => {
+                self.handle_nc_mouse_up_msg(handle, MouseButton::Right, wparam, lparam)
+            }
+            WM_NCMBUTTONUP => {
+                self.handle_nc_mouse_up_msg(handle, MouseButton::Middle, wparam, lparam)
+            }
+            WM_LBUTTONDOWN => self.handle_mouse_down_msg(handle, MouseButton::Left, lparam),
+            WM_RBUTTONDOWN => self.handle_mouse_down_msg(handle, MouseButton::Right, lparam),
+            WM_MBUTTONDOWN => self.handle_mouse_down_msg(handle, MouseButton::Middle, lparam),
+            WM_XBUTTONDOWN => {
+                self.handle_xbutton_msg(handle, wparam, lparam, Self::handle_mouse_down_msg)
+            }
+            WM_LBUTTONUP => self.handle_mouse_up_msg(handle, MouseButton::Left, lparam),
+            WM_RBUTTONUP => self.handle_mouse_up_msg(handle, MouseButton::Right, lparam),
+            WM_MBUTTONUP => self.handle_mouse_up_msg(handle, MouseButton::Middle, lparam),
+            WM_XBUTTONUP => {
+                self.handle_xbutton_msg(handle, wparam, lparam, Self::handle_mouse_up_msg)
+            }
+            WM_MOUSEWHEEL => self.handle_mouse_wheel_msg(handle, wparam, lparam),
+            WM_MOUSEHWHEEL => self.handle_mouse_horizontal_wheel_msg(handle, wparam, lparam),
+            WM_SYSKEYDOWN => self.handle_syskeydown_msg(handle, wparam, lparam),
+            WM_SYSKEYUP => self.handle_syskeyup_msg(handle, wparam, lparam),
+            WM_SYSCOMMAND => self.handle_system_command(wparam),
+            WM_KEYDOWN => self.handle_keydown_msg(handle, wparam, lparam),
+            WM_KEYUP => self.handle_keyup_msg(handle, wparam, lparam),
+            WM_CHAR => self.handle_char_msg(wparam),
+            WM_DEADCHAR => self.handle_dead_char_msg(wparam),
+            WM_IME_STARTCOMPOSITION => self.handle_ime_position(handle),
+            WM_IME_COMPOSITION => self.handle_ime_composition(handle, lparam),
+            WM_SETCURSOR => self.handle_set_cursor(handle, lparam),
+            WM_SETTINGCHANGE => self.handle_system_settings_changed(handle, wparam, lparam),
+            WM_INPUTLANGCHANGE => self.handle_input_language_changed(lparam),
+            WM_GPUI_CURSOR_STYLE_CHANGED => self.handle_cursor_changed(lparam),
+            WM_GPUI_FORCE_UPDATE_WINDOW => self.draw_window(handle, true),
+            _ => None,
+        };
+        if let Some(n) = handled {
+            LRESULT(n)
+        } else {
+            unsafe { DefWindowProcW(handle, msg, wparam, lparam) }
         }
-        WM_NCRBUTTONDOWN => {
-            handle_nc_mouse_down_msg(handle, MouseButton::Right, wparam, lparam, state_ptr)
+    }
+
+    fn handle_move_msg(&self, handle: HWND, lparam: LPARAM) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+        let origin = logical_point(
+            lparam.signed_loword() as f32,
+            lparam.signed_hiword() as f32,
+            lock.scale_factor,
+        );
+        lock.origin = origin;
+        let size = lock.logical_size;
+        let center_x = origin.x.0 + size.width.0 / 2.;
+        let center_y = origin.y.0 + size.height.0 / 2.;
+        let monitor_bounds = lock.display.bounds();
+        if center_x < monitor_bounds.left().0
+            || center_x > monitor_bounds.right().0
+            || center_y < monitor_bounds.top().0
+            || center_y > monitor_bounds.bottom().0
+        {
+            // center of the window may have moved to another monitor
+            let monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) };
+            // minimize the window can trigger this event too, in this case,
+            // monitor is invalid, we do nothing.
+            if !monitor.is_invalid() && lock.display.handle != monitor {
+                // we will get the same monitor if we only have one
+                lock.display = WindowsDisplay::new_with_handle(monitor);
+            }
         }
-        WM_NCMBUTTONDOWN => {
-            handle_nc_mouse_down_msg(handle, MouseButton::Middle, wparam, lparam, state_ptr)
+        if let Some(mut callback) = lock.callbacks.moved.take() {
+            drop(lock);
+            callback();
+            self.state.borrow_mut().callbacks.moved = Some(callback);
         }
-        WM_NCLBUTTONUP => {
-            handle_nc_mouse_up_msg(handle, MouseButton::Left, wparam, lparam, state_ptr)
+        Some(0)
+    }
+
+    fn handle_get_min_max_info_msg(&self, lparam: LPARAM) -> Option<isize> {
+        let lock = self.state.borrow();
+        let min_size = lock.min_size?;
+        let scale_factor = lock.scale_factor;
+        let boarder_offset = lock.border_offset;
+        drop(lock);
+        unsafe {
+            let minmax_info = &mut *(lparam.0 as *mut MINMAXINFO);
+            minmax_info.ptMinTrackSize.x =
+                min_size.width.scale(scale_factor).0 as i32 + boarder_offset.width_offset;
+            minmax_info.ptMinTrackSize.y =
+                min_size.height.scale(scale_factor).0 as i32 + boarder_offset.height_offset;
         }
-        WM_NCRBUTTONUP => {
-            handle_nc_mouse_up_msg(handle, MouseButton::Right, wparam, lparam, state_ptr)
+        Some(0)
+    }
+
+    fn handle_size_msg(&self, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+
+        // Don't resize the renderer when the window is minimized, but record that it was minimized so
+        // that on restore the swap chain can be recreated via `update_drawable_size_even_if_unchanged`.
+        if wparam.0 == SIZE_MINIMIZED as usize {
+            lock.restore_from_minimized = lock.callbacks.request_frame.take();
+            return Some(0);
         }
-        WM_NCMBUTTONUP => {
-            handle_nc_mouse_up_msg(handle, MouseButton::Middle, wparam, lparam, state_ptr)
+
+        let width = lparam.loword().max(1) as i32;
+        let height = lparam.hiword().max(1) as i32;
+        let new_size = size(DevicePixels(width), DevicePixels(height));
+        let scale_factor = lock.scale_factor;
+        if lock.restore_from_minimized.is_some() {
+            lock.callbacks.request_frame = lock.restore_from_minimized.take();
+        } else {
+            lock.renderer.resize(new_size).log_err();
         }
-        WM_LBUTTONDOWN => handle_mouse_down_msg(handle, MouseButton::Left, lparam, state_ptr),
-        WM_RBUTTONDOWN => handle_mouse_down_msg(handle, MouseButton::Right, lparam, state_ptr),
-        WM_MBUTTONDOWN => handle_mouse_down_msg(handle, MouseButton::Middle, lparam, state_ptr),
-        WM_XBUTTONDOWN => {
-            handle_xbutton_msg(handle, wparam, lparam, handle_mouse_down_msg, state_ptr)
+        let new_size = new_size.to_pixels(scale_factor);
+        lock.logical_size = new_size;
+        if let Some(mut callback) = lock.callbacks.resize.take() {
+            drop(lock);
+            callback(new_size, scale_factor);
+            self.state.borrow_mut().callbacks.resize = Some(callback);
         }
-        WM_LBUTTONUP => handle_mouse_up_msg(handle, MouseButton::Left, lparam, state_ptr),
-        WM_RBUTTONUP => handle_mouse_up_msg(handle, MouseButton::Right, lparam, state_ptr),
-        WM_MBUTTONUP => handle_mouse_up_msg(handle, MouseButton::Middle, lparam, state_ptr),
-        WM_XBUTTONUP => handle_xbutton_msg(handle, wparam, lparam, handle_mouse_up_msg, state_ptr),
-        WM_MOUSEWHEEL => handle_mouse_wheel_msg(handle, wparam, lparam, state_ptr),
-        WM_MOUSEHWHEEL => handle_mouse_horizontal_wheel_msg(handle, wparam, lparam, state_ptr),
-        WM_SYSKEYDOWN => handle_syskeydown_msg(handle, wparam, lparam, state_ptr),
-        WM_SYSKEYUP => handle_syskeyup_msg(handle, wparam, lparam, state_ptr),
-        WM_SYSCOMMAND => handle_system_command(wparam, state_ptr),
-        WM_KEYDOWN => handle_keydown_msg(handle, wparam, lparam, state_ptr),
-        WM_KEYUP => handle_keyup_msg(handle, wparam, lparam, state_ptr),
-        WM_CHAR => handle_char_msg(wparam, state_ptr),
-        WM_DEADCHAR => handle_dead_char_msg(wparam, state_ptr),
-        WM_IME_STARTCOMPOSITION => handle_ime_position(handle, state_ptr),
-        WM_IME_COMPOSITION => handle_ime_composition(handle, lparam, state_ptr),
-        WM_SETCURSOR => handle_set_cursor(handle, lparam, state_ptr),
-        WM_SETTINGCHANGE => handle_system_settings_changed(handle, wparam, lparam, state_ptr),
-        WM_INPUTLANGCHANGE => handle_input_language_changed(lparam, state_ptr),
-        WM_GPUI_CURSOR_STYLE_CHANGED => handle_cursor_changed(lparam, state_ptr),
-        WM_GPUI_FORCE_UPDATE_WINDOW => draw_window(handle, true, state_ptr),
-        _ => None,
-    };
-    if let Some(n) = handled {
-        LRESULT(n)
-    } else {
-        unsafe { DefWindowProcW(handle, msg, wparam, lparam) }
+        Some(0)
     }
-}
 
-fn handle_move_msg(
-    handle: HWND,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    let origin = logical_point(
-        lparam.signed_loword() as f32,
-        lparam.signed_hiword() as f32,
-        lock.scale_factor,
-    );
-    lock.origin = origin;
-    let size = lock.logical_size;
-    let center_x = origin.x.0 + size.width.0 / 2.;
-    let center_y = origin.y.0 + size.height.0 / 2.;
-    let monitor_bounds = lock.display.bounds();
-    if center_x < monitor_bounds.left().0
-        || center_x > monitor_bounds.right().0
-        || center_y < monitor_bounds.top().0
-        || center_y > monitor_bounds.bottom().0
-    {
-        // center of the window may have moved to another monitor
-        let monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) };
-        // minimize the window can trigger this event too, in this case,
-        // monitor is invalid, we do nothing.
-        if !monitor.is_invalid() && lock.display.handle != monitor {
-            // we will get the same monitor if we only have one
-            lock.display = WindowsDisplay::new_with_handle(monitor);
+    fn handle_size_move_loop(&self, handle: HWND) -> Option<isize> {
+        unsafe {
+            let ret = SetTimer(
+                Some(handle),
+                SIZE_MOVE_LOOP_TIMER_ID,
+                USER_TIMER_MINIMUM,
+                None,
+            );
+            if ret == 0 {
+                log::error!(
+                    "unable to create timer: {}",
+                    std::io::Error::last_os_error()
+                );
+            }
         }
+        None
     }
-    if let Some(mut callback) = lock.callbacks.moved.take() {
-        drop(lock);
-        callback();
-        state_ptr.state.borrow_mut().callbacks.moved = Some(callback);
-    }
-    Some(0)
-}
 
-fn handle_get_min_max_info_msg(
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let lock = state_ptr.state.borrow();
-    let min_size = lock.min_size?;
-    let scale_factor = lock.scale_factor;
-    let boarder_offset = lock.border_offset;
-    drop(lock);
-    unsafe {
-        let minmax_info = &mut *(lparam.0 as *mut MINMAXINFO);
-        minmax_info.ptMinTrackSize.x =
-            min_size.width.scale(scale_factor).0 as i32 + boarder_offset.width_offset;
-        minmax_info.ptMinTrackSize.y =
-            min_size.height.scale(scale_factor).0 as i32 + boarder_offset.height_offset;
+    fn handle_size_move_loop_exit(&self, handle: HWND) -> Option<isize> {
+        unsafe {
+            KillTimer(Some(handle), SIZE_MOVE_LOOP_TIMER_ID).log_err();
+        }
+        None
     }
-    Some(0)
-}
 
-fn handle_size_msg(
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-
-    // Don't resize the renderer when the window is minimized, but record that it was minimized so
-    // that on restore the swap chain can be recreated via `update_drawable_size_even_if_unchanged`.
-    if wparam.0 == SIZE_MINIMIZED as usize {
-        lock.restore_from_minimized = lock.callbacks.request_frame.take();
-        return Some(0);
+    fn handle_timer_msg(&self, handle: HWND, wparam: WPARAM) -> Option<isize> {
+        if wparam.0 == SIZE_MOVE_LOOP_TIMER_ID {
+            for runnable in self.main_receiver.drain() {
+                runnable.run();
+            }
+            self.handle_paint_msg(handle)
+        } else {
+            None
+        }
     }
 
-    let width = lparam.loword().max(1) as i32;
-    let height = lparam.hiword().max(1) as i32;
-    let new_size = size(DevicePixels(width), DevicePixels(height));
-    let scale_factor = lock.scale_factor;
-    if lock.restore_from_minimized.is_some() {
-        lock.callbacks.request_frame = lock.restore_from_minimized.take();
-    } else {
-        lock.renderer.resize(new_size).log_err();
+    fn handle_paint_msg(&self, handle: HWND) -> Option<isize> {
+        self.draw_window(handle, false)
     }
-    let new_size = new_size.to_pixels(scale_factor);
-    lock.logical_size = new_size;
-    if let Some(mut callback) = lock.callbacks.resize.take() {
-        drop(lock);
-        callback(new_size, scale_factor);
-        state_ptr.state.borrow_mut().callbacks.resize = Some(callback);
+
+    fn handle_close_msg(&self) -> Option<isize> {
+        let mut callback = self.state.borrow_mut().callbacks.should_close.take()?;
+        let should_close = callback();
+        self.state.borrow_mut().callbacks.should_close = Some(callback);
+        if should_close { None } else { Some(0) }
     }
-    Some(0)
-}
 
-fn handle_size_move_loop(handle: HWND) -> Option<isize> {
-    unsafe {
-        let ret = SetTimer(
-            Some(handle),
-            SIZE_MOVE_LOOP_TIMER_ID,
-            USER_TIMER_MINIMUM,
-            None,
-        );
-        if ret == 0 {
-            log::error!(
-                "unable to create timer: {}",
-                std::io::Error::last_os_error()
-            );
+    fn handle_destroy_msg(&self, handle: HWND) -> Option<isize> {
+        let callback = {
+            let mut lock = self.state.borrow_mut();
+            lock.callbacks.close.take()
+        };
+        if let Some(callback) = callback {
+            callback();
+        }
+        unsafe {
+            PostThreadMessageW(
+                self.main_thread_id_win32,
+                WM_GPUI_CLOSE_ONE_WINDOW,
+                WPARAM(self.validation_number),
+                LPARAM(handle.0 as isize),
+            )
+            .log_err();
         }
+        Some(0)
     }
-    None
-}
 
-fn handle_size_move_loop_exit(handle: HWND) -> Option<isize> {
-    unsafe {
-        KillTimer(Some(handle), SIZE_MOVE_LOOP_TIMER_ID).log_err();
+    fn handle_mouse_move_msg(&self, handle: HWND, lparam: LPARAM, wparam: WPARAM) -> Option<isize> {
+        self.start_tracking_mouse(handle, TME_LEAVE);
+
+        let mut lock = self.state.borrow_mut();
+        let Some(mut func) = lock.callbacks.input.take() else {
+            return Some(1);
+        };
+        let scale_factor = lock.scale_factor;
+        drop(lock);
+
+        let pressed_button = match MODIFIERKEYS_FLAGS(wparam.loword() as u32) {
+            flags if flags.contains(MK_LBUTTON) => Some(MouseButton::Left),
+            flags if flags.contains(MK_RBUTTON) => Some(MouseButton::Right),
+            flags if flags.contains(MK_MBUTTON) => Some(MouseButton::Middle),
+            flags if flags.contains(MK_XBUTTON1) => {
+                Some(MouseButton::Navigate(NavigationDirection::Back))
+            }
+            flags if flags.contains(MK_XBUTTON2) => {
+                Some(MouseButton::Navigate(NavigationDirection::Forward))
+            }
+            _ => None,
+        };
+        let x = lparam.signed_loword() as f32;
+        let y = lparam.signed_hiword() as f32;
+        let input = PlatformInput::MouseMove(MouseMoveEvent {
+            position: logical_point(x, y, scale_factor),
+            pressed_button,
+            modifiers: current_modifiers(),
+        });
+        let handled = !func(input).propagate;
+        self.state.borrow_mut().callbacks.input = Some(func);
+
+        if handled { Some(0) } else { Some(1) }
     }
-    None
-}
 
-fn handle_timer_msg(
-    handle: HWND,
-    wparam: WPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    if wparam.0 == SIZE_MOVE_LOOP_TIMER_ID {
-        for runnable in state_ptr.main_receiver.drain() {
-            runnable.run();
+    fn handle_mouse_leave_msg(&self) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+        lock.hovered = false;
+        if let Some(mut callback) = lock.callbacks.hovered_status_change.take() {
+            drop(lock);
+            callback(false);
+            self.state.borrow_mut().callbacks.hovered_status_change = Some(callback);
         }
-        handle_paint_msg(handle, state_ptr)
-    } else {
-        None
-    }
-}
 
-fn handle_paint_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    draw_window(handle, false, state_ptr)
-}
+        Some(0)
+    }
 
-fn handle_close_msg(state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    let mut callback = state_ptr.state.borrow_mut().callbacks.should_close.take()?;
-    let should_close = callback();
-    state_ptr.state.borrow_mut().callbacks.should_close = Some(callback);
-    if should_close { None } else { Some(0) }
-}
+    fn handle_syskeydown_msg(&self, handle: HWND, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+        let input = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| {
+            PlatformInput::KeyDown(KeyDownEvent {
+                keystroke,
+                is_held: lparam.0 & (0x1 << 30) > 0,
+            })
+        })?;
+        let mut func = lock.callbacks.input.take()?;
+        drop(lock);
 
-fn handle_destroy_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    let callback = {
-        let mut lock = state_ptr.state.borrow_mut();
-        lock.callbacks.close.take()
-    };
-    if let Some(callback) = callback {
-        callback();
-    }
-    unsafe {
-        PostThreadMessageW(
-            state_ptr.main_thread_id_win32,
-            WM_GPUI_CLOSE_ONE_WINDOW,
-            WPARAM(state_ptr.validation_number),
-            LPARAM(handle.0 as isize),
-        )
-        .log_err();
-    }
-    Some(0)
-}
+        let handled = !func(input).propagate;
 
-fn handle_mouse_move_msg(
-    handle: HWND,
-    lparam: LPARAM,
-    wparam: WPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    start_tracking_mouse(handle, &state_ptr, TME_LEAVE);
+        let mut lock = self.state.borrow_mut();
+        lock.callbacks.input = Some(func);
 
-    let mut lock = state_ptr.state.borrow_mut();
-    let Some(mut func) = lock.callbacks.input.take() else {
-        return Some(1);
-    };
-    let scale_factor = lock.scale_factor;
-    drop(lock);
-
-    let pressed_button = match MODIFIERKEYS_FLAGS(wparam.loword() as u32) {
-        flags if flags.contains(MK_LBUTTON) => Some(MouseButton::Left),
-        flags if flags.contains(MK_RBUTTON) => Some(MouseButton::Right),
-        flags if flags.contains(MK_MBUTTON) => Some(MouseButton::Middle),
-        flags if flags.contains(MK_XBUTTON1) => {
-            Some(MouseButton::Navigate(NavigationDirection::Back))
-        }
-        flags if flags.contains(MK_XBUTTON2) => {
-            Some(MouseButton::Navigate(NavigationDirection::Forward))
+        if handled {
+            lock.system_key_handled = true;
+            Some(0)
+        } else {
+            // we need to call `DefWindowProcW`, or we will lose the system-wide `Alt+F4`, `Alt+{other keys}`
+            // shortcuts.
+            None
         }
-        _ => None,
-    };
-    let x = lparam.signed_loword() as f32;
-    let y = lparam.signed_hiword() as f32;
-    let input = PlatformInput::MouseMove(MouseMoveEvent {
-        position: logical_point(x, y, scale_factor),
-        pressed_button,
-        modifiers: current_modifiers(),
-    });
-    let handled = !func(input).propagate;
-    state_ptr.state.borrow_mut().callbacks.input = Some(func);
-
-    if handled { Some(0) } else { Some(1) }
-}
+    }
 
-fn handle_mouse_leave_msg(state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    lock.hovered = false;
-    if let Some(mut callback) = lock.callbacks.hovered_status_change.take() {
+    fn handle_syskeyup_msg(&self, handle: HWND, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+        let input = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| {
+            PlatformInput::KeyUp(KeyUpEvent { keystroke })
+        })?;
+        let mut func = lock.callbacks.input.take()?;
         drop(lock);
-        callback(false);
-        state_ptr.state.borrow_mut().callbacks.hovered_status_change = Some(callback);
+        func(input);
+        self.state.borrow_mut().callbacks.input = Some(func);
+
+        // Always return 0 to indicate that the message was handled, so we could properly handle `ModifiersChanged` event.
+        Some(0)
     }
 
-    Some(0)
-}
+    // It's a known bug that you can't trigger `ctrl-shift-0`. See:
+    // https://superuser.com/questions/1455762/ctrl-shift-number-key-combination-has-stopped-working-for-a-few-numbers
+    fn handle_keydown_msg(&self, handle: HWND, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+        let Some(input) = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| {
+            PlatformInput::KeyDown(KeyDownEvent {
+                keystroke,
+                is_held: lparam.0 & (0x1 << 30) > 0,
+            })
+        }) else {
+            return Some(1);
+        };
+        drop(lock);
 
-fn handle_syskeydown_msg(
-    handle: HWND,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    let input = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| {
-        PlatformInput::KeyDown(KeyDownEvent {
-            keystroke,
-            is_held: lparam.0 & (0x1 << 30) > 0,
-        })
-    })?;
-    let mut func = lock.callbacks.input.take()?;
-    drop(lock);
+        let is_composing = self
+            .with_input_handler(|input_handler| input_handler.marked_text_range())
+            .flatten()
+            .is_some();
+        if is_composing {
+            translate_message(handle, wparam, lparam);
+            return Some(0);
+        }
 
-    let handled = !func(input).propagate;
+        let Some(mut func) = self.state.borrow_mut().callbacks.input.take() else {
+            return Some(1);
+        };
 
-    let mut lock = state_ptr.state.borrow_mut();
-    lock.callbacks.input = Some(func);
+        let handled = !func(input).propagate;
 
-    if handled {
-        lock.system_key_handled = true;
-        Some(0)
-    } else {
-        // we need to call `DefWindowProcW`, or we will lose the system-wide `Alt+F4`, `Alt+{other keys}`
-        // shortcuts.
-        None
+        self.state.borrow_mut().callbacks.input = Some(func);
+
+        if handled {
+            Some(0)
+        } else {
+            translate_message(handle, wparam, lparam);
+            Some(1)
+        }
     }
-}
 
-fn handle_syskeyup_msg(
-    handle: HWND,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    let input = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| {
-        PlatformInput::KeyUp(KeyUpEvent { keystroke })
-    })?;
-    let mut func = lock.callbacks.input.take()?;
-    drop(lock);
-    func(input);
-    state_ptr.state.borrow_mut().callbacks.input = Some(func);
+    fn handle_keyup_msg(&self, handle: HWND, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+        let Some(input) = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| {
+            PlatformInput::KeyUp(KeyUpEvent { keystroke })
+        }) else {
+            return Some(1);
+        };
 
-    // Always return 0 to indicate that the message was handled, so we could properly handle `ModifiersChanged` event.
-    Some(0)
-}
+        let Some(mut func) = lock.callbacks.input.take() else {
+            return Some(1);
+        };
+        drop(lock);
 
-// It's a known bug that you can't trigger `ctrl-shift-0`. See:
-// https://superuser.com/questions/1455762/ctrl-shift-number-key-combination-has-stopped-working-for-a-few-numbers
-fn handle_keydown_msg(
-    handle: HWND,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    let Some(input) = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| {
-        PlatformInput::KeyDown(KeyDownEvent {
-            keystroke,
-            is_held: lparam.0 & (0x1 << 30) > 0,
-        })
-    }) else {
-        return Some(1);
-    };
-    drop(lock);
+        let handled = !func(input).propagate;
+        self.state.borrow_mut().callbacks.input = Some(func);
 
-    let is_composing = with_input_handler(&state_ptr, |input_handler| {
-        input_handler.marked_text_range()
-    })
-    .flatten()
-    .is_some();
-    if is_composing {
-        translate_message(handle, wparam, lparam);
-        return Some(0);
+        if handled { Some(0) } else { Some(1) }
     }
 
-    let Some(mut func) = state_ptr.state.borrow_mut().callbacks.input.take() else {
-        return Some(1);
-    };
-
-    let handled = !func(input).propagate;
-
-    state_ptr.state.borrow_mut().callbacks.input = Some(func);
+    fn handle_char_msg(&self, wparam: WPARAM) -> Option<isize> {
+        let input = self.parse_char_message(wparam)?;
+        self.with_input_handler(|input_handler| {
+            input_handler.replace_text_in_range(None, &input);
+        });
 
-    if handled {
         Some(0)
-    } else {
-        translate_message(handle, wparam, lparam);
-        Some(1)
     }
-}
 
-fn handle_keyup_msg(
-    handle: HWND,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    let Some(input) = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| {
-        PlatformInput::KeyUp(KeyUpEvent { keystroke })
-    }) else {
-        return Some(1);
-    };
+    fn handle_dead_char_msg(&self, wparam: WPARAM) -> Option<isize> {
+        let ch = char::from_u32(wparam.0 as u32)?.to_string();
+        self.with_input_handler(|input_handler| {
+            input_handler.replace_and_mark_text_in_range(None, &ch, None);
+        });
+        None
+    }
 
-    let Some(mut func) = lock.callbacks.input.take() else {
-        return Some(1);
-    };
-    drop(lock);
+    fn handle_mouse_down_msg(
+        &self,
+        handle: HWND,
+        button: MouseButton,
+        lparam: LPARAM,
+    ) -> Option<isize> {
+        unsafe { SetCapture(handle) };
+        let mut lock = self.state.borrow_mut();
+        let Some(mut func) = lock.callbacks.input.take() else {
+            return Some(1);
+        };
+        let x = lparam.signed_loword();
+        let y = lparam.signed_hiword();
+        let physical_point = point(DevicePixels(x as i32), DevicePixels(y as i32));
+        let click_count = lock.click_state.update(button, physical_point);
+        let scale_factor = lock.scale_factor;
+        drop(lock);
 
-    let handled = !func(input).propagate;
-    state_ptr.state.borrow_mut().callbacks.input = Some(func);
+        let input = PlatformInput::MouseDown(MouseDownEvent {
+            button,
+            position: logical_point(x as f32, y as f32, scale_factor),
+            modifiers: current_modifiers(),
+            click_count,
+            first_mouse: false,
+        });
+        let handled = !func(input).propagate;
+        self.state.borrow_mut().callbacks.input = Some(func);
 
-    if handled { Some(0) } else { Some(1) }
-}
+        if handled { Some(0) } else { Some(1) }
+    }
 
-fn handle_char_msg(wparam: WPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    let input = parse_char_message(wparam, &state_ptr)?;
-    with_input_handler(&state_ptr, |input_handler| {
-        input_handler.replace_text_in_range(None, &input);
-    });
+    fn handle_mouse_up_msg(
+        &self,
+        _handle: HWND,
+        button: MouseButton,
+        lparam: LPARAM,
+    ) -> Option<isize> {
+        unsafe { ReleaseCapture().log_err() };
+        let mut lock = self.state.borrow_mut();
+        let Some(mut func) = lock.callbacks.input.take() else {
+            return Some(1);
+        };
+        let x = lparam.signed_loword() as f32;
+        let y = lparam.signed_hiword() as f32;
+        let click_count = lock.click_state.current_count;
+        let scale_factor = lock.scale_factor;
+        drop(lock);
 
-    Some(0)
-}
+        let input = PlatformInput::MouseUp(MouseUpEvent {
+            button,
+            position: logical_point(x, y, scale_factor),
+            modifiers: current_modifiers(),
+            click_count,
+        });
+        let handled = !func(input).propagate;
+        self.state.borrow_mut().callbacks.input = Some(func);
 
-fn handle_dead_char_msg(wparam: WPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    let ch = char::from_u32(wparam.0 as u32)?.to_string();
-    with_input_handler(&state_ptr, |input_handler| {
-        input_handler.replace_and_mark_text_in_range(None, &ch, None);
-    });
-    None
-}
+        if handled { Some(0) } else { Some(1) }
+    }
 
-fn handle_mouse_down_msg(
-    handle: HWND,
-    button: MouseButton,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    unsafe { SetCapture(handle) };
-    let mut lock = state_ptr.state.borrow_mut();
-    let Some(mut func) = lock.callbacks.input.take() else {
-        return Some(1);
-    };
-    let x = lparam.signed_loword();
-    let y = lparam.signed_hiword();
-    let physical_point = point(DevicePixels(x as i32), DevicePixels(y as i32));
-    let click_count = lock.click_state.update(button, physical_point);
-    let scale_factor = lock.scale_factor;
-    drop(lock);
-
-    let input = PlatformInput::MouseDown(MouseDownEvent {
-        button,
-        position: logical_point(x as f32, y as f32, scale_factor),
-        modifiers: current_modifiers(),
-        click_count,
-        first_mouse: false,
-    });
-    let handled = !func(input).propagate;
-    state_ptr.state.borrow_mut().callbacks.input = Some(func);
-
-    if handled { Some(0) } else { Some(1) }
-}
+    fn handle_xbutton_msg(
+        &self,
+        handle: HWND,
+        wparam: WPARAM,
+        lparam: LPARAM,
+        handler: impl Fn(&Self, HWND, MouseButton, LPARAM) -> Option<isize>,
+    ) -> Option<isize> {
+        let nav_dir = match wparam.hiword() {
+            XBUTTON1 => NavigationDirection::Back,
+            XBUTTON2 => NavigationDirection::Forward,
+            _ => return Some(1),
+        };
+        handler(self, handle, MouseButton::Navigate(nav_dir), lparam)
+    }
 
-fn handle_mouse_up_msg(
-    _handle: HWND,
-    button: MouseButton,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    unsafe { ReleaseCapture().log_err() };
-    let mut lock = state_ptr.state.borrow_mut();
-    let Some(mut func) = lock.callbacks.input.take() else {
-        return Some(1);
-    };
-    let x = lparam.signed_loword() as f32;
-    let y = lparam.signed_hiword() as f32;
-    let click_count = lock.click_state.current_count;
-    let scale_factor = lock.scale_factor;
-    drop(lock);
-
-    let input = PlatformInput::MouseUp(MouseUpEvent {
-        button,
-        position: logical_point(x, y, scale_factor),
-        modifiers: current_modifiers(),
-        click_count,
-    });
-    let handled = !func(input).propagate;
-    state_ptr.state.borrow_mut().callbacks.input = Some(func);
-
-    if handled { Some(0) } else { Some(1) }
-}
+    fn handle_mouse_wheel_msg(
+        &self,
+        handle: HWND,
+        wparam: WPARAM,
+        lparam: LPARAM,
+    ) -> Option<isize> {
+        let modifiers = current_modifiers();
+        let mut lock = self.state.borrow_mut();
+        let Some(mut func) = lock.callbacks.input.take() else {
+            return Some(1);
+        };
+        let scale_factor = lock.scale_factor;
+        let wheel_scroll_amount = match modifiers.shift {
+            true => lock.system_settings.mouse_wheel_settings.wheel_scroll_chars,
+            false => lock.system_settings.mouse_wheel_settings.wheel_scroll_lines,
+        };
+        drop(lock);
 
-fn handle_xbutton_msg(
-    handle: HWND,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    handler: impl Fn(HWND, MouseButton, LPARAM, Rc<WindowsWindowStatePtr>) -> Option<isize>,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let nav_dir = match wparam.hiword() {
-        XBUTTON1 => NavigationDirection::Back,
-        XBUTTON2 => NavigationDirection::Forward,
-        _ => return Some(1),
-    };
-    handler(handle, MouseButton::Navigate(nav_dir), lparam, state_ptr)
-}
+        let wheel_distance =
+            (wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_amount as f32;
+        let mut cursor_point = POINT {
+            x: lparam.signed_loword().into(),
+            y: lparam.signed_hiword().into(),
+        };
+        unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
+        let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
+            position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
+            delta: ScrollDelta::Lines(match modifiers.shift {
+                true => Point {
+                    x: wheel_distance,
+                    y: 0.0,
+                },
+                false => Point {
+                    y: wheel_distance,
+                    x: 0.0,
+                },
+            }),
+            modifiers,
+            touch_phase: TouchPhase::Moved,
+        });
+        let handled = !func(input).propagate;
+        self.state.borrow_mut().callbacks.input = Some(func);
 
-fn handle_mouse_wheel_msg(
-    handle: HWND,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let modifiers = current_modifiers();
-    let mut lock = state_ptr.state.borrow_mut();
-    let Some(mut func) = lock.callbacks.input.take() else {
-        return Some(1);
-    };
-    let scale_factor = lock.scale_factor;
-    let wheel_scroll_amount = match modifiers.shift {
-        true => lock.system_settings.mouse_wheel_settings.wheel_scroll_chars,
-        false => lock.system_settings.mouse_wheel_settings.wheel_scroll_lines,
-    };
-    drop(lock);
+        if handled { Some(0) } else { Some(1) }
+    }
 
-    let wheel_distance =
-        (wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_amount as f32;
-    let mut cursor_point = POINT {
-        x: lparam.signed_loword().into(),
-        y: lparam.signed_hiword().into(),
-    };
-    unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
-    let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
-        position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
-        delta: ScrollDelta::Lines(match modifiers.shift {
-            true => Point {
+    fn handle_mouse_horizontal_wheel_msg(
+        &self,
+        handle: HWND,
+        wparam: WPARAM,
+        lparam: LPARAM,
+    ) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+        let Some(mut func) = lock.callbacks.input.take() else {
+            return Some(1);
+        };
+        let scale_factor = lock.scale_factor;
+        let wheel_scroll_chars = lock.system_settings.mouse_wheel_settings.wheel_scroll_chars;
+        drop(lock);
+
+        let wheel_distance =
+            (-wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_chars as f32;
+        let mut cursor_point = POINT {
+            x: lparam.signed_loword().into(),
+            y: lparam.signed_hiword().into(),
+        };
+        unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
+        let event = PlatformInput::ScrollWheel(ScrollWheelEvent {
+            position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
+            delta: ScrollDelta::Lines(Point {
                 x: wheel_distance,
                 y: 0.0,
-            },
-            false => Point {
-                y: wheel_distance,
-                x: 0.0,
-            },
-        }),
-        modifiers,
-        touch_phase: TouchPhase::Moved,
-    });
-    let handled = !func(input).propagate;
-    state_ptr.state.borrow_mut().callbacks.input = Some(func);
-
-    if handled { Some(0) } else { Some(1) }
-}
+            }),
+            modifiers: current_modifiers(),
+            touch_phase: TouchPhase::Moved,
+        });
+        let handled = !func(event).propagate;
+        self.state.borrow_mut().callbacks.input = Some(func);
 
-fn handle_mouse_horizontal_wheel_msg(
-    handle: HWND,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    let Some(mut func) = lock.callbacks.input.take() else {
-        return Some(1);
-    };
-    let scale_factor = lock.scale_factor;
-    let wheel_scroll_chars = lock.system_settings.mouse_wheel_settings.wheel_scroll_chars;
-    drop(lock);
-
-    let wheel_distance =
-        (-wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_chars as f32;
-    let mut cursor_point = POINT {
-        x: lparam.signed_loword().into(),
-        y: lparam.signed_hiword().into(),
-    };
-    unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
-    let event = PlatformInput::ScrollWheel(ScrollWheelEvent {
-        position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
-        delta: ScrollDelta::Lines(Point {
-            x: wheel_distance,
-            y: 0.0,
-        }),
-        modifiers: current_modifiers(),
-        touch_phase: TouchPhase::Moved,
-    });
-    let handled = !func(event).propagate;
-    state_ptr.state.borrow_mut().callbacks.input = Some(func);
-
-    if handled { Some(0) } else { Some(1) }
-}
+        if handled { Some(0) } else { Some(1) }
+    }
 
-fn retrieve_caret_position(state_ptr: &Rc<WindowsWindowStatePtr>) -> Option<POINT> {
-    with_input_handler_and_scale_factor(state_ptr, |input_handler, scale_factor| {
-        let caret_range = input_handler.selected_text_range(false)?;
-        let caret_position = input_handler.bounds_for_range(caret_range.range)?;
-        Some(POINT {
-            // logical to physical
-            x: (caret_position.origin.x.0 * scale_factor) as i32,
-            y: (caret_position.origin.y.0 * scale_factor) as i32
-                + ((caret_position.size.height.0 * scale_factor) as i32 / 2),
+    fn retrieve_caret_position(&self) -> Option<POINT> {
+        self.with_input_handler_and_scale_factor(|input_handler, scale_factor| {
+            let caret_range = input_handler.selected_text_range(false)?;
+            let caret_position = input_handler.bounds_for_range(caret_range.range)?;
+            Some(POINT {
+                // logical to physical
+                x: (caret_position.origin.x.0 * scale_factor) as i32,
+                y: (caret_position.origin.y.0 * scale_factor) as i32
+                    + ((caret_position.size.height.0 * scale_factor) as i32 / 2),
+            })
         })
-    })
-}
-
-fn handle_ime_position(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    unsafe {
-        let ctx = ImmGetContext(handle);
-
-        let Some(caret_position) = retrieve_caret_position(&state_ptr) else {
-            return Some(0);
-        };
-        {
-            let config = COMPOSITIONFORM {
-                dwStyle: CFS_POINT,
-                ptCurrentPos: caret_position,
-                ..Default::default()
-            };
-            ImmSetCompositionWindow(ctx, &config as _).ok().log_err();
-        }
-        {
-            let config = CANDIDATEFORM {
-                dwStyle: CFS_CANDIDATEPOS,
-                ptCurrentPos: caret_position,
-                ..Default::default()
-            };
-            ImmSetCandidateWindow(ctx, &config as _).ok().log_err();
-        }
-        ImmReleaseContext(handle, ctx).ok().log_err();
-        Some(0)
     }
-}
 
-fn handle_ime_composition(
-    handle: HWND,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let ctx = unsafe { ImmGetContext(handle) };
-    let result = handle_ime_composition_inner(ctx, lparam, state_ptr);
-    unsafe { ImmReleaseContext(handle, ctx).ok().log_err() };
-    result
-}
+    fn handle_ime_position(&self, handle: HWND) -> Option<isize> {
+        unsafe {
+            let ctx = ImmGetContext(handle);
 
-fn handle_ime_composition_inner(
-    ctx: HIMC,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let lparam = lparam.0 as u32;
-    if lparam == 0 {
-        // Japanese IME may send this message with lparam = 0, which indicates that
-        // there is no composition string.
-        with_input_handler(&state_ptr, |input_handler| {
-            input_handler.replace_text_in_range(None, "");
-        })?;
-        Some(0)
-    } else {
-        if lparam & GCS_COMPSTR.0 > 0 {
-            let comp_string = parse_ime_composition_string(ctx, GCS_COMPSTR)?;
-            let caret_pos = (!comp_string.is_empty() && lparam & GCS_CURSORPOS.0 > 0).then(|| {
-                let pos = retrieve_composition_cursor_position(ctx);
-                pos..pos
-            });
-            with_input_handler(&state_ptr, |input_handler| {
-                input_handler.replace_and_mark_text_in_range(None, &comp_string, caret_pos);
-            })?;
-        }
-        if lparam & GCS_RESULTSTR.0 > 0 {
-            let comp_result = parse_ime_composition_string(ctx, GCS_RESULTSTR)?;
-            with_input_handler(&state_ptr, |input_handler| {
-                input_handler.replace_text_in_range(None, &comp_result);
-            })?;
-            return Some(0);
+            let Some(caret_position) = self.retrieve_caret_position() else {
+                return Some(0);
+            };
+            {
+                let config = COMPOSITIONFORM {
+                    dwStyle: CFS_POINT,
+                    ptCurrentPos: caret_position,
+                    ..Default::default()
+                };
+                ImmSetCompositionWindow(ctx, &config as _).ok().log_err();
+            }
+            {
+                let config = CANDIDATEFORM {
+                    dwStyle: CFS_CANDIDATEPOS,
+                    ptCurrentPos: caret_position,
+                    ..Default::default()
+                };
+                ImmSetCandidateWindow(ctx, &config as _).ok().log_err();
+            }
+            ImmReleaseContext(handle, ctx).ok().log_err();
+            Some(0)
         }
-
-        // currently, we don't care other stuff
-        None
     }
-}
 
-/// SEE: https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize
-fn handle_calc_client_size(
-    handle: HWND,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    if !state_ptr.hide_title_bar || state_ptr.state.borrow().is_fullscreen() || wparam.0 == 0 {
-        return None;
+    fn handle_ime_composition(&self, handle: HWND, lparam: LPARAM) -> Option<isize> {
+        let ctx = unsafe { ImmGetContext(handle) };
+        let result = self.handle_ime_composition_inner(ctx, lparam);
+        unsafe { ImmReleaseContext(handle, ctx).ok().log_err() };
+        result
     }
 
-    let is_maximized = state_ptr.state.borrow().is_maximized();
-    let insets = get_client_area_insets(handle, is_maximized, state_ptr.windows_version);
-    // wparam is TRUE so lparam points to an NCCALCSIZE_PARAMS structure
-    let mut params = lparam.0 as *mut NCCALCSIZE_PARAMS;
-    let mut requested_client_rect = unsafe { &mut ((*params).rgrc) };
-
-    requested_client_rect[0].left += insets.left;
-    requested_client_rect[0].top += insets.top;
-    requested_client_rect[0].right -= insets.right;
-    requested_client_rect[0].bottom -= insets.bottom;
-
-    // Fix auto hide taskbar not showing. This solution is based on the approach
-    // used by Chrome. However, it may result in one row of pixels being obscured
-    // in our client area. But as Chrome says, "there seems to be no better solution."
-    if is_maximized {
-        if let Some(ref taskbar_position) = state_ptr
-            .state
-            .borrow()
-            .system_settings
-            .auto_hide_taskbar_position
-        {
-            // Fot the auto-hide taskbar, adjust in by 1 pixel on taskbar edge,
-            // so the window isn't treated as a "fullscreen app", which would cause
-            // the taskbar to disappear.
-            match taskbar_position {
-                AutoHideTaskbarPosition::Left => {
-                    requested_client_rect[0].left += AUTO_HIDE_TASKBAR_THICKNESS_PX
-                }
-                AutoHideTaskbarPosition::Top => {
-                    requested_client_rect[0].top += AUTO_HIDE_TASKBAR_THICKNESS_PX
-                }
-                AutoHideTaskbarPosition::Right => {
-                    requested_client_rect[0].right -= AUTO_HIDE_TASKBAR_THICKNESS_PX
-                }
-                AutoHideTaskbarPosition::Bottom => {
-                    requested_client_rect[0].bottom -= AUTO_HIDE_TASKBAR_THICKNESS_PX
-                }
+    fn handle_ime_composition_inner(&self, ctx: HIMC, lparam: LPARAM) -> Option<isize> {
+        let lparam = lparam.0 as u32;
+        if lparam == 0 {
+            // Japanese IME may send this message with lparam = 0, which indicates that
+            // there is no composition string.
+            self.with_input_handler(|input_handler| {
+                input_handler.replace_text_in_range(None, "");
+            })?;
+            Some(0)
+        } else {
+            if lparam & GCS_COMPSTR.0 > 0 {
+                let comp_string = parse_ime_composition_string(ctx, GCS_COMPSTR)?;
+                let caret_pos =
+                    (!comp_string.is_empty() && lparam & GCS_CURSORPOS.0 > 0).then(|| {
+                        let pos = retrieve_composition_cursor_position(ctx);
+                        pos..pos
+                    });
+                self.with_input_handler(|input_handler| {
+                    input_handler.replace_and_mark_text_in_range(None, &comp_string, caret_pos);
+                })?;
             }
-        }
-    }
-
-    Some(0)
-}
-
-fn handle_activate_msg(wparam: WPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    let activated = wparam.loword() > 0;
-    let this = state_ptr.clone();
-    state_ptr
-        .executor
-        .spawn(async move {
-            let mut lock = this.state.borrow_mut();
-            if let Some(mut func) = lock.callbacks.active_status_change.take() {
-                drop(lock);
-                func(activated);
-                this.state.borrow_mut().callbacks.active_status_change = Some(func);
+            if lparam & GCS_RESULTSTR.0 > 0 {
+                let comp_result = parse_ime_composition_string(ctx, GCS_RESULTSTR)?;
+                self.with_input_handler(|input_handler| {
+                    input_handler.replace_text_in_range(None, &comp_result);
+                })?;
+                return Some(0);
             }
-        })
-        .detach();
 
-    None
-}
-
-fn handle_create_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    if state_ptr.hide_title_bar {
-        notify_frame_changed(handle);
-        Some(0)
-    } else {
-        None
+            // currently, we don't care other stuff
+            None
+        }
     }
-}
 
-fn handle_dpi_changed_msg(
-    handle: HWND,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let new_dpi = wparam.loword() as f32;
-    let mut lock = state_ptr.state.borrow_mut();
-    lock.scale_factor = new_dpi / USER_DEFAULT_SCREEN_DPI as f32;
-    lock.border_offset.update(handle).log_err();
-    drop(lock);
-
-    let rect = unsafe { &*(lparam.0 as *const RECT) };
-    let width = rect.right - rect.left;
-    let height = rect.bottom - rect.top;
-    // this will emit `WM_SIZE` and `WM_MOVE` right here
-    // even before this function returns
-    // the new size is handled in `WM_SIZE`
-    unsafe {
-        SetWindowPos(
-            handle,
-            None,
-            rect.left,
-            rect.top,
-            width,
-            height,
-            SWP_NOZORDER | SWP_NOACTIVATE,
-        )
-        .context("unable to set window position after dpi has changed")
-        .log_err();
+    /// SEE: https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize
+    fn handle_calc_client_size(
+        &self,
+        handle: HWND,
+        wparam: WPARAM,
+        lparam: LPARAM,
+    ) -> Option<isize> {
+        if !self.hide_title_bar || self.state.borrow().is_fullscreen() || wparam.0 == 0 {
+            return None;
+        }
+
+        let is_maximized = self.state.borrow().is_maximized();
+        let insets = get_client_area_insets(handle, is_maximized, self.windows_version);
+        // wparam is TRUE so lparam points to an NCCALCSIZE_PARAMS structure
+        let mut params = lparam.0 as *mut NCCALCSIZE_PARAMS;
+        let mut requested_client_rect = unsafe { &mut ((*params).rgrc) };
+
+        requested_client_rect[0].left += insets.left;
+        requested_client_rect[0].top += insets.top;
+        requested_client_rect[0].right -= insets.right;
+        requested_client_rect[0].bottom -= insets.bottom;
+
+        // Fix auto hide taskbar not showing. This solution is based on the approach
+        // used by Chrome. However, it may result in one row of pixels being obscured
+        // in our client area. But as Chrome says, "there seems to be no better solution."
+        if is_maximized {
+            if let Some(ref taskbar_position) = self
+                .state
+                .borrow()
+                .system_settings
+                .auto_hide_taskbar_position
+            {
+                // Fot the auto-hide taskbar, adjust in by 1 pixel on taskbar edge,
+                // so the window isn't treated as a "fullscreen app", which would cause
+                // the taskbar to disappear.
+                match taskbar_position {
+                    AutoHideTaskbarPosition::Left => {
+                        requested_client_rect[0].left += AUTO_HIDE_TASKBAR_THICKNESS_PX
+                    }
+                    AutoHideTaskbarPosition::Top => {
+                        requested_client_rect[0].top += AUTO_HIDE_TASKBAR_THICKNESS_PX
+                    }
+                    AutoHideTaskbarPosition::Right => {
+                        requested_client_rect[0].right -= AUTO_HIDE_TASKBAR_THICKNESS_PX
+                    }
+                    AutoHideTaskbarPosition::Bottom => {
+                        requested_client_rect[0].bottom -= AUTO_HIDE_TASKBAR_THICKNESS_PX
+                    }
+                }
+            }
+        }
+
+        Some(0)
     }
 
-    Some(0)
-}
+    fn handle_activate_msg(self: &Rc<Self>, wparam: WPARAM) -> Option<isize> {
+        let activated = wparam.loword() > 0;
+        let this = self.clone();
+        self.executor
+            .spawn(async move {
+                let mut lock = this.state.borrow_mut();
+                if let Some(mut func) = lock.callbacks.active_status_change.take() {
+                    drop(lock);
+                    func(activated);
+                    this.state.borrow_mut().callbacks.active_status_change = Some(func);
+                }
+            })
+            .detach();
 
-/// The following conditions will trigger this event:
-/// 1. The monitor on which the window is located goes offline or changes resolution.
-/// 2. Another monitor goes offline, is plugged in, or changes resolution.
-///
-/// In either case, the window will only receive information from the monitor on which
-/// it is located.
-///
-/// For example, in the case of condition 2, where the monitor on which the window is
-/// located has actually changed nothing, it will still receive this event.
-fn handle_display_change_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    // NOTE:
-    // Even the `lParam` holds the resolution of the screen, we just ignore it.
-    // Because WM_DPICHANGED, WM_MOVE, WM_SIZE will come first, window reposition and resize
-    // are handled there.
-    // So we only care about if monitor is disconnected.
-    let previous_monitor = state_ptr.state.borrow().display;
-    if WindowsDisplay::is_connected(previous_monitor.handle) {
-        // we are fine, other display changed
-        return None;
-    }
-    // display disconnected
-    // in this case, the OS will move our window to another monitor, and minimize it.
-    // we deminimize the window and query the monitor after moving
-    unsafe {
-        let _ = ShowWindow(handle, SW_SHOWNORMAL);
-    };
-    let new_monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) };
-    // all monitors disconnected
-    if new_monitor.is_invalid() {
-        log::error!("No monitor detected!");
-        return None;
+        None
     }
-    let new_display = WindowsDisplay::new_with_handle(new_monitor);
-    state_ptr.state.borrow_mut().display = new_display;
-    Some(0)
-}
 
-fn handle_hit_test_msg(
-    handle: HWND,
-    msg: u32,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    if !state_ptr.is_movable || state_ptr.state.borrow().is_fullscreen() {
-        return None;
+    fn handle_create_msg(&self, handle: HWND) -> Option<isize> {
+        if self.hide_title_bar {
+            notify_frame_changed(handle);
+            Some(0)
+        } else {
+            None
+        }
     }
 
-    let mut lock = state_ptr.state.borrow_mut();
-    if let Some(mut callback) = lock.callbacks.hit_test_window_control.take() {
+    fn handle_dpi_changed_msg(
+        &self,
+        handle: HWND,
+        wparam: WPARAM,
+        lparam: LPARAM,
+    ) -> Option<isize> {
+        let new_dpi = wparam.loword() as f32;
+        let mut lock = self.state.borrow_mut();
+        lock.scale_factor = new_dpi / USER_DEFAULT_SCREEN_DPI as f32;
+        lock.border_offset.update(handle).log_err();
         drop(lock);
-        let area = callback();
-        state_ptr
-            .state
-            .borrow_mut()
-            .callbacks
-            .hit_test_window_control = Some(callback);
-        if let Some(area) = area {
-            return match area {
-                WindowControlArea::Drag => Some(HTCAPTION as _),
-                WindowControlArea::Close => Some(HTCLOSE as _),
-                WindowControlArea::Max => Some(HTMAXBUTTON as _),
-                WindowControlArea::Min => Some(HTMINBUTTON as _),
-            };
+
+        let rect = unsafe { &*(lparam.0 as *const RECT) };
+        let width = rect.right - rect.left;
+        let height = rect.bottom - rect.top;
+        // this will emit `WM_SIZE` and `WM_MOVE` right here
+        // even before this function returns
+        // the new size is handled in `WM_SIZE`
+        unsafe {
+            SetWindowPos(
+                handle,
+                None,
+                rect.left,
+                rect.top,
+                width,
+                height,
+                SWP_NOZORDER | SWP_NOACTIVATE,
+            )
+            .context("unable to set window position after dpi has changed")
+            .log_err();
         }
-    } else {
-        drop(lock);
-    }
 
-    if !state_ptr.hide_title_bar {
-        // If the OS draws the title bar, we don't need to handle hit test messages.
-        return None;
+        Some(0)
     }
 
-    // default handler for resize areas
-    let hit = unsafe { DefWindowProcW(handle, msg, wparam, lparam) };
-    if matches!(
-        hit.0 as u32,
-        HTNOWHERE
-            | HTRIGHT
-            | HTLEFT
-            | HTTOPLEFT
-            | HTTOP
-            | HTTOPRIGHT
-            | HTBOTTOMRIGHT
-            | HTBOTTOM
-            | HTBOTTOMLEFT
-    ) {
-        return Some(hit.0);
+    /// The following conditions will trigger this event:
+    /// 1. The monitor on which the window is located goes offline or changes resolution.
+    /// 2. Another monitor goes offline, is plugged in, or changes resolution.
+    ///
+    /// In either case, the window will only receive information from the monitor on which
+    /// it is located.
+    ///
+    /// For example, in the case of condition 2, where the monitor on which the window is
+    /// located has actually changed nothing, it will still receive this event.
+    fn handle_display_change_msg(&self, handle: HWND) -> Option<isize> {
+        // NOTE:
+        // Even the `lParam` holds the resolution of the screen, we just ignore it.
+        // Because WM_DPICHANGED, WM_MOVE, WM_SIZE will come first, window reposition and resize
+        // are handled there.
+        // So we only care about if monitor is disconnected.
+        let previous_monitor = self.state.borrow().display;
+        if WindowsDisplay::is_connected(previous_monitor.handle) {
+            // we are fine, other display changed
+            return None;
+        }
+        // display disconnected
+        // in this case, the OS will move our window to another monitor, and minimize it.
+        // we deminimize the window and query the monitor after moving
+        unsafe {
+            let _ = ShowWindow(handle, SW_SHOWNORMAL);
+        };
+        let new_monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) };
+        // all monitors disconnected
+        if new_monitor.is_invalid() {
+            log::error!("No monitor detected!");
+            return None;
+        }
+        let new_display = WindowsDisplay::new_with_handle(new_monitor);
+        self.state.borrow_mut().display = new_display;
+        Some(0)
     }
 
-    if state_ptr.state.borrow().is_fullscreen() {
-        return Some(HTCLIENT as _);
-    }
+    fn handle_hit_test_msg(
+        &self,
+        handle: HWND,
+        msg: u32,
+        wparam: WPARAM,
+        lparam: LPARAM,
+    ) -> Option<isize> {
+        if !self.is_movable || self.state.borrow().is_fullscreen() {
+            return None;
+        }
 
-    let dpi = unsafe { GetDpiForWindow(handle) };
-    let frame_y = unsafe { GetSystemMetricsForDpi(SM_CYFRAME, dpi) };
+        let mut lock = self.state.borrow_mut();
+        if let Some(mut callback) = lock.callbacks.hit_test_window_control.take() {
+            drop(lock);
+            let area = callback();
+            self.state.borrow_mut().callbacks.hit_test_window_control = Some(callback);
+            if let Some(area) = area {
+                return match area {
+                    WindowControlArea::Drag => Some(HTCAPTION as _),
+                    WindowControlArea::Close => Some(HTCLOSE as _),
+                    WindowControlArea::Max => Some(HTMAXBUTTON as _),
+                    WindowControlArea::Min => Some(HTMINBUTTON as _),
+                };
+            }
+        } else {
+            drop(lock);
+        }
 
-    let mut cursor_point = POINT {
-        x: lparam.signed_loword().into(),
-        y: lparam.signed_hiword().into(),
-    };
-    unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
-    if !state_ptr.state.borrow().is_maximized() && cursor_point.y >= 0 && cursor_point.y <= frame_y
-    {
-        return Some(HTTOP as _);
-    }
+        if !self.hide_title_bar {
+            // If the OS draws the title bar, we don't need to handle hit test messages.
+            return None;
+        }
 
-    Some(HTCLIENT as _)
-}
+        // default handler for resize areas
+        let hit = unsafe { DefWindowProcW(handle, msg, wparam, lparam) };
+        if matches!(
+            hit.0 as u32,
+            HTNOWHERE
+                | HTRIGHT
+                | HTLEFT
+                | HTTOPLEFT
+                | HTTOP
+                | HTTOPRIGHT
+                | HTBOTTOMRIGHT
+                | HTBOTTOM
+                | HTBOTTOMLEFT
+        ) {
+            return Some(hit.0);
+        }
 
-fn handle_nc_mouse_move_msg(
-    handle: HWND,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    start_tracking_mouse(handle, &state_ptr, TME_LEAVE | TME_NONCLIENT);
-
-    let mut lock = state_ptr.state.borrow_mut();
-    let mut func = lock.callbacks.input.take()?;
-    let scale_factor = lock.scale_factor;
-    drop(lock);
-
-    let mut cursor_point = POINT {
-        x: lparam.signed_loword().into(),
-        y: lparam.signed_hiword().into(),
-    };
-    unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
-    let input = PlatformInput::MouseMove(MouseMoveEvent {
-        position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
-        pressed_button: None,
-        modifiers: current_modifiers(),
-    });
-    let handled = !func(input).propagate;
-    state_ptr.state.borrow_mut().callbacks.input = Some(func);
-
-    if handled { Some(0) } else { None }
-}
+        if self.state.borrow().is_fullscreen() {
+            return Some(HTCLIENT as _);
+        }
+
+        let dpi = unsafe { GetDpiForWindow(handle) };
+        let frame_y = unsafe { GetSystemMetricsForDpi(SM_CYFRAME, dpi) };
 
-fn handle_nc_mouse_down_msg(
-    handle: HWND,
-    button: MouseButton,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    if let Some(mut func) = lock.callbacks.input.take() {
-        let scale_factor = lock.scale_factor;
         let mut cursor_point = POINT {
             x: lparam.signed_loword().into(),
             y: lparam.signed_hiword().into(),
         };
         unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() };
-        let physical_point = point(DevicePixels(cursor_point.x), DevicePixels(cursor_point.y));
-        let click_count = lock.click_state.update(button, physical_point);
-        drop(lock);
-
-        let input = PlatformInput::MouseDown(MouseDownEvent {
-            button,
-            position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
-            modifiers: current_modifiers(),
-            click_count,
-            first_mouse: false,
-        });
-        let result = func(input.clone());
-        let handled = !result.propagate || result.default_prevented;
-        state_ptr.state.borrow_mut().callbacks.input = Some(func);
-
-        if handled {
-            return Some(0);
+        if !self.state.borrow().is_maximized() && cursor_point.y >= 0 && cursor_point.y <= frame_y {
+            return Some(HTTOP as _);
         }
-    } else {
-        drop(lock);
-    };
 
-    // Since these are handled in handle_nc_mouse_up_msg we must prevent the default window proc
-    if button == MouseButton::Left {
-        match wparam.0 as u32 {
-            HTMINBUTTON => state_ptr.state.borrow_mut().nc_button_pressed = Some(HTMINBUTTON),
-            HTMAXBUTTON => state_ptr.state.borrow_mut().nc_button_pressed = Some(HTMAXBUTTON),
-            HTCLOSE => state_ptr.state.borrow_mut().nc_button_pressed = Some(HTCLOSE),
-            _ => return None,
-        };
-        Some(0)
-    } else {
-        None
+        Some(HTCLIENT as _)
     }
-}
 
-fn handle_nc_mouse_up_msg(
-    handle: HWND,
-    button: MouseButton,
-    wparam: WPARAM,
-    lparam: LPARAM,
-    state_ptr: Rc<WindowsWindowStatePtr>,
-) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    if let Some(mut func) = lock.callbacks.input.take() {
+    fn handle_nc_mouse_move_msg(&self, handle: HWND, lparam: LPARAM) -> Option<isize> {
+        self.start_tracking_mouse(handle, TME_LEAVE | TME_NONCLIENT);
+
+        let mut lock = self.state.borrow_mut();
+        let mut func = lock.callbacks.input.take()?;
         let scale_factor = lock.scale_factor;
         drop(lock);
 

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -144,12 +144,12 @@ impl WindowsPlatform {
         }
     }
 
-    pub fn try_get_windows_inner_from_hwnd(&self, hwnd: HWND) -> Option<Rc<WindowsWindowStatePtr>> {
+    pub fn window_from_hwnd(&self, hwnd: HWND) -> Option<Rc<WindowsWindowInner>> {
         self.raw_window_handles
             .read()
             .iter()
             .find(|entry| *entry == &hwnd)
-            .and_then(|hwnd| try_get_window_inner(*hwnd))
+            .and_then(|hwnd| window_from_hwnd(*hwnd))
     }
 
     #[inline]
@@ -434,7 +434,7 @@ impl Platform for WindowsPlatform {
 
     fn active_window(&self) -> Option<AnyWindowHandle> {
         let active_window_hwnd = unsafe { GetActiveWindow() };
-        self.try_get_windows_inner_from_hwnd(active_window_hwnd)
+        self.window_from_hwnd(active_window_hwnd)
             .map(|inner| inner.handle)
     }
 

crates/gpui/src/platform/windows/window.rs 🔗

@@ -28,7 +28,7 @@ use windows::{
 
 use crate::*;
 
-pub(crate) struct WindowsWindow(pub Rc<WindowsWindowStatePtr>);
+pub(crate) struct WindowsWindow(pub Rc<WindowsWindowInner>);
 
 pub struct WindowsWindowState {
     pub origin: Point<Pixels>,
@@ -61,9 +61,9 @@ pub struct WindowsWindowState {
     hwnd: HWND,
 }
 
-pub(crate) struct WindowsWindowStatePtr {
+pub(crate) struct WindowsWindowInner {
     hwnd: HWND,
-    this: Weak<Self>,
+    pub(super) this: Weak<Self>,
     drop_target_helper: IDropTargetHelper,
     pub(crate) state: RefCell<WindowsWindowState>,
     pub(crate) handle: AnyWindowHandle,
@@ -79,7 +79,7 @@ pub(crate) struct WindowsWindowStatePtr {
 impl WindowsWindowState {
     fn new(
         hwnd: HWND,
-        cs: &CREATESTRUCTW,
+        window_params: &CREATESTRUCTW,
         current_cursor: Option<HCURSOR>,
         display: WindowsDisplay,
         min_size: Option<Size<Pixels>>,
@@ -90,9 +90,12 @@ impl WindowsWindowState {
             let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32;
             monitor_dpi / USER_DEFAULT_SCREEN_DPI as f32
         };
-        let origin = logical_point(cs.x as f32, cs.y as f32, scale_factor);
+        let origin = logical_point(window_params.x as f32, window_params.y as f32, scale_factor);
         let logical_size = {
-            let physical_size = size(DevicePixels(cs.cx), DevicePixels(cs.cy));
+            let physical_size = size(
+                DevicePixels(window_params.cx),
+                DevicePixels(window_params.cy),
+            );
             physical_size.to_pixels(scale_factor)
         };
         let fullscreen_restore_bounds = Bounds {
@@ -201,7 +204,7 @@ impl WindowsWindowState {
     }
 }
 
-impl WindowsWindowStatePtr {
+impl WindowsWindowInner {
     fn new(context: &WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result<Rc<Self>> {
         let state = RefCell::new(WindowsWindowState::new(
             hwnd,
@@ -230,13 +233,13 @@ impl WindowsWindowStatePtr {
     }
 
     fn toggle_fullscreen(&self) {
-        let Some(state_ptr) = self.this.upgrade() else {
+        let Some(this) = self.this.upgrade() else {
             log::error!("Unable to toggle fullscreen: window has been dropped");
             return;
         };
         self.executor
             .spawn(async move {
-                let mut lock = state_ptr.state.borrow_mut();
+                let mut lock = this.state.borrow_mut();
                 let StyleAndBounds {
                     style,
                     x,
@@ -248,10 +251,9 @@ impl WindowsWindowStatePtr {
                 } else {
                     let (window_bounds, _) = lock.calculate_window_bounds();
                     lock.fullscreen_restore_bounds = window_bounds;
-                    let style =
-                        WINDOW_STYLE(unsafe { get_window_long(state_ptr.hwnd, GWL_STYLE) } as _);
+                    let style = WINDOW_STYLE(unsafe { get_window_long(this.hwnd, GWL_STYLE) } as _);
                     let mut rc = RECT::default();
-                    unsafe { GetWindowRect(state_ptr.hwnd, &mut rc) }.log_err();
+                    unsafe { GetWindowRect(this.hwnd, &mut rc) }.log_err();
                     let _ = lock.fullscreen.insert(StyleAndBounds {
                         style,
                         x: rc.left,
@@ -275,10 +277,10 @@ impl WindowsWindowStatePtr {
                     }
                 };
                 drop(lock);
-                unsafe { set_window_long(state_ptr.hwnd, GWL_STYLE, style.0 as isize) };
+                unsafe { set_window_long(this.hwnd, GWL_STYLE, style.0 as isize) };
                 unsafe {
                     SetWindowPos(
-                        state_ptr.hwnd,
+                        this.hwnd,
                         None,
                         x,
                         y,
@@ -328,7 +330,7 @@ pub(crate) struct Callbacks {
 }
 
 struct WindowCreateContext {
-    inner: Option<Result<Rc<WindowsWindowStatePtr>>>,
+    inner: Option<Result<Rc<WindowsWindowInner>>>,
     handle: AnyWindowHandle,
     hide_title_bar: bool,
     display: WindowsDisplay,
@@ -362,13 +364,13 @@ impl WindowsWindow {
             main_thread_id_win32,
             disable_direct_composition,
         } = creation_info;
-        let classname = register_wnd_class(icon);
+        register_window_class(icon);
         let hide_title_bar = params
             .titlebar
             .as_ref()
             .map(|titlebar| titlebar.appears_transparent)
             .unwrap_or(true);
-        let windowname = HSTRING::from(
+        let window_name = HSTRING::from(
             params
                 .titlebar
                 .as_ref()
@@ -414,12 +416,11 @@ impl WindowsWindow {
             appearance,
             disable_direct_composition,
         };
-        let lpparam = Some(&context as *const _ as *const _);
         let creation_result = unsafe {
             CreateWindowExW(
                 dwexstyle,
-                classname,
-                &windowname,
+                WINDOW_CLASS_NAME,
+                &window_name,
                 dwstyle,
                 CW_USEDEFAULT,
                 CW_USEDEFAULT,
@@ -428,33 +429,35 @@ impl WindowsWindow {
                 None,
                 None,
                 Some(hinstance.into()),
-                lpparam,
+                Some(&context as *const _ as *const _),
             )
         };
-        // We should call `?` on state_ptr first, then call `?` on hwnd.
-        // Or, we will lose the error info reported by `WindowsWindowState::new`
-        let state_ptr = context.inner.take().unwrap()?;
+
+        // Failure to create a `WindowsWindowState` can cause window creation to fail,
+        // so check the inner result first.
+        let this = context.inner.take().unwrap()?;
         let hwnd = creation_result?;
-        register_drag_drop(state_ptr.clone())?;
+
+        register_drag_drop(&this)?;
         configure_dwm_dark_mode(hwnd, appearance);
-        state_ptr.state.borrow_mut().border_offset.update(hwnd)?;
+        this.state.borrow_mut().border_offset.update(hwnd)?;
         let placement = retrieve_window_placement(
             hwnd,
             display,
             params.bounds,
-            state_ptr.state.borrow().scale_factor,
-            state_ptr.state.borrow().border_offset,
+            this.state.borrow().scale_factor,
+            this.state.borrow().border_offset,
         )?;
         if params.show {
             unsafe { SetWindowPlacement(hwnd, &placement)? };
         } else {
-            state_ptr.state.borrow_mut().initial_placement = Some(WindowOpenStatus {
+            this.state.borrow_mut().initial_placement = Some(WindowOpenStatus {
                 placement,
                 state: WindowOpenState::Windowed,
             });
         }
 
-        Ok(Self(state_ptr))
+        Ok(Self(this))
     }
 }
 
@@ -803,7 +806,7 @@ impl PlatformWindow for WindowsWindow {
 }
 
 #[implement(IDropTarget)]
-struct WindowsDragDropHandler(pub Rc<WindowsWindowStatePtr>);
+struct WindowsDragDropHandler(pub Rc<WindowsWindowInner>);
 
 impl WindowsDragDropHandler {
     fn handle_drag_drop(&self, input: PlatformInput) {
@@ -1084,15 +1087,15 @@ enum WindowOpenState {
     Windowed,
 }
 
-fn register_wnd_class(icon_handle: HICON) -> PCWSTR {
-    const CLASS_NAME: PCWSTR = w!("Zed::Window");
+const WINDOW_CLASS_NAME: PCWSTR = w!("Zed::Window");
 
+fn register_window_class(icon_handle: HICON) {
     static ONCE: Once = Once::new();
     ONCE.call_once(|| {
         let wc = WNDCLASSW {
-            lpfnWndProc: Some(wnd_proc),
+            lpfnWndProc: Some(window_procedure),
             hIcon: icon_handle,
-            lpszClassName: PCWSTR(CLASS_NAME.as_ptr()),
+            lpszClassName: PCWSTR(WINDOW_CLASS_NAME.as_ptr()),
             style: CS_HREDRAW | CS_VREDRAW,
             hInstance: get_module_handle().into(),
             hbrBackground: unsafe { CreateSolidBrush(COLORREF(0x00000000)) },
@@ -1100,54 +1103,58 @@ fn register_wnd_class(icon_handle: HICON) -> PCWSTR {
         };
         unsafe { RegisterClassW(&wc) };
     });
-
-    CLASS_NAME
 }
 
-unsafe extern "system" fn wnd_proc(
+unsafe extern "system" fn window_procedure(
     hwnd: HWND,
     msg: u32,
     wparam: WPARAM,
     lparam: LPARAM,
 ) -> LRESULT {
     if msg == WM_NCCREATE {
-        let cs = lparam.0 as *const CREATESTRUCTW;
-        let cs = unsafe { &*cs };
-        let ctx = cs.lpCreateParams as *mut WindowCreateContext;
-        let ctx = unsafe { &mut *ctx };
-        let creation_result = WindowsWindowStatePtr::new(ctx, hwnd, cs);
-        if creation_result.is_err() {
-            ctx.inner = Some(creation_result);
-            return LRESULT(0);
-        }
-        let weak = Box::new(Rc::downgrade(creation_result.as_ref().unwrap()));
-        unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) };
-        ctx.inner = Some(creation_result);
-        return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) };
+        let window_params = lparam.0 as *const CREATESTRUCTW;
+        let window_params = unsafe { &*window_params };
+        let window_creation_context = window_params.lpCreateParams as *mut WindowCreateContext;
+        let window_creation_context = unsafe { &mut *window_creation_context };
+        return match WindowsWindowInner::new(window_creation_context, hwnd, window_params) {
+            Ok(window_state) => {
+                let weak = Box::new(Rc::downgrade(&window_state));
+                unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) };
+                window_creation_context.inner = Some(Ok(window_state));
+                unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
+            }
+            Err(error) => {
+                window_creation_context.inner = Some(Err(error));
+                LRESULT(0)
+            }
+        };
     }
-    let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowStatePtr>;
+
+    let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowInner>;
     if ptr.is_null() {
         return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) };
     }
     let inner = unsafe { &*ptr };
-    let r = if let Some(state) = inner.upgrade() {
-        handle_msg(hwnd, msg, wparam, lparam, state)
+    let result = if let Some(inner) = inner.upgrade() {
+        inner.handle_msg(hwnd, msg, wparam, lparam)
     } else {
         unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
     };
+
     if msg == WM_NCDESTROY {
         unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) };
         unsafe { drop(Box::from_raw(ptr)) };
     }
-    r
+
+    result
 }
 
-pub(crate) fn try_get_window_inner(hwnd: HWND) -> Option<Rc<WindowsWindowStatePtr>> {
+pub(crate) fn window_from_hwnd(hwnd: HWND) -> Option<Rc<WindowsWindowInner>> {
     if hwnd.is_invalid() {
         return None;
     }
 
-    let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowStatePtr>;
+    let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowInner>;
     if !ptr.is_null() {
         let inner = unsafe { &*ptr };
         inner.upgrade()
@@ -1170,9 +1177,9 @@ fn get_module_handle() -> HMODULE {
     }
 }
 
-fn register_drag_drop(state_ptr: Rc<WindowsWindowStatePtr>) -> Result<()> {
-    let window_handle = state_ptr.hwnd;
-    let handler = WindowsDragDropHandler(state_ptr);
+fn register_drag_drop(window: &Rc<WindowsWindowInner>) -> Result<()> {
+    let window_handle = window.hwnd;
+    let handler = WindowsDragDropHandler(window.clone());
     // The lifetime of `IDropTarget` is handled by Windows, it won't release until
     // we call `RevokeDragDrop`.
     // So, it's safe to drop it here.