sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

testgamecontroller.c (19779B)


      1 /*
      2   Copyright (C) 1997-2020 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 game controller routines */
     14 
     15 #include <stdio.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 
     19 #include "SDL.h"
     20 
     21 #ifdef __EMSCRIPTEN__
     22 #include <emscripten/emscripten.h>
     23 #endif
     24 
     25 #ifndef SDL_JOYSTICK_DISABLED
     26 
     27 #define SCREEN_WIDTH    512
     28 #define SCREEN_HEIGHT   320
     29 
     30 /* This is indexed by SDL_GameControllerButton. */
     31 static const struct { int x; int y; } button_positions[] = {
     32     {387, 167},  /* SDL_CONTROLLER_BUTTON_A */
     33     {431, 132},  /* SDL_CONTROLLER_BUTTON_B */
     34     {342, 132},  /* SDL_CONTROLLER_BUTTON_X */
     35     {389, 101},  /* SDL_CONTROLLER_BUTTON_Y */
     36     {174, 132},  /* SDL_CONTROLLER_BUTTON_BACK */
     37     {232, 128},  /* SDL_CONTROLLER_BUTTON_GUIDE */
     38     {289, 132},  /* SDL_CONTROLLER_BUTTON_START */
     39     {75,  154},  /* SDL_CONTROLLER_BUTTON_LEFTSTICK */
     40     {305, 230},  /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */
     41     {77,  40},   /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */
     42     {396, 36},   /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */
     43     {154, 188},  /* SDL_CONTROLLER_BUTTON_DPAD_UP */
     44     {154, 249},  /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */
     45     {116, 217},  /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */
     46     {186, 217},  /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */
     47     {232, 174},  /* SDL_CONTROLLER_BUTTON_MISC1 */
     48     {132, 135},  /* SDL_CONTROLLER_BUTTON_PADDLE1 */
     49     {330, 135},  /* SDL_CONTROLLER_BUTTON_PADDLE2 */
     50     {132, 175},  /* SDL_CONTROLLER_BUTTON_PADDLE3 */
     51     {330, 175},  /* SDL_CONTROLLER_BUTTON_PADDLE4 */
     52 };
     53 
     54 /* This is indexed by SDL_GameControllerAxis. */
     55 static const struct { int x; int y; double angle; } axis_positions[] = {
     56     {74,  153, 270.0},  /* LEFTX */
     57     {74,  153,   0.0},  /* LEFTY */
     58     {306, 231, 270.0},  /* RIGHTX */
     59     {306, 231,   0.0},  /* RIGHTY */
     60     {91,  -20,   0.0},  /* TRIGGERLEFT */
     61     {375, -20,   0.0},  /* TRIGGERRIGHT */
     62 };
     63 
     64 SDL_Window *window = NULL;
     65 SDL_Renderer *screen = NULL;
     66 SDL_bool retval = SDL_FALSE;
     67 SDL_bool done = SDL_FALSE;
     68 SDL_bool set_LED = SDL_FALSE;
     69 SDL_Texture *background_front, *background_back, *button, *axis;
     70 SDL_GameController *gamecontroller;
     71 SDL_GameController **gamecontrollers;
     72 int num_controllers = 0;
     73 
     74 static void UpdateWindowTitle()
     75 {
     76     if (!window) {
     77         return;
     78     }
     79 
     80     if (gamecontroller) {
     81         const char *name = SDL_GameControllerName(gamecontroller);
     82         const char *serial = SDL_GameControllerGetSerial(gamecontroller);
     83         const char *basetitle = "Game Controller Test: ";
     84         const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + (serial ? 3 + SDL_strlen(serial) : 0) + 1;
     85         char *title = (char *)SDL_malloc(titlelen);
     86 
     87         retval = SDL_FALSE;
     88         done = SDL_FALSE;
     89 
     90         if (title) {
     91             SDL_snprintf(title, titlelen, "%s%s", basetitle, name);
     92             if (serial) {
     93                 SDL_strlcat(title, " (", titlelen);
     94                 SDL_strlcat(title, serial, titlelen);
     95                 SDL_strlcat(title, ")", titlelen);
     96             }
     97             SDL_SetWindowTitle(window, title);
     98             SDL_free(title);
     99         }
    100     } else {
    101         SDL_SetWindowTitle(window, "Waiting for controller...");
    102     }
    103 }
    104 
    105 static int FindController(SDL_JoystickID controller_id)
    106 {
    107     int i;
    108 
    109     for (i = 0; i < num_controllers; ++i) {
    110         if (controller_id == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontrollers[i]))) {
    111             return i;
    112         }
    113     }
    114     return -1;
    115 }
    116 
    117 static void AddController(int device_index, SDL_bool verbose)
    118 {
    119     SDL_JoystickID controller_id = SDL_JoystickGetDeviceInstanceID(device_index);
    120     SDL_GameController *controller;
    121     SDL_GameController **controllers;
    122 
    123     controller_id = SDL_JoystickGetDeviceInstanceID(device_index);
    124     if (controller_id < 0) {
    125         SDL_Log("Couldn't get controller ID: %s\n", SDL_GetError());
    126         return;
    127     }
    128 
    129     if (FindController(controller_id) >= 0) {
    130         /* We already have this controller */
    131         return;
    132     }
    133 
    134     controller = SDL_GameControllerOpen(device_index);
    135     if (!controller) {
    136         SDL_Log("Couldn't open controller: %s\n", SDL_GetError());
    137         return;
    138     }
    139 
    140     controllers = (SDL_GameController **)SDL_realloc(gamecontrollers, (num_controllers + 1) * sizeof(*controllers));
    141     if (!controllers) {
    142         SDL_GameControllerClose(controller);
    143         return;
    144     }
    145 
    146     controllers[num_controllers++] = controller;
    147     gamecontrollers = controllers;
    148     gamecontroller = controller;
    149 
    150     if (verbose) {
    151         const char *name = SDL_GameControllerName(gamecontroller);
    152         SDL_Log("Opened game controller %s\n", name);
    153     }
    154 
    155     if (SDL_GameControllerHasSensor(gamecontroller, SDL_SENSOR_ACCEL)) {
    156         if (verbose) {
    157             SDL_Log("Enabling accelerometer\n");
    158         }
    159         SDL_GameControllerSetSensorEnabled(gamecontroller, SDL_SENSOR_ACCEL, SDL_TRUE);
    160     }
    161 
    162     if (SDL_GameControllerHasSensor(gamecontroller, SDL_SENSOR_GYRO)) {
    163         if (verbose) {
    164             SDL_Log("Enabling gyro\n");
    165         }
    166         SDL_GameControllerSetSensorEnabled(gamecontroller, SDL_SENSOR_GYRO, SDL_TRUE);
    167     }
    168 
    169     UpdateWindowTitle();
    170 }
    171 
    172 static void SetController(SDL_JoystickID controller)
    173 {
    174     int i = FindController(controller);
    175 
    176     if (i < 0) {
    177         return;
    178     }
    179 
    180     if (gamecontroller != gamecontrollers[i]) {
    181         gamecontroller = gamecontrollers[i];
    182         UpdateWindowTitle();
    183     }
    184 }
    185 
    186 static void DelController(SDL_JoystickID controller)
    187 {
    188     int i = FindController(controller);
    189 
    190     if (i < 0) {
    191         return;
    192     }
    193 
    194     SDL_GameControllerClose(gamecontrollers[i]);
    195 
    196     --num_controllers;
    197     if (i < num_controllers) {
    198         SDL_memcpy(&gamecontrollers[i], &gamecontrollers[i+1], (num_controllers - i) * sizeof(*gamecontrollers));
    199     }
    200 
    201     if (num_controllers > 0) {
    202         gamecontroller = gamecontrollers[0];
    203     } else {
    204         gamecontroller = NULL;
    205     }
    206     UpdateWindowTitle();
    207 }
    208 
    209 static SDL_Texture *
    210 LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
    211 {
    212     SDL_Surface *temp = NULL;
    213     SDL_Texture *texture = NULL;
    214 
    215     temp = SDL_LoadBMP(file);
    216     if (temp == NULL) {
    217         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
    218     } else {
    219         /* Set transparent pixel as the pixel at (0,0) */
    220         if (transparent) {
    221             if (temp->format->BytesPerPixel == 1) {
    222                 SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *)temp->pixels);
    223             }
    224         }
    225 
    226         texture = SDL_CreateTextureFromSurface(renderer, temp);
    227         if (!texture) {
    228             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
    229         }
    230     }
    231     if (temp) {
    232         SDL_FreeSurface(temp);
    233     }
    234     return texture;
    235 }
    236 
    237 static Uint16 ConvertAxisToRumble(Sint16 axis)
    238 {
    239     /* Only start rumbling if the axis is past the halfway point */
    240     const Sint16 half_axis = (Sint16)SDL_ceil(SDL_JOYSTICK_AXIS_MAX / 2.0f);
    241     if (axis > half_axis) {
    242         return (Uint16)(axis - half_axis) * 4;
    243     } else {
    244         return 0;
    245     }
    246 }
    247 
    248 void
    249 loop(void *arg)
    250 {
    251     SDL_Event event;
    252     int i;
    253     SDL_bool showing_front = SDL_TRUE;
    254 
    255     while (SDL_PollEvent(&event)) {
    256         switch (event.type) {
    257         case SDL_CONTROLLERDEVICEADDED:
    258             SDL_Log("Game controller device %d added.\n", (int) SDL_JoystickGetDeviceInstanceID(event.cdevice.which));
    259             AddController(event.cdevice.which, SDL_TRUE);
    260             break;
    261 
    262         case SDL_CONTROLLERDEVICEREMOVED:
    263             SDL_Log("Game controller device %d removed.\n", (int) event.cdevice.which);
    264             DelController(event.cdevice.which);
    265             break;
    266 
    267         case SDL_CONTROLLERTOUCHPADDOWN:
    268         case SDL_CONTROLLERTOUCHPADMOTION:
    269         case SDL_CONTROLLERTOUCHPADUP:
    270             SDL_Log("Controller touchpad %d finger %d %s %.2f, %.2f, %.2f\n",
    271                 event.ctouchpad.touchpad,
    272                 event.ctouchpad.finger,
    273                 (event.type == SDL_CONTROLLERTOUCHPADDOWN ? "pressed at" :
    274                 (event.type == SDL_CONTROLLERTOUCHPADUP ? "released at" :
    275                 "moved to")),
    276                 event.ctouchpad.x,
    277                 event.ctouchpad.y,
    278                 event.ctouchpad.pressure);
    279             break;
    280 
    281         case SDL_CONTROLLERSENSORUPDATE:
    282             SDL_Log("Controller sensor %s: %.2f, %.2f, %.2f\n",
    283                 event.csensor.sensor == SDL_SENSOR_ACCEL ? "accelerometer" :
    284                 event.csensor.sensor == SDL_SENSOR_GYRO ? "gyro" : "unknown",
    285                 event.csensor.data[0],
    286                 event.csensor.data[1],
    287                 event.csensor.data[2]);
    288             break;
    289 
    290         case SDL_CONTROLLERAXISMOTION:
    291             if (event.caxis.value <= (-SDL_JOYSTICK_AXIS_MAX / 2) || event.caxis.value >= (SDL_JOYSTICK_AXIS_MAX / 2)) {
    292                 SetController(event.caxis.which);
    293             }
    294             SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value);
    295             break;
    296 
    297         case SDL_CONTROLLERBUTTONDOWN:
    298         case SDL_CONTROLLERBUTTONUP:
    299             if (event.type == SDL_CONTROLLERBUTTONDOWN) {
    300                 SetController(event.cbutton.which);
    301             }
    302             SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released");
    303             break;
    304 
    305         case SDL_KEYDOWN:
    306             if (event.key.keysym.sym != SDLK_ESCAPE) {
    307                 break;
    308             }
    309             /* Fall through to signal quit */
    310         case SDL_QUIT:
    311             done = SDL_TRUE;
    312             break;
    313         default:
    314             break;
    315         }
    316     }
    317 
    318     if (gamecontroller) {
    319         /* Show the back of the controller if the paddles are being held */
    320         for (i = SDL_CONTROLLER_BUTTON_PADDLE1; i <= SDL_CONTROLLER_BUTTON_PADDLE4; ++i) {
    321             if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
    322                 showing_front = SDL_FALSE;
    323                 break;
    324             }
    325         }
    326     }
    327 
    328     /* blank screen, set up for drawing this frame. */
    329     SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
    330     SDL_RenderClear(screen);
    331     SDL_RenderCopy(screen, showing_front ? background_front : background_back, NULL, NULL);
    332 
    333     if (gamecontroller) {
    334         /* Update visual controller state */
    335         for (i = 0; i < SDL_CONTROLLER_BUTTON_TOUCHPAD; ++i) {
    336             if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
    337                 SDL_bool on_front = (i < SDL_CONTROLLER_BUTTON_PADDLE1 || i > SDL_CONTROLLER_BUTTON_PADDLE4);
    338                 if (on_front == showing_front) {
    339                     const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 };
    340                     SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE);
    341                 }
    342             }
    343         }
    344 
    345         if (showing_front) {
    346             for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) {
    347                 const Sint16 deadzone = 8000;  /* !!! FIXME: real deadzone */
    348                 const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i));
    349                 if (value < -deadzone) {
    350                     const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
    351                     const double angle = axis_positions[i].angle;
    352                     SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
    353                 } else if (value > deadzone) {
    354                     const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
    355                     const double angle = axis_positions[i].angle + 180.0;
    356                     SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
    357                 }
    358             }
    359         }
    360 
    361         /* Update LED based on left thumbstick position */
    362         {
    363             Sint16 x = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_LEFTX);
    364             Sint16 y = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_LEFTY);
    365 
    366             if (!set_LED) {
    367                 set_LED = (x < -8000 || x > 8000 || y > 8000);
    368             }
    369             if (set_LED) {
    370                 Uint8 r, g, b;
    371 
    372                 if (x < 0) {
    373                     r = (Uint8)(((int)(~x) * 255) / 32767);
    374                     b = 0;
    375                 } else {
    376                     r = 0;
    377                     b = (Uint8)(((int)(x) * 255) / 32767);
    378                 }
    379                 if (y > 0) {
    380                     g = (Uint8)(((int)(y) * 255) / 32767);
    381                 } else {
    382                     g = 0;
    383                 }
    384 
    385                 SDL_GameControllerSetLED(gamecontroller, r, g, b);
    386             }
    387         }
    388 
    389         /* Update rumble based on trigger state */
    390         {
    391             Sint16 left = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
    392             Sint16 right = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
    393             Uint16 low_frequency_rumble = ConvertAxisToRumble(left);
    394             Uint16 high_frequency_rumble = ConvertAxisToRumble(right);
    395             SDL_GameControllerRumble(gamecontroller, low_frequency_rumble, high_frequency_rumble, 250);
    396         }
    397 
    398         /* Update trigger rumble based on thumbstick state */
    399         {
    400             Sint16 left = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_LEFTY);
    401             Sint16 right = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_RIGHTY);
    402             Uint16 left_rumble = ConvertAxisToRumble(~left);
    403             Uint16 right_rumble = ConvertAxisToRumble(~right);
    404 
    405             SDL_GameControllerRumbleTriggers(gamecontroller, left_rumble, right_rumble, 250);
    406         }
    407     }
    408 
    409     SDL_RenderPresent(screen);
    410 
    411 #ifdef __EMSCRIPTEN__
    412     if (done) {
    413         emscripten_cancel_main_loop();
    414     }
    415 #endif
    416 }
    417 
    418 int
    419 main(int argc, char *argv[])
    420 {
    421     int i;
    422     int controller_count = 0;
    423     int controller_index = 0;
    424     char guid[64];
    425 
    426     SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
    427     SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
    428     SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
    429 
    430     /* Enable standard application logging */
    431     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
    432 
    433     /* Initialize SDL (Note: video is required to start event loop) */
    434     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER ) < 0) {
    435         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
    436         return 1;
    437     }
    438     
    439     SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
    440 
    441     /* Print information about the mappings */
    442     if (argv[1] && SDL_strcmp(argv[1], "--mappings") == 0) {
    443         SDL_Log("Supported mappings:\n");
    444         for (i = 0; i < SDL_GameControllerNumMappings(); ++i) {
    445             char *mapping = SDL_GameControllerMappingForIndex(i);
    446             if (mapping) {
    447                 SDL_Log("\t%s\n", mapping);
    448                 SDL_free(mapping);
    449             }
    450         }
    451         SDL_Log("\n");
    452     }
    453 
    454     /* Print information about the controller */
    455     for (i = 0; i < SDL_NumJoysticks(); ++i) {
    456         const char *name;
    457         const char *description;
    458 
    459         SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i),
    460                                   guid, sizeof (guid));
    461 
    462         if (SDL_IsGameController(i)) {
    463             controller_count++;
    464             name = SDL_GameControllerNameForIndex(i);
    465             switch (SDL_GameControllerTypeForIndex(i)) {
    466             case SDL_CONTROLLER_TYPE_XBOX360:
    467                 description = "XBox 360 Controller";
    468                 break;
    469             case SDL_CONTROLLER_TYPE_XBOXONE:
    470                 description = "XBox One Controller";
    471                 break;
    472             case SDL_CONTROLLER_TYPE_PS3:
    473                 description = "PS3 Controller";
    474                 break;
    475             case SDL_CONTROLLER_TYPE_PS4:
    476                 description = "PS4 Controller";
    477                 break;
    478             case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
    479                 description = "Nintendo Switch Pro Controller";
    480                 break;
    481             case SDL_CONTROLLER_TYPE_VIRTUAL:
    482                 description = "Virtual Game Controller";
    483                 break;
    484             default:
    485                 description = "Game Controller";
    486                 break;
    487             }
    488             AddController(i, SDL_FALSE);
    489         } else {
    490             name = SDL_JoystickNameForIndex(i);
    491             description = "Joystick";
    492         }
    493         SDL_Log("%s %d: %s (guid %s, VID 0x%.4x, PID 0x%.4x, player index = %d)\n",
    494             description, i, name ? name : "Unknown", guid,
    495             SDL_JoystickGetDeviceVendor(i), SDL_JoystickGetDeviceProduct(i), SDL_JoystickGetDevicePlayerIndex(i));
    496     }
    497     SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", controller_count, SDL_NumJoysticks());
    498 
    499     /* Create a window to display controller state */
    500     window = SDL_CreateWindow("Game Controller Test", SDL_WINDOWPOS_CENTERED,
    501                               SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
    502                               SCREEN_HEIGHT, 0);
    503     if (window == NULL) {
    504         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
    505         return 2;
    506     }
    507 
    508     screen = SDL_CreateRenderer(window, -1, 0);
    509     if (screen == NULL) {
    510         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
    511         SDL_DestroyWindow(window);
    512         return 2;
    513     }
    514 
    515     SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
    516     SDL_RenderClear(screen);
    517     SDL_RenderPresent(screen);
    518 
    519     /* scale for platforms that don't give you the window size you asked for. */
    520     SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
    521 
    522     background_front = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
    523     background_back = LoadTexture(screen, "controllermap_back.bmp", SDL_FALSE);
    524     button = LoadTexture(screen, "button.bmp", SDL_TRUE);
    525     axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
    526 
    527     if (!background_front || !background_back || !button || !axis) {
    528         SDL_DestroyRenderer(screen);
    529         SDL_DestroyWindow(window);
    530         return 2;
    531     }
    532     SDL_SetTextureColorMod(button, 10, 255, 21);
    533     SDL_SetTextureColorMod(axis, 10, 255, 21);
    534 
    535     /* !!! FIXME: */
    536     /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/
    537 
    538     if (argv[1] && *argv[1] != '-') {
    539         controller_index = SDL_atoi(argv[1]);
    540     }
    541     if (controller_index < num_controllers) {
    542         gamecontroller = gamecontrollers[controller_index];
    543     } else {
    544         gamecontroller = NULL;
    545     }
    546     UpdateWindowTitle();
    547 
    548     /* Loop, getting controller events! */
    549 #ifdef __EMSCRIPTEN__
    550     emscripten_set_main_loop_arg(loop, NULL, 0, 1);
    551 #else
    552     while (!done) {
    553         loop(NULL);
    554     }
    555 #endif
    556 
    557     SDL_DestroyRenderer(screen);
    558     SDL_DestroyWindow(window);
    559     SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
    560 
    561     return 0;
    562 }
    563 
    564 #else
    565 
    566 int
    567 main(int argc, char *argv[])
    568 {
    569     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n");
    570     return 1;
    571 }
    572 
    573 #endif
    574 
    575 /* vi: set ts=4 sw=4 expandtab: */