Win10 Fall Creators Update breaks Mouse Warping

One addition to the “raw input fails” thing: For the guy who reported the problem at Yamagi Quake II, GetLastError() (called directly after RegisterRawInputDevices() returns FALSE) returns error code 87 (ERROR_INVALID_PARAMETER). No idea what to make of that, it’s not like Microsoft documents the possible errors of RegisterRawInputDevices()

Anyway, regarding the broken warping: I debugged the SDL2 relative mouse mode by warping code a bit, and it seems like the most relevant part is in SDL_PrivateSendMouseMotion() - you may wanna open this in a separate window to follow the following text in code. I’m currently at the “no idea how this could ever work anywhere” point, but maybe that’s just because I’m a bit hungry and have looked at the code too much. Will document my thoughts here, would be nice if anyone could look at it and see if it makes sense… I don’t have a definite fix either.
I added a few calls to SDL_Log() in SDL_PrivateSendMouseMotion() and found the following situation:

  • fn arg relative is false
  • fn args x and y as expected (640x480 window => center of window is 320,240 => x and y are around those values, depending on where I moved the mouse. So if I move the mouse to the right, x is something like 263)
  • (mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp) is true

This means that the following things happen:

  • in line 276ff, SDL_WarpMouseInWindow(window, center_x, center_y); is called to move the cursor back to the window’s center
  • xrel and yrel (which are later set in the corresponding fields of the event) are set in lines 302 and 303, like xrel = x - mouse->last_x;
    • those values have weird values in the events, causing the broken mouse view etc
  • in lines 389/390, mouse->last_x = x; is set

Example of how this causes the problem, looking just at the X coordinate, assuming a 640x480 window (center_x is 320):
Assume you move the mouse to the right. x == 340. By default mouse->last_x == 320 (see line 282), so xrel = 20 - great, like expected so far. However, mouse->last_x = x; is set now, so it’s 340.
So if in next frame x == 340 again, xrel will be 0 (because mouse->last_x is 340) - no further movement. If you moved the mouse slightly slower, x might even be 330 and xrel will be -10 (instead of 10, like it should be if mouse->last_x == 320 as it should be after warping).

So at this point, as usual when debugging something, the question is: How could this ever work, and why does it work on Linux? I haven’t debugged on Linux to figure that out…

Ignoring the “why did it ever work” part - how can this be fixed?
Very brief testing suggests, that reordering lines 281-285 like the following helps:

        mouse->last_x = center_x;
        mouse->last_y = center_y;
        if (x == center_x && y == center_y) {
            return 0;
        }

(the first two lines were in the if()-block before)
That way xrel = x - mouse->last_x; works as intended - and after that mouse->last_* is still overridden with the actual x and y values in lines 389/390 (might be good in case this is used elsewhere and expects to have the x/y values of the last event?)

However, I’m not sure what happens if the system somehow generates two mouse events in one frame and the second is generated before the first one calls SDL_WarpMouseInWindow() - in that case overriding mouse->last_* with center_* is incorrect…
No idea if that ever happens (and often enough to be annoying, if this happens every once in a while people probably won’t even notice one frame of twice as fast movement)…