2

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.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
xXTurner
  • 61
  • 9
  • 3
    Use absolute positioning instead. – Jonathan Potter Feb 17 '20 at 19:07
  • There is no API that exposes this information, nor is there documentation about implementation details of mouse movement. As Jonathan pointed out, you don't need either. – IInspectable Feb 17 '20 at 19:10
  • @JonathanPotter I know I can use absolute, but im making this for a full screen application and for me to be able to write that information to the game's window, i have to get equal or greater integrity level (UIPI), and I cannot do that, because of limitations. – xXTurner Feb 17 '20 at 19:15
  • @IInspectable "*There is no API that exposed this information*" - [are you sure](https://stackoverflow.com/a/60241144/65863)? – Remy Lebeau Feb 17 '20 at 19:34
  • @rem: That returns essentially the user setting (a value between 1 and 20). It doesn't give you the actual mouse speed multiplier, nor does it return the acceleration or related timing information in any way that allows you to recreate the effect. – IInspectable Feb 17 '20 at 20:22
  • 1
    @xxt: How does using absolute positioning instead of relative positioning introduce any UIPI constraints? If you cannot cross the UIPI boundary with absolute positioning then you cannot cross it with relative positioning either. If you do not want to use `SendInput`, you'll have to make that clear from your question. As written, there's nothing that hints towards you not wanting to use that API. – IInspectable Feb 17 '20 at 20:27
  • The only difference between using relative and absolute positioning is that you have to keep track of the old position and add the deltas yourself. – Jonathan Potter Feb 17 '20 at 22:21
  • @IInspectable UIPI does not apply to `SendInput()`, it applies to `(Send|Post)Message()`. I believe that is what xXTurner was referring to - sending messages to the game window is restricted, thus wanting to use `SendInput()` as a workaround. – Remy Lebeau Feb 18 '20 at 01:38
  • @RemyLebeau Exactly! – xXTurner Feb 19 '20 at 14:45
  • @xXTurner Does absolute value work for you? Or do you have any concern about using absolute value? – Rita Han Feb 20 '20 at 02:00
  • @RitaHan-MSFT I clearly said that i cannot use absolute mouse positioning because the target's window doesnt have lesser or equal UIPI permissions than my application. So my mouse message cannot be sended to that window – xXTurner Feb 20 '20 at 12:25
  • @xXTurner [`SendInput` is subject to UIPI](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput#remarks) regardless of using relative or absolute value. So the issue is **the target's window doesn't have lesser or equal UIPI permissions than your application**, the SendInput also doesn't help if the UIPI issue can't be solved, right? – Rita Han Feb 21 '20 at 01:56
  • @xXTurner If target application is third-party application which you can't control, running your application as elevated users receive high integrity level, does this help? – Rita Han Feb 21 '20 at 01:59
  • @xXTurner Do you mind sharing why do you want to simulate mouse movement by yourself for third-party application (Or is your own?)? – Rita Han Feb 21 '20 at 02:03
  • @RitaHan-MSFT First, Yes I have read that, but I can use relative, and not absolute (I have some theories. but in general i dont know why that is). Second, My application is running with admin rights (and tbh i dont know much about UIPI). Third, I want to make no recoil script for a game. – xXTurner Feb 24 '20 at 15:15
  • @xXTurner Does the game application also run as admin rights? Since you said **"target's window doesnt have lesser or equal UIPI permissions than my application"**. If it is true, what you are trying to do seems can't be achieved. – Rita Han Feb 25 '20 at 01:59
  • @RitaHan-MSFT I really dont know, it is not asking when started but it acts like a program with admin right (cant really modify it via task mngr). I said that but im just **assuiming** that is, not saying that its actually like that. – xXTurner Feb 25 '20 at 13:54
  • @xXTurner [Each app that requires the administrator access token must prompt for consent](https://learn.microsoft.com/en-us/windows/security/identity-protection/user-account-control/how-user-account-control-works#uac-process-and-interactions). – Rita Han Feb 27 '20 at 02:35
  • @xXTurner And you can check process integrity level using [`Process Explorer`](https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer). Find the process and right click and select `properties`, at `security tab` check the `flags` of `Mandatory Label`. Take `CMD.exe` as an example, when you start it using current user without admin rights the flag is `Mandatory Label\Medium Mandatory Level`. When you start it as admin rights the flag is `Mandatory Label\High Mandatory Level`. – Rita Han Feb 27 '20 at 02:39
  • @xXTurner Note, to check the security tab of a process running as admin rights you need to run Process Explorer as admin rights. – Rita Han Feb 27 '20 at 02:42

2 Answers2

1

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.

Lonami
  • 5,945
  • 2
  • 20
  • 38
0
  • 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.

Rita Han
  • 9,574
  • 1
  • 11
  • 24
  • You are not correct this is not the calculation for the actual movement, this is **only** for the mouse acceleration option.. – xXTurner Mar 02 '20 at 13:14
  • @xXTurner But it determines the **distance** based on mouse threshold value and mouse speed. I don't see any related to acceleration. – Rita Han Mar 03 '20 at 08:50
  • This is the distance with mouse acceleration **ON** it does not say anything about with it off – xXTurner Mar 04 '20 at 17:51
  • @xXTurner After check document of [mouse_event](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event) which use "**mouse acceleration level**". So you consider **mouse speed** in my answer is **mouse acceleration**, right? But system doubles the distance requires two conditions as described in my answer. If one of conditions is not meet, system will not doubles the distance. Do you find any problem during your tests? – Rita Han Mar 06 '20 at 09:50
  • Sorry i dont think you understood me, i meant that this _functionality_ with the 2 and 4 times multiplication is only present when mouse acceleration option is on as well, if its off this is irrelevant, so its not applied to your mouse movement. Hope i was more clear this time – xXTurner Mar 07 '20 at 13:51
  • @xXTurner Do you mean `Settings -> Mouse -> Additional mouse options -> Pointer Options -> Motion -> Enhance pointer precision` checked is "mouse acceleration option is on"? – Rita Han Mar 09 '20 at 08:19
  • I'm also confused by the "formula". My mousespeed from SPI_GETMOUSESPEED is 20, the 3 values from SPI_GETMOUSE is 6, 10, 1 So from the above "formula", any movement above 6 will always double by 2. – Robin Mar 10 '20 at 11:21
  • @Robin SPI_GETMOUSESPEED value is from `Settings -> Mouse -> Additional mouse options -> Pointer Options -> Motion -> Select a pointer speed`. It range from 1 (slowest) to 20 (fastest). – Rita Han Mar 11 '20 at 09:02
  • @Robin And I do such a test: Send mouse relative movement to target window using `SendInput` and check the movement via `WM_MOUSEMOVE` message in target window. I keep `dy` unchanged and only increase `dx`. But no matter what I sent: less than first mouse threshold, more than first mouse threshold and more than second mouse threshold, I always get exact value what I sent. No double no multiply by four. So I need to confirm if the document need some update. Do you get same result with mine? – Rita Han Mar 11 '20 at 09:11
  • @Robin BTW, setting mouse acceleration to `1` will enable the **Enhance pointer precision** option. Disable this option will cause two mouse thresholds and acceleration to all `0`. – Rita Han Mar 11 '20 at 09:16
  • @RitaHan-MSFT Yea, thats is it. (Talking about your comment where you tagged me) – xXTurner Mar 13 '20 at 09:14