简体   繁体   中英

SendInput() mouse movement calculation?

I'm playing around with SendInput() for mouse movement, and it wants INPUT structures. I'm using relative movement, and in the docs about this struct, in the Remarks it says:

Relative mouse motion is subject to the effects of the mouse speed and the two-mouse threshold values.

So my question is, what and how exactly is this calculated (what is the formula)? It is not specified.

I tried searching about it, but with no luck. Hope someone knows.

I need this information because I'm trying to make the mouse movement be independant of the actual Windows cursor speed. And in the docs it clearly says this is affected by those values.

  • Relative value

Relative mouse motion is subject to the effects of the mouse speed and the two-mouse threshold values. So my question is, what and how exactly is this calculated (what is the formula)? It is not specified.

How to calculate is stated in remarks of MOUSEINPUT structure document as below:

The system applies two tests to the specified relative mouse movement. If the specified distance along either the x or y axis is greater than the first mouse threshold value, and the mouse speed is not zero, the 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 speed is equal to two, the system doubles the distance that resulted from applying the first threshold test. It is thus possible for the system to multiply specified relative mouse movement along the x or y axis by up to four times.

To prevent system from doubling the distance or multiply specified relative mouse movement along the x or y axis by up to four times, for example, you can keep the specified relative mouse movement less than or equal to the the first mouse threshold value, and set the mouse speed to zero. Please test to see if this meets your requirement.

As for UIPI limitation, since your application already runs as admin rights, it has the high mandatory level, so this will not be a problem unless your target application runs as system integrity level .

  • Absolute values

And without UIPI limitation, you can also use absolute values which is not subject to the effects of the mouse speed and the two-mouse threshold values.

From the documentation for mouse_event function :

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.

I believe these refer to the following two queries:

  • SPI_GETMOUSE ( = 0x0003 ),

    Retrieves the two mouse threshold values and the mouse acceleration. The pvParam parameter must point to an array of three integers that receives these values.

    The three values are the first threshold, second threshold, and acceleration.

  • SPI_GETMOUSESPEED ( = 0x0070 ),

    Retrieves the current mouse speed. The mouse speed determines how far the pointer will move based on the distance the mouse moves. The pvParam parameter must point to an integer that receives a value which ranges between 1 (slowest) and 20 (fastest). A value of 10 is the default.

    This seems to follow a somewhat strange formula to scale the movement.

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.

Unfortunately I have not been able to crack this one out (it applies when "enhance pointer precision" is enabled in the "mouse properties"). It definitely does not seem to just double the value.

For values three times as high as the threshold, the scaling seems accurate (it doubles your input), but beyond that the factor of 2 keeps growing. These were tested with a mouse speed of 10:

  • For your input of 20, the final distance traveled is 40.
  • For 40, the final distance is 93 and not 80 (~1.16 higher).
  • For 80, the final distance is 201 and not 160 (~1.25 higher).
  • For 160, the final distance is 416 and not 320 (~1.30 higher).
  • For 320, the final distance is 846 and not 640 (~1.32 higher).
  • For higher values the ratio actual / expected hardly grows.

To make matters worse, with a speed of 20 (assuming the speed scaling as described later is the same):

  • For your input of 20, the final distance traveled is 66.
  • For 40, the final distance is 173 and not 280 (~0.62 lower).
  • For 80, the final distance is 388 and not 560 (~0.69 lower).
  • For 160, the final distance is 416 and not 320 (~0.73 lower).
  • For 320, the final distance is 1677 and not 2240 (~0.75 higher).
  • For higher values, my monitor is not large enough.

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.

From my testing, it appears 10 means no scaling (scale of 1). Every value above 10 seems to increase the scale factor by 1/4, and every value below 10 decreases it by 1/8 (except for the value of 2 where it would have reached 0, so the scaling is 1/16, and the scaling at 1 unit seems to be 0/32). The following algorithm (Python) predicts where the mouse ends if "enhance pointer precision" is disabled:

import ctypes

speed = ctypes.c_int()
ctypes.windll.user32.SystemParametersInfoA(0x0070, 0, ctypes.byref(speed), 0)  # SPI_GETMOUSESPEED
speed = out.value

# x is how much you want to move the mouse in the x axis (the same is true for the y axis)

if speed == 1:
    predict_x = x // 32
elif speed == 2:
    predict_x = x // 16
elif speed <= 10:
    predict_x = (x * (speed - 2)) // 8
else:
    predict_x = (x * (speed - 6)) // 4

If you wanted to move the mouse exactly x , you would need the reverse code:

if speed == 1:
    used_x = x * 32
elif speed == 2:
    used_x = x * 16
elif speed <= 10:
    used_x = int(round((x * 8) / (speed - 2)))
else:
    used_x = int(round((x * 4) / (speed - 6)))

My conclusion is it's not worth it to use relative movement for accurate mouse movements. You are better off querying the current position, adding your delta, and setting the absolute position. Otherwise you have to query mouse configuration which can change any time and is designed to feel good, not be exact in a single movement.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM