Atlas - SDL_sysjoystick.c

Home / ext / SDL2 / src / joystick / linux Lines: 11 | Size: 34248 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#ifdef SDL_JOYSTICK_LINUX 24 25#ifndef SDL_INPUT_LINUXEV 26#error SDL now requires a Linux 2.4+ kernel with /dev/input/event support. 27#endif 28 29/* This is the Linux implementation of the SDL joystick API */ 30 31#include <sys/stat.h> 32#include <errno.h> /* errno, strerror */ 33#include <fcntl.h> 34#include <limits.h> /* For the definition of PATH_MAX */ 35#include <sys/ioctl.h> 36#include <unistd.h> 37#include <linux/joystick.h> 38 39#include "SDL_assert.h" 40#include "SDL_joystick.h" 41#include "SDL_endian.h" 42#include "../../events/SDL_events_c.h" 43#include "../SDL_sysjoystick.h" 44#include "../SDL_joystick_c.h" 45#include "../steam/SDL_steamcontroller.h" 46#include "SDL_sysjoystick_c.h" 47#include "../hidapi/SDL_hidapijoystick_c.h" 48 49/* This isn't defined in older Linux kernel headers */ 50#ifndef SYN_DROPPED 51#define SYN_DROPPED 3 52#endif 53 54#include "../../core/linux/SDL_udev.h" 55 56static int MaybeAddDevice(const char *path); 57#if SDL_USE_LIBUDEV 58static int MaybeRemoveDevice(const char *path); 59static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); 60#endif /* SDL_USE_LIBUDEV */ 61 62 63/* A linked list of available joysticks */ 64typedef struct SDL_joylist_item 65{ 66 int device_instance; 67 char *path; /* "/dev/input/event2" or whatever */ 68 char *name; /* "SideWinder 3D Pro" or whatever */ 69 SDL_JoystickGUID guid; 70 dev_t devnum; 71 struct joystick_hwdata *hwdata; 72 struct SDL_joylist_item *next; 73 74 /* Steam Controller support */ 75 SDL_bool m_bSteamController; 76} SDL_joylist_item; 77 78static SDL_joylist_item *SDL_joylist = NULL; 79static SDL_joylist_item *SDL_joylist_tail = NULL; 80static int numjoysticks = 0; 81 82 83#define test_bit(nr, addr) \ 84 (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0) 85#define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1) 86 87static int 88IsJoystick(int fd, char *namebuf, const size_t namebuflen, SDL_JoystickGUID *guid) 89{ 90 /* This list is taken from: 91 https://raw.githubusercontent.com/denilsonsa/udev-joystick-blacklist/master/generate_rules.py 92 */ 93 static Uint32 joystick_blacklist[] = { 94 /* Microsoft Microsoft Wireless Optical Desktop® 2.10 */ 95 /* Microsoft Wireless Desktop - Comfort Edition */ 96 MAKE_VIDPID(0x045e, 0x009d), 97 98 /* Microsoft Microsoft® Digital Media Pro Keyboard */ 99 /* Microsoft Corp. Digital Media Pro Keyboard */ 100 MAKE_VIDPID(0x045e, 0x00b0), 101 102 /* Microsoft Microsoft® Digital Media Keyboard */ 103 /* Microsoft Corp. Digital Media Keyboard 1.0A */ 104 MAKE_VIDPID(0x045e, 0x00b4), 105 106 /* Microsoft Microsoft® Digital Media Keyboard 3000 */ 107 MAKE_VIDPID(0x045e, 0x0730), 108 109 /* Microsoft Microsoft® 2.4GHz Transceiver v6.0 */ 110 /* Microsoft Microsoft® 2.4GHz Transceiver v8.0 */ 111 /* Microsoft Corp. Nano Transceiver v1.0 for Bluetooth */ 112 /* Microsoft Wireless Mobile Mouse 1000 */ 113 /* Microsoft Wireless Desktop 3000 */ 114 MAKE_VIDPID(0x045e, 0x0745), 115 116 /* Microsoft® SideWinder(TM) 2.4GHz Transceiver */ 117 MAKE_VIDPID(0x045e, 0x0748), 118 119 /* Microsoft Corp. Wired Keyboard 600 */ 120 MAKE_VIDPID(0x045e, 0x0750), 121 122 /* Microsoft Corp. Sidewinder X4 keyboard */ 123 MAKE_VIDPID(0x045e, 0x0768), 124 125 /* Microsoft Corp. Arc Touch Mouse Transceiver */ 126 MAKE_VIDPID(0x045e, 0x0773), 127 128 /* Microsoft® 2.4GHz Transceiver v9.0 */ 129 /* Microsoft® Nano Transceiver v2.1 */ 130 /* Microsoft Sculpt Ergonomic Keyboard (5KV-00001) */ 131 MAKE_VIDPID(0x045e, 0x07a5), 132 133 /* Microsoft® Nano Transceiver v1.0 */ 134 /* Microsoft Wireless Keyboard 800 */ 135 MAKE_VIDPID(0x045e, 0x07b2), 136 137 /* Microsoft® Nano Transceiver v2.0 */ 138 MAKE_VIDPID(0x045e, 0x0800), 139 140 /* List of Wacom devices at: http://linuxwacom.sourceforge.net/wiki/index.php/Device_IDs */ 141 MAKE_VIDPID(0x056a, 0x0010), /* Wacom ET-0405 Graphire */ 142 MAKE_VIDPID(0x056a, 0x0011), /* Wacom ET-0405A Graphire2 (4x5) */ 143 MAKE_VIDPID(0x056a, 0x0012), /* Wacom ET-0507A Graphire2 (5x7) */ 144 MAKE_VIDPID(0x056a, 0x0013), /* Wacom CTE-430 Graphire3 (4x5) */ 145 MAKE_VIDPID(0x056a, 0x0014), /* Wacom CTE-630 Graphire3 (6x8) */ 146 MAKE_VIDPID(0x056a, 0x0015), /* Wacom CTE-440 Graphire4 (4x5) */ 147 MAKE_VIDPID(0x056a, 0x0016), /* Wacom CTE-640 Graphire4 (6x8) */ 148 MAKE_VIDPID(0x056a, 0x0017), /* Wacom CTE-450 Bamboo Fun (4x5) */ 149 MAKE_VIDPID(0x056a, 0x0016), /* Wacom CTE-640 Graphire 4 6x8 */ 150 MAKE_VIDPID(0x056a, 0x0017), /* Wacom CTE-450 Bamboo Fun 4x5 */ 151 MAKE_VIDPID(0x056a, 0x0018), /* Wacom CTE-650 Bamboo Fun 6x8 */ 152 MAKE_VIDPID(0x056a, 0x0019), /* Wacom CTE-631 Bamboo One */ 153 MAKE_VIDPID(0x056a, 0x00d1), /* Wacom Bamboo Pen and Touch CTH-460 */ 154 155 MAKE_VIDPID(0x09da, 0x054f), /* A4 Tech Co., G7 750 mouse */ 156 MAKE_VIDPID(0x09da, 0x3043), /* A4 Tech Co., Ltd Bloody R8A Gaming Mouse */ 157 MAKE_VIDPID(0x09da, 0x31b5), /* A4 Tech Co., Ltd Bloody TL80 Terminator Laser Gaming Mouse */ 158 MAKE_VIDPID(0x09da, 0x3997), /* A4 Tech Co., Ltd Bloody RT7 Terminator Wireless */ 159 MAKE_VIDPID(0x09da, 0x3f8b), /* A4 Tech Co., Ltd Bloody V8 mouse */ 160 MAKE_VIDPID(0x09da, 0x51f4), /* Modecom MC-5006 Keyboard */ 161 MAKE_VIDPID(0x09da, 0x5589), /* A4 Tech Co., Ltd Terminator TL9 Laser Gaming Mouse */ 162 MAKE_VIDPID(0x09da, 0x7b22), /* A4 Tech Co., Ltd Bloody V5 */ 163 MAKE_VIDPID(0x09da, 0x7f2d), /* A4 Tech Co., Ltd Bloody R3 mouse */ 164 MAKE_VIDPID(0x09da, 0x8090), /* A4 Tech Co., Ltd X-718BK Oscar Optical Gaming Mouse */ 165 MAKE_VIDPID(0x09da, 0x9066), /* A4 Tech Co., Sharkoon Fireglider Optical */ 166 MAKE_VIDPID(0x09da, 0x9090), /* A4 Tech Co., Ltd XL-730K / XL-750BK / XL-755BK Laser Mouse */ 167 MAKE_VIDPID(0x09da, 0x90c0), /* A4 Tech Co., Ltd X7 G800V keyboard */ 168 MAKE_VIDPID(0x09da, 0xf012), /* A4 Tech Co., Ltd Bloody V7 mouse */ 169 MAKE_VIDPID(0x09da, 0xf32a), /* A4 Tech Co., Ltd Bloody B540 keyboard */ 170 MAKE_VIDPID(0x09da, 0xf613), /* A4 Tech Co., Ltd Bloody V2 mouse */ 171 MAKE_VIDPID(0x09da, 0xf624), /* A4 Tech Co., Ltd Bloody B120 Keyboard */ 172 173 MAKE_VIDPID(0x1d57, 0xad03), /* [T3] 2.4GHz and IR Air Mouse Remote Control */ 174 175 MAKE_VIDPID(0x1e7d, 0x2e4a), /* Roccat Tyon Mouse */ 176 177 MAKE_VIDPID(0x20a0, 0x422d), /* Winkeyless.kr Keyboards */ 178 179 MAKE_VIDPID(0x2516, 0x001f), /* Cooler Master Storm Mizar Mouse */ 180 MAKE_VIDPID(0x2516, 0x0028), /* Cooler Master Storm Alcor Mouse */ 181 }; 182 struct input_id inpid; 183 int i; 184 Uint32 id; 185 Uint16 *guid16 = (Uint16 *)guid->data; 186 187#if !SDL_USE_LIBUDEV 188 /* When udev is enabled we only get joystick devices here, so there's no need to test them */ 189 unsigned long evbit[NBITS(EV_MAX)] = { 0 }; 190 unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; 191 unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; 192 193 if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) || 194 (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || 195 (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) { 196 return (0); 197 } 198 199 if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) && 200 test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) { 201 return 0; 202 } 203#endif 204 205 if (ioctl(fd, EVIOCGNAME(namebuflen), namebuf) < 0) { 206 return 0; 207 } 208 209 if (ioctl(fd, EVIOCGID, &inpid) < 0) { 210 return 0; 211 } 212 213#ifdef SDL_JOYSTICK_HIDAPI 214 if (HIDAPI_IsDevicePresent(inpid.vendor, inpid.product, inpid.version)) { 215 /* The HIDAPI driver is taking care of this device */ 216 return 0; 217 } 218#endif 219 220 /* Check the joystick blacklist */ 221 id = MAKE_VIDPID(inpid.vendor, inpid.product); 222 for (i = 0; i < SDL_arraysize(joystick_blacklist); ++i) { 223 if (id == joystick_blacklist[i]) { 224 return 0; 225 } 226 } 227 228#ifdef DEBUG_JOYSTICK 229 printf("Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d\n", namebuf, inpid.bustype, inpid.vendor, inpid.product, inpid.version); 230#endif 231 232 SDL_memset(guid->data, 0, sizeof(guid->data)); 233 234 /* We only need 16 bits for each of these; space them out to fill 128. */ 235 /* Byteswap so devices get same GUID on little/big endian platforms. */ 236 *guid16++ = SDL_SwapLE16(inpid.bustype); 237 *guid16++ = 0; 238 239 if (inpid.vendor && inpid.product) { 240 *guid16++ = SDL_SwapLE16(inpid.vendor); 241 *guid16++ = 0; 242 *guid16++ = SDL_SwapLE16(inpid.product); 243 *guid16++ = 0; 244 *guid16++ = SDL_SwapLE16(inpid.version); 245 *guid16++ = 0; 246 } else { 247 SDL_strlcpy((char*)guid16, namebuf, sizeof(guid->data) - 4); 248 } 249 250 if (SDL_ShouldIgnoreJoystick(namebuf, *guid)) { 251 return 0; 252 } 253 return 1; 254} 255 256#if SDL_USE_LIBUDEV 257static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) 258{ 259 if (devpath == NULL) { 260 return; 261 } 262 263 switch (udev_type) { 264 case SDL_UDEV_DEVICEADDED: 265 if (!(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) { 266 return; 267 } 268 MaybeAddDevice(devpath); 269 break; 270 271 case SDL_UDEV_DEVICEREMOVED: 272 MaybeRemoveDevice(devpath); 273 break; 274 275 default: 276 break; 277 } 278 279} 280#endif /* SDL_USE_LIBUDEV */ 281 282 283/* !!! FIXME: I would love to dump this code and use libudev instead. */ 284static int 285MaybeAddDevice(const char *path) 286{ 287 struct stat sb; 288 int fd = -1; 289 int isstick = 0; 290 char namebuf[128]; 291 SDL_JoystickGUID guid; 292 SDL_joylist_item *item; 293 294 if (path == NULL) { 295 return -1; 296 } 297 298 if (stat(path, &sb) == -1) { 299 return -1; 300 } 301 302 /* Check to make sure it's not already in list. */ 303 for (item = SDL_joylist; item != NULL; item = item->next) { 304 if (sb.st_rdev == item->devnum) { 305 return -1; /* already have this one */ 306 } 307 } 308 309 fd = open(path, O_RDONLY, 0); 310 if (fd < 0) { 311 return -1; 312 } 313 314#ifdef DEBUG_INPUT_EVENTS 315 printf("Checking %s\n", path); 316#endif 317 318 isstick = IsJoystick(fd, namebuf, sizeof (namebuf), &guid); 319 close(fd); 320 if (!isstick) { 321 return -1; 322 } 323 324 item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item)); 325 if (item == NULL) { 326 return -1; 327 } 328 329 SDL_zerop(item); 330 item->devnum = sb.st_rdev; 331 item->path = SDL_strdup(path); 332 item->name = SDL_strdup(namebuf); 333 item->guid = guid; 334 335 if ((item->path == NULL) || (item->name == NULL)) { 336 SDL_free(item->path); 337 SDL_free(item->name); 338 SDL_free(item); 339 return -1; 340 } 341 342 item->device_instance = SDL_GetNextJoystickInstanceID(); 343 if (SDL_joylist_tail == NULL) { 344 SDL_joylist = SDL_joylist_tail = item; 345 } else { 346 SDL_joylist_tail->next = item; 347 SDL_joylist_tail = item; 348 } 349 350 /* Need to increment the joystick count before we post the event */ 351 ++numjoysticks; 352 353 SDL_PrivateJoystickAdded(item->device_instance); 354 355 return numjoysticks; 356} 357 358#if SDL_USE_LIBUDEV 359/* !!! FIXME: I would love to dump this code and use libudev instead. */ 360static int 361MaybeRemoveDevice(const char *path) 362{ 363 SDL_joylist_item *item; 364 SDL_joylist_item *prev = NULL; 365 366 if (path == NULL) { 367 return -1; 368 } 369 370 for (item = SDL_joylist; item != NULL; item = item->next) { 371 /* found it, remove it. */ 372 if (SDL_strcmp(path, item->path) == 0) { 373 const int retval = item->device_instance; 374 if (item->hwdata) { 375 item->hwdata->item = NULL; 376 } 377 if (prev != NULL) { 378 prev->next = item->next; 379 } else { 380 SDL_assert(SDL_joylist == item); 381 SDL_joylist = item->next; 382 } 383 if (item == SDL_joylist_tail) { 384 SDL_joylist_tail = prev; 385 } 386 387 /* Need to decrement the joystick count before we post the event */ 388 --numjoysticks; 389 390 SDL_PrivateJoystickRemoved(item->device_instance); 391 392 SDL_free(item->path); 393 SDL_free(item->name); 394 SDL_free(item); 395 return retval; 396 } 397 prev = item; 398 } 399 400 return -1; 401} 402#endif 403 404#if ! SDL_USE_LIBUDEV 405static int 406JoystickInitWithoutUdev(void) 407{ 408 int i; 409 char path[PATH_MAX]; 410 411 /* !!! FIXME: only finds sticks if they're called /dev/input/event[0..31] */ 412 /* !!! FIXME: we could at least readdir() through /dev/input...? */ 413 /* !!! FIXME: (or delete this and rely on libudev?) */ 414 for (i = 0; i < 32; i++) { 415 SDL_snprintf(path, SDL_arraysize(path), "/dev/input/event%d", i); 416 MaybeAddDevice(path); 417 } 418 419 return 0; 420} 421#endif 422 423#if SDL_USE_LIBUDEV 424static int 425JoystickInitWithUdev(void) 426{ 427 if (SDL_UDEV_Init() < 0) { 428 return SDL_SetError("Could not initialize UDEV"); 429 } 430 431 /* Set up the udev callback */ 432 if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) { 433 SDL_UDEV_Quit(); 434 return SDL_SetError("Could not set up joystick <-> udev callback"); 435 } 436 437 /* Force a scan to build the initial device list */ 438 SDL_UDEV_Scan(); 439 440 return 0; 441} 442#endif 443 444static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance) 445{ 446 SDL_joylist_item *item; 447 448 item = (SDL_joylist_item *) SDL_calloc(1, sizeof (SDL_joylist_item)); 449 if (item == NULL) { 450 return SDL_FALSE; 451 } 452 453 item->path = SDL_strdup(""); 454 item->name = SDL_strdup(name); 455 item->guid = guid; 456 item->m_bSteamController = SDL_TRUE; 457 458 if ((item->path == NULL) || (item->name == NULL)) { 459 SDL_free(item->path); 460 SDL_free(item->name); 461 SDL_free(item); 462 return SDL_FALSE; 463 } 464 465 *device_instance = item->device_instance = SDL_GetNextJoystickInstanceID(); 466 if (SDL_joylist_tail == NULL) { 467 SDL_joylist = SDL_joylist_tail = item; 468 } else { 469 SDL_joylist_tail->next = item; 470 SDL_joylist_tail = item; 471 } 472 473 /* Need to increment the joystick count before we post the event */ 474 ++numjoysticks; 475 476 SDL_PrivateJoystickAdded(item->device_instance); 477 478 return SDL_TRUE; 479} 480 481static void SteamControllerDisconnectedCallback(int device_instance) 482{ 483 SDL_joylist_item *item; 484 SDL_joylist_item *prev = NULL; 485 486 for (item = SDL_joylist; item != NULL; item = item->next) { 487 /* found it, remove it. */ 488 if (item->device_instance == device_instance) { 489 if (item->hwdata) { 490 item->hwdata->item = NULL; 491 } 492 if (prev != NULL) { 493 prev->next = item->next; 494 } else { 495 SDL_assert(SDL_joylist == item); 496 SDL_joylist = item->next; 497 } 498 if (item == SDL_joylist_tail) { 499 SDL_joylist_tail = prev; 500 } 501 502 /* Need to decrement the joystick count before we post the event */ 503 --numjoysticks; 504 505 SDL_PrivateJoystickRemoved(item->device_instance); 506 507 SDL_free(item->name); 508 SDL_free(item); 509 return; 510 } 511 prev = item; 512 } 513} 514 515static int 516LINUX_JoystickInit(void) 517{ 518 /* First see if the user specified one or more joysticks to use */ 519 if (SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL) { 520 char *envcopy, *envpath, *delim; 521 envcopy = SDL_strdup(SDL_getenv("SDL_JOYSTICK_DEVICE")); 522 envpath = envcopy; 523 while (envpath != NULL) { 524 delim = SDL_strchr(envpath, ':'); 525 if (delim != NULL) { 526 *delim++ = '\0'; 527 } 528 MaybeAddDevice(envpath); 529 envpath = delim; 530 } 531 SDL_free(envcopy); 532 } 533 534 SDL_InitSteamControllers(SteamControllerConnectedCallback, 535 SteamControllerDisconnectedCallback); 536 537#if SDL_USE_LIBUDEV 538 return JoystickInitWithUdev(); 539#else 540 return JoystickInitWithoutUdev(); 541#endif 542} 543 544static int 545LINUX_JoystickGetCount(void) 546{ 547 return numjoysticks; 548} 549 550static void 551LINUX_JoystickDetect(void) 552{ 553#if SDL_USE_LIBUDEV 554 SDL_UDEV_Poll(); 555#endif 556 557 SDL_UpdateSteamControllers(); 558} 559 560static SDL_joylist_item * 561JoystickByDevIndex(int device_index) 562{ 563 SDL_joylist_item *item = SDL_joylist; 564 565 if ((device_index < 0) || (device_index >= numjoysticks)) { 566 return NULL; 567 } 568 569 while (device_index > 0) { 570 SDL_assert(item != NULL); 571 device_index--; 572 item = item->next; 573 } 574 575 return item; 576} 577 578/* Function to get the device-dependent name of a joystick */ 579static const char * 580LINUX_JoystickGetDeviceName(int device_index) 581{ 582 return JoystickByDevIndex(device_index)->name; 583} 584 585static SDL_JoystickGUID 586LINUX_JoystickGetDeviceGUID( int device_index ) 587{ 588 return JoystickByDevIndex(device_index)->guid; 589} 590 591/* Function to perform the mapping from device index to the instance id for this index */ 592static SDL_JoystickID 593LINUX_JoystickGetDeviceInstanceID(int device_index) 594{ 595 return JoystickByDevIndex(device_index)->device_instance; 596} 597 598static int 599allocate_hatdata(SDL_Joystick * joystick) 600{ 601 int i; 602 603 joystick->hwdata->hats = 604 (struct hwdata_hat *) SDL_malloc(joystick->nhats * 605 sizeof(struct hwdata_hat)); 606 if (joystick->hwdata->hats == NULL) { 607 return (-1); 608 } 609 for (i = 0; i < joystick->nhats; ++i) { 610 joystick->hwdata->hats[i].axis[0] = 1; 611 joystick->hwdata->hats[i].axis[1] = 1; 612 } 613 return (0); 614} 615 616static int 617allocate_balldata(SDL_Joystick * joystick) 618{ 619 int i; 620 621 joystick->hwdata->balls = 622 (struct hwdata_ball *) SDL_malloc(joystick->nballs * 623 sizeof(struct hwdata_ball)); 624 if (joystick->hwdata->balls == NULL) { 625 return (-1); 626 } 627 for (i = 0; i < joystick->nballs; ++i) { 628 joystick->hwdata->balls[i].axis[0] = 0; 629 joystick->hwdata->balls[i].axis[1] = 0; 630 } 631 return (0); 632} 633 634static void 635ConfigJoystick(SDL_Joystick * joystick, int fd) 636{ 637 int i, t; 638 unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; 639 unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; 640 unsigned long relbit[NBITS(REL_MAX)] = { 0 }; 641 unsigned long ffbit[NBITS(FF_MAX)] = { 0 }; 642 643 /* See if this device uses the new unified event API */ 644 if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) && 645 (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) && 646 (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) { 647 648 /* Get the number of buttons, axes, and other thingamajigs */ 649 for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) { 650 if (test_bit(i, keybit)) { 651#ifdef DEBUG_INPUT_EVENTS 652 printf("Joystick has button: 0x%x\n", i); 653#endif 654 joystick->hwdata->key_map[i] = joystick->nbuttons; 655 ++joystick->nbuttons; 656 } 657 } 658 for (i = 0; i < BTN_JOYSTICK; ++i) { 659 if (test_bit(i, keybit)) { 660#ifdef DEBUG_INPUT_EVENTS 661 printf("Joystick has button: 0x%x\n", i); 662#endif 663 joystick->hwdata->key_map[i] = joystick->nbuttons; 664 ++joystick->nbuttons; 665 } 666 } 667 for (i = 0; i < ABS_MAX; ++i) { 668 /* Skip hats */ 669 if (i == ABS_HAT0X) { 670 i = ABS_HAT3Y; 671 continue; 672 } 673 if (test_bit(i, absbit)) { 674 struct input_absinfo absinfo; 675 676 if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) { 677 continue; 678 } 679#ifdef DEBUG_INPUT_EVENTS 680 printf("Joystick has absolute axis: 0x%.2x\n", i); 681 printf("Values = { %d, %d, %d, %d, %d }\n", 682 absinfo.value, absinfo.minimum, absinfo.maximum, 683 absinfo.fuzz, absinfo.flat); 684#endif /* DEBUG_INPUT_EVENTS */ 685 joystick->hwdata->abs_map[i] = joystick->naxes; 686 if (absinfo.minimum == absinfo.maximum) { 687 joystick->hwdata->abs_correct[i].used = 0; 688 } else { 689 joystick->hwdata->abs_correct[i].used = 1; 690 joystick->hwdata->abs_correct[i].coef[0] = 691 (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat; 692 joystick->hwdata->abs_correct[i].coef[1] = 693 (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat; 694 t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat); 695 if (t != 0) { 696 joystick->hwdata->abs_correct[i].coef[2] = 697 (1 << 28) / t; 698 } else { 699 joystick->hwdata->abs_correct[i].coef[2] = 0; 700 } 701 } 702 ++joystick->naxes; 703 } 704 } 705 for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) { 706 if (test_bit(i, absbit) || test_bit(i + 1, absbit)) { 707 struct input_absinfo absinfo; 708 709 if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) { 710 continue; 711 } 712#ifdef DEBUG_INPUT_EVENTS 713 printf("Joystick has hat %d\n", (i - ABS_HAT0X) / 2); 714 printf("Values = { %d, %d, %d, %d, %d }\n", 715 absinfo.value, absinfo.minimum, absinfo.maximum, 716 absinfo.fuzz, absinfo.flat); 717#endif /* DEBUG_INPUT_EVENTS */ 718 ++joystick->nhats; 719 } 720 } 721 if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) { 722 ++joystick->nballs; 723 } 724 725 /* Allocate data to keep track of these thingamajigs */ 726 if (joystick->nhats > 0) { 727 if (allocate_hatdata(joystick) < 0) { 728 joystick->nhats = 0; 729 } 730 } 731 if (joystick->nballs > 0) { 732 if (allocate_balldata(joystick) < 0) { 733 joystick->nballs = 0; 734 } 735 } 736 } 737 738 if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) { 739 if (test_bit(FF_RUMBLE, ffbit)) { 740 joystick->hwdata->ff_rumble = SDL_TRUE; 741 } 742 if (test_bit(FF_SINE, ffbit)) { 743 joystick->hwdata->ff_sine = SDL_TRUE; 744 } 745 } 746} 747 748 749/* Function to open a joystick for use. 750 The joystick to open is specified by the device index. 751 This should fill the nbuttons and naxes fields of the joystick structure. 752 It returns 0, or -1 if there is an error. 753 */ 754static int 755LINUX_JoystickOpen(SDL_Joystick * joystick, int device_index) 756{ 757 SDL_joylist_item *item = JoystickByDevIndex(device_index); 758 759 if (item == NULL) { 760 return SDL_SetError("No such device"); 761 } 762 763 joystick->instance_id = item->device_instance; 764 joystick->hwdata = (struct joystick_hwdata *) 765 SDL_calloc(1, sizeof(*joystick->hwdata)); 766 if (joystick->hwdata == NULL) { 767 return SDL_OutOfMemory(); 768 } 769 joystick->hwdata->item = item; 770 joystick->hwdata->guid = item->guid; 771 joystick->hwdata->effect.id = -1; 772 joystick->hwdata->m_bSteamController = item->m_bSteamController; 773 774 if (item->m_bSteamController) { 775 joystick->hwdata->fd = -1; 776 SDL_GetSteamControllerInputs(&joystick->nbuttons, 777 &joystick->naxes, 778 &joystick->nhats); 779 } else { 780 int fd = open(item->path, O_RDWR, 0); 781 if (fd < 0) { 782 SDL_free(joystick->hwdata); 783 joystick->hwdata = NULL; 784 return SDL_SetError("Unable to open %s", item->path); 785 } 786 787 joystick->hwdata->fd = fd; 788 joystick->hwdata->fname = SDL_strdup(item->path); 789 if (joystick->hwdata->fname == NULL) { 790 SDL_free(joystick->hwdata); 791 joystick->hwdata = NULL; 792 close(fd); 793 return SDL_OutOfMemory(); 794 } 795 796 /* Set the joystick to non-blocking read mode */ 797 fcntl(fd, F_SETFL, O_NONBLOCK); 798 799 /* Get the number of buttons and axes on the joystick */ 800 ConfigJoystick(joystick, fd); 801 } 802 803 SDL_assert(item->hwdata == NULL); 804 item->hwdata = joystick->hwdata; 805 806 /* mark joystick as fresh and ready */ 807 joystick->hwdata->fresh = 1; 808 809 return (0); 810} 811 812static int 813LINUX_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) 814{ 815 struct input_event event; 816 817 if (joystick->hwdata->effect.id < 0) { 818 if (joystick->hwdata->ff_rumble) { 819 struct ff_effect *effect = &joystick->hwdata->effect; 820 821 effect->type = FF_RUMBLE; 822 effect->replay.length = SDL_min(duration_ms, 32767); 823 effect->u.rumble.strong_magnitude = low_frequency_rumble; 824 effect->u.rumble.weak_magnitude = high_frequency_rumble; 825 } else if (joystick->hwdata->ff_sine) { 826 /* Scale and average the two rumble strengths */ 827 Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2); 828 struct ff_effect *effect = &joystick->hwdata->effect; 829 830 effect->type = FF_PERIODIC; 831 effect->replay.length = SDL_min(duration_ms, 32767); 832 effect->u.periodic.waveform = FF_SINE; 833 effect->u.periodic.magnitude = magnitude; 834 } else { 835 return SDL_Unsupported(); 836 } 837 } 838 839 if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) { 840 return SDL_SetError("Couldn't update rumble effect: %s", strerror(errno)); 841 } 842 843 event.type = EV_FF; 844 event.code = joystick->hwdata->effect.id; 845 event.value = 1; 846 if (write(joystick->hwdata->fd, &event, sizeof(event)) < 0) { 847 return SDL_SetError("Couldn't start rumble effect: %s", strerror(errno)); 848 } 849 return 0; 850} 851 852static SDL_INLINE void 853HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value) 854{ 855 struct hwdata_hat *the_hat; 856 const Uint8 position_map[3][3] = { 857 {SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP}, 858 {SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT}, 859 {SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN} 860 }; 861 862 the_hat = &stick->hwdata->hats[hat]; 863 if (value < 0) { 864 value = 0; 865 } else if (value == 0) { 866 value = 1; 867 } else if (value > 0) { 868 value = 2; 869 } 870 if (value != the_hat->axis[axis]) { 871 the_hat->axis[axis] = value; 872 SDL_PrivateJoystickHat(stick, hat, 873 position_map[the_hat-> 874 axis[1]][the_hat->axis[0]]); 875 } 876} 877 878static SDL_INLINE void 879HandleBall(SDL_Joystick * stick, Uint8 ball, int axis, int value) 880{ 881 stick->hwdata->balls[ball].axis[axis] += value; 882} 883 884 885static SDL_INLINE int 886AxisCorrect(SDL_Joystick * joystick, int which, int value) 887{ 888 struct axis_correct *correct; 889 890 correct = &joystick->hwdata->abs_correct[which]; 891 if (correct->used) { 892 value *= 2; 893 if (value > correct->coef[0]) { 894 if (value < correct->coef[1]) { 895 return 0; 896 } 897 value -= correct->coef[1]; 898 } else { 899 value -= correct->coef[0]; 900 } 901 value *= correct->coef[2]; 902 value >>= 13; 903 } 904 905 /* Clamp and return */ 906 if (value < -32768) 907 return -32768; 908 if (value > 32767) 909 return 32767; 910 911 return value; 912} 913 914static SDL_INLINE void 915PollAllValues(SDL_Joystick * joystick) 916{ 917 struct input_absinfo absinfo; 918 int a, b = 0; 919 920 /* Poll all axis */ 921 for (a = ABS_X; b < ABS_MAX; a++) { 922 switch (a) { 923 case ABS_HAT0X: 924 case ABS_HAT0Y: 925 case ABS_HAT1X: 926 case ABS_HAT1Y: 927 case ABS_HAT2X: 928 case ABS_HAT2Y: 929 case ABS_HAT3X: 930 case ABS_HAT3Y: 931 /* ingore hats */ 932 break; 933 default: 934 if (joystick->hwdata->abs_correct[b].used) { 935 if (ioctl(joystick->hwdata->fd, EVIOCGABS(a), &absinfo) >= 0) { 936 absinfo.value = AxisCorrect(joystick, b, absinfo.value); 937 938#ifdef DEBUG_INPUT_EVENTS 939 printf("Joystick : Re-read Axis %d (%d) val= %d\n", 940 joystick->hwdata->abs_map[b], a, absinfo.value); 941#endif 942 SDL_PrivateJoystickAxis(joystick, 943 joystick->hwdata->abs_map[b], 944 absinfo.value); 945 } 946 } 947 b++; 948 } 949 } 950} 951 952static SDL_INLINE void 953HandleInputEvents(SDL_Joystick * joystick) 954{ 955 struct input_event events[32]; 956 int i, len; 957 int code; 958 959 if (joystick->hwdata->fresh) { 960 PollAllValues(joystick); 961 joystick->hwdata->fresh = 0; 962 } 963 964 while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) { 965 len /= sizeof(events[0]); 966 for (i = 0; i < len; ++i) { 967 code = events[i].code; 968 switch (events[i].type) { 969 case EV_KEY: 970 SDL_PrivateJoystickButton(joystick, 971 joystick->hwdata->key_map[code], 972 events[i].value); 973 break; 974 case EV_ABS: 975 switch (code) { 976 case ABS_HAT0X: 977 case ABS_HAT0Y: 978 case ABS_HAT1X: 979 case ABS_HAT1Y: 980 case ABS_HAT2X: 981 case ABS_HAT2Y: 982 case ABS_HAT3X: 983 case ABS_HAT3Y: 984 code -= ABS_HAT0X; 985 HandleHat(joystick, code / 2, code % 2, events[i].value); 986 break; 987 default: 988 events[i].value = 989 AxisCorrect(joystick, code, events[i].value); 990 SDL_PrivateJoystickAxis(joystick, 991 joystick->hwdata->abs_map[code], 992 events[i].value); 993 break; 994 } 995 break; 996 case EV_REL: 997 switch (code) { 998 case REL_X: 999 case REL_Y: 1000 code -= REL_X; 1001 HandleBall(joystick, code / 2, code % 2, events[i].value); 1002 break; 1003 default: 1004 break; 1005 } 1006 break; 1007 case EV_SYN: 1008 switch (code) { 1009 case SYN_DROPPED : 1010#ifdef DEBUG_INPUT_EVENTS 1011 printf("Event SYN_DROPPED detected\n"); 1012#endif 1013 PollAllValues(joystick); 1014 break; 1015 default: 1016 break; 1017 } 1018 default: 1019 break; 1020 } 1021 } 1022 } 1023} 1024 1025static void 1026LINUX_JoystickUpdate(SDL_Joystick * joystick) 1027{ 1028 int i; 1029 1030 if (joystick->hwdata->m_bSteamController) { 1031 SDL_UpdateSteamController(joystick); 1032 return; 1033 } 1034 1035 HandleInputEvents(joystick); 1036 1037 /* Deliver ball motion updates */ 1038 for (i = 0; i < joystick->nballs; ++i) { 1039 int xrel, yrel; 1040 1041 xrel = joystick->hwdata->balls[i].axis[0]; 1042 yrel = joystick->hwdata->balls[i].axis[1]; 1043 if (xrel || yrel) { 1044 joystick->hwdata->balls[i].axis[0] = 0; 1045 joystick->hwdata->balls[i].axis[1] = 0; 1046 SDL_PrivateJoystickBall(joystick, (Uint8) i, xrel, yrel); 1047 } 1048 } 1049} 1050 1051/* Function to close a joystick after use */ 1052static void 1053LINUX_JoystickClose(SDL_Joystick * joystick) 1054{ 1055 if (joystick->hwdata) { 1056 if (joystick->hwdata->effect.id >= 0) { 1057 ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id); 1058 joystick->hwdata->effect.id = -1; 1059 } 1060 if (joystick->hwdata->fd >= 0) { 1061 close(joystick->hwdata->fd); 1062 } 1063 if (joystick->hwdata->item) { 1064 joystick->hwdata->item->hwdata = NULL; 1065 } 1066 SDL_free(joystick->hwdata->hats); 1067 SDL_free(joystick->hwdata->balls); 1068 SDL_free(joystick->hwdata->fname); 1069 SDL_free(joystick->hwdata); 1070 } 1071} 1072 1073/* Function to perform any system-specific joystick related cleanup */ 1074static void 1075LINUX_JoystickQuit(void) 1076{ 1077 SDL_joylist_item *item = NULL; 1078 SDL_joylist_item *next = NULL; 1079 1080 for (item = SDL_joylist; item; item = next) { 1081 next = item->next; 1082 SDL_free(item->path); 1083 SDL_free(item->name); 1084 SDL_free(item); 1085 } 1086 1087 SDL_joylist = SDL_joylist_tail = NULL; 1088 1089 numjoysticks = 0; 1090 1091#if SDL_USE_LIBUDEV 1092 SDL_UDEV_DelCallback(joystick_udev_callback); 1093 SDL_UDEV_Quit(); 1094#endif 1095 1096 SDL_QuitSteamControllers(); 1097} 1098 1099SDL_JoystickDriver SDL_LINUX_JoystickDriver = 1100{ 1101 LINUX_JoystickInit, 1102 LINUX_JoystickGetCount, 1103 LINUX_JoystickDetect, 1104 LINUX_JoystickGetDeviceName, 1105 LINUX_JoystickGetDeviceGUID, 1106 LINUX_JoystickGetDeviceInstanceID, 1107 LINUX_JoystickOpen, 1108 LINUX_JoystickRumble, 1109 LINUX_JoystickUpdate, 1110 LINUX_JoystickClose, 1111 LINUX_JoystickQuit, 1112}; 1113 1114#endif /* SDL_JOYSTICK_LINUX */ 1115 1116/* vi: set ts=4 sw=4 expandtab: */ 1117
[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.