[ros-dev] [BUG] Utterly incorrect implementation of input device coordinates.

codeforfood at pulseforce.com codeforfood at pulseforce.com
Thu Jul 23 21:54:15 CEST 2009

Here's the information required to patch the incorrect implementation.

- SendInput()/NtUserSendInput() broken:
--- Relative hardware mouse input "working", but not well implemented due
to lack of modifying the travel distance using cursor acceleration and
movement speeds (I've provided info on this as well below), input-virtual
vs screen-real coordinates (even in the relative moves), etc. The two
coordinate spaces are not the same thing!
--- Absolute hardware mouse input broken (used by things like
touchscreens), by a complete misunderstanding of how the virtual input
device coordinate space works (it's NOT in ANY way a 1:1 map that's
translatable to the screen, but the function uses it this way everywhere!)
read below for full info since this is the serious problem.
- SetCursorPos() broken (should NOT be using SendInput() since that's
pointless and ineffective and is meant for the hardware input queue, we
already have the desired coordinates and should move the cursor directly;
and even its use of SendInput() is incorrect since it should FIRST convert
the desired SCREEN coordinates to absolute-INPUT-coordinate-space), BUT
since it's using the broken SendInput() (which currently doesn't know that
absolute input coordinates have NOTHING to do with screen coordinates) it
works correctly and will place cursors at the right location. This will
have to be changed when SendInput() is patched; preferrably to a direct
cursor update (elaborated in the recap below, before the log), rather than
the current indirect/slow approach which places a message in the serial
(as in serial-processing, first-come-first-serve) mouse-input-translation

So to recap:
- Need to improve relative movement (to handle acceleration and mouse
speed modifiers)
- Need to change the code everywhere where you've been thinking that input
coordinates are the same as screen coordinates (most likely only within
SetCursorPos(), and SendInput()/NtUserSendInput())
- Absolute cursor placement inside SendInput() needs to be rewritten to
convert absolute input-space coordinates to absolute screen-space
coordinates (quite an easy change) using the algorithm I provide below,
which is what Windows uses internally.
- SetCursorPos() needs to COMPLETELY stop using SendInput() and instead
manipulate the cursor X/Y struct directly, or equivalent methods, instead
of injecting itself into the input queue. Alternatively, if it keeps using
the input queue, it needs a reverse-algorithm to turn the desired
screen-coordinates into virtual inputspace-coordinates, such as int
mouseinput_abs_x_or_y_pos = (((65536 * screen_x_or_y_pos) /
screen_width_or_height) + 1). The +1 is necessary for proper rounding.
However, I see no reason to use the input queue within this function as
that involves much more work than simply writing the cursor's location
directly (which is what SendInput EVENTUALLY gets around to doing after
LOTS of processing, and saving on cycles/jumps/calls is GOOD ;)).

All details on how to fix these things are in the below log. Let me know
if there's any questions.

Log (most unrelated discussions/events snipped and long breaks between
explaining different details have been separated with "..."):
 19:56   Yewbacca        . I am sure you've implemented SetCursorPos()
incorrectly. Checking your source code for it at cursor.c:280. This
function is SUPPOSED to take a SCREEN X/Y coordinate (ie X=1400, Y=800 on
a 1440x900 screen, and then moves the cursor location directly, without
emulating mouse movement.
 19:57   Yewbacca        . Instead, your version calls SendInput() with
the x/y unmodified, that's awful. SendInput()'s absolute space is a
virtual desktop square that stretches from 0-65535 in each direction and
will not even be remotely close to what the intended destination is.
 19:57   _0_             . sounds like a patch is in order Yewbacca
 19:57   _0_             . :P
 19:58  +encoded         . Yewbacca, file bug report!
 19:58   Yewbacca        . _0_ Well I'd have to know how you're writing
the cursor position inside NtSendUserInput etc.
 19:58   Yewbacca        . Or file a bug report, I'll do that.
 20:01   Yewbacca        . Related to this, Windows actually uses a
slightly unexpected algorithm for its SendInput() absolute-mode
coordinates-to-screen conversion.
 20:01   Yewbacca        . int destx_or_y = (screen_width_or_height *
x_or_y_abs_coord) / 65536
 20:01   Yewbacca        . If you want pixel precise SendInput() that
works as on windows you'd have to verify this. I'll bug report that too.
 20:02  +encoded         . <3 Yewbacca
 20:02   Yewbacca        . :)
 20:02  +encoded         . you can ofcourse see the source code of
 20:03   Yewbacca        . Yeah I was about to check that to make sure
it's not already correct
 20:04  +encoded         . its in \subsystems\win32\win32k\ntuser\input.c
 20:04   Yewbacca        . Ah thanks, that saves a slow grep.
 20:05  +encoded         . wait opps
 20:05                   . garrythefish_
