Here's the information required to patch the incorrect implementation.
Abstract:
- 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
queue.
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
NtSendUserInput
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@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
errors.
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
screen).
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.
=EOF=
That's a good first step into using proper winsock!
WBR,
Aleksey Bragin.
On Jul 26, 2009, at 10:00 AM, cgutman(a)svn.reactos.org wrote:
> Author: cgutman
> Date: Sun Jul 26 08:00:32 2009
> New Revision: 42225
>
> URL: http://svn.reactos.org/svn/reactos?rev=42225&view=rev
> Log:
> - Begin using ws2help_new
> - I have tested this with various applications in ROS
> - Part 2 of 2
>
> Added:
> trunk/reactos/dll/win32/ws2help/ (props changed)
> - copied from r42178, trunk/reactos/dll/win32/ws2help_new/
> Removed:
> trunk/reactos/dll/win32/ws2help_new/
Hello all,
Now that also the mail migration is more or less completed, let me introduce
you to IServ.
IServ is a Groupware solution, which we will be using now for managing
E-Mail accounts, appointments, contact data and a common FTP space. It will
be available to everybody, who is qualified for a @reactos.org E-Mail
address. (so basically developers with SVN access and official testers)
For your @reactos.org E-Mail address, IServ provides a 250 MB mailbox. You
can send and receive E-Mails over the Web interface and also over POP3, IMAP
and SMTP.
The Web interface also offers an option to set up a redirection if you want
to use another mailbox for incoming mails.
Files can be uploaded either over the Web interface or over FTP.
Our IServ Web interface is available at http://iserv.reactos.org. The POP3,
IMAP, SMTP and FTP services can be accessed over the same domain.
If you have an @reactos.org E-Mail address, but no IServ account yet, I will
shortly send you your account name along with an initial password.
Your account name will also serve as your primary E-Mail address from now
on. (i.e. colin.finck(a)reactos.org). Of course, your previous E-Mail aliases
will continue to work. They are linked to your primary address now.
As most of you used a redirection from your @reactos.org address to another
mailbox, I've set up these redirections in the Web interface already. You
can change them anytime.
A big thanks go to Martin von Wittich and the IServ GmbH, who provided us
with a site license for IServ and helped us with the setup.
Also thank our translators Amine Khaldi, Gabriel Ilardi and Alex/care2debug
for the French, Italian, Spanish and Russian translations of the Web
interface.
Best regards,
Colin
This explains why we are using a trampoline function and not just
typecast there... Is there a reason you changed this?
- Thomas
dchapyshev(a)svn.reactos.org wrote:
> - /* NOTE: Don't use Function directly since the callback signature
> - differs. This might cause problems on certain platforms... */
> - Status = RtlQueueWorkItem(InternalWorkItemTrampoline,
> - WorkItemContext,
Hello,
I've heard many question about a branch I was working on recently,
arwinss, so I'd like to write some explanation (you may skip the
boring history part of the message)
Arwinss is a rewrite (the most advanced so far out of all previous
attempts) of some parts of the Win32 subsystem, namely the USER32 and
GDI32 API interfaces, along with the kernel counterpart win32k.sys.
The kernel32.dll, csrss, and other parts remain in their present
condition, and getting bugfixes if they come in the way of a rewrite.
[History and reasoning part starts here]
Why rewrite and not fix an existing Win32 subsystem? Timo, James and
all our other great developers were and are doing a great work. I put
a considerable amount of time in it to fix problems too. But, since
our project is a volunteer-driven one, everyone has a real life, real
work to do, and is not able to sit 24 hrs researching Windows
internal structures, inventing new algorithms, trying out thousands
of applications, not to say about graphics drivers.
Time is ticking, Win32 is improving, but most annoying bugs are there
for years - e.g. vmware installer hang, Firefox move the mouse bug,
drawing glitches, concurrency hacks in the code ("if (!a) /* someone
else was faster than us and already freed it */"), probable heap
misusage (relying on our heap implementation for desktop heaps) and
heap memory corruptions (I kept trying to update rtl/heaps to the
newest Wine code - and always failed without any obvious reason),
inability to change video mode on the fly, and the list can go on.
So I thought, that something should be done with it. I would even
want to trade off some speed gain in favor of stability (optimizing
is an enjoyable task which could be done later).
After teaming up with Stefan, we created an nwin32 branch - a totally
stubbed out win23k, user32 and gdi32. They had exactly matched
exports, and win32k had exactly same system calls and Windows 2003
SP1's win32k.sys.
However, due to really huge amount of work, the branch didn't went
farther than trying to boot Windows 2003 with it and see a few
stubbed functions being called.
Since then I started thinking on an alternative design of a Win32
subsystem. The idea turned out to be very simple, and is based on the
following questions:
- Why put so much effort into keeping the internal win32k system
calls interface the same as in Windows, why put so much effort in
converting to internal Windows structures, if we don't have something
working first?
- Why base on a stoneage Wine's code which James and Christoph
occasionally sync, and all my attempts to get more people into this
boring task failed?
- Why not use achievements of our closest project - Wine?
[End of reasoning, fancy stuff starts]
The result came by itself: Try to build up a win32 subsystem based as
much on Wine code as possible, and using Wine's modular design.
Before publicly announcing it, I have spent a month actually trying
all that stuff, and surprisingly it went very well, and a nice
byproduct: support of remote sessions via X Windows.
Proof of concept screenshots are here:
http://www.reactos.org/media/screenshots/2009/arw_xlog1.jpghttp://www.reactos.org/media/screenshots/2009/arw_xlog1.jpg
Noone has ever done this before: This is Windows 2003 inside VMware,
running my custom Win32 subsystem, with an X graphics driver module,
communicating with an X Server running in the host OS (Windows XP,
XMing X windows server), and with ReactOS's winlogon.exe and
msgina.dll (for ease of debugging and source code availability)!
Let's go straight to the architecture:
GDI32.dll and USER32.dll are ported Wine usermode code, with very few
modifications. GDI32 and USER32 depend on two things: Gdi and User
driver, and a server.
Gdi and User driver is a loadable DLL, which provides an abstraction
of a graphics driver in the system through a certain set of APIs. A
typical example of such a driver is winex11.drv, which routes all
drawing to the X Windows "client". However, this is not very useful
for a local system which has a Windows NT architecture, where there
is no need for remote windows displaying.
The server. GDI32 and USER32 rely on the server for managing all
global information. In Wine, the server is run as a usermode
wineserver.exe process, which communicates with gdi32/user32 via
custom RPC, and emulates quite a lof of stuff which Windows NT kernel
provides by default. My decision was to convert the RPC protocol from
a slow interprocess filedescriptors-based unix-specific invocations
to a fast system calls to win32k module. This way, win32k contains
small part (~300 kb vs 1.5Mb+) of wineserver's source code, which
deals with windows, window classes, atoms, windows stations, desktops
and other totally platform/implementation independent stuff. It will
be reduced further, because I even ported their own object manager
for win32 objects, which will be exchange to our native ntoskrnl's
object manager soon, when the testing phase is over.
The graphics driver, kernelmode part. As I said above, it's not very
convinient to fully rely on X Windows for graphics output, because
it's just not possible to run it in an NT-based OS which has no Win32
subsystem. Thus, I decided to create a totally native gdi/user driver
("winent.drv"), which would rely on win32k module to actually perform
all drawing. However, compared to our current implementation, the
drawing would be way more simple. For example, if currently LineTo
operation in win32k involves complex PATHOBJ, maintaining graphics
cursor -- all of that in a strictly windows compatible way because
apps depend on it, in this alternative win32k, LineTo is a simple
line drawing function: RosGdiLineTo(pDc, x1, y1, x2, y2). Same
applies to other functions, including e.g. text output, where all
rendering happens using a usermode freetype.dll, and win32k just
needs to display bitmap glyphs got from gdi32.
Don't be scared if you don't understand all that right away. I will
put up a good short summary, along with a TODO and FIXME lists, and a
HACKING guide.
Just a few cool facts about the new win32 subsystem:
- Based on a solid, very well maintained codebase, used by commercial
vendors.
- Ease of updating from upstream (vendor importing)
- Tested against more than 12 000 Windows applications (http://
appdb.winehq.org)
- ...
I think it's enough for the first introduction.
WBR,
Aleksey Bragin.