test/teststreaming.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 10 Dec 2017 09:17:33 -0800
changeset 11758 c70cf178aacb
parent 10737 3406a0f8b041
permissions -rw-r--r--
Workaround for bug 3931 - spurious SDL_MOUSEMOTION events with SDL_HINT_MOUSE_RELATIVE_MODE_WARP 1 since Windows 10 Fall Creators update

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.
     1 /*
     2   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     3 
     4   This software is provided 'as-is', without any express or implied
     5   warranty.  In no event will the authors be held liable for any damages
     6   arising from the use of this software.
     7 
     8   Permission is granted to anyone to use this software for any purpose,
     9   including commercial applications, and to alter it and redistribute it
    10   freely.
    11 */
    12 /********************************************************************************
    13  *                                                                              *
    14  * Running moose :) Coded by Mike Gorchak.                                      *
    15  *                                                                              *
    16  ********************************************************************************/
    17 
    18 #include <stdlib.h>
    19 #include <stdio.h>
    20 
    21 #ifdef __EMSCRIPTEN__
    22 #include <emscripten/emscripten.h>
    23 #endif
    24 
    25 #include "SDL.h"
    26 
    27 #define MOOSEPIC_W 64
    28 #define MOOSEPIC_H 88
    29 
    30 #define MOOSEFRAME_SIZE (MOOSEPIC_W * MOOSEPIC_H)
    31 #define MOOSEFRAMES_COUNT 10
    32 
    33 SDL_Color MooseColors[84] = {
    34     {49, 49, 49, 255}, {66, 24, 0, 255}, {66, 33, 0, 255}, {66, 66, 66, 255},
    35     {66, 115, 49, 255}, {74, 33, 0, 255}, {74, 41, 16, 255}, {82, 33, 8, 255},
    36     {82, 41, 8, 255}, {82, 49, 16, 255}, {82, 82, 82, 255}, {90, 41, 8, 255},
    37     {90, 41, 16, 255}, {90, 57, 24, 255}, {99, 49, 16, 255}, {99, 66, 24, 255},
    38     {99, 66, 33, 255}, {99, 74, 33, 255}, {107, 57, 24, 255}, {107, 82, 41, 255},
    39     {115, 57, 33, 255}, {115, 66, 33, 255}, {115, 66, 41, 255}, {115, 74, 0, 255},
    40     {115, 90, 49, 255}, {115, 115, 115, 255}, {123, 82, 0, 255}, {123, 99, 57, 255},
    41     {132, 66, 41, 255}, {132, 74, 41, 255}, {132, 90, 8, 255}, {132, 99, 33, 255},
    42     {132, 99, 66, 255}, {132, 107, 66, 255}, {140, 74, 49, 255}, {140, 99, 16, 255},
    43     {140, 107, 74, 255}, {140, 115, 74, 255}, {148, 107, 24, 255}, {148, 115, 82, 255},
    44     {148, 123, 74, 255}, {148, 123, 90, 255}, {156, 115, 33, 255}, {156, 115, 90, 255},
    45     {156, 123, 82, 255}, {156, 132, 82, 255}, {156, 132, 99, 255}, {156, 156, 156, 255},
    46     {165, 123, 49, 255}, {165, 123, 90, 255}, {165, 132, 82, 255}, {165, 132, 90, 255},
    47     {165, 132, 99, 255}, {165, 140, 90, 255}, {173, 132, 57, 255}, {173, 132, 99, 255},
    48     {173, 140, 107, 255}, {173, 140, 115, 255}, {173, 148, 99, 255}, {173, 173, 173, 255},
    49     {181, 140, 74, 255}, {181, 148, 115, 255}, {181, 148, 123, 255}, {181, 156, 107, 255},
    50     {189, 148, 123, 255}, {189, 156, 82, 255}, {189, 156, 123, 255}, {189, 156, 132, 255},
    51     {189, 189, 189, 255}, {198, 156, 123, 255}, {198, 165, 132, 255}, {206, 165, 99, 255},
    52     {206, 165, 132, 255}, {206, 173, 140, 255}, {206, 206, 206, 255}, {214, 173, 115, 255},
    53     {214, 173, 140, 255}, {222, 181, 148, 255}, {222, 189, 132, 255}, {222, 189, 156, 255},
    54     {222, 222, 222, 255}, {231, 198, 165, 255}, {231, 231, 231, 255}, {239, 206, 173, 255}
    55 };
    56 
    57 Uint8 MooseFrames[MOOSEFRAMES_COUNT][MOOSEFRAME_SIZE];
    58 
    59 SDL_Renderer *renderer;
    60 int frame;
    61 SDL_Texture *MooseTexture;
    62 SDL_bool done = SDL_FALSE;
    63 
    64 void quit(int rc)
    65 {
    66     SDL_Quit();
    67     exit(rc);
    68 }
    69 
    70 void UpdateTexture(SDL_Texture *texture, int frame)
    71 {
    72     SDL_Color *color;
    73     Uint8 *src;
    74     Uint32 *dst;
    75     int row, col;
    76     void *pixels;
    77     int pitch;
    78 
    79     if (SDL_LockTexture(texture, NULL, &pixels, &pitch) < 0) {
    80         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't lock texture: %s\n", SDL_GetError());
    81         quit(5);
    82     }
    83     src = MooseFrames[frame];
    84     for (row = 0; row < MOOSEPIC_H; ++row) {
    85         dst = (Uint32*)((Uint8*)pixels + row * pitch);
    86         for (col = 0; col < MOOSEPIC_W; ++col) {
    87             color = &MooseColors[*src++];
    88             *dst++ = (0xFF000000|(color->r<<16)|(color->g<<8)|color->b);
    89         }
    90     }
    91     SDL_UnlockTexture(texture);
    92 }
    93 
    94 void
    95 loop()
    96 {
    97     SDL_Event event;
    98 
    99     while (SDL_PollEvent(&event)) {
   100         switch (event.type) {
   101         case SDL_KEYDOWN:
   102             if (event.key.keysym.sym == SDLK_ESCAPE) {
   103                 done = SDL_TRUE;
   104             }
   105             break;
   106         case SDL_QUIT:
   107             done = SDL_TRUE;
   108             break;
   109         }
   110     }
   111 
   112     frame = (frame + 1) % MOOSEFRAMES_COUNT;
   113     UpdateTexture(MooseTexture, frame);
   114 
   115     SDL_RenderClear(renderer);
   116     SDL_RenderCopy(renderer, MooseTexture, NULL, NULL);
   117     SDL_RenderPresent(renderer);
   118 
   119 #ifdef __EMSCRIPTEN__
   120     if (done) {
   121         emscripten_cancel_main_loop();
   122     }
   123 #endif
   124 }
   125 
   126 int
   127 main(int argc, char **argv)
   128 {
   129     SDL_Window *window;
   130     SDL_RWops *handle;
   131 
   132     /* Enable standard application logging */
   133     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
   134 
   135     if (SDL_Init(SDL_INIT_VIDEO) < 0) {
   136         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
   137         return 1;
   138     }
   139 
   140     /* load the moose images */
   141     handle = SDL_RWFromFile("moose.dat", "rb");
   142     if (handle == NULL) {
   143         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Can't find the file moose.dat !\n");
   144         quit(2);
   145     }
   146     SDL_RWread(handle, MooseFrames, MOOSEFRAME_SIZE, MOOSEFRAMES_COUNT);
   147     SDL_RWclose(handle);
   148 
   149 
   150     /* Create the window and renderer */
   151     window = SDL_CreateWindow("Happy Moose",
   152                               SDL_WINDOWPOS_UNDEFINED,
   153                               SDL_WINDOWPOS_UNDEFINED,
   154                               MOOSEPIC_W*4, MOOSEPIC_H*4,
   155                               SDL_WINDOW_RESIZABLE);
   156     if (!window) {
   157         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create window: %s\n", SDL_GetError());
   158         quit(3);
   159     }
   160 
   161     renderer = SDL_CreateRenderer(window, -1, 0);
   162     if (!renderer) {
   163         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create renderer: %s\n", SDL_GetError());
   164         quit(4);
   165     }
   166 
   167     MooseTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, MOOSEPIC_W, MOOSEPIC_H);
   168     if (!MooseTexture) {
   169         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s\n", SDL_GetError());
   170         quit(5);
   171     }
   172 
   173     /* Loop, waiting for QUIT or the escape key */
   174     frame = 0;
   175 
   176 #ifdef __EMSCRIPTEN__
   177     emscripten_set_main_loop(loop, 0, 1);
   178 #else
   179     while (!done) {
   180         loop();
   181         }
   182 #endif
   183 
   184     SDL_DestroyRenderer(renderer);
   185 
   186     quit(0);
   187     return 0;
   188 }
   189 
   190 /* vi: set ts=4 sw=4 expandtab: */