This is implemented via a low-level keyboard hook. Unfortunately, this is
rather invasive, but it's how Microsoft recommends that it be done [0].
We want to do as little as possible in the hook, so we only intercept a few
crucial modifier keys there, while leaving other keys to the normal event
processing flow.
We will only install this hook if SDL_HINT_GRAB_KEYBOARD=1, which is not
the default. This will reduce any compatibility concerns to just the SDL
applications that explicitly ask for this behavior.
We also remove the hook when the grab is terminated to ensure that we're
not unnecessarily staying involved in key event processing when it's not
required anymore.
[0]: https://docs.microsoft.com/en-us/windows/win32/dxtecharts/disabling-shortcut-keys-in-games
GetAsyncKeyState() and GetRawInputData() report the state of the physical
buttons without applying the user's primary/secondary mouse button swap
preference. Swap the buttons returned from these functions, so we expose a
consistent view of the buttons to SDL callers. This new behavior also matches
the behavior of macOS and X11 backends.
See the Remarks section of the GetAsyncKeyState() function on MSDN.
We should only perform the VK_LEFT, VK_UP, etc. mapping if none of the other
special mappings apply. This allows the scancode normalization for the number
pad to take place as intended.
- Displays may have been added, removed or changed and all cached monitor
handles are invalidated as a result.
- Display events are handled in three steps:
1. Mark all currently know displays as invalid
2. Enumerate all displays, adding new ones and marking known displays as valid
3. Remove all displays still invalid after enumeration
- Display connect/disconnect events are sent when displays are added or removed
after initial setup
On some systems, GetClipCursor() impacts performance when called frequently, so only call it every once in a while to make sure we haven't lost our capture.
Alex Denisov
When using Win10 on-screen keyboard (tooltip.exe), the left and right cursor keys in it do not produce SDLK_LEFT and SDLK_RIGHT events.
Windows messages generated by the on-screen keyboard, for some reason, have their scancodes set to zeroes. Here is the log from Spy++:
WM_KEYDOWN nVirtKey:VK_LEFT cRepeat:1 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:0 fUp:0
WM_KEYUP nVirtKey:VK_LEFT cRepeat:1 ScanCode:00 fExtended:0 fAltDown:0 fRepeat:1 fUp:1
Regular physical keyboard produces VK_LEFT (ScanCode:4B) and VK_RIGHT (ScanCode:4D) which are interpreted correctly.
With on-screen keyboard, the switch statement in VKeytoScancode() does not check for VK_LEFT and VK_RIGHT, returning SDL_SCANCODE_UNKNOWN, which in turn does not get mapped to anything (because the scan codes are zeroes).
superfury
I notice that, somehow, when locking the mouse into place(using SDL_SetRelativeMouseMode), somehow at least the movement information gets through to both mouse movement and touch movement events?
My app handles both, so when moving a touched finger accross the app(using RDP from an Android device) I see the mouse moving inside the app when it shouldn't(meaning that the touch movement is ignored properly by the app(press-location dependant) but the mouse movement is still performed due to the mouse movement events)?
Windows generates fake raw mouse events for touchscreens for compatibility
with legacy apps that predate touch support in Windows. We already handle
touch events explicitly, so drop the synthetic events to avoid duplicates.
bplu4t2f
When num lock is on, the scancode reported for numpad 5 is SDL_SCANCODE_KP_5, which is correct. However, when num lock is off, windows reports the VK_CLEAR virtual key code, which is incorrectly translated into SDL_SCANCODE_CLEAR inside of the VKeytoScancode(WPARAM vkey) function.
Touch device types include SDL_TOUCH_DEVICE_DIRECT (a touch screen with window-relative coordinates for touches), SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE (a trackpad-style device with absolute device coordinates), and SDL_TOUCH_DEVICE_INDIRECT_RELATIVE (a trackpad-style device with screen cursor-relative coordinates).
Phone screens are an example of a direct device type. Mac trackpads are the indirect-absolute touch device type. The Apple TV remote is an indirect-relative touch device type.
This prevents us from clearing the clip rect globally when another application has set it.
There's also an experimental change to regularly update the clip rect for a window defensively, in case someone else has reset it. It works well, but I don't know if it's cheap enough to call as frequently as it would be called now, and might have other undesirable side effects.
Also fixed whitespace and SDL coding style
Alexei
On WM_WINDOWPOSCHANGED event, WIN_UpdateClipCursor() is called. SDL_WINDOW_INPUT_FOCUS is set even when the mouse pointer is not inside the SDL window and therefore ClipCursor(&rect) is called. When dragging the window and rect.bottom=800 (i.e. the bottom edge of the screen) the SDL window is clipped to the bottom of the screen and it is not possible to move it back to the center of the screen.
Daniel Gibson
Sorry, but it seems like Microsoft didn't fix the issue properly.
I just updated my Win10 machine, it now is Version 1803, Build 17134.1
I tested with SDL2 2.0.7 (my workaround was released with 2.0.8) and still got
lots of events that directly undid the prior "real" events - just like before.
(See simple testcase in attachement)
By default it sets SDL_HINT_MOUSE_RELATIVE_MODE_WARP - which triggered (and on my machine still triggers) the buggy behavior. You can start it with -raw, then it'll not set that hint and the events will be as expected.
The easiest way to see the difference is looking at the window title, which shows accumulated X and Y values: If you just move your mouse to the right, in -raw mode the number just increases. In non-raw mode (using mouse warping) it stays around 0.
I also had a WinAPI-only testcase: https://gist.github.com/DanielGibson/b5b033c67b9137f0280af9fc53352c68
It just calls SetCursorPos(320,240); on each WM_MOUSEMOVE event, and it also
logs all those events to a mouseevents.log textfile.
This log indeed looks a bit different since the latest Win10 update: It seems like all those events with x=320 y=240 do arrive - but only after I stopped moving the mouse - even though the cursor seems to be moved back every frame (or so).
So moving the mouse to the right gives X coordinates like
330, 325, 333, 340, 330, ...
and then when stopping movement I get lots of events with X coordinate 320
SDL now builds with gcc 7.2 with the following command line options:
-Wall -pedantic-errors -Wno-deprecated-declarations -Wno-overlength-strings --std=c99
Elis?e Maurer
The attached minimal program sets the SDL_HINT_MOUSE_RELATIVE_MODE_WARP to 1, enables relative mouse mode then logs all SDL_MOUSEMOTION xrel values as they happen.
When moving the mouse exclusively to the right:
* On a Windows 10 installation before Fall Creators update (for instance, Version 10.0.15063 Build 15063), only positive values are reported, as expected
* On a Windows 10 installation after Fall Creators update (for instance, Version 10.0.16299 Update 16299), a mix of positive and negative values are reported.
3 different people have reproduced this bug and have confirmed it started to happen after the Fall Creators update was installed. It happens with SDL 2.0.7 as well as latest default branch as of today.
It seems like some obscure (maybe unintended) Windows behavior change? Haven't been able to pin it down more yet.
(To force-upgrade a Windows installation to the Fall Creators update, you can use the update assistant at https://www.microsoft.com/en-us/software-download/windows10)
Eric Wasylishen
Broken GetCursorPos / SetCursorPos based games on Win 10 fall creators are not limited to SDL.. I just tested winquake.exe (original 1997 exe) and it now has "jumps" in the mouse input if you try to look around in a circle. It uses GetCursorPos/SetCursorPos by default. Switching WinQuake to use directinput (-dinput flag) seems to get rid of the jumps.
Daniel Gibson
A friend tested on Win10 1607 (which is before the Fall Creators Update) and the the bug doesn't occur there, so the regression that SetCursorPos() doesn't reliably generate mouse events was indeed introduced with that update.
I even reproduced it in a minimal WinAPI-only application (https://gist.github.com/DanielGibson/b5b033c67b9137f0280af9fc53352c68), the weird thing is that if you don't do anything each "frame" (i.e. the mainloop only polls the events and does nothing else), there are a lot of mouse events with the coordinates you passed to SetCursorPos(), but when sleeping for 10ms in each iteration of the mainloop, those events basically don't happen anymore. Which is bad, because in games the each iteration of the mainloop usually takes 16ms..
I have a patch now that I find acceptable.
It checks for the windows version with RtlGetVersion() (https://msdn.microsoft.com/en-us/library/windows/hardware/ff561910.aspx) and only if it's >= Win10 build 16299, enables the workaround.
All code is in video/windows/SDL_windowsevents.c
and the workaround is, that for each WM_MOUSEMOVE event, "if(isWin10FCUorNewer && mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp)", an addition mouse move event is generated with the coordinates of the center of the screen
(SDL_SendMouseMotion(data->window, mouseID, 0, center_x, center_y);) - which is exactly what would happen if windows generated those reliably itself.
This will cause SDL_PrivateSendMouseMotion() to set mouse->last_x = center_x; and mouse->last_y = center_y; so the next mouse relative mouse event will be calculated correctly.
If Microsoft ever fixes this bug, the IsWin10FCUorNewer() function would have to
be adjusted to also check for a maximum version, so the workaround is then disabled again.
Robert Turner
SDL_windowsevents.c contains code to retrieve the x and y coordinate for a requested hit test. It does this as follows:
POINT winpoint = { (int) LOWORD(lParam), (int) HIWORD(lParam) };
LOWORD(lParam) does not correctly mask off high bits that are set if the point is on a second (or third, etc.) monitor. This effectively offsets the x-coordinate by a large value.
MSDN documentation suggests that LOWORD() and HIWORD() are the wrong macros for the task, instead suggesting we should be doing something like the following:
POINT winpoint = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
Testing this change on my Windows 10 machine with 2 monitors gives the correct results.
- Fixing rendering borderless window. Need to force windows to send a WM_NCCALCSIZE then return 0 for non-client area size.
- Adding WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX to borderless windows, for reasons noted in comments.
- Fix SetupWindowData() setting SDL_WINDOW_BORDERLESS. This was being cleared at window creation, causing hanlding for the first WM_NCCALCSIZE message to fail
Martijn Courteaux
I implemented precise scrolling events. I have been through all the folders in /src/video/[platform] to implement where possible. This works on OS X, but I can't speak for others. Build farm will figure that out, I guess. I think this patch should introduce precise scrolling on OS X, Wayland, Mir, Windows, Android, Nacl, Windows RT.
The way I provide precise scrolling events is by adding two float fields to the SDL_MouseWheelScrollEvent datastructure, called "preciseX" and "preciseY". The old integer fields "x" and "y" are still present. The idea is that every platform specific code normalises the scroll amounts and forwards them to the SDL_SendMouseWheel function. It is this function that will now accumulate these (using a static variable, as I have seen how it was implemented in the Windows specific code) and once we hit a unit size, set the traditional integer "x" and "y" fields.
I believe this is pretty solid way of doing it, although I'm not the expert here.
There is also a fix in the patch for a typo recently introduced, that might need to be taken away by the time anybody merges this in. There is also a file in Nacl which I have stripped a horrible amount of trailing whitespaces. (Leave that part out if you want).
Alexey
Seems to be a missing functionality. I want to set an icon from RC file. I cant pass MAKEINTRESOURCE(X) string to SDL_RegisterApp() cause string returned by MAKEINTRESOURCE string is not actually a string and SDL_strlen will crash. Moreover LoadImage seems to be loading wrong icon size. LoadIcon seems to be fine.