From e7c2cf107a7bc63ab10e232383acc87c232763fe Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 15 Jul 2019 09:36:53 -0700 Subject: [PATCH] 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)? --- src/events/SDL_mouse.c | 20 +++++++--- src/events/SDL_mouse_c.h | 1 + src/video/windows/SDL_windowsevents.c | 53 +++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index 0ecb3cc77..98b7298ed 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 9f88d408a..b44d40d2a 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 2c9401b6b..9e95e338a 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); } @@ -1124,6 +1160,14 @@ struct SDL_WIN_OSVERSIONINFOW { WCHAR szCSDVersion[128]; }; +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) { @@ -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;