Atlas - SDL_windowsjoystick.c
Home / ext / SDL2 / src / joystick / windows Lines: 1 | Size: 16836 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23#if SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT 24 25/* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de 26 * A. Formiga's WINMM driver. 27 * 28 * Hats and sliders are completely untested; the app I'm writing this for mostly 29 * doesn't use them and I don't own any joysticks with them. 30 * 31 * We don't bother to use event notification here. It doesn't seem to work 32 * with polled devices, and it's fine to call IDirectInputDevice8_GetDeviceData and 33 * let it return 0 events. */ 34 35#include "SDL_error.h" 36#include "SDL_assert.h" 37#include "SDL_events.h" 38#include "SDL_timer.h" 39#include "SDL_mutex.h" 40#include "SDL_joystick.h" 41#include "../SDL_sysjoystick.h" 42#include "../../thread/SDL_systhread.h" 43#include "../../core/windows/SDL_windows.h" 44#if !defined(__WINRT__) 45#include <dbt.h> 46#endif 47 48#define INITGUID /* Only set here, if set twice will cause mingw32 to break. */ 49#include "SDL_windowsjoystick_c.h" 50#include "SDL_dinputjoystick_c.h" 51#include "SDL_xinputjoystick_c.h" 52 53#include "../../haptic/windows/SDL_dinputhaptic_c.h" /* For haptic hot plugging */ 54#include "../../haptic/windows/SDL_xinputhaptic_c.h" /* For haptic hot plugging */ 55 56 57#ifndef DEVICE_NOTIFY_WINDOW_HANDLE 58#define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000 59#endif 60 61/* local variables */ 62static SDL_bool s_bDeviceAdded = SDL_FALSE; 63static SDL_bool s_bDeviceRemoved = SDL_FALSE; 64static SDL_cond *s_condJoystickThread = NULL; 65static SDL_mutex *s_mutexJoyStickEnum = NULL; 66static SDL_Thread *s_threadJoystick = NULL; 67static SDL_bool s_bJoystickThreadQuit = SDL_FALSE; 68 69JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */ 70 71static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE; 72 73#ifdef __WINRT__ 74 75typedef struct 76{ 77 int unused; 78} SDL_DeviceNotificationData; 79 80static void 81SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) 82{ 83} 84 85static int 86SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) 87{ 88 return 0; 89} 90 91static SDL_bool 92SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex) 93{ 94 return SDL_FALSE; 95} 96 97#else /* !__WINRT__ */ 98 99typedef struct 100{ 101 HRESULT coinitialized; 102 WNDCLASSEX wincl; 103 HWND messageWindow; 104 HDEVNOTIFY hNotify; 105} SDL_DeviceNotificationData; 106 107#define IDT_SDL_DEVICE_CHANGE_TIMER_1 1200 108#define IDT_SDL_DEVICE_CHANGE_TIMER_2 1201 109 110/* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */ 111static LRESULT CALLBACK 112SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 113{ 114 switch (message) { 115 case WM_DEVICECHANGE: 116 switch (wParam) { 117 case DBT_DEVICEARRIVAL: 118 case DBT_DEVICEREMOVECOMPLETE: 119 if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { 120 /* notify 300ms and 2 seconds later to ensure all APIs have updated status */ 121 SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_1, 300, NULL); 122 SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_2, 2000, NULL); 123 } 124 break; 125 } 126 return 0; 127 case WM_TIMER: 128 KillTimer(hwnd, wParam); 129 s_bWindowsDeviceChanged = SDL_TRUE; 130 return 0; 131 } 132 133 return DefWindowProc (hwnd, message, wParam, lParam); 134} 135 136static void 137SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) 138{ 139 if (data->hNotify) 140 UnregisterDeviceNotification(data->hNotify); 141 142 if (data->messageWindow) 143 DestroyWindow(data->messageWindow); 144 145 UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance); 146 147 if (data->coinitialized == S_OK) { 148 WIN_CoUninitialize(); 149 } 150} 151 152static int 153SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) 154{ 155 DEV_BROADCAST_DEVICEINTERFACE dbh; 156 GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; 157 158 SDL_zerop(data); 159 160 data->coinitialized = WIN_CoInitialize(); 161 162 data->wincl.hInstance = GetModuleHandle(NULL); 163 data->wincl.lpszClassName = L"Message"; 164 data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; /* This function is called by windows */ 165 data->wincl.cbSize = sizeof (WNDCLASSEX); 166 167 if (!RegisterClassEx(&data->wincl)) { 168 WIN_SetError("Failed to create register class for joystick autodetect"); 169 SDL_CleanupDeviceNotification(data); 170 return -1; 171 } 172 173 data->messageWindow = (HWND)CreateWindowEx(0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); 174 if (!data->messageWindow) { 175 WIN_SetError("Failed to create message window for joystick autodetect"); 176 SDL_CleanupDeviceNotification(data); 177 return -1; 178 } 179 180 SDL_zero(dbh); 181 dbh.dbcc_size = sizeof(dbh); 182 dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 183 dbh.dbcc_classguid = GUID_DEVINTERFACE_HID; 184 185 data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE); 186 if (!data->hNotify) { 187 WIN_SetError("Failed to create notify device for joystick autodetect"); 188 SDL_CleanupDeviceNotification(data); 189 return -1; 190 } 191 return 0; 192} 193 194static SDL_bool 195SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex) 196{ 197 MSG msg; 198 int lastret = 1; 199 200 if (!data->messageWindow) { 201 return SDL_FALSE; /* device notifications require a window */ 202 } 203 204 SDL_UnlockMutex(mutex); 205 while (lastret > 0 && s_bWindowsDeviceChanged == SDL_FALSE) { 206 lastret = GetMessage(&msg, NULL, 0, 0); /* WM_QUIT causes return value of 0 */ 207 if (lastret > 0) { 208 TranslateMessage(&msg); 209 DispatchMessage(&msg); 210 } 211 } 212 SDL_LockMutex(mutex); 213 return (lastret != -1) ? SDL_TRUE : SDL_FALSE; 214} 215 216#endif /* __WINRT__ */ 217 218/* Function/thread to scan the system for joysticks. */ 219static int 220SDL_JoystickThread(void *_data) 221{ 222 SDL_DeviceNotificationData notification_data; 223 224#if SDL_JOYSTICK_XINPUT 225 SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT]; 226 SDL_zero(bOpenedXInputDevices); 227#endif 228 229 if (SDL_CreateDeviceNotification(¬ification_data) < 0) { 230 return -1; 231 } 232 233 SDL_LockMutex(s_mutexJoyStickEnum); 234 while (s_bJoystickThreadQuit == SDL_FALSE) { 235 SDL_bool bXInputChanged = SDL_FALSE; 236 237 if (SDL_WaitForDeviceNotification(¬ification_data, s_mutexJoyStickEnum) == SDL_FALSE) { 238#if SDL_JOYSTICK_XINPUT 239 /* WM_DEVICECHANGE not working, poll for new XINPUT controllers */ 240 SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 1000); 241 if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) { 242 /* scan for any change in XInput devices */ 243 Uint8 userId; 244 for (userId = 0; userId < XUSER_MAX_COUNT; userId++) { 245 XINPUT_CAPABILITIES capabilities; 246 const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities); 247 const SDL_bool available = (result == ERROR_SUCCESS); 248 if (bOpenedXInputDevices[userId] != available) { 249 bXInputChanged = SDL_TRUE; 250 bOpenedXInputDevices[userId] = available; 251 } 252 } 253 } 254#else 255 /* WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive */ 256 break; 257#endif /* SDL_JOYSTICK_XINPUT */ 258 } 259 260 if (s_bWindowsDeviceChanged || bXInputChanged) { 261 s_bDeviceRemoved = SDL_TRUE; 262 s_bDeviceAdded = SDL_TRUE; 263 s_bWindowsDeviceChanged = SDL_FALSE; 264 } 265 } 266 SDL_UnlockMutex(s_mutexJoyStickEnum); 267 268 SDL_CleanupDeviceNotification(¬ification_data); 269 270 return 1; 271} 272 273void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device) 274{ 275 device->send_add_event = SDL_TRUE; 276 device->nInstanceID = SDL_GetNextJoystickInstanceID(); 277 device->pNext = SYS_Joystick; 278 SYS_Joystick = device; 279 280 s_bDeviceAdded = SDL_TRUE; 281} 282 283static void WINDOWS_JoystickDetect(void); 284static void WINDOWS_JoystickQuit(void); 285 286/* Function to scan the system for joysticks. 287 * Joystick 0 should be the system default joystick. 288 * It should return 0, or -1 on an unrecoverable fatal error. 289 */ 290static int 291WINDOWS_JoystickInit(void) 292{ 293 if (SDL_DINPUT_JoystickInit() < 0) { 294 WINDOWS_JoystickQuit(); 295 return -1; 296 } 297 298 if (SDL_XINPUT_JoystickInit() < 0) { 299 WINDOWS_JoystickQuit(); 300 return -1; 301 } 302 303 s_mutexJoyStickEnum = SDL_CreateMutex(); 304 s_condJoystickThread = SDL_CreateCond(); 305 s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */ 306 307 WINDOWS_JoystickDetect(); 308 309 if (!s_threadJoystick) { 310 /* spin up the thread to detect hotplug of devices */ 311 s_bJoystickThreadQuit = SDL_FALSE; 312 s_threadJoystick = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL); 313 } 314 return 0; 315} 316 317/* return the number of joysticks that are connected right now */ 318static int 319WINDOWS_JoystickGetCount(void) 320{ 321 int nJoysticks = 0; 322 JoyStick_DeviceData *device = SYS_Joystick; 323 while (device) { 324 nJoysticks++; 325 device = device->pNext; 326 } 327 328 return nJoysticks; 329} 330 331/* detect any new joysticks being inserted into the system */ 332static void 333WINDOWS_JoystickDetect(void) 334{ 335 JoyStick_DeviceData *pCurList = NULL; 336 337 /* only enum the devices if the joystick thread told us something changed */ 338 if (!s_bDeviceAdded && !s_bDeviceRemoved) { 339 return; /* thread hasn't signaled, nothing to do right now. */ 340 } 341 342 SDL_LockMutex(s_mutexJoyStickEnum); 343 344 s_bDeviceAdded = SDL_FALSE; 345 s_bDeviceRemoved = SDL_FALSE; 346 347 pCurList = SYS_Joystick; 348 SYS_Joystick = NULL; 349 350 /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */ 351 SDL_DINPUT_JoystickDetect(&pCurList); 352 353 /* Look for XInput devices. Do this last, so they're first in the final list. */ 354 SDL_XINPUT_JoystickDetect(&pCurList); 355 356 SDL_UnlockMutex(s_mutexJoyStickEnum); 357 358 while (pCurList) { 359 JoyStick_DeviceData *pListNext = NULL; 360 361 if (pCurList->bXInputDevice) { 362 SDL_XINPUT_MaybeRemoveDevice(pCurList->XInputUserId); 363 } else { 364 SDL_DINPUT_MaybeRemoveDevice(&pCurList->dxdevice); 365 } 366 367 SDL_PrivateJoystickRemoved(pCurList->nInstanceID); 368 369 pListNext = pCurList->pNext; 370 SDL_free(pCurList->joystickname); 371 SDL_free(pCurList); 372 pCurList = pListNext; 373 } 374 375 if (s_bDeviceAdded) { 376 JoyStick_DeviceData *pNewJoystick; 377 int device_index = 0; 378 s_bDeviceAdded = SDL_FALSE; 379 pNewJoystick = SYS_Joystick; 380 while (pNewJoystick) { 381 if (pNewJoystick->send_add_event) { 382 if (pNewJoystick->bXInputDevice) { 383 SDL_XINPUT_MaybeAddDevice(pNewJoystick->XInputUserId); 384 } else { 385 SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice); 386 } 387 388 SDL_PrivateJoystickAdded(pNewJoystick->nInstanceID); 389 390 pNewJoystick->send_add_event = SDL_FALSE; 391 } 392 device_index++; 393 pNewJoystick = pNewJoystick->pNext; 394 } 395 } 396} 397 398/* Function to get the device-dependent name of a joystick */ 399static const char * 400WINDOWS_JoystickGetDeviceName(int device_index) 401{ 402 JoyStick_DeviceData *device = SYS_Joystick; 403 404 for (; device_index > 0; device_index--) 405 device = device->pNext; 406 407 return device->joystickname; 408} 409 410/* return the stable device guid for this device index */ 411static SDL_JoystickGUID 412WINDOWS_JoystickGetDeviceGUID(int device_index) 413{ 414 JoyStick_DeviceData *device = SYS_Joystick; 415 int index; 416 417 for (index = device_index; index > 0; index--) 418 device = device->pNext; 419 420 return device->guid; 421} 422 423/* Function to perform the mapping between current device instance and this joysticks instance id */ 424static SDL_JoystickID 425WINDOWS_JoystickGetDeviceInstanceID(int device_index) 426{ 427 JoyStick_DeviceData *device = SYS_Joystick; 428 int index; 429 430 for (index = device_index; index > 0; index--) 431 device = device->pNext; 432 433 return device->nInstanceID; 434} 435 436/* Function to open a joystick for use. 437 The joystick to open is specified by the device index. 438 This should fill the nbuttons and naxes fields of the joystick structure. 439 It returns 0, or -1 if there is an error. 440 */ 441static int 442WINDOWS_JoystickOpen(SDL_Joystick * joystick, int device_index) 443{ 444 JoyStick_DeviceData *joystickdevice = SYS_Joystick; 445 446 for (; device_index > 0; device_index--) 447 joystickdevice = joystickdevice->pNext; 448 449 /* allocate memory for system specific hardware data */ 450 joystick->instance_id = joystickdevice->nInstanceID; 451 joystick->hwdata = 452 (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata)); 453 if (joystick->hwdata == NULL) { 454 return SDL_OutOfMemory(); 455 } 456 SDL_zerop(joystick->hwdata); 457 joystick->hwdata->guid = joystickdevice->guid; 458 459 if (joystickdevice->bXInputDevice) { 460 return SDL_XINPUT_JoystickOpen(joystick, joystickdevice); 461 } else { 462 return SDL_DINPUT_JoystickOpen(joystick, joystickdevice); 463 } 464} 465 466static int 467WINDOWS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) 468{ 469 if (joystick->hwdata->bXInputDevice) { 470 return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms); 471 } else { 472 return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms); 473 } 474} 475 476static void 477WINDOWS_JoystickUpdate(SDL_Joystick * joystick) 478{ 479 if (!joystick->hwdata) { 480 return; 481 } 482 483 if (joystick->hwdata->bXInputDevice) { 484 SDL_XINPUT_JoystickUpdate(joystick); 485 } else { 486 SDL_DINPUT_JoystickUpdate(joystick); 487 } 488} 489 490/* Function to close a joystick after use */ 491static void 492WINDOWS_JoystickClose(SDL_Joystick * joystick) 493{ 494 if (joystick->hwdata->bXInputDevice) { 495 SDL_XINPUT_JoystickClose(joystick); 496 } else { 497 SDL_DINPUT_JoystickClose(joystick); 498 } 499 500 SDL_free(joystick->hwdata); 501} 502 503/* Function to perform any system-specific joystick related cleanup */ 504static void 505WINDOWS_JoystickQuit(void) 506{ 507 JoyStick_DeviceData *device = SYS_Joystick; 508 509 while (device) { 510 JoyStick_DeviceData *device_next = device->pNext; 511 SDL_free(device->joystickname); 512 SDL_free(device); 513 device = device_next; 514 } 515 SYS_Joystick = NULL; 516 517 if (s_threadJoystick) { 518 SDL_LockMutex(s_mutexJoyStickEnum); 519 s_bJoystickThreadQuit = SDL_TRUE; 520 SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */ 521 SDL_UnlockMutex(s_mutexJoyStickEnum); 522#ifndef __WINRT__ 523 PostThreadMessage(SDL_GetThreadID(s_threadJoystick), WM_QUIT, 0, 0); 524#endif 525 SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */ 526 527 SDL_DestroyMutex(s_mutexJoyStickEnum); 528 SDL_DestroyCond(s_condJoystickThread); 529 s_condJoystickThread= NULL; 530 s_mutexJoyStickEnum = NULL; 531 s_threadJoystick = NULL; 532 } 533 534 SDL_DINPUT_JoystickQuit(); 535 SDL_XINPUT_JoystickQuit(); 536 537 s_bDeviceAdded = SDL_FALSE; 538 s_bDeviceRemoved = SDL_FALSE; 539} 540 541SDL_JoystickDriver SDL_WINDOWS_JoystickDriver = 542{ 543 WINDOWS_JoystickInit, 544 WINDOWS_JoystickGetCount, 545 WINDOWS_JoystickDetect, 546 WINDOWS_JoystickGetDeviceName, 547 WINDOWS_JoystickGetDeviceGUID, 548 WINDOWS_JoystickGetDeviceInstanceID, 549 WINDOWS_JoystickOpen, 550 WINDOWS_JoystickRumble, 551 WINDOWS_JoystickUpdate, 552 WINDOWS_JoystickClose, 553 WINDOWS_JoystickQuit, 554}; 555 556#endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */ 557 558/* vi: set ts=4 sw=4 expandtab: */ 559[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.