Simple Directmedia Layer
at main 82 kB view raw
1/* 2 Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11*/ 12 13/* Simple program to test the SDL controller routines */ 14 15#include <SDL3/SDL.h> 16#include <SDL3/SDL_main.h> 17#include <SDL3/SDL_test.h> 18#include <SDL3/SDL_test_font.h> 19 20#ifdef SDL_PLATFORM_EMSCRIPTEN 21#include <emscripten/emscripten.h> 22#endif 23 24#include "gamepadutils.h" 25#include "testutils.h" 26 27#if 0 28#define DEBUG_AXIS_MAPPING 29#endif 30 31#define TITLE_HEIGHT 48.0f 32#define PANEL_SPACING 25.0f 33#define PANEL_WIDTH 250.0f 34#define MINIMUM_BUTTON_WIDTH 96.0f 35#define BUTTON_MARGIN 16.0f 36#define BUTTON_PADDING 12.0f 37#define GAMEPAD_WIDTH 512.0f 38#define GAMEPAD_HEIGHT 560.0f 39 40#define SCREEN_WIDTH (PANEL_WIDTH + PANEL_SPACING + GAMEPAD_WIDTH + PANEL_SPACING + PANEL_WIDTH) 41#define SCREEN_HEIGHT (TITLE_HEIGHT + GAMEPAD_HEIGHT) 42 43typedef struct 44{ 45 bool m_bMoving; 46 int m_nLastValue; 47 int m_nStartingValue; 48 int m_nFarthestValue; 49} AxisState; 50 51typedef struct 52{ 53 SDL_JoystickID id; 54 55 SDL_Joystick *joystick; 56 int num_axes; 57 AxisState *axis_state; 58 59 SDL_Gamepad *gamepad; 60 char *mapping; 61 bool has_bindings; 62 63 int audio_route; 64 int trigger_effect; 65} Controller; 66 67static SDL_Window *window = NULL; 68static SDL_Renderer *screen = NULL; 69static ControllerDisplayMode display_mode = CONTROLLER_MODE_TESTING; 70static GamepadImage *image = NULL; 71static GamepadDisplay *gamepad_elements = NULL; 72static GamepadTypeDisplay *gamepad_type = NULL; 73static JoystickDisplay *joystick_elements = NULL; 74static GamepadButton *setup_mapping_button = NULL; 75static GamepadButton *done_mapping_button = NULL; 76static GamepadButton *cancel_button = NULL; 77static GamepadButton *clear_button = NULL; 78static GamepadButton *copy_button = NULL; 79static GamepadButton *paste_button = NULL; 80static char *backup_mapping = NULL; 81static bool done = false; 82static bool set_LED = false; 83static int num_controllers = 0; 84static Controller *controllers; 85static Controller *controller; 86static SDL_JoystickID mapping_controller = 0; 87static int binding_element = SDL_GAMEPAD_ELEMENT_INVALID; 88static int last_binding_element = SDL_GAMEPAD_ELEMENT_INVALID; 89static bool binding_flow = false; 90static int binding_flow_direction = 0; 91static Uint64 binding_advance_time = 0; 92static SDL_FRect title_area; 93static bool title_highlighted; 94static bool title_pressed; 95static SDL_FRect type_area; 96static bool type_highlighted; 97static bool type_pressed; 98static char *controller_name; 99static SDL_Joystick *virtual_joystick = NULL; 100static SDL_GamepadAxis virtual_axis_active = SDL_GAMEPAD_AXIS_INVALID; 101static float virtual_axis_start_x; 102static float virtual_axis_start_y; 103static SDL_GamepadButton virtual_button_active = SDL_GAMEPAD_BUTTON_INVALID; 104static bool virtual_touchpad_active = false; 105static float virtual_touchpad_x; 106static float virtual_touchpad_y; 107 108static int s_arrBindingOrder[] = { 109 /* Standard sequence */ 110 SDL_GAMEPAD_BUTTON_SOUTH, 111 SDL_GAMEPAD_BUTTON_EAST, 112 SDL_GAMEPAD_BUTTON_WEST, 113 SDL_GAMEPAD_BUTTON_NORTH, 114 SDL_GAMEPAD_BUTTON_DPAD_LEFT, 115 SDL_GAMEPAD_BUTTON_DPAD_RIGHT, 116 SDL_GAMEPAD_BUTTON_DPAD_UP, 117 SDL_GAMEPAD_BUTTON_DPAD_DOWN, 118 SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_NEGATIVE, 119 SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_POSITIVE, 120 SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_NEGATIVE, 121 SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_POSITIVE, 122 SDL_GAMEPAD_BUTTON_LEFT_STICK, 123 SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_NEGATIVE, 124 SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_POSITIVE, 125 SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_NEGATIVE, 126 SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_POSITIVE, 127 SDL_GAMEPAD_BUTTON_RIGHT_STICK, 128 SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, 129 SDL_GAMEPAD_ELEMENT_AXIS_LEFT_TRIGGER, 130 SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, 131 SDL_GAMEPAD_ELEMENT_AXIS_RIGHT_TRIGGER, 132 SDL_GAMEPAD_BUTTON_BACK, 133 SDL_GAMEPAD_BUTTON_START, 134 SDL_GAMEPAD_BUTTON_GUIDE, 135 SDL_GAMEPAD_BUTTON_MISC1, 136 SDL_GAMEPAD_ELEMENT_INVALID, 137 138 /* Paddle sequence */ 139 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, 140 SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, 141 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, 142 SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, 143 SDL_GAMEPAD_ELEMENT_INVALID, 144}; 145 146 147static const char *GetSensorName(SDL_SensorType sensor) 148{ 149 switch (sensor) { 150 case SDL_SENSOR_ACCEL: 151 return "accelerometer"; 152 case SDL_SENSOR_GYRO: 153 return "gyro"; 154 case SDL_SENSOR_ACCEL_L: 155 return "accelerometer (L)"; 156 case SDL_SENSOR_GYRO_L: 157 return "gyro (L)"; 158 case SDL_SENSOR_ACCEL_R: 159 return "accelerometer (R)"; 160 case SDL_SENSOR_GYRO_R: 161 return "gyro (R)"; 162 default: 163 return "UNKNOWN"; 164 } 165} 166 167/* PS5 trigger effect documentation: 168 https://controllers.fandom.com/wiki/Sony_DualSense#FFB_Trigger_Modes 169*/ 170typedef struct 171{ 172 Uint8 ucEnableBits1; /* 0 */ 173 Uint8 ucEnableBits2; /* 1 */ 174 Uint8 ucRumbleRight; /* 2 */ 175 Uint8 ucRumbleLeft; /* 3 */ 176 Uint8 ucHeadphoneVolume; /* 4 */ 177 Uint8 ucSpeakerVolume; /* 5 */ 178 Uint8 ucMicrophoneVolume; /* 6 */ 179 Uint8 ucAudioEnableBits; /* 7 */ 180 Uint8 ucMicLightMode; /* 8 */ 181 Uint8 ucAudioMuteBits; /* 9 */ 182 Uint8 rgucRightTriggerEffect[11]; /* 10 */ 183 Uint8 rgucLeftTriggerEffect[11]; /* 21 */ 184 Uint8 rgucUnknown1[6]; /* 32 */ 185 Uint8 ucLedFlags; /* 38 */ 186 Uint8 rgucUnknown2[2]; /* 39 */ 187 Uint8 ucLedAnim; /* 41 */ 188 Uint8 ucLedBrightness; /* 42 */ 189 Uint8 ucPadLights; /* 43 */ 190 Uint8 ucLedRed; /* 44 */ 191 Uint8 ucLedGreen; /* 45 */ 192 Uint8 ucLedBlue; /* 46 */ 193} DS5EffectsState_t; 194 195static void CyclePS5AudioRoute(Controller *device) 196{ 197 DS5EffectsState_t state; 198 199 device->audio_route = (device->audio_route + 1) % 4; 200 201 SDL_zero(state); 202 switch (device->audio_route) { 203 case 0: 204 /* Audio disabled */ 205 state.ucEnableBits1 |= (0x80 | 0x20 | 0x10); /* Modify audio route and speaker / headphone volume */ 206 state.ucSpeakerVolume = 0; /* Minimum volume */ 207 state.ucHeadphoneVolume = 0; /* Minimum volume */ 208 state.ucAudioEnableBits = 0x00; /* Output to headphones */ 209 break; 210 case 1: 211 /* Headphones */ 212 state.ucEnableBits1 |= (0x80 | 0x10); /* Modify audio route and headphone volume */ 213 state.ucHeadphoneVolume = 50; /* 50% volume - don't blast into the ears */ 214 state.ucAudioEnableBits = 0x00; /* Output to headphones */ 215 break; 216 case 2: 217 /* Speaker */ 218 state.ucEnableBits1 |= (0x80 | 0x20); /* Modify audio route and speaker volume */ 219 state.ucSpeakerVolume = 100; /* Maximum volume */ 220 state.ucAudioEnableBits = 0x30; /* Output to speaker */ 221 break; 222 case 3: 223 /* Both */ 224 state.ucEnableBits1 |= (0x80 | 0x20 | 0x10); /* Modify audio route and speaker / headphone volume */ 225 state.ucSpeakerVolume = 100; /* Maximum volume */ 226 state.ucHeadphoneVolume = 50; /* 50% volume - don't blast into the ears */ 227 state.ucAudioEnableBits = 0x20; /* Output to both speaker and headphones */ 228 break; 229 } 230 SDL_SendGamepadEffect(device->gamepad, &state, sizeof(state)); 231} 232 233static void CyclePS5TriggerEffect(Controller *device) 234{ 235 DS5EffectsState_t state; 236 237 Uint8 effects[3][11] = { 238 /* Clear trigger effect */ 239 { 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 240 /* Constant resistance across entire trigger pull */ 241 { 0x01, 0, 110, 0, 0, 0, 0, 0, 0, 0, 0 }, 242 /* Resistance and vibration when trigger is pulled */ 243 { 0x06, 15, 63, 128, 0, 0, 0, 0, 0, 0, 0 }, 244 }; 245 246 device->trigger_effect = (device->trigger_effect + 1) % SDL_arraysize(effects); 247 248 SDL_zero(state); 249 state.ucEnableBits1 |= (0x04 | 0x08); /* Modify right and left trigger effect respectively */ 250 SDL_memcpy(state.rgucRightTriggerEffect, effects[device->trigger_effect], sizeof(effects[0])); 251 SDL_memcpy(state.rgucLeftTriggerEffect, effects[device->trigger_effect], sizeof(effects[0])); 252 SDL_SendGamepadEffect(device->gamepad, &state, sizeof(state)); 253} 254 255static void ClearButtonHighlights(void) 256{ 257 title_highlighted = false; 258 title_pressed = false; 259 260 type_highlighted = false; 261 type_pressed = false; 262 263 ClearGamepadImage(image); 264 SetGamepadDisplayHighlight(gamepad_elements, SDL_GAMEPAD_ELEMENT_INVALID, false); 265 SetGamepadTypeDisplayHighlight(gamepad_type, SDL_GAMEPAD_TYPE_UNSELECTED, false); 266 SetGamepadButtonHighlight(setup_mapping_button, false, false); 267 SetGamepadButtonHighlight(done_mapping_button, false, false); 268 SetGamepadButtonHighlight(cancel_button, false, false); 269 SetGamepadButtonHighlight(clear_button, false, false); 270 SetGamepadButtonHighlight(copy_button, false, false); 271 SetGamepadButtonHighlight(paste_button, false, false); 272} 273 274static void UpdateButtonHighlights(float x, float y, bool button_down) 275{ 276 ClearButtonHighlights(); 277 278 if (display_mode == CONTROLLER_MODE_TESTING) { 279 SetGamepadButtonHighlight(setup_mapping_button, GamepadButtonContains(setup_mapping_button, x, y), button_down); 280 } else if (display_mode == CONTROLLER_MODE_BINDING) { 281 SDL_FPoint point; 282 int gamepad_highlight_element = SDL_GAMEPAD_ELEMENT_INVALID; 283 char *joystick_highlight_element; 284 285 point.x = x; 286 point.y = y; 287 if (SDL_PointInRectFloat(&point, &title_area)) { 288 title_highlighted = true; 289 title_pressed = button_down; 290 } else { 291 title_highlighted = false; 292 title_pressed = false; 293 } 294 295 if (SDL_PointInRectFloat(&point, &type_area)) { 296 type_highlighted = true; 297 type_pressed = button_down; 298 } else { 299 type_highlighted = false; 300 type_pressed = false; 301 } 302 303 if (controller->joystick != virtual_joystick) { 304 gamepad_highlight_element = GetGamepadImageElementAt(image, x, y); 305 } 306 if (gamepad_highlight_element == SDL_GAMEPAD_ELEMENT_INVALID) { 307 gamepad_highlight_element = GetGamepadDisplayElementAt(gamepad_elements, controller->gamepad, x, y); 308 } 309 SetGamepadDisplayHighlight(gamepad_elements, gamepad_highlight_element, button_down); 310 311 if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE) { 312 int gamepad_highlight_type = GetGamepadTypeDisplayAt(gamepad_type, x, y); 313 SetGamepadTypeDisplayHighlight(gamepad_type, gamepad_highlight_type, button_down); 314 } 315 316 joystick_highlight_element = GetJoystickDisplayElementAt(joystick_elements, controller->joystick, x, y); 317 SetJoystickDisplayHighlight(joystick_elements, joystick_highlight_element, button_down); 318 SDL_free(joystick_highlight_element); 319 320 SetGamepadButtonHighlight(done_mapping_button, GamepadButtonContains(done_mapping_button, x, y), button_down); 321 SetGamepadButtonHighlight(cancel_button, GamepadButtonContains(cancel_button, x, y), button_down); 322 SetGamepadButtonHighlight(clear_button, GamepadButtonContains(clear_button, x, y), button_down); 323 SetGamepadButtonHighlight(copy_button, GamepadButtonContains(copy_button, x, y), button_down); 324 SetGamepadButtonHighlight(paste_button, GamepadButtonContains(paste_button, x, y), button_down); 325 } 326} 327 328static int StandardizeAxisValue(int nValue) 329{ 330 if (nValue > SDL_JOYSTICK_AXIS_MAX / 2) { 331 return SDL_JOYSTICK_AXIS_MAX; 332 } else if (nValue < SDL_JOYSTICK_AXIS_MIN / 2) { 333 return SDL_JOYSTICK_AXIS_MIN; 334 } else { 335 return 0; 336 } 337} 338 339static void RefreshControllerName(void) 340{ 341 const char *name = NULL; 342 343 SDL_free(controller_name); 344 controller_name = NULL; 345 346 if (controller) { 347 if (controller->gamepad) { 348 name = SDL_GetGamepadName(controller->gamepad); 349 } else { 350 name = SDL_GetJoystickName(controller->joystick); 351 } 352 } 353 354 if (name) { 355 controller_name = SDL_strdup(name); 356 } else { 357 controller_name = SDL_strdup(""); 358 } 359} 360 361static void SetAndFreeGamepadMapping(char *mapping) 362{ 363 SDL_SetGamepadMapping(controller->id, mapping); 364 SDL_free(mapping); 365} 366 367static void SetCurrentBindingElement(int element, bool flow) 368{ 369 int i; 370 371 if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) { 372 RefreshControllerName(); 373 } 374 375 if (element == SDL_GAMEPAD_ELEMENT_INVALID) { 376 binding_flow_direction = 0; 377 last_binding_element = SDL_GAMEPAD_ELEMENT_INVALID; 378 } else { 379 last_binding_element = binding_element; 380 } 381 binding_element = element; 382 binding_flow = flow || (element == SDL_GAMEPAD_BUTTON_SOUTH); 383 binding_advance_time = 0; 384 385 for (i = 0; i < controller->num_axes; ++i) { 386 controller->axis_state[i].m_nFarthestValue = controller->axis_state[i].m_nStartingValue; 387 } 388 389 SetGamepadDisplaySelected(gamepad_elements, element); 390} 391 392static void SetNextBindingElement(void) 393{ 394 int i; 395 396 if (binding_element == SDL_GAMEPAD_ELEMENT_INVALID) { 397 return; 398 } 399 400 for (i = 0; i < SDL_arraysize(s_arrBindingOrder); ++i) { 401 if (binding_element == s_arrBindingOrder[i]) { 402 binding_flow_direction = 1; 403 SetCurrentBindingElement(s_arrBindingOrder[i + 1], true); 404 return; 405 } 406 } 407 SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_INVALID, false); 408} 409 410static void SetPrevBindingElement(void) 411{ 412 int i; 413 414 if (binding_element == SDL_GAMEPAD_ELEMENT_INVALID) { 415 return; 416 } 417 418 for (i = 1; i < SDL_arraysize(s_arrBindingOrder); ++i) { 419 if (binding_element == s_arrBindingOrder[i]) { 420 binding_flow_direction = -1; 421 SetCurrentBindingElement(s_arrBindingOrder[i - 1], true); 422 return; 423 } 424 } 425 SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_INVALID, false); 426} 427 428static void StopBinding(void) 429{ 430 SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_INVALID, false); 431} 432 433typedef struct 434{ 435 int axis; 436 int direction; 437} AxisInfo; 438 439static bool ParseAxisInfo(const char *description, AxisInfo *info) 440{ 441 if (!description) { 442 return false; 443 } 444 445 if (*description == '-') { 446 info->direction = -1; 447 ++description; 448 } else if (*description == '+') { 449 info->direction = 1; 450 ++description; 451 } else { 452 info->direction = 0; 453 } 454 455 if (description[0] == 'a' && SDL_isdigit(description[1])) { 456 ++description; 457 info->axis = SDL_atoi(description); 458 return true; 459 } 460 return false; 461} 462 463static void CommitBindingElement(const char *binding, bool force) 464{ 465 char *mapping; 466 int direction = 1; 467 bool ignore_binding = false; 468 469 if (binding_element == SDL_GAMEPAD_ELEMENT_INVALID) { 470 return; 471 } 472 473 if (controller->mapping) { 474 mapping = SDL_strdup(controller->mapping); 475 } else { 476 mapping = NULL; 477 } 478 479 /* If the controller generates multiple events for a single element, pick the best one */ 480 if (!force && binding_advance_time) { 481 char *current = GetElementBinding(mapping, binding_element); 482 bool native_button = (binding_element < SDL_GAMEPAD_BUTTON_COUNT); 483 bool native_axis = (binding_element >= SDL_GAMEPAD_BUTTON_COUNT && 484 binding_element <= SDL_GAMEPAD_ELEMENT_AXIS_MAX); 485 bool native_trigger = (binding_element == SDL_GAMEPAD_ELEMENT_AXIS_LEFT_TRIGGER || 486 binding_element == SDL_GAMEPAD_ELEMENT_AXIS_RIGHT_TRIGGER); 487 bool native_dpad = (binding_element == SDL_GAMEPAD_BUTTON_DPAD_UP || 488 binding_element == SDL_GAMEPAD_BUTTON_DPAD_DOWN || 489 binding_element == SDL_GAMEPAD_BUTTON_DPAD_LEFT || 490 binding_element == SDL_GAMEPAD_BUTTON_DPAD_RIGHT); 491 492 if (native_button) { 493 bool current_button = (current && *current == 'b'); 494 bool proposed_button = (binding && *binding == 'b'); 495 if (current_button && !proposed_button) { 496 ignore_binding = true; 497 } 498 /* Use the lower index button (we map from lower to higher button index) */ 499 if (current_button && proposed_button && current[1] < binding[1]) { 500 ignore_binding = true; 501 } 502 } 503 if (native_axis) { 504 AxisInfo current_axis_info = { 0, 0 }; 505 AxisInfo proposed_axis_info = { 0, 0 }; 506 bool current_axis = ParseAxisInfo(current, &current_axis_info); 507 bool proposed_axis = ParseAxisInfo(binding, &proposed_axis_info); 508 509 if (current_axis) { 510 /* Ignore this unless the proposed binding extends the existing axis */ 511 ignore_binding = true; 512 513 if (native_trigger && 514 ((*current == '-' && *binding == '+' && 515 SDL_strcmp(current + 1, binding + 1) == 0) || 516 (*current == '+' && *binding == '-' && 517 SDL_strcmp(current + 1, binding + 1) == 0))) { 518 /* Merge two half axes into a whole axis for a trigger */ 519 ++binding; 520 ignore_binding = false; 521 } 522 523 /* Use the lower index axis (we map from lower to higher axis index) */ 524 if (proposed_axis && proposed_axis_info.axis < current_axis_info.axis) { 525 ignore_binding = false; 526 } 527 } 528 } 529 if (native_dpad) { 530 bool current_hat = (current && *current == 'h'); 531 bool proposed_hat = (binding && *binding == 'h'); 532 if (current_hat && !proposed_hat) { 533 ignore_binding = true; 534 } 535 /* Use the lower index hat (we map from lower to higher hat index) */ 536 if (current_hat && proposed_hat && current[1] < binding[1]) { 537 ignore_binding = true; 538 } 539 } 540 SDL_free(current); 541 } 542 543 if (!ignore_binding && binding_flow && !force) { 544 int existing = GetElementForBinding(mapping, binding); 545 if (existing != SDL_GAMEPAD_ELEMENT_INVALID) { 546 SDL_GamepadButton action_forward = SDL_GAMEPAD_BUTTON_SOUTH; 547 SDL_GamepadButton action_backward = SDL_GAMEPAD_BUTTON_EAST; 548 SDL_GamepadButton action_delete = SDL_GAMEPAD_BUTTON_WEST; 549 if (binding_element == action_forward) { 550 /* Bind it! */ 551 } else if (binding_element == action_backward) { 552 if (existing == action_forward) { 553 bool bound_backward = MappingHasElement(controller->mapping, action_backward); 554 if (bound_backward) { 555 /* Just move on to the next one */ 556 ignore_binding = true; 557 SetNextBindingElement(); 558 } else { 559 /* You can't skip the backward action, go back and start over */ 560 ignore_binding = true; 561 SetPrevBindingElement(); 562 } 563 } else if (existing == action_backward && binding_flow_direction == -1) { 564 /* Keep going backwards */ 565 ignore_binding = true; 566 SetPrevBindingElement(); 567 } else { 568 /* Bind it! */ 569 } 570 } else if (existing == action_forward) { 571 /* Just move on to the next one */ 572 ignore_binding = true; 573 SetNextBindingElement(); 574 } else if (existing == action_backward) { 575 ignore_binding = true; 576 SetPrevBindingElement(); 577 } else if (existing == binding_element) { 578 /* We're rebinding the same thing, just move to the next one */ 579 ignore_binding = true; 580 SetNextBindingElement(); 581 } else if (existing == action_delete) { 582 /* Clear the current binding and move to the next one */ 583 binding = NULL; 584 direction = 1; 585 force = true; 586 } else if (binding_element != action_forward && 587 binding_element != action_backward) { 588 /* Actually, we'll just clear the existing binding */ 589 /*ignore_binding = true;*/ 590 } 591 } 592 } 593 594 if (ignore_binding) { 595 SDL_free(mapping); 596 return; 597 } 598 599 mapping = ClearMappingBinding(mapping, binding); 600 mapping = SetElementBinding(mapping, binding_element, binding); 601 SetAndFreeGamepadMapping(mapping); 602 603 if (force) { 604 if (binding_flow) { 605 if (direction > 0) { 606 SetNextBindingElement(); 607 } else if (direction < 0) { 608 SetPrevBindingElement(); 609 } 610 } else { 611 StopBinding(); 612 } 613 } else { 614 /* Wait to see if any more bindings come in */ 615 binding_advance_time = SDL_GetTicks() + 30; 616 } 617} 618 619static void ClearBinding(void) 620{ 621 CommitBindingElement(NULL, true); 622} 623 624static void SetDisplayMode(ControllerDisplayMode mode) 625{ 626 float x, y; 627 SDL_MouseButtonFlags button_state; 628 629 if (mode == CONTROLLER_MODE_BINDING) { 630 /* Make a backup of the current mapping */ 631 if (controller->mapping) { 632 backup_mapping = SDL_strdup(controller->mapping); 633 } 634 mapping_controller = controller->id; 635 if (MappingHasBindings(backup_mapping)) { 636 SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_INVALID, false); 637 } else { 638 SetCurrentBindingElement(SDL_GAMEPAD_BUTTON_SOUTH, true); 639 } 640 } else { 641 if (backup_mapping) { 642 SDL_free(backup_mapping); 643 backup_mapping = NULL; 644 } 645 mapping_controller = 0; 646 StopBinding(); 647 } 648 649 display_mode = mode; 650 SetGamepadImageDisplayMode(image, mode); 651 SetGamepadDisplayDisplayMode(gamepad_elements, mode); 652 653 button_state = SDL_GetMouseState(&x, &y); 654 SDL_RenderCoordinatesFromWindow(screen, x, y, &x, &y); 655 UpdateButtonHighlights(x, y, button_state ? true : false); 656} 657 658static void CancelMapping(void) 659{ 660 SetAndFreeGamepadMapping(backup_mapping); 661 backup_mapping = NULL; 662 663 SetDisplayMode(CONTROLLER_MODE_TESTING); 664} 665 666static void ClearMapping(void) 667{ 668 SetAndFreeGamepadMapping(NULL); 669 SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_INVALID, false); 670} 671 672static void CopyMapping(void) 673{ 674 if (controller && controller->mapping) { 675 SDL_SetClipboardText(controller->mapping); 676 } 677} 678 679static void PasteMapping(void) 680{ 681 if (controller) { 682 char *mapping = SDL_GetClipboardText(); 683 if (MappingHasBindings(mapping)) { 684 StopBinding(); 685 SDL_SetGamepadMapping(controller->id, mapping); 686 RefreshControllerName(); 687 } else { 688 /* Not a valid mapping, ignore it */ 689 } 690 SDL_free(mapping); 691 } 692} 693 694static void CommitControllerName(void) 695{ 696 char *mapping = NULL; 697 698 if (controller->mapping) { 699 mapping = SDL_strdup(controller->mapping); 700 } else { 701 mapping = NULL; 702 } 703 mapping = SetMappingName(mapping, controller_name); 704 SetAndFreeGamepadMapping(mapping); 705} 706 707static void AddControllerNameText(const char *text) 708{ 709 size_t current_length = (controller_name ? SDL_strlen(controller_name) : 0); 710 size_t text_length = SDL_strlen(text); 711 size_t size = current_length + text_length + 1; 712 char *name = (char *)SDL_realloc(controller_name, size); 713 if (name) { 714 SDL_memcpy(&name[current_length], text, text_length + 1); 715 controller_name = name; 716 } 717 CommitControllerName(); 718} 719 720static void BackspaceControllerName(void) 721{ 722 size_t length = (controller_name ? SDL_strlen(controller_name) : 0); 723 if (length > 0) { 724 controller_name[length - 1] = '\0'; 725 } 726 CommitControllerName(); 727} 728 729static void ClearControllerName(void) 730{ 731 if (controller_name) { 732 *controller_name = '\0'; 733 } 734 CommitControllerName(); 735} 736 737static void CopyControllerName(void) 738{ 739 SDL_SetClipboardText(controller_name); 740} 741 742static void PasteControllerName(void) 743{ 744 SDL_free(controller_name); 745 controller_name = SDL_GetClipboardText(); 746 CommitControllerName(); 747} 748 749static void CommitGamepadType(SDL_GamepadType type) 750{ 751 char *mapping = NULL; 752 753 if (controller->mapping) { 754 mapping = SDL_strdup(controller->mapping); 755 } else { 756 mapping = NULL; 757 } 758 mapping = SetMappingType(mapping, type); 759 SetAndFreeGamepadMapping(mapping); 760} 761 762static const char *GetBindingInstruction(void) 763{ 764 switch (binding_element) { 765 case SDL_GAMEPAD_ELEMENT_INVALID: 766 return "Select an element to bind from the list on the left"; 767 case SDL_GAMEPAD_BUTTON_SOUTH: 768 case SDL_GAMEPAD_BUTTON_EAST: 769 case SDL_GAMEPAD_BUTTON_WEST: 770 case SDL_GAMEPAD_BUTTON_NORTH: 771 switch (SDL_GetGamepadButtonLabelForType(GetGamepadImageType(image), (SDL_GamepadButton)binding_element)) { 772 case SDL_GAMEPAD_BUTTON_LABEL_A: 773 return "Press the A button"; 774 case SDL_GAMEPAD_BUTTON_LABEL_B: 775 return "Press the B button"; 776 case SDL_GAMEPAD_BUTTON_LABEL_X: 777 return "Press the X button"; 778 case SDL_GAMEPAD_BUTTON_LABEL_Y: 779 return "Press the Y button"; 780 case SDL_GAMEPAD_BUTTON_LABEL_CROSS: 781 return "Press the Cross (X) button"; 782 case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE: 783 return "Press the Circle button"; 784 case SDL_GAMEPAD_BUTTON_LABEL_SQUARE: 785 return "Press the Square button"; 786 case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE: 787 return "Press the Triangle button"; 788 default: 789 return ""; 790 } 791 break; 792 case SDL_GAMEPAD_BUTTON_BACK: 793 return "Press the left center button (Back/View/Share)"; 794 case SDL_GAMEPAD_BUTTON_GUIDE: 795 return "Press the center button (Home/Guide)"; 796 case SDL_GAMEPAD_BUTTON_START: 797 return "Press the right center button (Start/Menu/Options)"; 798 case SDL_GAMEPAD_BUTTON_LEFT_STICK: 799 return "Press the left thumbstick button (LSB/L3)"; 800 case SDL_GAMEPAD_BUTTON_RIGHT_STICK: 801 return "Press the right thumbstick button (RSB/R3)"; 802 case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: 803 return "Press the left shoulder button (LB/L1)"; 804 case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: 805 return "Press the right shoulder button (RB/R1)"; 806 case SDL_GAMEPAD_BUTTON_DPAD_UP: 807 return "Press the D-Pad up"; 808 case SDL_GAMEPAD_BUTTON_DPAD_DOWN: 809 return "Press the D-Pad down"; 810 case SDL_GAMEPAD_BUTTON_DPAD_LEFT: 811 return "Press the D-Pad left"; 812 case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: 813 return "Press the D-Pad right"; 814 case SDL_GAMEPAD_BUTTON_MISC1: 815 return "Press the bottom center button (Share/Capture)"; 816 case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1: 817 return "Press the upper paddle under your right hand"; 818 case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1: 819 return "Press the upper paddle under your left hand"; 820 case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2: 821 return "Press the lower paddle under your right hand"; 822 case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2: 823 return "Press the lower paddle under your left hand"; 824 case SDL_GAMEPAD_BUTTON_TOUCHPAD: 825 return "Press down on the touchpad"; 826 case SDL_GAMEPAD_BUTTON_MISC2: 827 case SDL_GAMEPAD_BUTTON_MISC3: 828 case SDL_GAMEPAD_BUTTON_MISC4: 829 case SDL_GAMEPAD_BUTTON_MISC5: 830 case SDL_GAMEPAD_BUTTON_MISC6: 831 return "Press any additional button not already bound"; 832 case SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_NEGATIVE: 833 return "Move the left thumbstick to the left"; 834 case SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_POSITIVE: 835 return "Move the left thumbstick to the right"; 836 case SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_NEGATIVE: 837 return "Move the left thumbstick up"; 838 case SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_POSITIVE: 839 return "Move the left thumbstick down"; 840 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_NEGATIVE: 841 return "Move the right thumbstick to the left"; 842 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_POSITIVE: 843 return "Move the right thumbstick to the right"; 844 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_NEGATIVE: 845 return "Move the right thumbstick up"; 846 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_POSITIVE: 847 return "Move the right thumbstick down"; 848 case SDL_GAMEPAD_ELEMENT_AXIS_LEFT_TRIGGER: 849 return "Pull the left trigger (LT/L2)"; 850 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHT_TRIGGER: 851 return "Pull the right trigger (RT/R2)"; 852 case SDL_GAMEPAD_ELEMENT_NAME: 853 return "Type the name of your controller"; 854 case SDL_GAMEPAD_ELEMENT_TYPE: 855 return "Select the type of your controller"; 856 default: 857 return ""; 858 } 859} 860 861static int FindController(SDL_JoystickID id) 862{ 863 int i; 864 865 for (i = 0; i < num_controllers; ++i) { 866 if (id == controllers[i].id) { 867 return i; 868 } 869 } 870 return -1; 871} 872 873static void SetController(SDL_JoystickID id) 874{ 875 int i = FindController(id); 876 877 if (i < 0 && num_controllers > 0) { 878 i = 0; 879 } 880 881 if (i >= 0) { 882 controller = &controllers[i]; 883 } else { 884 controller = NULL; 885 } 886 887 RefreshControllerName(); 888} 889 890static void AddController(SDL_JoystickID id, bool verbose) 891{ 892 Controller *new_controllers; 893 Controller *new_controller; 894 SDL_Joystick *joystick; 895 896 if (FindController(id) >= 0) { 897 /* We already have this controller */ 898 return; 899 } 900 901 new_controllers = (Controller *)SDL_realloc(controllers, (num_controllers + 1) * sizeof(*controllers)); 902 if (!new_controllers) { 903 return; 904 } 905 906 controller = NULL; 907 controllers = new_controllers; 908 new_controller = &new_controllers[num_controllers++]; 909 SDL_zerop(new_controller); 910 new_controller->id = id; 911 912 new_controller->joystick = SDL_OpenJoystick(id); 913 new_controller->num_axes = SDL_GetNumJoystickAxes(new_controller->joystick); 914 new_controller->axis_state = (AxisState *)SDL_calloc(new_controller->num_axes, sizeof(*new_controller->axis_state)); 915 916 joystick = new_controller->joystick; 917 if (joystick) { 918 if (verbose && !SDL_IsGamepad(id)) { 919 const char *name = SDL_GetJoystickName(joystick); 920 const char *path = SDL_GetJoystickPath(joystick); 921 char guid[33]; 922 SDL_Log("Opened joystick %s%s%s\n", name, path ? ", " : "", path ? path : ""); 923 SDL_GUIDToString(SDL_GetJoystickGUID(joystick), guid, sizeof(guid)); 924 SDL_Log("No gamepad mapping for %s\n", guid); 925 } 926 } else { 927 SDL_Log("Couldn't open joystick: %s", SDL_GetError()); 928 } 929 930 if (mapping_controller) { 931 SetController(mapping_controller); 932 } else { 933 SetController(id); 934 } 935} 936 937static void DelController(SDL_JoystickID id) 938{ 939 int i = FindController(id); 940 941 if (i < 0) { 942 return; 943 } 944 945 if (display_mode == CONTROLLER_MODE_BINDING && id == controller->id) { 946 SetDisplayMode(CONTROLLER_MODE_TESTING); 947 } 948 949 /* Reset trigger state */ 950 if (controllers[i].trigger_effect != 0) { 951 controllers[i].trigger_effect = -1; 952 CyclePS5TriggerEffect(&controllers[i]); 953 } 954 SDL_assert(controllers[i].gamepad == NULL); 955 if (controllers[i].axis_state) { 956 SDL_free(controllers[i].axis_state); 957 } 958 if (controllers[i].joystick) { 959 SDL_CloseJoystick(controllers[i].joystick); 960 } 961 962 --num_controllers; 963 if (i < num_controllers) { 964 SDL_memcpy(&controllers[i], &controllers[i + 1], (num_controllers - i) * sizeof(*controllers)); 965 } 966 967 if (mapping_controller) { 968 SetController(mapping_controller); 969 } else { 970 SetController(id); 971 } 972} 973 974static void HandleGamepadRemapped(SDL_JoystickID id) 975{ 976 char *mapping; 977 int i = FindController(id); 978 979 SDL_assert(i >= 0); 980 if (i < 0) { 981 return; 982 } 983 984 if (!controllers[i].gamepad) { 985 /* Failed to open this controller */ 986 return; 987 } 988 989 /* Get the current mapping */ 990 mapping = SDL_GetGamepadMapping(controllers[i].gamepad); 991 992 /* Make sure the mapping has a valid name */ 993 if (mapping && !MappingHasName(mapping)) { 994 mapping = SetMappingName(mapping, SDL_GetJoystickName(controllers[i].joystick)); 995 } 996 997 SDL_free(controllers[i].mapping); 998 controllers[i].mapping = mapping; 999 controllers[i].has_bindings = MappingHasBindings(mapping); 1000} 1001 1002static void HandleGamepadAdded(SDL_JoystickID id, bool verbose) 1003{ 1004 SDL_Gamepad *gamepad; 1005 Uint16 firmware_version; 1006 SDL_SensorType sensors[] = { 1007 SDL_SENSOR_ACCEL, 1008 SDL_SENSOR_GYRO, 1009 SDL_SENSOR_ACCEL_L, 1010 SDL_SENSOR_GYRO_L, 1011 SDL_SENSOR_ACCEL_R, 1012 SDL_SENSOR_GYRO_R 1013 }; 1014 int i; 1015 1016 i = FindController(id); 1017 if (i < 0) { 1018 return; 1019 } 1020 1021 SDL_assert(!controllers[i].gamepad); 1022 controllers[i].gamepad = SDL_OpenGamepad(id); 1023 1024 gamepad = controllers[i].gamepad; 1025 if (gamepad) { 1026 if (verbose) { 1027 SDL_PropertiesID props = SDL_GetGamepadProperties(gamepad); 1028 const char *name = SDL_GetGamepadName(gamepad); 1029 const char *path = SDL_GetGamepadPath(gamepad); 1030 SDL_GUID guid = SDL_GetGamepadGUIDForID(id); 1031 char guid_string[33]; 1032 SDL_GUIDToString(guid, guid_string, sizeof(guid_string)); 1033 SDL_Log("Opened gamepad %s, guid %s%s%s\n", name, guid_string, path ? ", " : "", path ? path : ""); 1034 1035 firmware_version = SDL_GetGamepadFirmwareVersion(gamepad); 1036 if (firmware_version) { 1037 SDL_Log("Firmware version: 0x%x (%d)\n", firmware_version, firmware_version); 1038 } 1039 1040 if (SDL_GetBooleanProperty(props, SDL_PROP_GAMEPAD_CAP_PLAYER_LED_BOOLEAN, false)) { 1041 SDL_Log("Has player LED"); 1042 } 1043 1044 if (SDL_GetBooleanProperty(props, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, false)) { 1045 SDL_Log("Rumble supported"); 1046 } 1047 1048 if (SDL_GetBooleanProperty(props, SDL_PROP_GAMEPAD_CAP_TRIGGER_RUMBLE_BOOLEAN, false)) { 1049 SDL_Log("Trigger rumble supported"); 1050 } 1051 1052 if (SDL_GetGamepadPlayerIndex(gamepad) >= 0) { 1053 SDL_Log("Player index: %d\n", SDL_GetGamepadPlayerIndex(gamepad)); 1054 } 1055 } 1056 1057 for (i = 0; i < SDL_arraysize(sensors); ++i) { 1058 SDL_SensorType sensor = sensors[i]; 1059 1060 if (SDL_GamepadHasSensor(gamepad, sensor)) { 1061 if (verbose) { 1062 SDL_Log("Enabling %s at %.2f Hz\n", GetSensorName(sensor), SDL_GetGamepadSensorDataRate(gamepad, sensor)); 1063 } 1064 SDL_SetGamepadSensorEnabled(gamepad, sensor, true); 1065 } 1066 } 1067 1068 if (verbose) { 1069 char *mapping = SDL_GetGamepadMapping(gamepad); 1070 if (mapping) { 1071 SDL_Log("Mapping: %s\n", mapping); 1072 SDL_free(mapping); 1073 } 1074 } 1075 } else { 1076 SDL_Log("Couldn't open gamepad: %s", SDL_GetError()); 1077 } 1078 1079 HandleGamepadRemapped(id); 1080 SetController(id); 1081} 1082 1083static void HandleGamepadRemoved(SDL_JoystickID id) 1084{ 1085 int i = FindController(id); 1086 1087 SDL_assert(i >= 0); 1088 if (i < 0) { 1089 return; 1090 } 1091 1092 if (controllers[i].mapping) { 1093 SDL_free(controllers[i].mapping); 1094 controllers[i].mapping = NULL; 1095 } 1096 if (controllers[i].gamepad) { 1097 SDL_CloseGamepad(controllers[i].gamepad); 1098 controllers[i].gamepad = NULL; 1099 } 1100} 1101 1102static Uint16 ConvertAxisToRumble(Sint16 axisval) 1103{ 1104 /* Only start rumbling if the axis is past the halfway point */ 1105 const Sint16 half_axis = (Sint16)SDL_ceil(SDL_JOYSTICK_AXIS_MAX / 2.0f); 1106 if (axisval > half_axis) { 1107 return (Uint16)(axisval - half_axis) * 4; 1108 } else { 1109 return 0; 1110 } 1111} 1112 1113static bool ShowingFront(void) 1114{ 1115 bool showing_front = true; 1116 int i; 1117 1118 /* Show the back of the gamepad if the paddles are being held or bound */ 1119 for (i = SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1; i <= SDL_GAMEPAD_BUTTON_LEFT_PADDLE2; ++i) { 1120 if (SDL_GetGamepadButton(controller->gamepad, (SDL_GamepadButton)i) || 1121 binding_element == i) { 1122 showing_front = false; 1123 break; 1124 } 1125 } 1126 if ((SDL_GetModState() & SDL_KMOD_SHIFT) && binding_element != SDL_GAMEPAD_ELEMENT_NAME) { 1127 showing_front = false; 1128 } 1129 return showing_front; 1130} 1131 1132static void SDLCALL VirtualGamepadSetPlayerIndex(void *userdata, int player_index) 1133{ 1134 SDL_Log("Virtual Gamepad: player index set to %d\n", player_index); 1135} 1136 1137static bool SDLCALL VirtualGamepadRumble(void *userdata, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 1138{ 1139 SDL_Log("Virtual Gamepad: rumble set to %d/%d\n", low_frequency_rumble, high_frequency_rumble); 1140 return true; 1141} 1142 1143static bool SDLCALL VirtualGamepadRumbleTriggers(void *userdata, Uint16 left_rumble, Uint16 right_rumble) 1144{ 1145 SDL_Log("Virtual Gamepad: trigger rumble set to %d/%d\n", left_rumble, right_rumble); 1146 return true; 1147} 1148 1149static bool SDLCALL VirtualGamepadSetLED(void *userdata, Uint8 red, Uint8 green, Uint8 blue) 1150{ 1151 SDL_Log("Virtual Gamepad: LED set to RGB %d,%d,%d\n", red, green, blue); 1152 return true; 1153} 1154 1155static void OpenVirtualGamepad(void) 1156{ 1157 SDL_VirtualJoystickTouchpadDesc virtual_touchpad = { 1, { 0, 0, 0 } }; 1158 SDL_VirtualJoystickSensorDesc virtual_sensor = { SDL_SENSOR_ACCEL, 0.0f }; 1159 SDL_VirtualJoystickDesc desc; 1160 SDL_JoystickID virtual_id; 1161 1162 if (virtual_joystick) { 1163 return; 1164 } 1165 1166 SDL_INIT_INTERFACE(&desc); 1167 desc.type = SDL_JOYSTICK_TYPE_GAMEPAD; 1168 desc.naxes = SDL_GAMEPAD_AXIS_COUNT; 1169 desc.nbuttons = SDL_GAMEPAD_BUTTON_COUNT; 1170 desc.ntouchpads = 1; 1171 desc.touchpads = &virtual_touchpad; 1172 desc.nsensors = 1; 1173 desc.sensors = &virtual_sensor; 1174 desc.SetPlayerIndex = VirtualGamepadSetPlayerIndex; 1175 desc.Rumble = VirtualGamepadRumble; 1176 desc.RumbleTriggers = VirtualGamepadRumbleTriggers; 1177 desc.SetLED = VirtualGamepadSetLED; 1178 1179 virtual_id = SDL_AttachVirtualJoystick(&desc); 1180 if (virtual_id == 0) { 1181 SDL_Log("Couldn't attach virtual device: %s\n", SDL_GetError()); 1182 } else { 1183 virtual_joystick = SDL_OpenJoystick(virtual_id); 1184 if (!virtual_joystick) { 1185 SDL_Log("Couldn't open virtual device: %s\n", SDL_GetError()); 1186 } 1187 } 1188} 1189 1190static void CloseVirtualGamepad(void) 1191{ 1192 int i; 1193 SDL_JoystickID *joysticks = SDL_GetJoysticks(NULL); 1194 if (joysticks) { 1195 for (i = 0; joysticks[i]; ++i) { 1196 SDL_JoystickID instance_id = joysticks[i]; 1197 if (SDL_IsJoystickVirtual(instance_id)) { 1198 SDL_DetachVirtualJoystick(instance_id); 1199 } 1200 } 1201 SDL_free(joysticks); 1202 } 1203 1204 if (virtual_joystick) { 1205 SDL_CloseJoystick(virtual_joystick); 1206 virtual_joystick = NULL; 1207 } 1208} 1209 1210static void VirtualGamepadMouseMotion(float x, float y) 1211{ 1212 if (virtual_button_active != SDL_GAMEPAD_BUTTON_INVALID) { 1213 if (virtual_axis_active != SDL_GAMEPAD_AXIS_INVALID) { 1214 const float MOVING_DISTANCE = 2.0f; 1215 if (SDL_fabs(x - virtual_axis_start_x) >= MOVING_DISTANCE || 1216 SDL_fabs(y - virtual_axis_start_y) >= MOVING_DISTANCE) { 1217 SDL_SetJoystickVirtualButton(virtual_joystick, virtual_button_active, false); 1218 virtual_button_active = SDL_GAMEPAD_BUTTON_INVALID; 1219 } 1220 } 1221 } 1222 1223 if (virtual_axis_active != SDL_GAMEPAD_AXIS_INVALID) { 1224 if (virtual_axis_active == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || 1225 virtual_axis_active == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { 1226 int range = (SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN); 1227 float distance = SDL_clamp((y - virtual_axis_start_y) / GetGamepadImageAxisHeight(image), 0.0f, 1.0f); 1228 Sint16 value = (Sint16)(SDL_JOYSTICK_AXIS_MIN + (distance * range)); 1229 SDL_SetJoystickVirtualAxis(virtual_joystick, virtual_axis_active, value); 1230 } else { 1231 float distanceX = SDL_clamp((x - virtual_axis_start_x) / GetGamepadImageAxisWidth(image), -1.0f, 1.0f); 1232 float distanceY = SDL_clamp((y - virtual_axis_start_y) / GetGamepadImageAxisHeight(image), -1.0f, 1.0f); 1233 Sint16 valueX, valueY; 1234 1235 if (distanceX >= 0) { 1236 valueX = (Sint16)(distanceX * SDL_JOYSTICK_AXIS_MAX); 1237 } else { 1238 valueX = (Sint16)(distanceX * -SDL_JOYSTICK_AXIS_MIN); 1239 } 1240 if (distanceY >= 0) { 1241 valueY = (Sint16)(distanceY * SDL_JOYSTICK_AXIS_MAX); 1242 } else { 1243 valueY = (Sint16)(distanceY * -SDL_JOYSTICK_AXIS_MIN); 1244 } 1245 SDL_SetJoystickVirtualAxis(virtual_joystick, virtual_axis_active, valueX); 1246 SDL_SetJoystickVirtualAxis(virtual_joystick, virtual_axis_active + 1, valueY); 1247 } 1248 } 1249 1250 if (virtual_touchpad_active) { 1251 SDL_FRect touchpad; 1252 GetGamepadTouchpadArea(image, &touchpad); 1253 virtual_touchpad_x = (x - touchpad.x) / touchpad.w; 1254 virtual_touchpad_y = (y - touchpad.y) / touchpad.h; 1255 SDL_SetJoystickVirtualTouchpad(virtual_joystick, 0, 0, true, virtual_touchpad_x, virtual_touchpad_y, 1.0f); 1256 } 1257} 1258 1259static void VirtualGamepadMouseDown(float x, float y) 1260{ 1261 int element = GetGamepadImageElementAt(image, x, y); 1262 1263 if (element == SDL_GAMEPAD_ELEMENT_INVALID) { 1264 SDL_FPoint point = { x, y }; 1265 SDL_FRect touchpad; 1266 GetGamepadTouchpadArea(image, &touchpad); 1267 if (SDL_PointInRectFloat(&point, &touchpad)) { 1268 virtual_touchpad_active = true; 1269 virtual_touchpad_x = (x - touchpad.x) / touchpad.w; 1270 virtual_touchpad_y = (y - touchpad.y) / touchpad.h; 1271 SDL_SetJoystickVirtualTouchpad(virtual_joystick, 0, 0, true, virtual_touchpad_x, virtual_touchpad_y, 1.0f); 1272 } 1273 return; 1274 } 1275 1276 if (element < SDL_GAMEPAD_BUTTON_COUNT) { 1277 virtual_button_active = (SDL_GamepadButton)element; 1278 SDL_SetJoystickVirtualButton(virtual_joystick, virtual_button_active, true); 1279 } else { 1280 switch (element) { 1281 case SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_NEGATIVE: 1282 case SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_POSITIVE: 1283 case SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_NEGATIVE: 1284 case SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_POSITIVE: 1285 virtual_axis_active = SDL_GAMEPAD_AXIS_LEFTX; 1286 break; 1287 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_NEGATIVE: 1288 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_POSITIVE: 1289 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_NEGATIVE: 1290 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_POSITIVE: 1291 virtual_axis_active = SDL_GAMEPAD_AXIS_RIGHTX; 1292 break; 1293 case SDL_GAMEPAD_ELEMENT_AXIS_LEFT_TRIGGER: 1294 virtual_axis_active = SDL_GAMEPAD_AXIS_LEFT_TRIGGER; 1295 break; 1296 case SDL_GAMEPAD_ELEMENT_AXIS_RIGHT_TRIGGER: 1297 virtual_axis_active = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER; 1298 break; 1299 } 1300 virtual_axis_start_x = x; 1301 virtual_axis_start_y = y; 1302 } 1303} 1304 1305static void VirtualGamepadMouseUp(float x, float y) 1306{ 1307 if (virtual_button_active != SDL_GAMEPAD_BUTTON_INVALID) { 1308 SDL_SetJoystickVirtualButton(virtual_joystick, virtual_button_active, false); 1309 virtual_button_active = SDL_GAMEPAD_BUTTON_INVALID; 1310 } 1311 1312 if (virtual_axis_active != SDL_GAMEPAD_AXIS_INVALID) { 1313 if (virtual_axis_active == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || 1314 virtual_axis_active == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { 1315 SDL_SetJoystickVirtualAxis(virtual_joystick, virtual_axis_active, SDL_JOYSTICK_AXIS_MIN); 1316 } else { 1317 SDL_SetJoystickVirtualAxis(virtual_joystick, virtual_axis_active, 0); 1318 SDL_SetJoystickVirtualAxis(virtual_joystick, virtual_axis_active + 1, 0); 1319 } 1320 virtual_axis_active = SDL_GAMEPAD_AXIS_INVALID; 1321 } 1322 1323 if (virtual_touchpad_active) { 1324 SDL_SetJoystickVirtualTouchpad(virtual_joystick, 0, 0, false, virtual_touchpad_x, virtual_touchpad_y, 0.0f); 1325 virtual_touchpad_active = false; 1326 } 1327} 1328 1329static void DrawGamepadWaiting(SDL_Renderer *renderer) 1330{ 1331 const char *text = "Waiting for gamepad, press A to add a virtual controller"; 1332 float x, y; 1333 1334 x = SCREEN_WIDTH / 2 - (FONT_CHARACTER_SIZE * SDL_strlen(text)) / 2; 1335 y = TITLE_HEIGHT / 2 - FONT_CHARACTER_SIZE / 2; 1336 SDLTest_DrawString(renderer, x, y, text); 1337} 1338 1339static void DrawGamepadInfo(SDL_Renderer *renderer) 1340{ 1341 const char *type; 1342 const char *serial; 1343 char text[128]; 1344 float x, y; 1345 1346 if (title_highlighted) { 1347 Uint8 r, g, b, a; 1348 1349 SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); 1350 1351 if (title_pressed) { 1352 SDL_SetRenderDrawColor(renderer, PRESSED_COLOR); 1353 } else { 1354 SDL_SetRenderDrawColor(renderer, HIGHLIGHT_COLOR); 1355 } 1356 SDL_RenderFillRect(renderer, &title_area); 1357 1358 SDL_SetRenderDrawColor(renderer, r, g, b, a); 1359 } 1360 1361 if (type_highlighted) { 1362 Uint8 r, g, b, a; 1363 1364 SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); 1365 1366 if (type_pressed) { 1367 SDL_SetRenderDrawColor(renderer, PRESSED_COLOR); 1368 } else { 1369 SDL_SetRenderDrawColor(renderer, HIGHLIGHT_COLOR); 1370 } 1371 SDL_RenderFillRect(renderer, &type_area); 1372 1373 SDL_SetRenderDrawColor(renderer, r, g, b, a); 1374 } 1375 1376 if (controller->joystick) { 1377 SDL_snprintf(text, sizeof(text), "(%" SDL_PRIu32 ")", SDL_GetJoystickID(controller->joystick)); 1378 x = SCREEN_WIDTH - (FONT_CHARACTER_SIZE * SDL_strlen(text)) - 8.0f; 1379 y = 8.0f; 1380 SDLTest_DrawString(renderer, x, y, text); 1381 } 1382 1383 if (controller_name && *controller_name) { 1384 x = title_area.x + title_area.w / 2 - (FONT_CHARACTER_SIZE * SDL_strlen(controller_name)) / 2; 1385 y = title_area.y + title_area.h / 2 - FONT_CHARACTER_SIZE / 2; 1386 SDLTest_DrawString(renderer, x, y, controller_name); 1387 } 1388 1389 if (SDL_IsJoystickVirtual(controller->id)) { 1390 SDL_strlcpy(text, "Click on the gamepad image below to generate input", sizeof(text)); 1391 x = SCREEN_WIDTH / 2 - (FONT_CHARACTER_SIZE * SDL_strlen(text)) / 2; 1392 y = TITLE_HEIGHT / 2 - FONT_CHARACTER_SIZE / 2 + FONT_LINE_HEIGHT + 2.0f; 1393 SDLTest_DrawString(renderer, x, y, text); 1394 } 1395 1396 type = GetGamepadTypeString(SDL_GetGamepadType(controller->gamepad)); 1397 x = type_area.x + type_area.w / 2 - (FONT_CHARACTER_SIZE * SDL_strlen(type)) / 2; 1398 y = type_area.y + type_area.h / 2 - FONT_CHARACTER_SIZE / 2; 1399 SDLTest_DrawString(renderer, x, y, type); 1400 1401 if (display_mode == CONTROLLER_MODE_TESTING) { 1402 Uint64 steam_handle = SDL_GetGamepadSteamHandle(controller->gamepad); 1403 if (steam_handle) { 1404 SDL_snprintf(text, SDL_arraysize(text), "Steam: 0x%.16" SDL_PRIx64, steam_handle); 1405 y = SCREEN_HEIGHT - 2 * (8.0f + FONT_LINE_HEIGHT); 1406 x = SCREEN_WIDTH - 8.0f - (FONT_CHARACTER_SIZE * SDL_strlen(text)); 1407 SDLTest_DrawString(renderer, x, y, text); 1408 } 1409 1410 SDL_snprintf(text, SDL_arraysize(text), "VID: 0x%.4x PID: 0x%.4x", 1411 SDL_GetJoystickVendor(controller->joystick), 1412 SDL_GetJoystickProduct(controller->joystick)); 1413 y = SCREEN_HEIGHT - 8.0f - FONT_LINE_HEIGHT; 1414 x = SCREEN_WIDTH - 8.0f - (FONT_CHARACTER_SIZE * SDL_strlen(text)); 1415 SDLTest_DrawString(renderer, x, y, text); 1416 1417 serial = SDL_GetJoystickSerial(controller->joystick); 1418 if (serial && *serial) { 1419 SDL_snprintf(text, SDL_arraysize(text), "Serial: %s", serial); 1420 x = SCREEN_WIDTH / 2 - (FONT_CHARACTER_SIZE * SDL_strlen(text)) / 2; 1421 y = SCREEN_HEIGHT - 8.0f - FONT_LINE_HEIGHT; 1422 SDLTest_DrawString(renderer, x, y, text); 1423 } 1424 } 1425} 1426 1427static const char *GetButtonLabel(SDL_GamepadType type, SDL_GamepadButton button) 1428{ 1429 switch (SDL_GetGamepadButtonLabelForType(type, button)) { 1430 case SDL_GAMEPAD_BUTTON_LABEL_A: 1431 return "A"; 1432 case SDL_GAMEPAD_BUTTON_LABEL_B: 1433 return "B"; 1434 case SDL_GAMEPAD_BUTTON_LABEL_X: 1435 return "X"; 1436 case SDL_GAMEPAD_BUTTON_LABEL_Y: 1437 return "Y"; 1438 case SDL_GAMEPAD_BUTTON_LABEL_CROSS: 1439 return "Cross (X)"; 1440 case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE: 1441 return "Circle"; 1442 case SDL_GAMEPAD_BUTTON_LABEL_SQUARE: 1443 return "Square"; 1444 case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE: 1445 return "Triangle"; 1446 default: 1447 return "UNKNOWN"; 1448 } 1449} 1450 1451static void DrawBindingTips(SDL_Renderer *renderer) 1452{ 1453 const char *text; 1454 SDL_FRect image_area, button_area; 1455 float x, y; 1456 1457 GetGamepadImageArea(image, &image_area); 1458 GetGamepadButtonArea(done_mapping_button, &button_area); 1459 x = image_area.x + image_area.w / 2; 1460 y = image_area.y + image_area.h; 1461 y += (button_area.y - y - FONT_CHARACTER_SIZE) / 2; 1462 1463 text = GetBindingInstruction(); 1464 1465 if (binding_element == SDL_GAMEPAD_ELEMENT_INVALID) { 1466 SDLTest_DrawString(renderer, x - (FONT_CHARACTER_SIZE * SDL_strlen(text)) / 2, y, text); 1467 } else { 1468 Uint8 r, g, b, a; 1469 SDL_FRect rect; 1470 SDL_GamepadButton action_forward = SDL_GAMEPAD_BUTTON_SOUTH; 1471 bool bound_forward = MappingHasElement(controller->mapping, action_forward); 1472 SDL_GamepadButton action_backward = SDL_GAMEPAD_BUTTON_EAST; 1473 bool bound_backward = MappingHasElement(controller->mapping, action_backward); 1474 SDL_GamepadButton action_delete = SDL_GAMEPAD_BUTTON_WEST; 1475 bool bound_delete = MappingHasElement(controller->mapping, action_delete); 1476 1477 y -= (FONT_CHARACTER_SIZE + BUTTON_MARGIN) / 2; 1478 1479 rect.w = 2.0f + (FONT_CHARACTER_SIZE * SDL_strlen(text)) + 2.0f; 1480 rect.h = 2.0f + FONT_CHARACTER_SIZE + 2.0f; 1481 rect.x = x - rect.w / 2; 1482 rect.y = y - 2.0f; 1483 1484 SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); 1485 SDL_SetRenderDrawColor(renderer, SELECTED_COLOR); 1486 SDL_RenderFillRect(renderer, &rect); 1487 SDL_SetRenderDrawColor(renderer, r, g, b, a); 1488 SDLTest_DrawString(renderer, x - (FONT_CHARACTER_SIZE * SDL_strlen(text)) / 2, y, text); 1489 1490 y += (FONT_CHARACTER_SIZE + BUTTON_MARGIN); 1491 1492 if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) { 1493 text = "(press RETURN to complete)"; 1494 } else if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE || 1495 binding_element == action_forward || 1496 binding_element == action_backward) { 1497 text = "(press ESC to cancel)"; 1498 } else { 1499 static char dynamic_text[128]; 1500 SDL_GamepadType type = GetGamepadImageType(image); 1501 if (binding_flow && bound_forward && bound_backward) { 1502 if (binding_element != action_delete && bound_delete) { 1503 SDL_snprintf(dynamic_text, sizeof(dynamic_text), "(press %s to skip, %s to go back, %s to delete, and ESC to cancel)", GetButtonLabel(type, action_forward), GetButtonLabel(type, action_backward), GetButtonLabel(type, action_delete)); 1504 } else { 1505 SDL_snprintf(dynamic_text, sizeof(dynamic_text), "(press %s to skip, %s to go back, SPACE to delete, and ESC to cancel)", GetButtonLabel(type, action_forward), GetButtonLabel(type, action_backward)); 1506 } 1507 text = dynamic_text; 1508 } else { 1509 text = "(press SPACE to delete and ESC to cancel)"; 1510 } 1511 } 1512 SDLTest_DrawString(renderer, x - (FONT_CHARACTER_SIZE * SDL_strlen(text)) / 2, y, text); 1513 } 1514} 1515 1516static void UpdateGamepadEffects(void) 1517{ 1518 if (display_mode != CONTROLLER_MODE_TESTING || !controller->gamepad) { 1519 return; 1520 } 1521 1522 /* Update LED based on left thumbstick position */ 1523 { 1524 Sint16 x = SDL_GetGamepadAxis(controller->gamepad, SDL_GAMEPAD_AXIS_LEFTX); 1525 Sint16 y = SDL_GetGamepadAxis(controller->gamepad, SDL_GAMEPAD_AXIS_LEFTY); 1526 1527 if (!set_LED) { 1528 set_LED = (x < -8000 || x > 8000 || y > 8000); 1529 } 1530 if (set_LED) { 1531 Uint8 r, g, b; 1532 1533 if (x < 0) { 1534 r = (Uint8)(((~x) * 255) / 32767); 1535 b = 0; 1536 } else { 1537 r = 0; 1538 b = (Uint8)(((int)(x)*255) / 32767); 1539 } 1540 if (y > 0) { 1541 g = (Uint8)(((int)(y)*255) / 32767); 1542 } else { 1543 g = 0; 1544 } 1545 1546 SDL_SetGamepadLED(controller->gamepad, r, g, b); 1547 } 1548 } 1549 1550 if (controller->trigger_effect == 0) { 1551 /* Update rumble based on trigger state */ 1552 { 1553 Sint16 left = SDL_GetGamepadAxis(controller->gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER); 1554 Sint16 right = SDL_GetGamepadAxis(controller->gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER); 1555 Uint16 low_frequency_rumble = ConvertAxisToRumble(left); 1556 Uint16 high_frequency_rumble = ConvertAxisToRumble(right); 1557 SDL_RumbleGamepad(controller->gamepad, low_frequency_rumble, high_frequency_rumble, 250); 1558 } 1559 1560 /* Update trigger rumble based on thumbstick state */ 1561 { 1562 Sint16 left = SDL_GetGamepadAxis(controller->gamepad, SDL_GAMEPAD_AXIS_LEFTY); 1563 Sint16 right = SDL_GetGamepadAxis(controller->gamepad, SDL_GAMEPAD_AXIS_RIGHTY); 1564 Uint16 left_rumble = ConvertAxisToRumble(~left); 1565 Uint16 right_rumble = ConvertAxisToRumble(~right); 1566 1567 SDL_RumbleGamepadTriggers(controller->gamepad, left_rumble, right_rumble, 250); 1568 } 1569 } 1570} 1571 1572static void loop(void *arg) 1573{ 1574 SDL_Event event; 1575 1576 /* If we have a virtual controller, send a virtual accelerometer sensor reading */ 1577 if (virtual_joystick) { 1578 float data[3] = { 0.0f, SDL_STANDARD_GRAVITY, 0.0f }; 1579 SDL_SendJoystickVirtualSensorData(virtual_joystick, SDL_SENSOR_ACCEL, SDL_GetTicksNS(), data, SDL_arraysize(data)); 1580 } 1581 1582 /* Update to get the current event state */ 1583 SDL_PumpEvents(); 1584 1585 /* Process all currently pending events */ 1586 while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST) == 1) { 1587 SDL_ConvertEventToRenderCoordinates(screen, &event); 1588 1589 switch (event.type) { 1590 case SDL_EVENT_JOYSTICK_ADDED: 1591 AddController(event.jdevice.which, true); 1592 break; 1593 1594 case SDL_EVENT_JOYSTICK_REMOVED: 1595 DelController(event.jdevice.which); 1596 break; 1597 1598 case SDL_EVENT_JOYSTICK_AXIS_MOTION: 1599 if (display_mode == CONTROLLER_MODE_TESTING) { 1600 if (event.jaxis.value <= (-SDL_JOYSTICK_AXIS_MAX / 2) || event.jaxis.value >= (SDL_JOYSTICK_AXIS_MAX / 2)) { 1601 SetController(event.jaxis.which); 1602 } 1603 } else if (display_mode == CONTROLLER_MODE_BINDING && 1604 event.jaxis.which == controller->id && 1605 event.jaxis.axis < controller->num_axes && 1606 binding_element != SDL_GAMEPAD_ELEMENT_INVALID) { 1607 const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 gamepad needed 96 */ 1608 AxisState *pAxisState = &controller->axis_state[event.jaxis.axis]; 1609 int nValue = event.jaxis.value; 1610 int nCurrentDistance, nFarthestDistance; 1611 if (!pAxisState->m_bMoving) { 1612 Sint16 nInitialValue; 1613 pAxisState->m_bMoving = SDL_GetJoystickAxisInitialState(controller->joystick, event.jaxis.axis, &nInitialValue); 1614 pAxisState->m_nLastValue = nValue; 1615 pAxisState->m_nStartingValue = nInitialValue; 1616 pAxisState->m_nFarthestValue = nInitialValue; 1617 } else if (SDL_abs(nValue - pAxisState->m_nLastValue) <= MAX_ALLOWED_JITTER) { 1618 break; 1619 } else { 1620 pAxisState->m_nLastValue = nValue; 1621 } 1622 nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue); 1623 nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); 1624 if (nCurrentDistance > nFarthestDistance) { 1625 pAxisState->m_nFarthestValue = nValue; 1626 nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); 1627 } 1628 1629#ifdef DEBUG_AXIS_MAPPING 1630 SDL_Log("AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d\n", event.jaxis.axis, nValue, nCurrentDistance, nFarthestDistance); 1631#endif 1632 /* If we've gone out far enough and started to come back, let's bind this axis */ 1633 if (nFarthestDistance >= 16000 && nCurrentDistance <= 10000) { 1634 char binding[12]; 1635 int axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue); 1636 int axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue); 1637 1638 if (axis_min == 0 && axis_max == SDL_JOYSTICK_AXIS_MIN) { 1639 /* The negative half axis */ 1640 (void)SDL_snprintf(binding, sizeof(binding), "-a%d", event.jaxis.axis); 1641 } else if (axis_min == 0 && axis_max == SDL_JOYSTICK_AXIS_MAX) { 1642 /* The positive half axis */ 1643 (void)SDL_snprintf(binding, sizeof(binding), "+a%d", event.jaxis.axis); 1644 } else { 1645 (void)SDL_snprintf(binding, sizeof(binding), "a%d", event.jaxis.axis); 1646 if (axis_min > axis_max) { 1647 /* Invert the axis */ 1648 SDL_strlcat(binding, "~", SDL_arraysize(binding)); 1649 } 1650 } 1651#ifdef DEBUG_AXIS_MAPPING 1652 SDL_Log("AXIS %d axis_min = %d, axis_max = %d, binding = %s\n", event.jaxis.axis, axis_min, axis_max, binding); 1653#endif 1654 CommitBindingElement(binding, false); 1655 } 1656 } 1657 break; 1658 1659 case SDL_EVENT_JOYSTICK_BUTTON_DOWN: 1660 if (display_mode == CONTROLLER_MODE_TESTING) { 1661 SetController(event.jbutton.which); 1662 } 1663 break; 1664 1665 case SDL_EVENT_JOYSTICK_BUTTON_UP: 1666 if (display_mode == CONTROLLER_MODE_BINDING && 1667 event.jbutton.which == controller->id && 1668 binding_element != SDL_GAMEPAD_ELEMENT_INVALID) { 1669 char binding[12]; 1670 1671 SDL_snprintf(binding, sizeof(binding), "b%d", event.jbutton.button); 1672 CommitBindingElement(binding, false); 1673 } 1674 break; 1675 1676 case SDL_EVENT_JOYSTICK_HAT_MOTION: 1677 if (display_mode == CONTROLLER_MODE_BINDING && 1678 event.jhat.which == controller->id && 1679 event.jhat.value != SDL_HAT_CENTERED && 1680 binding_element != SDL_GAMEPAD_ELEMENT_INVALID) { 1681 char binding[12]; 1682 1683 SDL_snprintf(binding, sizeof(binding), "h%d.%d", event.jhat.hat, event.jhat.value); 1684 CommitBindingElement(binding, false); 1685 } 1686 break; 1687 1688 case SDL_EVENT_GAMEPAD_ADDED: 1689 HandleGamepadAdded(event.gdevice.which, true); 1690 break; 1691 1692 case SDL_EVENT_GAMEPAD_REMOVED: 1693 HandleGamepadRemoved(event.gdevice.which); 1694 break; 1695 1696 case SDL_EVENT_GAMEPAD_REMAPPED: 1697 HandleGamepadRemapped(event.gdevice.which); 1698 break; 1699 1700 case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: 1701 RefreshControllerName(); 1702 break; 1703 1704#ifdef VERBOSE_TOUCHPAD 1705 case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: 1706 case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: 1707 case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: 1708 SDL_Log("Gamepad %" SDL_PRIu32 " touchpad %" SDL_PRIs32 " finger %" SDL_PRIs32 " %s %.2f, %.2f, %.2f\n", 1709 event.gtouchpad.which, 1710 event.gtouchpad.touchpad, 1711 event.gtouchpad.finger, 1712 (event.type == SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN ? "pressed at" : (event.type == SDL_EVENT_GAMEPAD_TOUCHPAD_UP ? "released at" : "moved to")), 1713 event.gtouchpad.x, 1714 event.gtouchpad.y, 1715 event.gtouchpad.pressure); 1716 break; 1717#endif /* VERBOSE_TOUCHPAD */ 1718 1719#ifdef VERBOSE_SENSORS 1720 case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: 1721 SDL_Log("Gamepad %" SDL_PRIu32 " sensor %s: %.2f, %.2f, %.2f (%" SDL_PRIu64 ")\n", 1722 event.gsensor.which, 1723 GetSensorName((SDL_SensorType) event.gsensor.sensor), 1724 event.gsensor.data[0], 1725 event.gsensor.data[1], 1726 event.gsensor.data[2], 1727 event.gsensor.sensor_timestamp); 1728 break; 1729#endif /* VERBOSE_SENSORS */ 1730 1731#ifdef VERBOSE_AXES 1732 case SDL_EVENT_GAMEPAD_AXIS_MOTION: 1733 if (display_mode == CONTROLLER_MODE_TESTING) { 1734 if (event.gaxis.value <= (-SDL_JOYSTICK_AXIS_MAX / 2) || event.gaxis.value >= (SDL_JOYSTICK_AXIS_MAX / 2)) { 1735 SetController(event.gaxis.which); 1736 } 1737 } 1738 SDL_Log("Gamepad %" SDL_PRIu32 " axis %s changed to %d\n", 1739 event.gaxis.which, 1740 SDL_GetGamepadStringForAxis((SDL_GamepadAxis) event.gaxis.axis), 1741 event.gaxis.value); 1742 break; 1743#endif /* VERBOSE_AXES */ 1744 1745 case SDL_EVENT_GAMEPAD_BUTTON_DOWN: 1746 case SDL_EVENT_GAMEPAD_BUTTON_UP: 1747 if (display_mode == CONTROLLER_MODE_TESTING) { 1748 if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { 1749 SetController(event.gbutton.which); 1750 } 1751 } 1752#ifdef VERBOSE_BUTTONS 1753 SDL_Log("Gamepad %" SDL_PRIu32 " button %s %s\n", 1754 event.gbutton.which, 1755 SDL_GetGamepadStringForButton((SDL_GamepadButton) event.gbutton.button), 1756 event.gbutton.state ? "pressed" : "released"); 1757#endif /* VERBOSE_BUTTONS */ 1758 1759 if (display_mode == CONTROLLER_MODE_TESTING) { 1760 if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN && 1761 controller && SDL_GetGamepadType(controller->gamepad) == SDL_GAMEPAD_TYPE_PS5) { 1762 /* Cycle PS5 audio routing when the microphone button is pressed */ 1763 if (event.gbutton.button == SDL_GAMEPAD_BUTTON_MISC1) { 1764 CyclePS5AudioRoute(controller); 1765 } 1766 1767 /* Cycle PS5 trigger effects when the triangle button is pressed */ 1768 if (event.gbutton.button == SDL_GAMEPAD_BUTTON_NORTH) { 1769 CyclePS5TriggerEffect(controller); 1770 } 1771 } 1772 } 1773 break; 1774 1775 case SDL_EVENT_MOUSE_BUTTON_DOWN: 1776 if (virtual_joystick && controller && controller->joystick == virtual_joystick) { 1777 VirtualGamepadMouseDown(event.button.x, event.button.y); 1778 } 1779 UpdateButtonHighlights(event.button.x, event.button.y, event.button.down); 1780 break; 1781 1782 case SDL_EVENT_MOUSE_BUTTON_UP: 1783 if (virtual_joystick && controller && controller->joystick == virtual_joystick) { 1784 VirtualGamepadMouseUp(event.button.x, event.button.y); 1785 } 1786 1787 if (display_mode == CONTROLLER_MODE_TESTING) { 1788 if (GamepadButtonContains(setup_mapping_button, event.button.x, event.button.y)) { 1789 SetDisplayMode(CONTROLLER_MODE_BINDING); 1790 } 1791 } else if (display_mode == CONTROLLER_MODE_BINDING) { 1792 if (GamepadButtonContains(done_mapping_button, event.button.x, event.button.y)) { 1793 if (controller->mapping) { 1794 SDL_Log("Mapping complete:\n"); 1795 SDL_Log("%s\n", controller->mapping); 1796 } 1797 SetDisplayMode(CONTROLLER_MODE_TESTING); 1798 } else if (GamepadButtonContains(cancel_button, event.button.x, event.button.y)) { 1799 CancelMapping(); 1800 } else if (GamepadButtonContains(clear_button, event.button.x, event.button.y)) { 1801 ClearMapping(); 1802 } else if (controller->has_bindings && 1803 GamepadButtonContains(copy_button, event.button.x, event.button.y)) { 1804 CopyMapping(); 1805 } else if (GamepadButtonContains(paste_button, event.button.x, event.button.y)) { 1806 PasteMapping(); 1807 } else if (title_pressed) { 1808 SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_NAME, false); 1809 } else if (type_pressed) { 1810 SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_TYPE, false); 1811 } else if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE) { 1812 int type = GetGamepadTypeDisplayAt(gamepad_type, event.button.x, event.button.y); 1813 if (type != SDL_GAMEPAD_TYPE_UNSELECTED) { 1814 CommitGamepadType((SDL_GamepadType)type); 1815 StopBinding(); 1816 } 1817 } else { 1818 int gamepad_element = SDL_GAMEPAD_ELEMENT_INVALID; 1819 char *joystick_element; 1820 1821 if (controller->joystick != virtual_joystick) { 1822 gamepad_element = GetGamepadImageElementAt(image, event.button.x, event.button.y); 1823 } 1824 if (gamepad_element == SDL_GAMEPAD_ELEMENT_INVALID) { 1825 gamepad_element = GetGamepadDisplayElementAt(gamepad_elements, controller->gamepad, event.button.x, event.button.y); 1826 } 1827 if (gamepad_element != SDL_GAMEPAD_ELEMENT_INVALID) { 1828 /* Set this to false if you don't want to start the binding flow at this point */ 1829 const bool should_start_flow = true; 1830 SetCurrentBindingElement(gamepad_element, should_start_flow); 1831 } 1832 1833 joystick_element = GetJoystickDisplayElementAt(joystick_elements, controller->joystick, event.button.x, event.button.y); 1834 if (joystick_element) { 1835 CommitBindingElement(joystick_element, true); 1836 SDL_free(joystick_element); 1837 } 1838 } 1839 } 1840 UpdateButtonHighlights(event.button.x, event.button.y, event.button.down); 1841 break; 1842 1843 case SDL_EVENT_MOUSE_MOTION: 1844 if (virtual_joystick && controller && controller->joystick == virtual_joystick) { 1845 VirtualGamepadMouseMotion(event.motion.x, event.motion.y); 1846 } 1847 UpdateButtonHighlights(event.motion.x, event.motion.y, event.motion.state ? true : false); 1848 break; 1849 1850 case SDL_EVENT_KEY_DOWN: 1851 if (display_mode == CONTROLLER_MODE_TESTING) { 1852 if (event.key.key >= SDLK_0 && event.key.key <= SDLK_9) { 1853 if (controller && controller->gamepad) { 1854 int player_index = (event.key.key - SDLK_0); 1855 1856 SDL_SetGamepadPlayerIndex(controller->gamepad, player_index); 1857 } 1858 break; 1859 } else if (event.key.key == SDLK_A) { 1860 OpenVirtualGamepad(); 1861 } else if (event.key.key == SDLK_D) { 1862 CloseVirtualGamepad(); 1863 } else if (event.key.key == SDLK_R && (event.key.mod & SDL_KMOD_CTRL)) { 1864 SDL_ReloadGamepadMappings(); 1865 } else if (event.key.key == SDLK_ESCAPE) { 1866 done = true; 1867 } 1868 } else if (display_mode == CONTROLLER_MODE_BINDING) { 1869 if (event.key.key == SDLK_C && (event.key.mod & SDL_KMOD_CTRL)) { 1870 if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) { 1871 CopyControllerName(); 1872 } else { 1873 CopyMapping(); 1874 } 1875 } else if (event.key.key == SDLK_V && (event.key.mod & SDL_KMOD_CTRL)) { 1876 if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) { 1877 ClearControllerName(); 1878 PasteControllerName(); 1879 } else { 1880 PasteMapping(); 1881 } 1882 } else if (event.key.key == SDLK_X && (event.key.mod & SDL_KMOD_CTRL)) { 1883 if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) { 1884 CopyControllerName(); 1885 ClearControllerName(); 1886 } else { 1887 CopyMapping(); 1888 ClearMapping(); 1889 } 1890 } else if (event.key.key == SDLK_SPACE) { 1891 if (binding_element != SDL_GAMEPAD_ELEMENT_NAME) { 1892 ClearBinding(); 1893 } 1894 } else if (event.key.key == SDLK_BACKSPACE) { 1895 if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) { 1896 BackspaceControllerName(); 1897 } 1898 } else if (event.key.key == SDLK_RETURN) { 1899 if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) { 1900 StopBinding(); 1901 } 1902 } else if (event.key.key == SDLK_ESCAPE) { 1903 if (binding_element != SDL_GAMEPAD_ELEMENT_INVALID) { 1904 StopBinding(); 1905 } else { 1906 CancelMapping(); 1907 } 1908 } 1909 } 1910 break; 1911 case SDL_EVENT_TEXT_INPUT: 1912 if (display_mode == CONTROLLER_MODE_BINDING) { 1913 if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) { 1914 AddControllerNameText(event.text.text); 1915 } 1916 } 1917 break; 1918 case SDL_EVENT_QUIT: 1919 done = true; 1920 break; 1921 default: 1922 break; 1923 } 1924 } 1925 1926 /* Wait 30 ms for joystick events to stop coming in, 1927 in case a gamepad sends multiple events for a single control (e.g. axis and button for trigger) 1928 */ 1929 if (binding_advance_time && SDL_GetTicks() > (binding_advance_time + 30)) { 1930 if (binding_flow) { 1931 SetNextBindingElement(); 1932 } else { 1933 StopBinding(); 1934 } 1935 } 1936 1937 /* blank screen, set up for drawing this frame. */ 1938 SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE); 1939 SDL_RenderClear(screen); 1940 SDL_SetRenderDrawColor(screen, 0x10, 0x10, 0x10, SDL_ALPHA_OPAQUE); 1941 1942 if (controller) { 1943 SetGamepadImageShowingFront(image, ShowingFront()); 1944 UpdateGamepadImageFromGamepad(image, controller->gamepad); 1945 if (display_mode == CONTROLLER_MODE_BINDING && 1946 binding_element != SDL_GAMEPAD_ELEMENT_INVALID) { 1947 SetGamepadImageElement(image, binding_element, true); 1948 } 1949 RenderGamepadImage(image); 1950 1951 if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE) { 1952 SetGamepadTypeDisplayRealType(gamepad_type, SDL_GetRealGamepadType(controller->gamepad)); 1953 RenderGamepadTypeDisplay(gamepad_type); 1954 } else { 1955 RenderGamepadDisplay(gamepad_elements, controller->gamepad); 1956 } 1957 RenderJoystickDisplay(joystick_elements, controller->joystick); 1958 1959 if (display_mode == CONTROLLER_MODE_TESTING) { 1960 RenderGamepadButton(setup_mapping_button); 1961 } else if (display_mode == CONTROLLER_MODE_BINDING) { 1962 DrawBindingTips(screen); 1963 RenderGamepadButton(done_mapping_button); 1964 RenderGamepadButton(cancel_button); 1965 RenderGamepadButton(clear_button); 1966 if (controller->has_bindings) { 1967 RenderGamepadButton(copy_button); 1968 } 1969 RenderGamepadButton(paste_button); 1970 } 1971 1972 DrawGamepadInfo(screen); 1973 1974 UpdateGamepadEffects(); 1975 } else { 1976 DrawGamepadWaiting(screen); 1977 } 1978 SDL_Delay(16); 1979 SDL_RenderPresent(screen); 1980 1981#ifdef SDL_PLATFORM_EMSCRIPTEN 1982 if (done) { 1983 emscripten_cancel_main_loop(); 1984 } 1985#endif 1986} 1987 1988int main(int argc, char *argv[]) 1989{ 1990 bool show_mappings = false; 1991 int i; 1992 float content_scale; 1993 int screen_width, screen_height; 1994 SDL_FRect area; 1995 int gamepad_index = -1; 1996 SDLTest_CommonState *state; 1997 1998 /* Initialize test framework */ 1999 state = SDLTest_CommonCreateState(argv, 0); 2000 if (!state) { 2001 return 1; 2002 } 2003 2004 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI, "1"); 2005 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); 2006 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); 2007 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1"); 2008 SDL_SetHint(SDL_HINT_JOYSTICK_ROG_CHAKRAM, "1"); 2009 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); 2010 SDL_SetHint(SDL_HINT_JOYSTICK_LINUX_DEADZONES, "1"); 2011 2012 /* Enable input debug logging */ 2013 SDL_SetLogPriority(SDL_LOG_CATEGORY_INPUT, SDL_LOG_PRIORITY_DEBUG); 2014 2015 /* Parse commandline */ 2016 for (i = 1; i < argc;) { 2017 int consumed; 2018 2019 consumed = SDLTest_CommonArg(state, i); 2020 if (!consumed) { 2021 if (SDL_strcmp(argv[i], "--mappings") == 0) { 2022 show_mappings = true; 2023 consumed = 1; 2024 } else if (SDL_strcmp(argv[i], "--virtual") == 0) { 2025 OpenVirtualGamepad(); 2026 consumed = 1; 2027 } else if (gamepad_index < 0) { 2028 char *endptr = NULL; 2029 gamepad_index = (int)SDL_strtol(argv[i], &endptr, 0); 2030 if (endptr != argv[i] && *endptr == '\0' && gamepad_index >= 0) { 2031 consumed = 1; 2032 } 2033 } 2034 } 2035 if (consumed <= 0) { 2036 static const char *options[] = { "[--mappings]", "[--virtual]", "[index]", NULL }; 2037 SDLTest_CommonLogUsage(state, argv[0], options); 2038 return 1; 2039 } 2040 2041 i += consumed; 2042 } 2043 if (gamepad_index < 0) { 2044 gamepad_index = 0; 2045 } 2046 2047 /* Initialize SDL (Note: video is required to start event loop) */ 2048 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD)) { 2049 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); 2050 return 1; 2051 } 2052 2053 SDL_AddGamepadMappingsFromFile("gamecontrollerdb.txt"); 2054 2055 if (show_mappings) { 2056 int count = 0; 2057 char **mappings = SDL_GetGamepadMappings(&count); 2058 int map_i; 2059 SDL_Log("Supported mappings:\n"); 2060 for (map_i = 0; map_i < count; ++map_i) { 2061 SDL_Log("\t%s\n", mappings[map_i]); 2062 } 2063 SDL_Log("\n"); 2064 SDL_free(mappings); 2065 } 2066 2067 /* Create a window to display gamepad state */ 2068 content_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); 2069 if (content_scale == 0.0f) { 2070 content_scale = 1.0f; 2071 } 2072 screen_width = (int)SDL_ceilf(SCREEN_WIDTH * content_scale); 2073 screen_height = (int)SDL_ceilf(SCREEN_HEIGHT * content_scale); 2074 window = SDL_CreateWindow("SDL Controller Test", screen_width, screen_height, 0); 2075 if (!window) { 2076 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); 2077 return 2; 2078 } 2079 2080 screen = SDL_CreateRenderer(window, NULL); 2081 if (!screen) { 2082 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); 2083 SDL_DestroyWindow(window); 2084 return 2; 2085 } 2086 2087 SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); 2088 SDL_RenderClear(screen); 2089 SDL_RenderPresent(screen); 2090 2091 /* scale for platforms that don't give you the window size you asked for. */ 2092 SDL_SetRenderLogicalPresentation(screen, (int)SCREEN_WIDTH, (int)SCREEN_HEIGHT, 2093 SDL_LOGICAL_PRESENTATION_LETTERBOX); 2094 2095 2096 title_area.w = GAMEPAD_WIDTH; 2097 title_area.h = FONT_CHARACTER_SIZE + 2 * BUTTON_MARGIN; 2098 title_area.x = PANEL_WIDTH + PANEL_SPACING; 2099 title_area.y = TITLE_HEIGHT / 2 - title_area.h / 2; 2100 2101 type_area.w = PANEL_WIDTH - 2 * BUTTON_MARGIN; 2102 type_area.h = FONT_CHARACTER_SIZE + 2 * BUTTON_MARGIN; 2103 type_area.x = BUTTON_MARGIN; 2104 type_area.y = TITLE_HEIGHT / 2 - type_area.h / 2; 2105 2106 image = CreateGamepadImage(screen); 2107 if (!image) { 2108 SDL_DestroyRenderer(screen); 2109 SDL_DestroyWindow(window); 2110 return 2; 2111 } 2112 SetGamepadImagePosition(image, PANEL_WIDTH + PANEL_SPACING, TITLE_HEIGHT); 2113 2114 gamepad_elements = CreateGamepadDisplay(screen); 2115 area.x = 0; 2116 area.y = TITLE_HEIGHT; 2117 area.w = PANEL_WIDTH; 2118 area.h = GAMEPAD_HEIGHT; 2119 SetGamepadDisplayArea(gamepad_elements, &area); 2120 2121 gamepad_type = CreateGamepadTypeDisplay(screen); 2122 area.x = 0; 2123 area.y = TITLE_HEIGHT; 2124 area.w = PANEL_WIDTH; 2125 area.h = GAMEPAD_HEIGHT; 2126 SetGamepadTypeDisplayArea(gamepad_type, &area); 2127 2128 joystick_elements = CreateJoystickDisplay(screen); 2129 area.x = PANEL_WIDTH + PANEL_SPACING + GAMEPAD_WIDTH + PANEL_SPACING; 2130 area.y = TITLE_HEIGHT; 2131 area.w = PANEL_WIDTH; 2132 area.h = GAMEPAD_HEIGHT; 2133 SetJoystickDisplayArea(joystick_elements, &area); 2134 2135 setup_mapping_button = CreateGamepadButton(screen, "Setup Mapping"); 2136 area.w = SDL_max(MINIMUM_BUTTON_WIDTH, GetGamepadButtonLabelWidth(setup_mapping_button) + 2 * BUTTON_PADDING); 2137 area.h = GetGamepadButtonLabelHeight(setup_mapping_button) + 2 * BUTTON_PADDING; 2138 area.x = BUTTON_MARGIN; 2139 area.y = SCREEN_HEIGHT - BUTTON_MARGIN - area.h; 2140 SetGamepadButtonArea(setup_mapping_button, &area); 2141 2142 cancel_button = CreateGamepadButton(screen, "Cancel"); 2143 area.w = SDL_max(MINIMUM_BUTTON_WIDTH, GetGamepadButtonLabelWidth(cancel_button) + 2 * BUTTON_PADDING); 2144 area.h = GetGamepadButtonLabelHeight(cancel_button) + 2 * BUTTON_PADDING; 2145 area.x = BUTTON_MARGIN; 2146 area.y = SCREEN_HEIGHT - BUTTON_MARGIN - area.h; 2147 SetGamepadButtonArea(cancel_button, &area); 2148 2149 clear_button = CreateGamepadButton(screen, "Clear"); 2150 area.x += area.w + BUTTON_PADDING; 2151 area.w = SDL_max(MINIMUM_BUTTON_WIDTH, GetGamepadButtonLabelWidth(clear_button) + 2 * BUTTON_PADDING); 2152 area.h = GetGamepadButtonLabelHeight(clear_button) + 2 * BUTTON_PADDING; 2153 area.y = SCREEN_HEIGHT - BUTTON_MARGIN - area.h; 2154 SetGamepadButtonArea(clear_button, &area); 2155 2156 copy_button = CreateGamepadButton(screen, "Copy"); 2157 area.x += area.w + BUTTON_PADDING; 2158 area.w = SDL_max(MINIMUM_BUTTON_WIDTH, GetGamepadButtonLabelWidth(copy_button) + 2 * BUTTON_PADDING); 2159 area.h = GetGamepadButtonLabelHeight(copy_button) + 2 * BUTTON_PADDING; 2160 area.y = SCREEN_HEIGHT - BUTTON_MARGIN - area.h; 2161 SetGamepadButtonArea(copy_button, &area); 2162 2163 paste_button = CreateGamepadButton(screen, "Paste"); 2164 area.x += area.w + BUTTON_PADDING; 2165 area.w = SDL_max(MINIMUM_BUTTON_WIDTH, GetGamepadButtonLabelWidth(paste_button) + 2 * BUTTON_PADDING); 2166 area.h = GetGamepadButtonLabelHeight(paste_button) + 2 * BUTTON_PADDING; 2167 area.y = SCREEN_HEIGHT - BUTTON_MARGIN - area.h; 2168 SetGamepadButtonArea(paste_button, &area); 2169 2170 done_mapping_button = CreateGamepadButton(screen, "Done"); 2171 area.w = SDL_max(MINIMUM_BUTTON_WIDTH, GetGamepadButtonLabelWidth(done_mapping_button) + 2 * BUTTON_PADDING); 2172 area.h = GetGamepadButtonLabelHeight(done_mapping_button) + 2 * BUTTON_PADDING; 2173 area.x = SCREEN_WIDTH / 2 - area.w / 2; 2174 area.y = SCREEN_HEIGHT - BUTTON_MARGIN - area.h; 2175 SetGamepadButtonArea(done_mapping_button, &area); 2176 2177 /* Process the initial gamepad list */ 2178 loop(NULL); 2179 2180 if (gamepad_index < num_controllers) { 2181 SetController(controllers[gamepad_index].id); 2182 } else if (num_controllers > 0) { 2183 SetController(controllers[0].id); 2184 } 2185 2186 /* Loop, getting gamepad events! */ 2187#ifdef SDL_PLATFORM_EMSCRIPTEN 2188 emscripten_set_main_loop_arg(loop, NULL, 0, 1); 2189#else 2190 while (!done) { 2191 loop(NULL); 2192 } 2193#endif 2194 2195 CloseVirtualGamepad(); 2196 while (num_controllers > 0) { 2197 HandleGamepadRemoved(controllers[0].id); 2198 DelController(controllers[0].id); 2199 } 2200 SDL_free(controllers); 2201 SDL_free(controller_name); 2202 DestroyGamepadImage(image); 2203 DestroyGamepadDisplay(gamepad_elements); 2204 DestroyGamepadTypeDisplay(gamepad_type); 2205 DestroyJoystickDisplay(joystick_elements); 2206 DestroyGamepadButton(setup_mapping_button); 2207 DestroyGamepadButton(done_mapping_button); 2208 DestroyGamepadButton(cancel_button); 2209 DestroyGamepadButton(clear_button); 2210 DestroyGamepadButton(copy_button); 2211 DestroyGamepadButton(paste_button); 2212 SDLTest_CleanupTextDrawing(); 2213 SDL_DestroyRenderer(screen); 2214 SDL_DestroyWindow(window); 2215 SDL_Quit(); 2216 SDLTest_CommonDestroyState(state); 2217 return 0; 2218}