keep joystick thread from waking unnecessarily, and from possibly blocking for 300ms at shutdown if a joystick was just plugged in
authorSam Lantinga <slouken@libsdl.org>
Fri, 08 Sep 2017 07:15:47 -0700
changeset 11470b3bb3855bc41
parent 11469 71610a71bbb2
child 11471 510e461e584a
keep joystick thread from waking unnecessarily, and from possibly blocking for 300ms at shutdown if a joystick was just plugged in

CR: SamL
src/joystick/windows/SDL_windowsjoystick.c
     1.1 --- a/src/joystick/windows/SDL_windowsjoystick.c	Fri Sep 08 15:08:50 2017 -0700
     1.2 +++ b/src/joystick/windows/SDL_windowsjoystick.c	Fri Sep 08 07:15:47 2017 -0700
     1.3 @@ -89,9 +89,10 @@
     1.4      return 0;
     1.5  }
     1.6  
     1.7 -static void
     1.8 -SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
     1.9 +static SDL_bool
    1.10 +SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex)
    1.11  {
    1.12 +    return SDL_FALSE;
    1.13  }
    1.14  
    1.15  #else /* !__WINRT__ */
    1.16 @@ -104,6 +105,8 @@
    1.17      HDEVNOTIFY hNotify;
    1.18  } SDL_DeviceNotificationData;
    1.19  
    1.20 +#define IDT_SDL_DEVICE_CHANGE_TIMER_1 1200
    1.21 +#define IDT_SDL_DEVICE_CHANGE_TIMER_2 1201
    1.22  
    1.23  /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */
    1.24  static LRESULT CALLBACK
    1.25 @@ -113,17 +116,19 @@
    1.26      case WM_DEVICECHANGE:
    1.27          switch (wParam) {
    1.28          case DBT_DEVICEARRIVAL:
    1.29 -            if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
    1.30 -                s_bWindowsDeviceChanged = SDL_TRUE;
    1.31 -            }
    1.32 -            break;
    1.33          case DBT_DEVICEREMOVECOMPLETE:
    1.34              if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
    1.35 -                s_bWindowsDeviceChanged = SDL_TRUE;
    1.36 +                /* notify 300ms and 2 seconds later to ensure all APIs have updated status */
    1.37 +                SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_1, 300, NULL);
    1.38 +                SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_2, 2000, NULL);
    1.39              }
    1.40              break;
    1.41          }
    1.42          return 0;
    1.43 +    case WM_TIMER:
    1.44 +        KillTimer(hwnd, wParam);
    1.45 +        s_bWindowsDeviceChanged = SDL_TRUE;
    1.46 +        return 0;
    1.47      }
    1.48  
    1.49      return DefWindowProc (hwnd, message, wParam, lParam);
    1.50 @@ -187,21 +192,26 @@
    1.51      return 0;
    1.52  }
    1.53  
    1.54 -static void
    1.55 -SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
    1.56 +static SDL_bool
    1.57 +SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex)
    1.58  {
    1.59      MSG msg;
    1.60 +    int lastret = 1;
    1.61  
    1.62      if (!data->messageWindow) {
    1.63 -        return;
    1.64 +        return SDL_FALSE; /* device notifications require a window */
    1.65      }
    1.66  
    1.67 -    while (PeekMessage(&msg, data->messageWindow, 0, 0, PM_NOREMOVE)) {
    1.68 -        if (GetMessage(&msg, data->messageWindow, 0, 0) != 0)  {
    1.69 +    SDL_UnlockMutex(mutex);
    1.70 +    while (lastret > 0 && s_bWindowsDeviceChanged == SDL_FALSE) {
    1.71 +        lastret = GetMessage(&msg, NULL, 0, 0); /* WM_QUIT causes return value of 0 */
    1.72 +        if (lastret > 0) {
    1.73              TranslateMessage(&msg);
    1.74              DispatchMessage(&msg);
    1.75          }
    1.76      }
    1.77 +    SDL_LockMutex(mutex);
    1.78 +    return (lastret != -1) ? SDL_TRUE : SDL_FALSE;
    1.79  }
    1.80  
    1.81  #endif /* __WINRT__ */
    1.82 @@ -225,31 +235,30 @@
    1.83      while (s_bJoystickThreadQuit == SDL_FALSE) {
    1.84          SDL_bool bXInputChanged = SDL_FALSE;
    1.85  
    1.86 -        SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 300);
    1.87 -
    1.88 -        SDL_CheckDeviceNotification(&notification_data);
    1.89 -
    1.90 +        if (SDL_WaitForDeviceNotification(&notification_data, s_mutexJoyStickEnum) == SDL_FALSE) {
    1.91  #if SDL_JOYSTICK_XINPUT
    1.92 -        if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) {
    1.93 -            /* scan for any change in XInput devices */
    1.94 -            Uint8 userId;
    1.95 -            for (userId = 0; userId < XUSER_MAX_COUNT; userId++) {
    1.96 -                XINPUT_CAPABILITIES capabilities;
    1.97 -                const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities);
    1.98 -                const SDL_bool available = (result == ERROR_SUCCESS);
    1.99 -                if (bOpenedXInputDevices[userId] != available) {
   1.100 -                    bXInputChanged = SDL_TRUE;
   1.101 -                    bOpenedXInputDevices[userId] = available;
   1.102 +            /* WM_DEVICECHANGE not working, poll for new XINPUT controllers */
   1.103 +            SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 1000);
   1.104 +            if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) {
   1.105 +                /* scan for any change in XInput devices */
   1.106 +                Uint8 userId;
   1.107 +                for (userId = 0; userId < XUSER_MAX_COUNT; userId++) {
   1.108 +                    XINPUT_CAPABILITIES capabilities;
   1.109 +                    const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities);
   1.110 +                    const SDL_bool available = (result == ERROR_SUCCESS);
   1.111 +                    if (bOpenedXInputDevices[userId] != available) {
   1.112 +                        bXInputChanged = SDL_TRUE;
   1.113 +                        bOpenedXInputDevices[userId] = available;
   1.114 +                    }
   1.115                  }
   1.116              }
   1.117 -        }
   1.118 +#else
   1.119 +            /* WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive */
   1.120 +            break;
   1.121  #endif /* SDL_JOYSTICK_XINPUT */
   1.122 +		}
   1.123  
   1.124          if (s_bWindowsDeviceChanged || bXInputChanged) {
   1.125 -            SDL_UnlockMutex(s_mutexJoyStickEnum);  /* let main thread go while we SDL_Delay(). */
   1.126 -            SDL_Delay(300); /* wait for direct input to find out about this device */
   1.127 -            SDL_LockMutex(s_mutexJoyStickEnum);
   1.128 -
   1.129              s_bDeviceRemoved = SDL_TRUE;
   1.130              s_bDeviceAdded = SDL_TRUE;
   1.131              s_bWindowsDeviceChanged = SDL_FALSE;
   1.132 @@ -496,6 +505,9 @@
   1.133          s_bJoystickThreadQuit = SDL_TRUE;
   1.134          SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
   1.135          SDL_UnlockMutex(s_mutexJoyStickEnum);
   1.136 +#ifndef __WINRT__
   1.137 +        PostThreadMessage(SDL_GetThreadID(s_threadJoystick), WM_QUIT, 0, 0);
   1.138 +#endif
   1.139          SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
   1.140  
   1.141          SDL_DestroyMutex(s_mutexJoyStickEnum);