Fixed bug 4704 - SDL_HINT_ANDROID_SEPERATE_MOUSE_AND_TOUCH on Windows?
authorSam Lantinga <slouken@libsdl.org>
Mon, 15 Jul 2019 09:36:53 -0700
changeset 1294349190e92b7d1
parent 12942 2c2adb2ebc96
child 12944 886b79177762
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
src/events/SDL_mouse_c.h
src/video/windows/SDL_windowsevents.c
     1.1 --- a/src/events/SDL_mouse.c	Sun Jul 14 16:59:39 2019 -0700
     1.2 +++ b/src/events/SDL_mouse.c	Mon Jul 15 09:36:53 2019 -0700
     1.3 @@ -156,6 +156,8 @@
     1.4      SDL_AddHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS,
     1.5                          SDL_MouseTouchEventsChanged, mouse);
     1.6  
     1.7 +    mouse->was_touch_mouse_events = SDL_FALSE; /* no touch to mouse movement event pending */
     1.8 +
     1.9      mouse->cursor_shown = SDL_TRUE;
    1.10  
    1.11      return (0);
    1.12 @@ -244,7 +246,7 @@
    1.13  
    1.14  /* Check to see if we need to synthesize focus events */
    1.15  static SDL_bool
    1.16 -SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
    1.17 +SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate, SDL_bool send_mouse_motion)
    1.18  {
    1.19      SDL_Mouse *mouse = SDL_GetMouse();
    1.20      SDL_bool inWindow = SDL_TRUE;
    1.21 @@ -275,7 +277,9 @@
    1.22  #ifdef DEBUG_MOUSE
    1.23              printf("Mouse left window, synthesizing move & focus lost event\n");
    1.24  #endif
    1.25 -            SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
    1.26 +            if (send_mouse_motion) {
    1.27 +                SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
    1.28 +            }
    1.29              SDL_SetMouseFocus(NULL);
    1.30          }
    1.31          return SDL_FALSE;
    1.32 @@ -286,7 +290,9 @@
    1.33          printf("Mouse entered window, synthesizing focus gain & move event\n");
    1.34  #endif
    1.35          SDL_SetMouseFocus(window);
    1.36 -        SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
    1.37 +        if (send_mouse_motion) {
    1.38 +            SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
    1.39 +        }
    1.40      }
    1.41      return SDL_TRUE;
    1.42  }
    1.43 @@ -296,7 +302,7 @@
    1.44  {
    1.45      if (window && !relative) {
    1.46          SDL_Mouse *mouse = SDL_GetMouse();
    1.47 -        if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate)) {
    1.48 +        if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate, (mouseID == SDL_TOUCH_MOUSEID) ? SDL_FALSE : SDL_TRUE)) {
    1.49              return 0;
    1.50          }
    1.51      }
    1.52 @@ -446,6 +452,8 @@
    1.53          event.motion.type = SDL_MOUSEMOTION;
    1.54          event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
    1.55          event.motion.which = mouseID;
    1.56 +        /* Set us pending (or clear during a normal mouse movement event) as having triggered */
    1.57 +        mouse->was_touch_mouse_events = (mouseID == SDL_TOUCH_MOUSEID)? SDL_TRUE : SDL_FALSE;
    1.58          event.motion.state = mouse->buttonstate;
    1.59          event.motion.x = mouse->x;
    1.60          event.motion.y = mouse->y;
    1.61 @@ -530,7 +538,7 @@
    1.62  
    1.63      /* We do this after calculating buttonstate so button presses gain focus */
    1.64      if (window && state == SDL_PRESSED) {
    1.65 -        SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
    1.66 +        SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE);
    1.67      }
    1.68  
    1.69      if (buttonstate == mouse->buttonstate) {
    1.70 @@ -580,7 +588,7 @@
    1.71  
    1.72      /* We do this after dispatching event so button releases can lose focus */
    1.73      if (window && state == SDL_RELEASED) {
    1.74 -        SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
    1.75 +        SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE);
    1.76      }
    1.77  
    1.78      return posted;
     2.1 --- a/src/events/SDL_mouse_c.h	Sun Jul 14 16:59:39 2019 -0700
     2.2 +++ b/src/events/SDL_mouse_c.h	Mon Jul 15 09:36:53 2019 -0700
     2.3 @@ -94,6 +94,7 @@
     2.4      int double_click_radius;
     2.5      SDL_bool touch_mouse_events;
     2.6      SDL_bool mouse_touch_events;
     2.7 +    SDL_bool was_touch_mouse_events; /* Was a touch-mouse event pending? */
     2.8  
     2.9      /* Data for double-click tracking */
    2.10      int num_clickstates;
     3.1 --- a/src/video/windows/SDL_windowsevents.c	Sun Jul 14 16:59:39 2019 -0700
     3.2 +++ b/src/video/windows/SDL_windowsevents.c	Mon Jul 15 09:36:53 2019 -0700
     3.3 @@ -359,14 +359,32 @@
     3.4      return !SDL_GetHintBoolean(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, SDL_FALSE);
     3.5  }
     3.6  
     3.7 +static SDL_bool isVistaOrNewer = SDL_FALSE;
     3.8  /* Win10 "Fall Creators Update" introduced the bug that SetCursorPos() (as used by SDL_WarpMouseInWindow())
     3.9     doesn't reliably generate WM_MOUSEMOVE events anymore (see #3931) which breaks relative mouse mode via warping.
    3.10     This is used to implement a workaround.. */
    3.11  static SDL_bool isWin10FCUorNewer = SDL_FALSE;
    3.12  
    3.13 +/* Checks a mouse or raw packet for touch indication.
    3.14 +   returns: 0 for not touch input, 1 for touch input.
    3.15 +*/
    3.16 +static LPARAM
    3.17 +GetMessageExtraInfoAndCheckMousePacketTouch(int *checkTouch) {
    3.18 +    LPARAM extrainfo = GetMessageExtraInfo();
    3.19 +    /* Mouse data (ignoring synthetic mouse events generated for touchscreens) */
    3.20 +    /* Versions below Vista will set the low 7 bits to the Mouse ID and don't use bit 7:
    3.21 +       Check bits 8-32 for the signature (which will indicate a Tablet PC Pen or Touch Device).
    3.22 +       Only check bit 7 when Vista and up(Cleared=Pen, Set=Touch(which we need to filter out)),
    3.23 +       when the signature is set. The Mouse ID will be zero for an actual mouse. */
    3.24 +    *checkTouch = (!(((extrainfo & 0x7F) && (isVistaOrNewer ? (extrainfo & 0x80) : 1)) || ((extrainfo & 0xFFFFFF00) == 0xFF515700)));
    3.25 +    return extrainfo;
    3.26 +}
    3.27 +
    3.28  LRESULT CALLBACK
    3.29  WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    3.30  {
    3.31 +    int checkTouch = -1; /* Default to -1 for not yet loaded */
    3.32 +    LPARAM extrainfo;    /* The extra info when checkTouch >= 0. */
    3.33      SDL_WindowData *data;
    3.34      LRESULT returnCode = -1;
    3.35  
    3.36 @@ -494,9 +512,10 @@
    3.37      case WM_MOUSEMOVE:
    3.38          {
    3.39              SDL_Mouse *mouse = SDL_GetMouse();
    3.40 +            extrainfo = GetMessageExtraInfoAndCheckMousePacketTouch(&checkTouch); /* load */
    3.41              if (!mouse->relative_mode || mouse->relative_mode_warp) {
    3.42                  /* Only generate mouse events for real mouse */
    3.43 -                if ((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) != MOUSEEVENTF_FROMTOUCH) {
    3.44 +                if (((extrainfo & MOUSEEVENTF_FROMTOUCH) != MOUSEEVENTF_FROMTOUCH) && checkTouch) {
    3.45                      SDL_SendMouseMotion(data->window, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
    3.46                      if (isWin10FCUorNewer && mouse->relative_mode_warp) {
    3.47                          /* To work around #3931, Win10 bug introduced in Fall Creators Update, where
    3.48 @@ -527,8 +546,11 @@
    3.49      case WM_XBUTTONDBLCLK:
    3.50          {
    3.51              SDL_Mouse *mouse = SDL_GetMouse();
    3.52 +            if (checkTouch < 0) {
    3.53 +                extrainfo = GetMessageExtraInfoAndCheckMousePacketTouch(&checkTouch);
    3.54 +            }
    3.55              if (!mouse->relative_mode || mouse->relative_mode_warp) {
    3.56 -                if ((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) != MOUSEEVENTF_FROMTOUCH) {
    3.57 +                if (((extrainfo & MOUSEEVENTF_FROMTOUCH) != MOUSEEVENTF_FROMTOUCH) && checkTouch) {
    3.58                      WIN_CheckWParamMouseButtons(wParam, data, 0);
    3.59                  }
    3.60              }
    3.61 @@ -553,7 +575,10 @@
    3.62              GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
    3.63  
    3.64              /* Mouse data (ignoring synthetic mouse events generated for touchscreens) */
    3.65 -            if (inp.header.dwType == RIM_TYPEMOUSE && (GetMessageExtraInfo() & 0x80) == 0) {
    3.66 +            if (inp.header.dwType == RIM_TYPEMOUSE) {
    3.67 +                extrainfo = GetMessageExtraInfoAndCheckMousePacketTouch(&checkTouch);
    3.68 +                if (!checkTouch)
    3.69 +                    break;
    3.70                  if (isRelative) {
    3.71                      RAWMOUSE* rawmouse = &inp.data.mouse;
    3.72  
    3.73 @@ -616,10 +641,21 @@
    3.74      case WM_MOUSELEAVE:
    3.75          if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
    3.76              if (!IsIconic(hwnd)) {
    3.77 +                SDL_Mouse *mouse;
    3.78                  POINT cursorPos;
    3.79                  GetCursorPos(&cursorPos);
    3.80                  ScreenToClient(hwnd, &cursorPos);
    3.81 -                SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
    3.82 +                mouse = SDL_GetMouse();
    3.83 +                if (!mouse->was_touch_mouse_events) { /* we're not a touch handler causing a mouse leave? */
    3.84 +                    SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
    3.85 +                } else { /* touch handling? */
    3.86 +                    mouse->was_touch_mouse_events = SDL_FALSE; /* not anymore */
    3.87 +                    if (mouse->touch_mouse_events) { /* convert touch to mouse events */
    3.88 +                        SDL_SendMouseMotion(data->window, SDL_TOUCH_MOUSEID, 0, cursorPos.x, cursorPos.y);
    3.89 +                    } else { /* normal handling */
    3.90 +                        SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
    3.91 +                    }
    3.92 +               }
    3.93              }
    3.94              SDL_SetMouseFocus(NULL);
    3.95          }
    3.96 @@ -1125,6 +1161,14 @@
    3.97  };
    3.98  
    3.99  static SDL_bool
   3.100 +IsWinVistaOrNewer(void)
   3.101 +{
   3.102 +    DWORD version = GetVersion();
   3.103 +    DWORD major = (DWORD)(LOBYTE(LOWORD(version)));
   3.104 +    return (major >= 6)? SDL_TRUE : SDL_FALSE;
   3.105 +}
   3.106 +
   3.107 +static SDL_bool
   3.108  IsWin10FCUorNewer(void)
   3.109  {
   3.110      HMODULE handle = GetModuleHandleW(L"ntdll.dll");
   3.111 @@ -1212,6 +1256,7 @@
   3.112          return SDL_SetError("Couldn't register application class");
   3.113      }
   3.114  
   3.115 +    isVistaOrNewer = IsWinVistaOrNewer();
   3.116      isWin10FCUorNewer = IsWin10FCUorNewer();
   3.117  
   3.118      app_registered = 1;