win32: account for mouse wheel speed setting
Also adds a method to toggle this behavior during runtime.
This commit is contained in:
parent
b13b39aa0b
commit
317d62fb93
5 changed files with 96 additions and 18 deletions
|
|
@ -42,22 +42,23 @@ use windows_sys::Win32::UI::Input::{
|
||||||
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||||
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect, GetCursorPos,
|
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect, GetCursorPos,
|
||||||
GetMenu, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW,
|
GetMenu, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW,
|
||||||
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos, TranslateMessage,
|
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos, SystemParametersInfoW,
|
||||||
CREATESTRUCTW, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG,
|
TranslateMessage, CREATESTRUCTW, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO,
|
||||||
MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PM_REMOVE, PT_TOUCH, QS_ALLINPUT, RI_MOUSE_HWHEEL,
|
MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PM_REMOVE, PT_TOUCH, QS_ALLINPUT,
|
||||||
RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE,
|
RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
|
||||||
SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM, WMSZ_BOTTOMLEFT,
|
SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE,
|
||||||
WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT,
|
SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT,
|
||||||
WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE,
|
WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE,
|
||||||
WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
|
WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO,
|
||||||
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS,
|
WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION,
|
||||||
WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL,
|
WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
|
||||||
WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
|
WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE,
|
||||||
WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN,
|
WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN,
|
||||||
WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND,
|
WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS,
|
||||||
WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING,
|
WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH,
|
||||||
WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
|
WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW,
|
||||||
WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP,
|
||||||
|
WS_VISIBLE,
|
||||||
};
|
};
|
||||||
use winit_core::application::ApplicationHandler;
|
use winit_core::application::ApplicationHandler;
|
||||||
use winit_core::cursor::{CustomCursor, CustomCursorSource};
|
use winit_core::cursor::{CustomCursor, CustomCursorSource};
|
||||||
|
|
@ -92,6 +93,13 @@ use crate::window::{InitData, Window};
|
||||||
use crate::window_state::{CursorFlags, ImeState, WindowFlags, WindowState};
|
use crate::window_state::{CursorFlags, ImeState, WindowFlags, WindowState};
|
||||||
use crate::{raw_input, util};
|
use crate::{raw_input, util};
|
||||||
|
|
||||||
|
// This is defined in `winuser.h` as a macro that expands to `UINT_MAX`
|
||||||
|
const WHEEL_PAGESCROLL: u32 = u32::MAX;
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa#:~:text=SPI_GETWHEELSCROLLLINES
|
||||||
|
const DEFAULT_SCROLL_LINES_PER_WHEEL_DELTA: isize = 3;
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa#:~:text=SPI_GETWHEELSCROLLCHARS
|
||||||
|
const DEFAULT_SCROLL_CHARACTERS_PER_WHEEL_DELTA: isize = 3;
|
||||||
|
|
||||||
pub(crate) struct WindowData {
|
pub(crate) struct WindowData {
|
||||||
pub window_state: Arc<Mutex<WindowState>>,
|
pub window_state: Arc<Mutex<WindowState>>,
|
||||||
pub event_loop_runner: Rc<EventLoopRunner>,
|
pub event_loop_runner: Rc<EventLoopRunner>,
|
||||||
|
|
@ -1636,9 +1644,26 @@ unsafe fn public_window_callback_inner(
|
||||||
|
|
||||||
update_modifiers(window, userdata);
|
update_modifiers(window, userdata);
|
||||||
|
|
||||||
|
let scroll_lines_multiplier = if userdata.window_state_lock().use_system_wheel_speed {
|
||||||
|
let mut scroll_lines = DEFAULT_SCROLL_LINES_PER_WHEEL_DELTA;
|
||||||
|
let _ = SystemParametersInfoW(
|
||||||
|
SPI_GETWHEELSCROLLLINES,
|
||||||
|
0,
|
||||||
|
&mut scroll_lines as *mut isize as *mut c_void,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
if scroll_lines as u32 == WHEEL_PAGESCROLL {
|
||||||
|
// TODO: figure out how to handle page scrolls
|
||||||
|
scroll_lines = DEFAULT_SCROLL_LINES_PER_WHEEL_DELTA;
|
||||||
|
}
|
||||||
|
scroll_lines
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
userdata.send_window_event(window, WindowEvent::MouseWheel {
|
userdata.send_window_event(window, WindowEvent::MouseWheel {
|
||||||
device_id: None,
|
device_id: None,
|
||||||
delta: LineDelta(0.0, value),
|
delta: LineDelta(0.0, value * scroll_lines_multiplier as f32),
|
||||||
phase: TouchPhase::Moved,
|
phase: TouchPhase::Moved,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1653,9 +1678,23 @@ unsafe fn public_window_callback_inner(
|
||||||
|
|
||||||
update_modifiers(window, userdata);
|
update_modifiers(window, userdata);
|
||||||
|
|
||||||
|
let scroll_characters_multiplier =
|
||||||
|
if userdata.window_state_lock().use_system_wheel_speed {
|
||||||
|
let mut scroll_characters = DEFAULT_SCROLL_CHARACTERS_PER_WHEEL_DELTA;
|
||||||
|
let _ = SystemParametersInfoW(
|
||||||
|
SPI_GETWHEELSCROLLCHARS,
|
||||||
|
0,
|
||||||
|
&mut scroll_characters as *mut isize as *mut c_void,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
scroll_characters
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
userdata.send_window_event(window, WindowEvent::MouseWheel {
|
userdata.send_window_event(window, WindowEvent::MouseWheel {
|
||||||
device_id: None,
|
device_id: None,
|
||||||
delta: LineDelta(value, 0.0),
|
delta: LineDelta(value * scroll_characters_multiplier as f32, 0.0),
|
||||||
phase: TouchPhase::Moved,
|
phase: TouchPhase::Moved,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,15 @@ pub trait WindowExtWindows {
|
||||||
/// Supported starting with Windows 11 Build 22000.
|
/// Supported starting with Windows 11 Build 22000.
|
||||||
fn set_corner_preference(&self, preference: CornerPreference);
|
fn set_corner_preference(&self, preference: CornerPreference);
|
||||||
|
|
||||||
|
/// Sets if the reported [`winit_core::event::WindowEvent::MouseWheel`] event
|
||||||
|
/// should account for scroll speed system settings.
|
||||||
|
///
|
||||||
|
/// The default scroll speed on Windows is 3 lines/characters per scroll,
|
||||||
|
/// this will be 1 if you set it to false.
|
||||||
|
///
|
||||||
|
/// The default is `true`.
|
||||||
|
fn set_use_system_scroll_speed(&self, should_use: bool);
|
||||||
|
|
||||||
/// Get the raw window handle for this [`Window`] without checking for thread affinity.
|
/// Get the raw window handle for this [`Window`] without checking for thread affinity.
|
||||||
///
|
///
|
||||||
/// Window handles in Win32 have a property called "thread affinity" that ties them to their
|
/// Window handles in Win32 have a property called "thread affinity" that ties them to their
|
||||||
|
|
@ -398,6 +407,11 @@ impl WindowExtWindows for dyn CoreWindow + '_ {
|
||||||
window.set_corner_preference(preference)
|
window.set_corner_preference(preference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_use_system_scroll_speed(&self, should_use: bool) {
|
||||||
|
let window = self.cast_ref::<Window>().unwrap();
|
||||||
|
window.set_use_system_scroll_speed(should_use)
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn window_handle_any_thread(
|
unsafe fn window_handle_any_thread(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||||
|
|
@ -459,6 +473,7 @@ pub struct WindowAttributesWindows {
|
||||||
pub(crate) title_background_color: Option<Color>,
|
pub(crate) title_background_color: Option<Color>,
|
||||||
pub(crate) title_text_color: Option<Color>,
|
pub(crate) title_text_color: Option<Color>,
|
||||||
pub(crate) corner_preference: Option<CornerPreference>,
|
pub(crate) corner_preference: Option<CornerPreference>,
|
||||||
|
pub(crate) use_system_wheel_speed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WindowAttributesWindows {
|
impl Default for WindowAttributesWindows {
|
||||||
|
|
@ -478,6 +493,7 @@ impl Default for WindowAttributesWindows {
|
||||||
title_background_color: None,
|
title_background_color: None,
|
||||||
title_text_color: None,
|
title_text_color: None,
|
||||||
corner_preference: None,
|
corner_preference: None,
|
||||||
|
use_system_wheel_speed: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -610,6 +626,18 @@ impl WindowAttributesWindows {
|
||||||
self.corner_preference = Some(corners);
|
self.corner_preference = Some(corners);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets if the reported [`winit_core::event::WindowEvent::MouseWheel`] event
|
||||||
|
/// should account for scroll speed system settings.
|
||||||
|
///
|
||||||
|
/// The default scroll speed on Windows is 3 lines/characters per scroll,
|
||||||
|
/// this will be 1 if you set it to false.
|
||||||
|
///
|
||||||
|
/// The default is `true`.
|
||||||
|
pub fn with_use_system_scroll_speed(mut self, should_use: bool) -> Self {
|
||||||
|
self.use_system_wheel_speed = should_use;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformWindowAttributes for WindowAttributesWindows {
|
impl PlatformWindowAttributes for WindowAttributesWindows {
|
||||||
|
|
|
||||||
|
|
@ -344,6 +344,11 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_use_system_scroll_speed(&self, should_use: bool) {
|
||||||
|
self.window_state_lock().use_system_wheel_speed = should_use;
|
||||||
|
}
|
||||||
|
|
||||||
fn set_icon(&self, mut new_icon: Icon, icon_type: IconType) {
|
fn set_icon(&self, mut new_icon: Icon, icon_type: IconType) {
|
||||||
if let Some(icon) = new_icon.cast_ref::<RgbaIcon>() {
|
if let Some(icon) = new_icon.cast_ref::<RgbaIcon>() {
|
||||||
let icon = match WinIcon::from_rgba(icon) {
|
let icon = match WinIcon::from_rgba(icon) {
|
||||||
|
|
@ -1303,6 +1308,7 @@ impl InitData<'_> {
|
||||||
win.set_skip_taskbar(self.win_attributes.skip_taskbar);
|
win.set_skip_taskbar(self.win_attributes.skip_taskbar);
|
||||||
win.set_window_icon(self.attributes.window_icon.clone());
|
win.set_window_icon(self.attributes.window_icon.clone());
|
||||||
win.set_taskbar_icon(self.win_attributes.taskbar_icon.clone());
|
win.set_taskbar_icon(self.win_attributes.taskbar_icon.clone());
|
||||||
|
win.set_use_system_scroll_speed(self.win_attributes.use_system_wheel_speed);
|
||||||
|
|
||||||
let attributes = self.attributes.clone();
|
let attributes = self.attributes.clone();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ pub(crate) struct WindowState {
|
||||||
pub dragging: bool,
|
pub dragging: bool,
|
||||||
|
|
||||||
pub skip_taskbar: bool,
|
pub skip_taskbar: bool,
|
||||||
|
|
||||||
|
pub use_system_wheel_speed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -187,6 +189,8 @@ impl WindowState {
|
||||||
dragging: false,
|
dragging: false,
|
||||||
|
|
||||||
skip_taskbar: false,
|
skip_taskbar: false,
|
||||||
|
|
||||||
|
use_system_wheel_speed: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -256,3 +256,4 @@ changelog entry.
|
||||||
- On macOS, fixed redundant `SurfaceResized` event at window creation.
|
- On macOS, fixed redundant `SurfaceResized` event at window creation.
|
||||||
- On macOS, don't panic on monitors with unknown bit-depths.
|
- On macOS, don't panic on monitors with unknown bit-depths.
|
||||||
- On macOS, fixed crash when closing the window on macOS 26+.
|
- On macOS, fixed crash when closing the window on macOS 26+.
|
||||||
|
- On Windows, account for mouse wheel lines per scroll setting for `WindowEvent::MouseWheel`.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue