123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536 |
- /*
- Simple DirectMedia Layer
- Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- */
- #include "../../SDL_internal.h"
- #if SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT
- /* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de
- * A. Formiga's WINMM driver.
- *
- * Hats and sliders are completely untested; the app I'm writing this for mostly
- * doesn't use them and I don't own any joysticks with them.
- *
- * We don't bother to use event notification here. It doesn't seem to work
- * with polled devices, and it's fine to call IDirectInputDevice8_GetDeviceData and
- * let it return 0 events. */
- #include "SDL_error.h"
- #include "SDL_assert.h"
- #include "SDL_events.h"
- #include "SDL_timer.h"
- #include "SDL_mutex.h"
- #include "SDL_joystick.h"
- #include "../SDL_sysjoystick.h"
- #include "../../thread/SDL_systhread.h"
- #include "../../core/windows/SDL_windows.h"
- #if !defined(__WINRT__)
- #include <dbt.h>
- #endif
- #define INITGUID /* Only set here, if set twice will cause mingw32 to break. */
- #include "SDL_windowsjoystick_c.h"
- #include "SDL_dinputjoystick_c.h"
- #include "SDL_xinputjoystick_c.h"
- #include "../../haptic/windows/SDL_dinputhaptic_c.h" /* For haptic hot plugging */
- #include "../../haptic/windows/SDL_xinputhaptic_c.h" /* For haptic hot plugging */
- #ifndef DEVICE_NOTIFY_WINDOW_HANDLE
- #define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000
- #endif
- /* 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;
- static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
- JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */
- static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
- #ifdef __WINRT__
- typedef struct
- {
- int unused;
- } SDL_DeviceNotificationData;
- static void
- SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
- {
- }
- static int
- SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
- {
- return 0;
- }
- static void
- SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
- {
- }
- #else /* !__WINRT__ */
- typedef struct
- {
- HRESULT coinitialized;
- WNDCLASSEX wincl;
- HWND messageWindow;
- HDEVNOTIFY hNotify;
- } SDL_DeviceNotificationData;
- /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */
- static LRESULT CALLBACK
- SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- switch (message) {
- 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;
- }
- break;
- }
- return 0;
- }
- return DefWindowProc (hwnd, message, wParam, lParam);
- }
- static void
- SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
- {
- if (data->hNotify)
- UnregisterDeviceNotification(data->hNotify);
- if (data->messageWindow)
- DestroyWindow(data->messageWindow);
- UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance);
- if (data->coinitialized == S_OK) {
- WIN_CoUninitialize();
- }
- }
- static int
- SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
- {
- DEV_BROADCAST_DEVICEINTERFACE dbh;
- GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
- SDL_zerop(data);
- data->coinitialized = WIN_CoInitialize();
- data->wincl.hInstance = GetModuleHandle(NULL);
- data->wincl.lpszClassName = L"Message";
- data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; /* This function is called by windows */
- data->wincl.cbSize = sizeof (WNDCLASSEX);
- if (!RegisterClassEx(&data->wincl)) {
- WIN_SetError("Failed to create register class for joystick autodetect");
- SDL_CleanupDeviceNotification(data);
- return -1;
- }
- data->messageWindow = (HWND)CreateWindowEx(0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
- if (!data->messageWindow) {
- WIN_SetError("Failed to create message window for joystick autodetect");
- SDL_CleanupDeviceNotification(data);
- return -1;
- }
- SDL_zero(dbh);
- dbh.dbcc_size = sizeof(dbh);
- dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
- dbh.dbcc_classguid = GUID_DEVINTERFACE_HID;
- data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
- if (!data->hNotify) {
- WIN_SetError("Failed to create notify device for joystick autodetect");
- SDL_CleanupDeviceNotification(data);
- return -1;
- }
- return 0;
- }
- static void
- SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data)
- {
- MSG msg;
- if (!data->messageWindow) {
- return;
- }
- while (PeekMessage(&msg, data->messageWindow, 0, 0, PM_NOREMOVE)) {
- if (GetMessage(&msg, data->messageWindow, 0, 0) != 0) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- }
- #endif /* __WINRT__ */
- /* Function/thread to scan the system for joysticks. */
- static int
- SDL_JoystickThread(void *_data)
- {
- SDL_DeviceNotificationData notification_data;
- #if SDL_JOYSTICK_XINPUT
- SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT];
- SDL_zero(bOpenedXInputDevices);
- #endif
- if (SDL_CreateDeviceNotification(¬ification_data) < 0) {
- return -1;
- }
- SDL_LockMutex(s_mutexJoyStickEnum);
- while (s_bJoystickThreadQuit == SDL_FALSE) {
- SDL_bool bXInputChanged = SDL_FALSE;
- SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 300);
- SDL_CheckDeviceNotification(¬ification_data);
- #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;
- }
- }
- }
- #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;
- }
- }
- SDL_UnlockMutex(s_mutexJoyStickEnum);
- SDL_CleanupDeviceNotification(¬ification_data);
- return 1;
- }
- void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device)
- {
- device->send_add_event = SDL_TRUE;
- device->nInstanceID = ++s_nInstanceID;
- device->pNext = SYS_Joystick;
- SYS_Joystick = device;
- s_bDeviceAdded = SDL_TRUE;
- }
- /* 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)
- {
- if (SDL_DINPUT_JoystickInit() < 0) {
- SDL_SYS_JoystickQuit();
- return -1;
- }
- if (SDL_XINPUT_JoystickInit() < 0) {
- SDL_SYS_JoystickQuit();
- return -1;
- }
- s_mutexJoyStickEnum = SDL_CreateMutex();
- s_condJoystickThread = SDL_CreateCond();
- s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
- SDL_SYS_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 the number of joysticks that are connected right now */
- int
- SDL_SYS_NumJoysticks(void)
- {
- int nJoysticks = 0;
- JoyStick_DeviceData *device = SYS_Joystick;
- while (device) {
- nJoysticks++;
- device = device->pNext;
- }
- return nJoysticks;
- }
- /* detect any new joysticks being inserted into the system */
- void
- SDL_SYS_JoystickDetect(void)
- {
- JoyStick_DeviceData *pCurList = NULL;
- /* only enum the devices if the joystick thread told us something changed */
- if (!s_bDeviceAdded && !s_bDeviceRemoved) {
- return; /* thread hasn't signaled, nothing to do right now. */
- }
- SDL_LockMutex(s_mutexJoyStickEnum);
- s_bDeviceAdded = SDL_FALSE;
- s_bDeviceRemoved = SDL_FALSE;
- pCurList = SYS_Joystick;
- SYS_Joystick = NULL;
- /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
- SDL_DINPUT_JoystickDetect(&pCurList);
- /* Look for XInput devices. Do this last, so they're first in the final list. */
- SDL_XINPUT_JoystickDetect(&pCurList);
- SDL_UnlockMutex(s_mutexJoyStickEnum);
- while (pCurList) {
- JoyStick_DeviceData *pListNext = NULL;
- if (pCurList->bXInputDevice) {
- SDL_XINPUT_MaybeRemoveDevice(pCurList->XInputUserId);
- } else {
- SDL_DINPUT_MaybeRemoveDevice(&pCurList->dxdevice);
- }
- SDL_PrivateJoystickRemoved(pCurList->nInstanceID);
- pListNext = pCurList->pNext;
- SDL_free(pCurList->joystickname);
- SDL_free(pCurList);
- pCurList = pListNext;
- }
- if (s_bDeviceAdded) {
- JoyStick_DeviceData *pNewJoystick;
- int device_index = 0;
- s_bDeviceAdded = SDL_FALSE;
- pNewJoystick = SYS_Joystick;
- while (pNewJoystick) {
- if (pNewJoystick->send_add_event) {
- if (pNewJoystick->bXInputDevice) {
- SDL_XINPUT_MaybeAddDevice(pNewJoystick->XInputUserId);
- } else {
- SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice);
- }
- SDL_PrivateJoystickAdded(device_index);
- pNewJoystick->send_add_event = SDL_FALSE;
- }
- device_index++;
- pNewJoystick = pNewJoystick->pNext;
- }
- }
- }
- /* Function to get the device-dependent name of a joystick */
- const char *
- SDL_SYS_JoystickNameForDeviceIndex(int device_index)
- {
- JoyStick_DeviceData *device = SYS_Joystick;
- for (; device_index > 0; device_index--)
- device = device->pNext;
- return device->joystickname;
- }
- /* Function to perform the mapping between current device instance and this joysticks instance id */
- SDL_JoystickID
- SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
- {
- JoyStick_DeviceData *device = SYS_Joystick;
- int index;
- for (index = device_index; index > 0; index--)
- device = device->pNext;
- return device->nInstanceID;
- }
- /* Function to open a joystick for use.
- The joystick to open is specified by the device 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)
- {
- JoyStick_DeviceData *joystickdevice = SYS_Joystick;
- for (; device_index > 0; device_index--)
- joystickdevice = joystickdevice->pNext;
- /* allocate memory for system specific hardware data */
- joystick->instance_id = joystickdevice->nInstanceID;
- joystick->hwdata =
- (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
- if (joystick->hwdata == NULL) {
- return SDL_OutOfMemory();
- }
- SDL_zerop(joystick->hwdata);
- joystick->hwdata->guid = joystickdevice->guid;
- if (joystickdevice->bXInputDevice) {
- return SDL_XINPUT_JoystickOpen(joystick, joystickdevice);
- } else {
- return SDL_DINPUT_JoystickOpen(joystick, joystickdevice);
- }
- }
- /* return true if this joystick is plugged in right now */
- SDL_bool
- SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
- {
- return joystick->hwdata && !joystick->hwdata->removed;
- }
- void
- SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
- {
- if (!joystick->hwdata || joystick->hwdata->removed) {
- return;
- }
- if (joystick->hwdata->bXInputDevice) {
- SDL_XINPUT_JoystickUpdate(joystick);
- } 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)
- {
- if (joystick->hwdata->bXInputDevice) {
- SDL_XINPUT_JoystickClose(joystick);
- } else {
- SDL_DINPUT_JoystickClose(joystick);
- }
- SDL_free(joystick->hwdata);
- }
- /* Function to perform any system-specific joystick related cleanup */
- void
- SDL_SYS_JoystickQuit(void)
- {
- JoyStick_DeviceData *device = SYS_Joystick;
- while (device) {
- JoyStick_DeviceData *device_next = device->pNext;
- SDL_free(device->joystickname);
- SDL_free(device);
- device = device_next;
- }
- SYS_Joystick = NULL;
- if (s_threadJoystick) {
- SDL_LockMutex(s_mutexJoyStickEnum);
- s_bJoystickThreadQuit = SDL_TRUE;
- SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
- SDL_UnlockMutex(s_mutexJoyStickEnum);
- SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */
- SDL_DestroyMutex(s_mutexJoyStickEnum);
- SDL_DestroyCond(s_condJoystickThread);
- s_condJoystickThread= NULL;
- s_mutexJoyStickEnum = NULL;
- s_threadJoystick = NULL;
- }
- SDL_DINPUT_JoystickQuit();
- SDL_XINPUT_JoystickQuit();
- s_bDeviceAdded = SDL_FALSE;
- s_bDeviceRemoved = SDL_FALSE;
- }
- /* return the stable device guid for this device index */
- SDL_JoystickGUID
- SDL_SYS_JoystickGetDeviceGUID(int device_index)
- {
- 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;
- }
- #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
- /* vi: set ts=4 sw=4 expandtab: */
|