test/testgamecontroller.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 28 May 2020 15:18:41 -0400
changeset 13869 1cd318c35a97
parent 13768 5e7c6765ceda
permissions -rw-r--r--
wayland: Changed output removal in handle_surface_leave()

No longer needs an extra malloc, handles unexpected cases like the same
output being listed twice.
     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 #ifdef __IPHONEOS__
    28 #define SCREEN_WIDTH    480
    29 #define SCREEN_HEIGHT    320
    30 #else
    31 #define SCREEN_WIDTH    512
    32 #define SCREEN_HEIGHT   320
    33 #endif
    34 
    35 /* This is indexed by SDL_GameControllerButton. */
    36 static const struct { int x; int y; } button_positions[] = {
    37     {387, 167},  /* A */
    38     {431, 132},  /* B */
    39     {342, 132},  /* X */
    40     {389, 101},  /* Y */
    41     {174, 132},  /* BACK */
    42     {233, 132},  /* GUIDE */
    43     {289, 132},  /* START */
    44     {75,  154},  /* LEFTSTICK */
    45     {305, 230},  /* RIGHTSTICK */
    46     {77,  40},   /* LEFTSHOULDER */
    47     {396, 36},   /* RIGHTSHOULDER */
    48     {154, 188},  /* DPAD_UP */
    49     {154, 249},  /* DPAD_DOWN */
    50     {116, 217},  /* DPAD_LEFT */
    51     {186, 217},  /* DPAD_RIGHT */
    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_Texture *background, *button, *axis;
    69 SDL_GameController *gamecontroller;
    70 
    71 static SDL_Texture *
    72 LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
    73 {
    74     SDL_Surface *temp = NULL;
    75     SDL_Texture *texture = NULL;
    76 
    77     temp = SDL_LoadBMP(file);
    78     if (temp == NULL) {
    79         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
    80     } else {
    81         /* Set transparent pixel as the pixel at (0,0) */
    82         if (transparent) {
    83             if (temp->format->BytesPerPixel == 1) {
    84                 SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *)temp->pixels);
    85             }
    86         }
    87 
    88         texture = SDL_CreateTextureFromSurface(renderer, temp);
    89         if (!texture) {
    90             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
    91         }
    92     }
    93     if (temp) {
    94         SDL_FreeSurface(temp);
    95     }
    96     return texture;
    97 }
    98 
    99 static void
   100 UpdateWindowTitle()
   101 {
   102     const char *name = SDL_GameControllerName(gamecontroller);
   103     const char *basetitle = "Game Controller Test: ";
   104     const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1;
   105     char *title = (char *)SDL_malloc(titlelen);
   106 
   107     retval = SDL_FALSE;
   108     done = SDL_FALSE;
   109 
   110     if (title) {
   111         SDL_snprintf(title, titlelen, "%s%s", basetitle, name);
   112         SDL_SetWindowTitle(window, title);
   113         SDL_free(title);
   114     }
   115 }
   116 
   117 void
   118 loop(void *arg)
   119 {
   120     SDL_Event event;
   121     int i;
   122 
   123     /* blank screen, set up for drawing this frame. */
   124     SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
   125     SDL_RenderClear(screen);
   126     SDL_RenderCopy(screen, background, NULL, NULL);
   127 
   128     while (SDL_PollEvent(&event)) {
   129         switch (event.type) {
   130         case SDL_CONTROLLERDEVICEADDED:
   131             SDL_Log("Game controller device %d added.\n", (int) event.cdevice.which);
   132             if (!gamecontroller) {
   133                 gamecontroller = SDL_GameControllerOpen(event.cdevice.which);
   134                 if (gamecontroller) {
   135                     UpdateWindowTitle();
   136                 } else {
   137                     SDL_Log("Couldn't open controller: %s\n", SDL_GetError());
   138                 }
   139             }
   140             break;
   141 
   142         case SDL_CONTROLLERDEVICEREMOVED:
   143             SDL_Log("Game controller device %d removed.\n", (int) event.cdevice.which);
   144             if (gamecontroller && event.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) {
   145                 SDL_GameControllerClose(gamecontroller);
   146                 gamecontroller = SDL_GameControllerOpen(0);
   147                 if (gamecontroller) {
   148                     UpdateWindowTitle();
   149                 }
   150             }
   151             break;
   152 
   153         case SDL_CONTROLLERAXISMOTION:
   154             SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value);
   155             break;
   156         case SDL_CONTROLLERBUTTONDOWN:
   157         case SDL_CONTROLLERBUTTONUP:
   158             SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released");
   159             break;
   160         case SDL_KEYDOWN:
   161             if (event.key.keysym.sym != SDLK_ESCAPE) {
   162                 break;
   163             }
   164             /* Fall through to signal quit */
   165         case SDL_QUIT:
   166             done = SDL_TRUE;
   167             break;
   168         default:
   169             break;
   170         }
   171     }
   172 
   173     if (gamecontroller) {
   174         /* Update visual controller state */
   175         for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
   176             if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
   177                 const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 };
   178                 SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE);
   179             }
   180         }
   181 
   182         for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) {
   183             const Sint16 deadzone = 8000;  /* !!! FIXME: real deadzone */
   184             const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i));
   185             if (value < -deadzone) {
   186                 const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
   187                 const double angle = axis_positions[i].angle;
   188                 SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
   189             } else if (value > deadzone) {
   190                 const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
   191                 const double angle = axis_positions[i].angle + 180.0;
   192                 SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
   193             }
   194         }
   195 
   196         /* Update rumble based on trigger state */
   197         {
   198             Uint16 low_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) * 2;
   199             Uint16 high_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) * 2;
   200             SDL_GameControllerRumble(gamecontroller, low_frequency_rumble, high_frequency_rumble, 250);
   201         }
   202     }
   203 
   204     SDL_RenderPresent(screen);
   205 
   206 #ifdef __EMSCRIPTEN__
   207     if (done) {
   208         emscripten_cancel_main_loop();
   209     }
   210 #endif
   211 }
   212 
   213 int
   214 main(int argc, char *argv[])
   215 {
   216     int i;
   217     int nController = 0;
   218     char guid[64];
   219 
   220     /* Enable standard application logging */
   221     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
   222 
   223     /* Initialize SDL (Note: video is required to start event loop) */
   224     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER ) < 0) {
   225         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
   226         return 1;
   227     }
   228     
   229     SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
   230 
   231     /* Print information about the mappings */
   232     if (argv[1] && SDL_strcmp(argv[1], "--mappings") == 0) {
   233         SDL_Log("Supported mappings:\n");
   234         for (i = 0; i < SDL_GameControllerNumMappings(); ++i) {
   235             char *mapping = SDL_GameControllerMappingForIndex(i);
   236             if (mapping) {
   237                 SDL_Log("\t%s\n", mapping);
   238                 SDL_free(mapping);
   239             }
   240         }
   241         SDL_Log("\n");
   242     }
   243 
   244     /* Print information about the controller */
   245     for (i = 0; i < SDL_NumJoysticks(); ++i) {
   246         const char *name;
   247         const char *description;
   248 
   249         SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i),
   250                                   guid, sizeof (guid));
   251 
   252         if ( SDL_IsGameController(i) ) {
   253             nController++;
   254             name = SDL_GameControllerNameForIndex(i);
   255             switch (SDL_GameControllerTypeForIndex(i)) {
   256             case SDL_CONTROLLER_TYPE_XBOX360:
   257                 description = "XBox 360 Controller";
   258                 break;
   259             case SDL_CONTROLLER_TYPE_XBOXONE:
   260                 description = "XBox One Controller";
   261                 break;
   262             case SDL_CONTROLLER_TYPE_PS3:
   263                 description = "PS3 Controller";
   264                 break;
   265             case SDL_CONTROLLER_TYPE_PS4:
   266                 description = "PS4 Controller";
   267                 break;
   268             case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
   269                 description = "Nintendo Switch Pro Controller";
   270                 break;
   271             case SDL_CONTROLLER_TYPE_VIRTUAL:
   272                 description = "Virtual Game Controller";
   273                 break;
   274             default:
   275                 description = "Game Controller";
   276                 break;
   277             }
   278         } else {
   279             name = SDL_JoystickNameForIndex(i);
   280             description = "Joystick";
   281         }
   282         SDL_Log("%s %d: %s (guid %s, VID 0x%.4x, PID 0x%.4x, player index = %d)\n",
   283             description, i, name ? name : "Unknown", guid,
   284             SDL_JoystickGetDeviceVendor(i), SDL_JoystickGetDeviceProduct(i), SDL_JoystickGetDevicePlayerIndex(i));
   285     }
   286     SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", nController, SDL_NumJoysticks());
   287 
   288     /* Create a window to display controller state */
   289     window = SDL_CreateWindow("Game Controller Test", SDL_WINDOWPOS_CENTERED,
   290                               SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
   291                               SCREEN_HEIGHT, 0);
   292     if (window == NULL) {
   293         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
   294         return 2;
   295     }
   296 
   297     screen = SDL_CreateRenderer(window, -1, 0);
   298     if (screen == NULL) {
   299         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
   300         SDL_DestroyWindow(window);
   301         return 2;
   302     }
   303 
   304     SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
   305     SDL_RenderClear(screen);
   306     SDL_RenderPresent(screen);
   307 
   308     /* scale for platforms that don't give you the window size you asked for. */
   309     SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
   310 
   311     background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
   312     button = LoadTexture(screen, "button.bmp", SDL_TRUE);
   313     axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
   314 
   315     if (!background || !button || !axis) {
   316         SDL_DestroyRenderer(screen);
   317         SDL_DestroyWindow(window);
   318         return 2;
   319     }
   320     SDL_SetTextureColorMod(button, 10, 255, 21);
   321     SDL_SetTextureColorMod(axis, 10, 255, 21);
   322 
   323     /* !!! FIXME: */
   324     /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/
   325 
   326     /* Loop, getting controller events! */
   327 #ifdef __EMSCRIPTEN__
   328     emscripten_set_main_loop_arg(loop, NULL, 0, 1);
   329 #else
   330     while (!done) {
   331         loop(NULL);
   332     }
   333 #endif
   334 
   335     SDL_DestroyRenderer(screen);
   336     screen = NULL;
   337     background = NULL;
   338     button = NULL;
   339     axis = NULL;
   340     SDL_DestroyWindow(window);
   341     SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
   342 
   343     return 0;
   344 }
   345 
   346 #else
   347 
   348 int
   349 main(int argc, char *argv[])
   350 {
   351     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n");
   352     return 1;
   353 }
   354 
   355 #endif
   356 
   357 /* vi: set ts=4 sw=4 expandtab: */