Commit e7c2cf107a7bc63ab10e232383acc87c232763fe

Sam Lantinga 2019-07-15T09:36:53

Fixed bug 4704 - SDL_HINT_ANDROID_SEPERATE_MOUSE_AND_TOUCH on Windows? 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)?

diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c
index 0ecb3cc..98b7298 100644
--- a/src/events/SDL_mouse.c
+++ b/src/events/SDL_mouse.c
@@ -156,6 +156,8 @@ SDL_MouseInit(void)
     SDL_AddHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS,
                         SDL_MouseTouchEventsChanged, mouse);
 
+    mouse->was_touch_mouse_events = SDL_FALSE; /* no touch to mouse movement event pending */
+
     mouse->cursor_shown = SDL_TRUE;
 
     return (0);
@@ -244,7 +246,7 @@ SDL_SetMouseFocus(SDL_Window * window)
 
 /* Check to see if we need to synthesize focus events */
 static SDL_bool
-SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
+SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate, SDL_bool send_mouse_motion)
 {
     SDL_Mouse *mouse = SDL_GetMouse();
     SDL_bool inWindow = SDL_TRUE;
@@ -275,7 +277,9 @@ SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
 #ifdef DEBUG_MOUSE
             printf("Mouse left window, synthesizing move & focus lost event\n");
 #endif
-            SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
+            if (send_mouse_motion) {
+                SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
+            }
             SDL_SetMouseFocus(NULL);
         }
         return SDL_FALSE;
@@ -286,7 +290,9 @@ SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
         printf("Mouse entered window, synthesizing focus gain & move event\n");
 #endif
         SDL_SetMouseFocus(window);
-        SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
+        if (send_mouse_motion) {
+            SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
+        }
     }
     return SDL_TRUE;
 }
@@ -296,7 +302,7 @@ SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int 
 {
     if (window && !relative) {
         SDL_Mouse *mouse = SDL_GetMouse();
-        if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate)) {
+        if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate, (mouseID == SDL_TOUCH_MOUSEID) ? SDL_FALSE : SDL_TRUE)) {
             return 0;
         }
     }
@@ -446,6 +452,8 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ
         event.motion.type = SDL_MOUSEMOTION;
         event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
         event.motion.which = mouseID;
+        /* Set us pending (or clear during a normal mouse movement event) as having triggered */
+        mouse->was_touch_mouse_events = (mouseID == SDL_TOUCH_MOUSEID)? SDL_TRUE : SDL_FALSE;
         event.motion.state = mouse->buttonstate;
         event.motion.x = mouse->x;
         event.motion.y = mouse->y;
@@ -530,7 +538,7 @@ SDL_PrivateSendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state
 
     /* We do this after calculating buttonstate so button presses gain focus */
     if (window && state == SDL_PRESSED) {
-        SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
+        SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE);
     }
 
     if (buttonstate == mouse->buttonstate) {
@@ -580,7 +588,7 @@ SDL_PrivateSendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state
 
     /* We do this after dispatching event so button releases can lose focus */
     if (window && state == SDL_RELEASED) {
-        SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
+        SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE);
     }
 
     return posted;
diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h
index 9f88d40..b44d40d 100644
--- a/src/events/SDL_mouse_c.h
+++ b/src/events/SDL_mouse_c.h
@@ -94,6 +94,7 @@ typedef struct
     int double_click_radius;
     SDL_bool touch_mouse_events;
     SDL_bool mouse_touch_events;
+    SDL_bool was_touch_mouse_events; /* Was a touch-mouse event pending? */
 
     /* Data for double-click tracking */
     int num_clickstates;
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 2c9401b..9e95e33 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -359,14 +359,32 @@ ShouldGenerateWindowCloseOnAltF4(void)
     return !SDL_GetHintBoolean(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, SDL_FALSE);
 }
 
+static SDL_bool isVistaOrNewer = SDL_FALSE;
 /* Win10 "Fall Creators Update" introduced the bug that SetCursorPos() (as used by SDL_WarpMouseInWindow())
    doesn't reliably generate WM_MOUSEMOVE events anymore (see #3931) which breaks relative mouse mode via warping.
    This is used to implement a workaround.. */
 static SDL_bool isWin10FCUorNewer = SDL_FALSE;
 
