| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> |
| | | |
| | | This software is provided 'as-is', without any express or implied |
| | | warranty. In no event will the authors be held liable for any damages |
| | |
| | | #include "SDL_events.h" |
| | | #include "SDL_timer.h" |
| | | #include "SDL_mutex.h" |
| | | #include "SDL_events.h" |
| | | #include "SDL_hints.h" |
| | | #include "SDL_joystick.h" |
| | | #include "../SDL_sysjoystick.h" |
| | | #include "../../thread/SDL_systhread.h" |
| | |
| | | /* local variables */ |
| | | static SDL_bool s_bDeviceAdded = SDL_FALSE; |
| | | static SDL_bool s_bDeviceRemoved = SDL_FALSE; |
| | | static SDL_JoystickID s_nInstanceID = -1; |
| | | static SDL_cond *s_condJoystickThread = NULL; |
| | | static SDL_mutex *s_mutexJoyStickEnum = NULL; |
| | | static SDL_Thread *s_threadJoystick = NULL; |
| | |
| | | return 0; |
| | | } |
| | | |
| | | static void |
| | | SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data) |
| | | static SDL_bool |
| | | SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex) |
| | | { |
| | | return SDL_FALSE; |
| | | } |
| | | |
| | | #else /* !__WINRT__ */ |
| | |
| | | HDEVNOTIFY hNotify; |
| | | } SDL_DeviceNotificationData; |
| | | |
| | | #define IDT_SDL_DEVICE_CHANGE_TIMER_1 1200 |
| | | #define IDT_SDL_DEVICE_CHANGE_TIMER_2 1201 |
| | | |
| | | /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */ |
| | | static LRESULT CALLBACK |
| | |
| | | case WM_DEVICECHANGE: |
| | | switch (wParam) { |
| | | case DBT_DEVICEARRIVAL: |
| | | if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { |
| | | s_bWindowsDeviceChanged = SDL_TRUE; |
| | | } |
| | | break; |
| | | case DBT_DEVICEREMOVECOMPLETE: |
| | | if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { |
| | | s_bWindowsDeviceChanged = SDL_TRUE; |
| | | /* notify 300ms and 2 seconds later to ensure all APIs have updated status */ |
| | | SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_1, 300, NULL); |
| | | SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_2, 2000, NULL); |
| | | } |
| | | break; |
| | | } |
| | | return 0; |
| | | case WM_TIMER: |
| | | KillTimer(hwnd, wParam); |
| | | s_bWindowsDeviceChanged = SDL_TRUE; |
| | | return 0; |
| | | } |
| | | |
| | |
| | | return 0; |
| | | } |
| | | |
| | | static void |
| | | SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data) |
| | | static SDL_bool |
| | | SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex) |
| | | { |
| | | MSG msg; |
| | | int lastret = 1; |
| | | |
| | | if (!data->messageWindow) { |
| | | return; |
| | | return SDL_FALSE; /* device notifications require a window */ |
| | | } |
| | | |
| | | while (PeekMessage(&msg, data->messageWindow, 0, 0, PM_NOREMOVE)) { |
| | | if (GetMessage(&msg, data->messageWindow, 0, 0) != 0) { |
| | | SDL_UnlockMutex(mutex); |
| | | while (lastret > 0 && s_bWindowsDeviceChanged == SDL_FALSE) { |
| | | lastret = GetMessage(&msg, NULL, 0, 0); /* WM_QUIT causes return value of 0 */ |
| | | if (lastret > 0) { |
| | | TranslateMessage(&msg); |
| | | DispatchMessage(&msg); |
| | | } |
| | | } |
| | | SDL_LockMutex(mutex); |
| | | return (lastret != -1) ? SDL_TRUE : SDL_FALSE; |
| | | } |
| | | |
| | | #endif /* __WINRT__ */ |
| | |
| | | while (s_bJoystickThreadQuit == SDL_FALSE) { |
| | | SDL_bool bXInputChanged = SDL_FALSE; |
| | | |
| | | SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 300); |
| | | |
| | | SDL_CheckDeviceNotification(¬ification_data); |
| | | |
| | | if (SDL_WaitForDeviceNotification(¬ification_data, s_mutexJoyStickEnum) == SDL_FALSE) { |
| | | #if SDL_JOYSTICK_XINPUT |
| | | if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) { |
| | | /* scan for any change in XInput devices */ |
| | | Uint8 userId; |
| | | for (userId = 0; userId < XUSER_MAX_COUNT; userId++) { |
| | | XINPUT_CAPABILITIES capabilities; |
| | | const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities); |
| | | const SDL_bool available = (result == ERROR_SUCCESS); |
| | | if (bOpenedXInputDevices[userId] != available) { |
| | | bXInputChanged = SDL_TRUE; |
| | | bOpenedXInputDevices[userId] = available; |
| | | /* WM_DEVICECHANGE not working, poll for new XINPUT controllers */ |
| | | SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 1000); |
| | | if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) { |
| | | /* scan for any change in XInput devices */ |
| | | Uint8 userId; |
| | | for (userId = 0; userId < XUSER_MAX_COUNT; userId++) { |
| | | XINPUT_CAPABILITIES capabilities; |
| | | const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities); |
| | | const SDL_bool available = (result == ERROR_SUCCESS); |
| | | if (bOpenedXInputDevices[userId] != available) { |
| | | bXInputChanged = SDL_TRUE; |
| | | bOpenedXInputDevices[userId] = available; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | #else |
| | | /* WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive */ |
| | | break; |
| | | #endif /* SDL_JOYSTICK_XINPUT */ |
| | | } |
| | | |
| | | if (s_bWindowsDeviceChanged || bXInputChanged) { |
| | | SDL_UnlockMutex(s_mutexJoyStickEnum); /* let main thread go while we SDL_Delay(). */ |
| | | SDL_Delay(300); /* wait for direct input to find out about this device */ |
| | | SDL_LockMutex(s_mutexJoyStickEnum); |
| | | |
| | | s_bDeviceRemoved = SDL_TRUE; |
| | | s_bDeviceAdded = SDL_TRUE; |
| | | s_bWindowsDeviceChanged = SDL_FALSE; |
| | |
| | | return 1; |
| | | } |
| | | |
| | | void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device) |
| | | void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device) |
| | | { |
| | | device->send_add_event = SDL_TRUE; |
| | | device->nInstanceID = ++s_nInstanceID; |
| | | device->nInstanceID = SDL_GetNextJoystickInstanceID(); |
| | | device->pNext = SYS_Joystick; |
| | | SYS_Joystick = device; |
| | | |
| | | s_bDeviceAdded = SDL_TRUE; |
| | | } |
| | | |
| | | static void WINDOWS_JoystickDetect(void); |
| | | static void WINDOWS_JoystickQuit(void); |
| | | |
| | | /* Function to scan the system for joysticks. |
| | | * Joystick 0 should be the system default joystick. |
| | | * It should return 0, or -1 on an unrecoverable fatal error. |
| | | */ |
| | | int |
| | | SDL_SYS_JoystickInit(void) |
| | | static int |
| | | WINDOWS_JoystickInit(void) |
| | | { |
| | | if (SDL_DINPUT_JoystickInit() < 0) { |
| | | SDL_SYS_JoystickQuit(); |
| | | WINDOWS_JoystickQuit(); |
| | | return -1; |
| | | } |
| | | |
| | | if (SDL_XINPUT_JoystickInit() < 0) { |
| | | SDL_SYS_JoystickQuit(); |
| | | WINDOWS_JoystickQuit(); |
| | | return -1; |
| | | } |
| | | |
| | |
| | | s_condJoystickThread = SDL_CreateCond(); |
| | | s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */ |
| | | |
| | | SDL_SYS_JoystickDetect(); |
| | | WINDOWS_JoystickDetect(); |
| | | |
| | | if (!s_threadJoystick) { |
| | | /* spin up the thread to detect hotplug of devices */ |
| | | s_bJoystickThreadQuit = SDL_FALSE; |
| | | s_threadJoystick = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL); |
| | | } |
| | | return SDL_SYS_NumJoysticks(); |
| | | return 0; |
| | | } |
| | | |
| | | /* return the number of joysticks that are connected right now */ |
| | | int |
| | | SDL_SYS_NumJoysticks() |
| | | static int |
| | | WINDOWS_JoystickGetCount(void) |
| | | { |
| | | int nJoysticks = 0; |
| | | JoyStick_DeviceData *device = SYS_Joystick; |
| | |
| | | } |
| | | |
| | | /* detect any new joysticks being inserted into the system */ |
| | | void |
| | | SDL_SYS_JoystickDetect() |
| | | static void |
| | | WINDOWS_JoystickDetect(void) |
| | | { |
| | | JoyStick_DeviceData *pCurList = NULL; |
| | | |
| | |
| | | SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice); |
| | | } |
| | | |
| | | SDL_PrivateJoystickAdded(device_index); |
| | | SDL_PrivateJoystickAdded(pNewJoystick->nInstanceID); |
| | | |
| | | pNewJoystick->send_add_event = SDL_FALSE; |
| | | } |
| | |
| | | } |
| | | |
| | | /* Function to get the device-dependent name of a joystick */ |
| | | const char * |
| | | SDL_SYS_JoystickNameForDeviceIndex(int device_index) |
| | | static const char * |
| | | WINDOWS_JoystickGetDeviceName(int device_index) |
| | | { |
| | | JoyStick_DeviceData *device = SYS_Joystick; |
| | | |
| | |
| | | return device->joystickname; |
| | | } |
| | | |
| | | static int |
| | | WINDOWS_JoystickGetDevicePlayerIndex(int device_index) |
| | | { |
| | | JoyStick_DeviceData *device = SYS_Joystick; |
| | | int index; |
| | | |
| | | for (index = device_index; index > 0; index--) |
| | | device = device->pNext; |
| | | |
| | | return device->bXInputDevice ? (int)device->XInputUserId : -1; |
| | | } |
| | | |
| | | /* return the stable device guid for this device index */ |
| | | static SDL_JoystickGUID |
| | | WINDOWS_JoystickGetDeviceGUID(int device_index) |
| | | { |
| | | JoyStick_DeviceData *device = SYS_Joystick; |
| | | int index; |
| | | |
| | | for (index = device_index; index > 0; index--) |
| | | device = device->pNext; |
| | | |
| | | return device->guid; |
| | | } |
| | | |
| | | /* Function to perform the mapping between current device instance and this joysticks instance id */ |
| | | SDL_JoystickID |
| | | SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index) |
| | | static SDL_JoystickID |
| | | WINDOWS_JoystickGetDeviceInstanceID(int device_index) |
| | | { |
| | | JoyStick_DeviceData *device = SYS_Joystick; |
| | | int index; |
| | |
| | | This should fill the nbuttons and naxes fields of the joystick structure. |
| | | It returns 0, or -1 if there is an error. |
| | | */ |
| | | int |
| | | SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) |
| | | static int |
| | | WINDOWS_JoystickOpen(SDL_Joystick * joystick, int device_index) |
| | | { |
| | | JoyStick_DeviceData *joystickdevice = SYS_Joystick; |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | /* return true if this joystick is plugged in right now */ |
| | | SDL_bool |
| | | SDL_SYS_JoystickAttached(SDL_Joystick * joystick) |
| | | static int |
| | | WINDOWS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) |
| | | { |
| | | return joystick->hwdata && !joystick->hwdata->removed; |
| | | if (joystick->hwdata->bXInputDevice) { |
| | | return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms); |
| | | } else { |
| | | return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms); |
| | | } |
| | | } |
| | | |
| | | void |
| | | SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) |
| | | static void |
| | | WINDOWS_JoystickUpdate(SDL_Joystick * joystick) |
| | | { |
| | | if (!joystick->hwdata || joystick->hwdata->removed) { |
| | | if (!joystick->hwdata) { |
| | | return; |
| | | } |
| | | |
| | |
| | | } else { |
| | | SDL_DINPUT_JoystickUpdate(joystick); |
| | | } |
| | | |
| | | if (joystick->hwdata->removed) { |
| | | joystick->force_recentering = SDL_TRUE; |
| | | } |
| | | } |
| | | |
| | | /* Function to close a joystick after use */ |
| | | void |
| | | SDL_SYS_JoystickClose(SDL_Joystick * joystick) |
| | | static void |
| | | WINDOWS_JoystickClose(SDL_Joystick * joystick) |
| | | { |
| | | if (joystick->hwdata->bXInputDevice) { |
| | | SDL_XINPUT_JoystickClose(joystick); |
| | |
| | | } |
| | | |
| | | /* Function to perform any system-specific joystick related cleanup */ |
| | | void |
| | | SDL_SYS_JoystickQuit(void) |
| | | static void |
| | | WINDOWS_JoystickQuit(void) |
| | | { |
| | | JoyStick_DeviceData *device = SYS_Joystick; |
| | | |
| | |
| | | s_bJoystickThreadQuit = SDL_TRUE; |
| | | SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */ |
| | | SDL_UnlockMutex(s_mutexJoyStickEnum); |
| | | #ifndef __WINRT__ |
| | | PostThreadMessage(SDL_GetThreadID(s_threadJoystick), WM_QUIT, 0, 0); |
| | | #endif |
| | | SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */ |
| | | |
| | | SDL_DestroyMutex(s_mutexJoyStickEnum); |
| | |
| | | s_bDeviceRemoved = SDL_FALSE; |
| | | } |
| | | |
| | | /* return the stable device guid for this device index */ |
| | | SDL_JoystickGUID |
| | | SDL_SYS_JoystickGetDeviceGUID(int device_index) |
| | | SDL_JoystickDriver SDL_WINDOWS_JoystickDriver = |
| | | { |
| | | JoyStick_DeviceData *device = SYS_Joystick; |
| | | int index; |
| | | |
| | | for (index = device_index; index > 0; index--) |
| | | device = device->pNext; |
| | | |
| | | return device->guid; |
| | | } |
| | | |
| | | SDL_JoystickGUID |
| | | SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) |
| | | { |
| | | return joystick->hwdata->guid; |
| | | } |
| | | WINDOWS_JoystickInit, |
| | | WINDOWS_JoystickGetCount, |
| | | WINDOWS_JoystickDetect, |
| | | WINDOWS_JoystickGetDeviceName, |
| | | WINDOWS_JoystickGetDevicePlayerIndex, |
| | | WINDOWS_JoystickGetDeviceGUID, |
| | | WINDOWS_JoystickGetDeviceInstanceID, |
| | | WINDOWS_JoystickOpen, |
| | | WINDOWS_JoystickRumble, |
| | | WINDOWS_JoystickUpdate, |
| | | WINDOWS_JoystickClose, |
| | | WINDOWS_JoystickQuit, |
| | | }; |
| | | |
| | | #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */ |
| | | |