@@ -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);
@@ -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.