+/* Checks a mouse or raw packet for touch indication.
+   returns: 0 for not touch input, 1 for touch input.
+*/
+static LPARAM
+GetMessageExtraInfoAndCheckMousePacketTouch(int *checkTouch) {
+    LPARAM extrainfo = GetMessageExtraInfo();
+    /* Mouse data (ignoring synthetic mouse events generated for touchscreens) */
+    /* Versions below Vista will set the low 7 bits to the Mouse ID and don't use bit 7:
+       Check bits 8-32 for the signature (which will indicate a Tablet PC Pen or Touch Device).
+       Only check bit 7 when Vista and up(Cleared=Pen, Set=Touch(which we need to filter out)),
+       when the signature is set. The Mouse ID will be zero for an actual mouse. */
+    *checkTouch = (!(((extrainfo & 0x7F) && (isVistaOrNewer ? (extrainfo & 0x80) : 1)) || ((extrainfo & 0xFFFFFF00) == 0xFF515700)));
+    return extrainfo;
+}
+
 LRESULT CALLBACK
 WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
+    int checkTouch = -1; /* Default to -1 for not yet loaded */
+    LPARAM extrainfo;    /* The extra info when checkTouch >= 0. */
     SDL_WindowData *data;
     LRESULT returnCode = -1;
 
@@ -494,9 +512,10 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     case WM_MOUSEMOVE:
         {
             SDL_Mouse *mouse = SDL_GetMouse();
+            extrainfo = GetMessageExtraInfoAndCheckMousePacketTouch(&checkTouch); /* load */
             if (!mouse->relative_mode || mouse->relative_mode_warp) {
                 /* Only generate mouse events for real mouse */
-                if ((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) != MOUSEEVENTF_FROMTOUCH) {
+                if (((extrainfo & MOUSEEVENTF_FROMTOUCH) != MOUSEEVENTF_FROMTOUCH) && checkTouch) {
                     SDL_SendMouseMotion(data->window, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
                     if (isWin10FCUorNewer && mouse->relative_mode_warp) {
                         /* To work around #3931, Win10 bug introduced in Fall Creators Update, where
@@ -527,8 +546,11 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     case WM_XBUTTONDBLCLK:
         {
             SDL_Mouse *mouse = SDL_GetMouse();
+            if (checkTouch < 0) {
+                extrainfo = GetMessageExtraInfoAndCheckMousePacketTouch(&checkTouch);
+            }
             if (!mouse->relative_mode || mouse->relative_mode_warp) {
-                if ((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) != MOUSEEVENTF_FROMTOUCH) {
+                if (((extrainfo & MOUSEEVENTF_FROMTOUCH) != MOUSEEVENTF_FROMTOUCH) && checkTouch) {
                     WIN_CheckWParamMouseButtons(wParam, data, 0);
                 }
             }
@@ -553,7 +575,10 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
             GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
 
             /* Mouse data (ignoring synthetic mouse events generated for touchscreens) */
-            if (inp.header.dwType == RIM_TYPEMOUSE && (GetMessageExtraInfo() & 0x80) == 0) {
+            if (inp.header.dwType == RIM_TYPEMOUSE) {
+                extrainfo = GetMessageExtraInfoAndCheckMousePacketTouch(&checkTouch);
+                if (!checkTouch)
+                    break;
                 if (isRelative) {
                     RAWMOUSE* rawmouse = &inp.data.mouse;
 
@@ -616,10 +641,21 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     case WM_MOUSELEAVE:
         if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
             if (!IsIconic(hwnd)) {
+                SDL_Mouse *mouse;
                 POINT cursorPos;
                 GetCursorPos(&cursorPos);
                 ScreenToClient(hwnd, &cursorPos);
-                SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
+                mouse = SDL_GetMouse();
+                if (!mouse->was_touch_mouse_events) { /* we're not a touch handler causing a mouse leave? */
+                    SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
+                } else { /* touch handling? */
+                    mouse->was_touch_mouse_events = SDL_FALSE; /* not anymore */
+                    if (mouse->touch_mouse_events) { /* convert touch to mouse events */
+                        SDL_SendMouseMotion(data->window, SDL_TOUCH_MOUSEID, 0, cursorPos.x, cursorPos.y);
+                    } else { /* normal handling */
+                        SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
+                    }
+               }
             }
             SDL_SetMouseFocus(NULL);
         }
@@ -1125,6 +1161,14 @@ struct SDL_WIN_OSVERSIONINFOW {
 };
 
 static SDL_bool
+IsWinVistaOrNewer(void)
+{
+    DWORD version = GetVersion();
+    DWORD major = (DWORD)(LOBYTE(LOWORD(version)));
+    return (major >= 6)? SDL_TRUE : SDL_FALSE;
+}
+
+static SDL_bool
 IsWin10FCUorNewer(void)
 {
     HMODULE handle = GetModuleHandleW(L"ntdll.dll");
@@ -1212,6 +1256,7 @@ SDL_RegisterApp(char *name, Uint32 style, void *hInst)
         return SDL_SetError("Couldn't register application class");
     }
 
+    isVistaOrNewer = IsWinVistaOrNewer();
     isWin10FCUorNewer = IsWin10FCUorNewer();
 
     app_registered = 1;