Atlas - controllermap.c
Home / ext / SDL2 / test Lines: 43 | Size: 30829 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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/* Game controller mapping generator */ 14/* Gabriel Jacobo <[email protected]> */ 15 16#include <stdio.h> 17#include <stdlib.h> 18#include <string.h> 19 20#include "SDL.h" 21 22#ifndef SDL_JOYSTICK_DISABLED 23 24#ifdef __IPHONEOS__ 25#define SCREEN_WIDTH 320 26#define SCREEN_HEIGHT 480 27#else 28#define SCREEN_WIDTH 512 29#define SCREEN_HEIGHT 320 30#endif 31 32#define MARKER_BUTTON 1 33#define MARKER_AXIS 2 34 35enum 36{ 37 SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE, 38 SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE, 39 SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE, 40 SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE, 41 SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE, 42 SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE, 43 SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE, 44 SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE, 45 SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT, 46 SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT, 47 SDL_CONTROLLER_BINDING_AXIS_MAX, 48}; 49 50#define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_MAX) 51 52static struct 53{ 54 int x, y; 55 double angle; 56 int marker; 57 58} s_arrBindingDisplay[BINDING_COUNT] = { 59 { 387, 167, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_A */ 60 { 431, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_B */ 61 { 342, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_X */ 62 { 389, 101, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_Y */ 63 { 174, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_BACK */ 64 { 233, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_GUIDE */ 65 { 289, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_START */ 66 { 75, 154, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSTICK */ 67 { 305, 230, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */ 68 { 77, 40, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */ 69 { 396, 36, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */ 70 { 154, 188, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_UP */ 71 { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */ 72 { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */ 73 { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */ 74 { 74, 153, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE */ 75 { 74, 153, 90.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE */ 76 { 74, 153, 0.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE */ 77 { 74, 153, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE */ 78 { 306, 231, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE */ 79 { 306, 231, 90.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE */ 80 { 306, 231, 0.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE */ 81 { 306, 231, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE */ 82 { 91, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT */ 83 { 375, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT */ 84}; 85 86static int s_arrBindingOrder[BINDING_COUNT] = { 87 SDL_CONTROLLER_BUTTON_A, 88 SDL_CONTROLLER_BUTTON_B, 89 SDL_CONTROLLER_BUTTON_Y, 90 SDL_CONTROLLER_BUTTON_X, 91 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE, 92 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE, 93 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE, 94 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE, 95 SDL_CONTROLLER_BUTTON_LEFTSTICK, 96 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE, 97 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE, 98 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE, 99 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE, 100 SDL_CONTROLLER_BUTTON_RIGHTSTICK, 101 SDL_CONTROLLER_BUTTON_LEFTSHOULDER, 102 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT, 103 SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, 104 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT, 105 SDL_CONTROLLER_BUTTON_DPAD_UP, 106 SDL_CONTROLLER_BUTTON_DPAD_RIGHT, 107 SDL_CONTROLLER_BUTTON_DPAD_DOWN, 108 SDL_CONTROLLER_BUTTON_DPAD_LEFT, 109 SDL_CONTROLLER_BUTTON_BACK, 110 SDL_CONTROLLER_BUTTON_GUIDE, 111 SDL_CONTROLLER_BUTTON_START, 112}; 113 114typedef struct 115{ 116 SDL_GameControllerBindType bindType; 117 union 118 { 119 int button; 120 121 struct { 122 int axis; 123 int axis_min; 124 int axis_max; 125 } axis; 126 127 struct { 128 int hat; 129 int hat_mask; 130 } hat; 131 132 } value; 133 134 SDL_bool committed; 135 136} SDL_GameControllerExtendedBind; 137 138static SDL_GameControllerExtendedBind s_arrBindings[BINDING_COUNT]; 139 140typedef struct 141{ 142 SDL_bool m_bMoving; 143 int m_nStartingValue; 144 int m_nFarthestValue; 145} AxisState; 146 147static int s_nNumAxes; 148static AxisState *s_arrAxisState; 149 150static int s_iCurrentBinding; 151static Uint32 s_unPendingAdvanceTime; 152static SDL_bool s_bBindingComplete; 153 154SDL_Texture * 155LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent) 156{ 157 SDL_Surface *temp; 158 SDL_Texture *texture; 159 160 /* Load the sprite image */ 161 temp = SDL_LoadBMP(file); 162 if (temp == NULL) { 163 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError()); 164 return NULL; 165 } 166 167 /* Set transparent pixel as the pixel at (0,0) */ 168 if (transparent) { 169 if (temp->format->palette) { 170 SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels); 171 } 172 } 173 174 /* Create textures from the image */ 175 texture = SDL_CreateTextureFromSurface(renderer, temp); 176 if (!texture) { 177 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError()); 178 SDL_FreeSurface(temp); 179 return NULL; 180 } 181 SDL_FreeSurface(temp); 182 183 /* We're ready to roll. :) */ 184 return texture; 185} 186 187static int 188StandardizeAxisValue(int nValue) 189{ 190 if (nValue > SDL_JOYSTICK_AXIS_MAX/2) { 191 return SDL_JOYSTICK_AXIS_MAX; 192 } else if (nValue < SDL_JOYSTICK_AXIS_MIN/2) { 193 return SDL_JOYSTICK_AXIS_MIN; 194 } else { 195 return 0; 196 } 197} 198 199static void 200SetCurrentBinding(int iBinding) 201{ 202 int iIndex; 203 SDL_GameControllerExtendedBind *pBinding; 204 205 if (iBinding < 0) { 206 return; 207 } 208 209 if (iBinding == BINDING_COUNT) { 210 s_bBindingComplete = SDL_TRUE; 211 return; 212 } 213 214 s_iCurrentBinding = iBinding; 215 216 pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]]; 217 SDL_zerop(pBinding); 218 219 for (iIndex = 0; iIndex < s_nNumAxes; ++iIndex) { 220 s_arrAxisState[iIndex].m_nFarthestValue = s_arrAxisState[iIndex].m_nStartingValue; 221 } 222 223 s_unPendingAdvanceTime = 0; 224} 225 226static SDL_bool 227BBindingContainsBinding(const SDL_GameControllerExtendedBind *pBindingA, const SDL_GameControllerExtendedBind *pBindingB) 228{ 229 if (pBindingA->bindType != pBindingB->bindType) 230 { 231 return SDL_FALSE; 232 } 233 switch (pBindingA->bindType) 234 { 235 case SDL_CONTROLLER_BINDTYPE_AXIS: 236 if (pBindingA->value.axis.axis != pBindingB->value.axis.axis) { 237 return SDL_FALSE; 238 } 239 if (!pBindingA->committed) { 240 return SDL_FALSE; 241 } 242 { 243 int minA = SDL_min(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max); 244 int maxA = SDL_max(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max); 245 int minB = SDL_min(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max); 246 int maxB = SDL_max(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max); 247 return (minA <= minB && maxA >= maxB); 248 } 249 /* Not reached */ 250 default: 251 return SDL_memcmp(pBindingA, pBindingB, sizeof(*pBindingA)) == 0; 252 } 253} 254 255static void 256ConfigureBinding(const SDL_GameControllerExtendedBind *pBinding) 257{ 258 SDL_GameControllerExtendedBind *pCurrent; 259 int iIndex; 260 int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding]; 261 262 /* Do we already have this binding? */ 263 for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) { 264 pCurrent = &s_arrBindings[iIndex]; 265 if (BBindingContainsBinding(pCurrent, pBinding)) { 266 if (iIndex == SDL_CONTROLLER_BUTTON_A && iCurrentElement != SDL_CONTROLLER_BUTTON_B) { 267 /* Skip to the next binding */ 268 SetCurrentBinding(s_iCurrentBinding + 1); 269 return; 270 } 271 272 if (iIndex == SDL_CONTROLLER_BUTTON_B) { 273 /* Go back to the previous binding */ 274 SetCurrentBinding(s_iCurrentBinding - 1); 275 return; 276 } 277 278 /* Already have this binding, ignore it */ 279 return; 280 } 281 } 282 283#ifdef DEBUG_CONTROLLERMAP 284 switch ( pBinding->bindType ) 285 { 286 case SDL_CONTROLLER_BINDTYPE_NONE: 287 break; 288 case SDL_CONTROLLER_BINDTYPE_BUTTON: 289 SDL_Log("Configuring button binding for button %d\n", pBinding->value.button); 290 break; 291 case SDL_CONTROLLER_BINDTYPE_AXIS: 292 SDL_Log("Configuring axis binding for axis %d %d/%d committed = %s\n", pBinding->value.axis.axis, pBinding->value.axis.axis_min, pBinding->value.axis.axis_max, pBinding->committed ? "true" : "false"); 293 break; 294 case SDL_CONTROLLER_BINDTYPE_HAT: 295 SDL_Log("Configuring hat binding for hat %d %d\n", pBinding->value.hat.hat, pBinding->value.hat.hat_mask); 296 break; 297 } 298#endif /* DEBUG_CONTROLLERMAP */ 299 300 /* Should the new binding override the existing one? */ 301 pCurrent = &s_arrBindings[iCurrentElement]; 302 if (pCurrent->bindType != SDL_CONTROLLER_BINDTYPE_NONE) { 303 SDL_bool bNativeDPad, bCurrentDPad; 304 SDL_bool bNativeAxis, bCurrentAxis; 305 306 bNativeDPad = (iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_UP || 307 iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_DOWN || 308 iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_LEFT || 309 iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_RIGHT); 310 bCurrentDPad = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_HAT); 311 if (bNativeDPad && bCurrentDPad) { 312 /* We already have a binding of the type we want, ignore the new one */ 313 return; 314 } 315 316 bNativeAxis = (iCurrentElement >= SDL_CONTROLLER_BUTTON_MAX); 317 bCurrentAxis = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_AXIS); 318 if (bNativeAxis == bCurrentAxis && 319 (pBinding->bindType != SDL_CONTROLLER_BINDTYPE_AXIS || 320 pBinding->value.axis.axis != pCurrent->value.axis.axis)) { 321 /* We already have a binding of the type we want, ignore the new one */ 322 return; 323 } 324 } 325 326 *pCurrent = *pBinding; 327 328 if (pBinding->committed) { 329 s_unPendingAdvanceTime = SDL_GetTicks(); 330 } else { 331 s_unPendingAdvanceTime = 0; 332 } 333} 334 335static SDL_bool 336BMergeAxisBindings(int iIndex) 337{ 338 SDL_GameControllerExtendedBind *pBindingA = &s_arrBindings[iIndex]; 339 SDL_GameControllerExtendedBind *pBindingB = &s_arrBindings[iIndex+1]; 340 if (pBindingA->bindType == SDL_CONTROLLER_BINDTYPE_AXIS && 341 pBindingB->bindType == SDL_CONTROLLER_BINDTYPE_AXIS && 342 pBindingA->value.axis.axis == pBindingB->value.axis.axis) { 343 if (pBindingA->value.axis.axis_min == pBindingB->value.axis.axis_min) { 344 pBindingA->value.axis.axis_min = pBindingA->value.axis.axis_max; 345 pBindingA->value.axis.axis_max = pBindingB->value.axis.axis_max; 346 pBindingB->bindType = SDL_CONTROLLER_BINDTYPE_NONE; 347 return SDL_TRUE; 348 } 349 } 350 return SDL_FALSE; 351} 352 353static void 354WatchJoystick(SDL_Joystick * joystick) 355{ 356 SDL_Window *window = NULL; 357 SDL_Renderer *screen = NULL; 358 SDL_Texture *background, *button, *axis, *marker; 359 const char *name = NULL; 360 SDL_bool done = SDL_FALSE; 361 SDL_Event event; 362 SDL_Rect dst; 363 Uint8 alpha=200, alpha_step = -1; 364 Uint32 alpha_ticks = 0; 365 SDL_JoystickID nJoystickID; 366 int iIndex; 367 368 /* Create a window to display joystick axis position */ 369 window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED, 370 SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, 371 SCREEN_HEIGHT, 0); 372 if (window == NULL) { 373 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); 374 return; 375 } 376 377 screen = SDL_CreateRenderer(window, -1, 0); 378 if (screen == NULL) { 379 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); 380 SDL_DestroyWindow(window); 381 return; 382 } 383 384 background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE); 385 button = LoadTexture(screen, "button.bmp", SDL_TRUE); 386 axis = LoadTexture(screen, "axis.bmp", SDL_TRUE); 387 SDL_RaiseWindow(window); 388 389 /* scale for platforms that don't give you the window size you asked for. */ 390 SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT); 391 392 /* Print info about the joystick we are watching */ 393 name = SDL_JoystickName(joystick); 394 SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick), 395 name ? name : "Unknown Joystick"); 396 SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n", 397 SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick), 398 SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick)); 399 400 SDL_Log("\n\n\ 401 ====================================================================================\n\ 402 Press the buttons on your controller when indicated\n\ 403 (Your controller may look different than the picture)\n\ 404 If you want to correct a mistake, press backspace or the back button on your device\n\ 405 To skip a button, press SPACE or click/touch the screen\n\ 406 To exit, press ESC\n\ 407 ====================================================================================\n"); 408 409 nJoystickID = SDL_JoystickInstanceID(joystick); 410 411 s_nNumAxes = SDL_JoystickNumAxes(joystick); 412 s_arrAxisState = (AxisState *)SDL_calloc(s_nNumAxes, sizeof(*s_arrAxisState)); 413 for (iIndex = 0; iIndex < s_nNumAxes; ++iIndex) { 414 AxisState *pAxisState = &s_arrAxisState[iIndex]; 415 Sint16 nInitialValue; 416 pAxisState->m_bMoving = SDL_JoystickGetAxisInitialState(joystick, iIndex, &nInitialValue); 417 pAxisState->m_nStartingValue = nInitialValue; 418 pAxisState->m_nFarthestValue = nInitialValue; 419 } 420 421 /* Loop, getting joystick events! */ 422 while (!done && !s_bBindingComplete) { 423 int iElement = s_arrBindingOrder[s_iCurrentBinding]; 424 425 switch (s_arrBindingDisplay[iElement].marker) { 426 case MARKER_AXIS: 427 marker = axis; 428 break; 429 case MARKER_BUTTON: 430 marker = button; 431 break; 432 default: 433 break; 434 } 435 436 dst.x = s_arrBindingDisplay[iElement].x; 437 dst.y = s_arrBindingDisplay[iElement].y; 438 SDL_QueryTexture(marker, NULL, NULL, &dst.w, &dst.h); 439 440 if (SDL_GetTicks() - alpha_ticks > 5) { 441 alpha_ticks = SDL_GetTicks(); 442 alpha += alpha_step; 443 if (alpha == 255) { 444 alpha_step = -1; 445 } 446 if (alpha < 128) { 447 alpha_step = 1; 448 } 449 } 450 451 SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE); 452 SDL_RenderClear(screen); 453 SDL_RenderCopy(screen, background, NULL, NULL); 454 SDL_SetTextureAlphaMod(marker, alpha); 455 SDL_SetTextureColorMod(marker, 10, 255, 21); 456 SDL_RenderCopyEx(screen, marker, NULL, &dst, s_arrBindingDisplay[iElement].angle, NULL, SDL_FLIP_NONE); 457 SDL_RenderPresent(screen); 458 459 while (SDL_PollEvent(&event) > 0) { 460 switch (event.type) { 461 case SDL_JOYDEVICEREMOVED: 462 if (event.jaxis.which == nJoystickID) { 463 done = SDL_TRUE; 464 } 465 break; 466 case SDL_JOYAXISMOTION: 467 if (event.jaxis.which == nJoystickID) { 468 AxisState *pAxisState = &s_arrAxisState[event.jaxis.axis]; 469 int nValue = event.jaxis.value; 470 int nCurrentDistance, nFarthestDistance; 471 if (!pAxisState->m_bMoving) { 472 pAxisState->m_bMoving = SDL_TRUE; 473 pAxisState->m_nStartingValue = nValue; 474 pAxisState->m_nFarthestValue = nValue; 475 } 476 nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue); 477 nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); 478 if (nCurrentDistance > nFarthestDistance) { 479 pAxisState->m_nFarthestValue = nValue; 480 nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); 481 } 482 483#ifdef DEBUG_CONTROLLERMAP 484 SDL_Log("AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d\n", event.jaxis.axis, nValue, nCurrentDistance, nFarthestDistance); 485#endif 486 if (nFarthestDistance >= 16000) { 487 /* If we've gone out far enough and started to come back, let's bind this axis */ 488 SDL_bool bCommitBinding = (nCurrentDistance <= 10000) ? SDL_TRUE : SDL_FALSE; 489 SDL_GameControllerExtendedBind binding; 490 SDL_zero(binding); 491 binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; 492 binding.value.axis.axis = event.jaxis.axis; 493 binding.value.axis.axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue); 494 binding.value.axis.axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue); 495 binding.committed = bCommitBinding; 496 ConfigureBinding(&binding); 497 } 498 } 499 break; 500 case SDL_JOYHATMOTION: 501 if (event.jhat.which == nJoystickID) { 502 if (event.jhat.value != SDL_HAT_CENTERED) { 503 SDL_GameControllerExtendedBind binding; 504 505#ifdef DEBUG_CONTROLLERMAP 506 SDL_Log("HAT %d %d\n", event.jhat.hat, event.jhat.value); 507#endif 508 SDL_zero(binding); 509 binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT; 510 binding.value.hat.hat = event.jhat.hat; 511 binding.value.hat.hat_mask = event.jhat.value; 512 binding.committed = SDL_TRUE; 513 ConfigureBinding(&binding); 514 } 515 } 516 break; 517 case SDL_JOYBALLMOTION: 518 break; 519 case SDL_JOYBUTTONDOWN: 520 if (event.jbutton.which == nJoystickID) { 521 SDL_GameControllerExtendedBind binding; 522 523#ifdef DEBUG_CONTROLLERMAP 524 SDL_Log("BUTTON %d\n", event.jbutton.button); 525#endif 526 SDL_zero(binding); 527 binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; 528 binding.value.button = event.jbutton.button; 529 binding.committed = SDL_TRUE; 530 ConfigureBinding(&binding); 531 } 532 break; 533 case SDL_FINGERDOWN: 534 case SDL_MOUSEBUTTONDOWN: 535 /* Skip this step */ 536 SetCurrentBinding(s_iCurrentBinding + 1); 537 break; 538 case SDL_KEYDOWN: 539 if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) { 540 SetCurrentBinding(s_iCurrentBinding - 1); 541 break; 542 } 543 if (event.key.keysym.sym == SDLK_SPACE) { 544 SetCurrentBinding(s_iCurrentBinding + 1); 545 break; 546 } 547 548 if ((event.key.keysym.sym != SDLK_ESCAPE)) { 549 break; 550 } 551 /* Fall through to signal quit */ 552 case SDL_QUIT: 553 done = SDL_TRUE; 554 break; 555 default: 556 break; 557 } 558 } 559 560 SDL_Delay(15); 561 562 /* Wait 100 ms for joystick events to stop coming in, 563 in case a controller sends multiple events for a single control (e.g. axis and button for trigger) 564 */ 565 if (s_unPendingAdvanceTime && SDL_GetTicks() - s_unPendingAdvanceTime >= 100) { 566 SetCurrentBinding(s_iCurrentBinding + 1); 567 } 568 } 569 570 if (s_bBindingComplete) { 571 char mapping[1024]; 572 char trimmed_name[128]; 573 char *spot; 574 int iIndex; 575 char pszElement[12]; 576 577 SDL_strlcpy(trimmed_name, name, SDL_arraysize(trimmed_name)); 578 while (SDL_isspace(trimmed_name[0])) { 579 SDL_memmove(&trimmed_name[0], &trimmed_name[1], SDL_strlen(trimmed_name)); 580 } 581 while (trimmed_name[0] && SDL_isspace(trimmed_name[SDL_strlen(trimmed_name) - 1])) { 582 trimmed_name[SDL_strlen(trimmed_name) - 1] = '\0'; 583 } 584 while ((spot = SDL_strchr(trimmed_name, ',')) != NULL) { 585 SDL_memmove(spot, spot + 1, SDL_strlen(spot)); 586 } 587 588 /* Initialize mapping with GUID and name */ 589 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), mapping, SDL_arraysize(mapping)); 590 SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); 591 SDL_strlcat(mapping, trimmed_name, SDL_arraysize(mapping)); 592 SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); 593 SDL_strlcat(mapping, "platform:", SDL_arraysize(mapping)); 594 SDL_strlcat(mapping, SDL_GetPlatform(), SDL_arraysize(mapping)); 595 SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); 596 597 for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) { 598 SDL_GameControllerExtendedBind *pBinding = &s_arrBindings[iIndex]; 599 if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) { 600 continue; 601 } 602 603 if (iIndex < SDL_CONTROLLER_BUTTON_MAX) { 604 SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex; 605 SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping)); 606 } else { 607 const char *pszAxisName; 608 switch (iIndex - SDL_CONTROLLER_BUTTON_MAX) { 609 case SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE: 610 if (!BMergeAxisBindings(iIndex)) { 611 SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); 612 } 613 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX); 614 break; 615 case SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE: 616 SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); 617 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX); 618 break; 619 case SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE: 620 if (!BMergeAxisBindings(iIndex)) { 621 SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); 622 } 623 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY); 624 break; 625 case SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE: 626 SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); 627 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY); 628 break; 629 case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE: 630 if (!BMergeAxisBindings(iIndex)) { 631 SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); 632 } 633 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX); 634 break; 635 case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE: 636 SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); 637 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX); 638 break; 639 case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE: 640 if (!BMergeAxisBindings(iIndex)) { 641 SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); 642 } 643 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY); 644 break; 645 case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE: 646 SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); 647 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY); 648 break; 649 case SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT: 650 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT); 651 break; 652 case SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT: 653 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT); 654 break; 655 } 656 SDL_strlcat(mapping, pszAxisName, SDL_arraysize(mapping)); 657 } 658 SDL_strlcat(mapping, ":", SDL_arraysize(mapping)); 659 660 pszElement[0] = '\0'; 661 switch (pBinding->bindType) { 662 case SDL_CONTROLLER_BINDTYPE_BUTTON: 663 SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button); 664 break; 665 case SDL_CONTROLLER_BINDTYPE_AXIS: 666 if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MIN) { 667 /* The negative half axis */ 668 SDL_snprintf(pszElement, sizeof(pszElement), "-a%d", pBinding->value.axis.axis); 669 } else if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MAX) { 670 /* The positive half axis */ 671 SDL_snprintf(pszElement, sizeof(pszElement), "+a%d", pBinding->value.axis.axis); 672 } else { 673 SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis.axis); 674 if (pBinding->value.axis.axis_min > pBinding->value.axis.axis_max) { 675 /* Invert the axis */ 676 SDL_strlcat(pszElement, "~", SDL_arraysize(pszElement)); 677 } 678 } 679 break; 680 case SDL_CONTROLLER_BINDTYPE_HAT: 681 SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask); 682 break; 683 default: 684 SDL_assert(!"Unknown bind type"); 685 break; 686 } 687 SDL_strlcat(mapping, pszElement, SDL_arraysize(mapping)); 688 SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); 689 } 690 691 SDL_Log("Mapping:\n\n%s\n\n", mapping); 692 /* Print to stdout as well so the user can cat the output somewhere */ 693 printf("%s\n", mapping); 694 } 695 696 SDL_free(s_arrAxisState); 697 s_arrAxisState = NULL; 698 699 SDL_DestroyRenderer(screen); 700 SDL_DestroyWindow(window); 701} 702 703int 704main(int argc, char *argv[]) 705{ 706 const char *name; 707 int i; 708 SDL_Joystick *joystick; 709 710 /* Enable standard application logging */ 711 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); 712 713 /* Initialize SDL (Note: video is required to start event loop) */ 714 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 715 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); 716 exit(1); 717 } 718 719 /* Print information about the joysticks */ 720 SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks()); 721 for (i = 0; i < SDL_NumJoysticks(); ++i) { 722 name = SDL_JoystickNameForIndex(i); 723 SDL_Log("Joystick %d: %s\n", i, name ? name : "Unknown Joystick"); 724 joystick = SDL_JoystickOpen(i); 725 if (joystick == NULL) { 726 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n", i, 727 SDL_GetError()); 728 } else { 729 char guid[64]; 730 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), 731 guid, sizeof (guid)); 732 SDL_Log(" axes: %d\n", SDL_JoystickNumAxes(joystick)); 733 SDL_Log(" balls: %d\n", SDL_JoystickNumBalls(joystick)); 734 SDL_Log(" hats: %d\n", SDL_JoystickNumHats(joystick)); 735 SDL_Log(" buttons: %d\n", SDL_JoystickNumButtons(joystick)); 736 SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick)); 737 SDL_Log(" guid: %s\n", guid); 738 SDL_Log(" VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick)); 739 SDL_JoystickClose(joystick); 740 } 741 } 742 743#ifdef __ANDROID__ 744 if (SDL_NumJoysticks() > 0) { 745#else 746 if (argv[1]) { 747#endif 748 int device; 749#ifdef __ANDROID__ 750 device = 0; 751#else 752 device = atoi(argv[1]); 753#endif 754 joystick = SDL_JoystickOpen(device); 755 if (joystick == NULL) { 756 SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError()); 757 } else { 758 WatchJoystick(joystick); 759 SDL_JoystickClose(joystick); 760 } 761 } 762 else { 763 SDL_Log("\n\nUsage: ./controllermap number\nFor example: ./controllermap 0\nOr: ./controllermap 0 >> gamecontrollerdb.txt"); 764 } 765 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); 766 767 return 0; 768} 769 770#else 771 772int 773main(int argc, char *argv[]) 774{ 775 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n"); 776 exit(1); 777} 778 779#endif 780 781/* vi: set ts=4 sw=4 expandtab: */ 782[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.