[n=fisher at unaffiliated/garrythefish] has joined #reactos
 20:05  +encoded         . oh well, nvm me
 20:05   garrythefish_   . community choice award today. hope you guys
win. :D
 20:10   Yewbacca        . Okay after brief confusion I noticed that
IntMouseInput() contains the actual logic for mouse-type input to
SendInput(), input.c:1081. Time to check it.
 20:14   Yewbacca        . Okay now I'm really confused. I read through
IntMouseInput() from input.c:1081-1333 and unless I missed a call to a
driver-coordinates-to-screen-coordinates somewhere, this function is
miscoded as well. That'd mean that SetCursorPos() would work correctly
since SendInput() would work incorrectly, and that normal SendInput()
calls that expect a 0 to 65535 range would not work at all.
 20:15   Yewbacca        . It really seems to directly write the pointers
for cursor X and Y with the values it has received
 20:15   Yewbacca        . with no conversion whatsoever
 20:15   Yewbacca        . even though the incoming values are in a 0 to
65535 range
 20:15   Yewbacca        . of that virtual space
 20:16   Yewbacca        . Basically for anyone unfamiliar with it, the
driver implements a virtual space for the cursor to either move
relatively within, or absolutely, to allow things like touchscreens to
function easily (they'd just have to SendInput() their current position
as a percentage of the 65535 range).
 20:16   Yewbacca        . So all incoming absolute-mode coordinates must
be converted to screen coordinates, and microsoft does it with int
destx_or_y = (screen_width_or_height * x_or_y_abs_coord) / 65536
 20:17   Yewbacca        . I'm going to re-read it again to see if I
missed some conversion call. If not, that'll be 2 reports. Quite easy
fixes luckily.
 20:18  +encoded         . <3 Yewbacca
 20:18   Yewbacca        . I think the reason you got by this far is
because the HARDWARE mouse sends coordinates relatively
 20:18   Yewbacca        . The problem'd only appear with absolute input
like touchscreens. And since SendInput()'s absolute mode is broken that
means program's SetCursorPos() would work, so it's doubly broken and
would only cause problems for absolute hardware-input (again,
touchscreens mainly).
 20:18   Yewbacca        . :P
 20:19   Yewbacca        . Reading again
 20:20  +encoded         . Yewbacca, we can use someone like you who has
knowledge of windows internals
 20:20   Yewbacca        . Maybe, right now I'm tied up with some major
work :)
 20:28   Yewbacca        . Alright I've finished checking everything.
IntMouseInput() stretches from input.c:1081-1333 and is the function
that's called when NtUserSendInput()/SendInput() receives a mouse-type
event. At line 1140 it grabs the pointer to the cursor's X/Y struct with
IntGetCursorLocation(WinSta, &MousePos). Then we get to the significant
 20:29   Yewbacca        . At lines 1146-1150 it incorrectly writes the
virtual x/y of the INPUT coordinate system right into the memory holding
the SCREEN x/y cursor location
 20:30   Yewbacca        . At lines 1151-1155 it handles relative movement
but doesn't take into account cursor acceleration and movement speed
(those settings we all love in the control panel->mouse), which in turn
can increase the movement distance as much as 4 times the input value.
 20:31   Yewbacca        . Those two behaviours right there would need to
be patched. The first should determine the destination on the screen
through int destx_or_y = (screen_width_or_height * x_or_y_abs_coord) /
65536. The latter should implement handling for cursor acceleration
(check MSDN, it describes acceleration on SendInput()).
 20:32   Yewbacca        . And lastly, SetCursorPos() (forgot which file
it's in) needs to *completely* skip the SendInput() baloney that it's
doing right now (http://pastebin.com/d2db76985) and just directly grab
the cursor X/Y pointer and update it in a simple assignment, AFTER
checking that it's in range of course (otherwise clamping it to the
 20:33  +Lone_Rifle      . Yewbacca, suggest that you subscribe to ros-dev
and retype or copy-paste your findings there, it's quite a bit of text to
go through in one sitting
 20:33   Yewbacca        . Oh yeah and if you wanna be 100% complete, also
implement a check for ClipCursor() and don't allow the cursor to move
outside that, no matter if it's from hardware input or SetCursorPos(). If
it's outside, it should be clamped to the nearest inside value.
 20:34   Yewbacca        . Lone_Rifle Yeah, I agree and was going to
submit it. I'll do that now.
 20:37   Yewbacca        . Acceleration is described at
http://msdn.microsoft.com/en-us/library/ms646273(VS.85).aspx (info for
the MOUSEINPUT struct). I'll submit this log to the mailing list.


More information about the Ros-dev mailing list