diff --git a/CHANGES.md b/CHANGES.md index d20e54ff..dcd5e6bc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,7 @@ - macOS: Fallback to ASCII-capable keyboard layout for handling non-standard input sources - macOS: Check if the application has the necessary permissions. If they are missing, `enigo` will ask the user to grant them. You can change this default behavior with the `Settings` when constructing an `Enigo` struct. - all: Added `Token::Location` and `Token::MainDisplay` mostly for debugging purposes. They allow you to check if your expectations are correct +- win: Added a setting to take the mouse speed and acceleration level into account for relative mouse movement or reliably move the mouse to the expected location. ## Fixed - macOS: No more sleeps!! (Only when the `Enigo` struct is dropped) ([#105](https://github.com/enigo-rs/enigo/issues/105)) diff --git a/src/lib.rs b/src/lib.rs index 03135e27..5415327f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -433,6 +433,7 @@ impl Error for NewConError {} /// Settings for creating the Enigo struct and it's behavior #[allow(dead_code)] // It is not dead code on other platforms +#[allow(clippy::struct_excessive_bools)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Settings { @@ -460,6 +461,13 @@ pub struct Settings { /// The default is true. If the Shift key for example is pressed, /// following simulated input will not be capitalized. pub independent_of_keyboard_state: bool, + /// If this is set to true, the relative mouse motion will be subject to the + /// settings for mouse speed and acceleration level. An end user sets + /// these values using the Mouse application in Control Panel. An + /// application obtains and sets these values with the + /// `windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoA` + /// function. The default value is false. + pub windows_subject_to_mouse_speed_and_acceleration_level: bool, } impl Default for Settings { @@ -474,6 +482,7 @@ impl Default for Settings { release_keys_when_dropped: true, open_prompt_to_get_permissions: true, independent_of_keyboard_state: true, + windows_subject_to_mouse_speed_and_acceleration_level: false, } } } diff --git a/src/win/win_impl.rs b/src/win/win_impl.rs index efbd78ff..4df4a942 100644 --- a/src/win/win_impl.rs +++ b/src/win/win_impl.rs @@ -32,6 +32,7 @@ pub struct Enigo { held: (Vec, Vec), // Currently held keys release_keys_when_dropped: bool, dw_extra_info: usize, + windows_subject_to_mouse_speed_and_acceleration_level: bool, } fn send_input(input: &[INPUT]) -> InputResult<()> { @@ -169,9 +170,39 @@ impl Mouse for Enigo { let y = y as i64; let x = (x * 65535 + w / 2 * x.signum()) / w; let y = (y * 65535 + h / 2 * y.signum()) / h; + // TODO: Check if we should use MOUSEEVENTF_VIRTUALDESK too (MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, x as i32, y as i32) - } else { + } else if self.windows_subject_to_mouse_speed_and_acceleration_level { + // Quote from documentation (http://web.archive.org/web/20241118235853/https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event): + // Relative mouse motion is subject to the settings for mouse speed and + // acceleration level. An end user sets these values using the Mouse application + // in Control Panel. An application obtains and sets these values with the + // SystemParametersInfo function. + // + // The system applies two tests to the specified relative mouse motion when + // applying acceleration. If the specified distance along either the x or y axis + // is greater than the first mouse threshold value, and the mouse acceleration + // level is not zero, the operating system doubles the distance. If the + // specified distance along either the x- or y-axis is greater than the second + // mouse threshold value, and the mouse acceleration level is equal to two, the + // operating system doubles the distance that resulted from applying the first + // threshold test. It is thus possible for the operating system to multiply + // relatively-specified mouse motion along the x- or y-axis by up to four times. + // + // Once acceleration has been applied, the system scales the resultant value by + // the desired mouse speed. Mouse speed can range from 1 (slowest) to 20 + // (fastest) and represents how much the pointer moves based on the distance the + // mouse moves. The default value is 10, which results in no additional + // modification to the mouse motion. + debug!("\x1b[93mRelative mouse move is subject to mouse speed and acceleration level\x1b[0m"); (MOUSEEVENTF_MOVE, x, y) + } else { + // Instead of moving the mouse by a relative amount, we calculate the resulting + // location and move it to the absolute location so it is not subject to mouse + // speed and acceleration levels + debug!("\x1b[93mRelative mouse move is NOT subject to mouse speed and acceleration level\x1b[0m"); + let (current_x, current_y) = self.location()?; + return self.move_mouse(current_x + x, current_y + y, Coordinate::Abs); }; let input = mouse_event(flags, 0, x, y, self.dw_extra_info); send_input(&[input]) @@ -344,6 +375,7 @@ impl Enigo { let Settings { windows_dw_extra_info: dw_extra_info, release_keys_when_dropped, + windows_subject_to_mouse_speed_and_acceleration_level, .. } = settings; @@ -355,6 +387,8 @@ impl Enigo { held, release_keys_when_dropped: *release_keys_when_dropped, dw_extra_info: dw_extra_info.unwrap_or(crate::EVENT_MARKER as usize), + windows_subject_to_mouse_speed_and_acceleration_level: + *windows_subject_to_mouse_speed_and_acceleration_level, }) }