test/testgamecontroller.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 07 Dec 2017 16:08:09 -0800
changeset 11730 ac6c607e065c
parent 10737 3406a0f8b041
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Enable building the Metal renderer by default, and weak link the Metal framework so the SDL library is safe to use on older Macs
Also generate iOS versions of the Metal shaders
     1 /*
     2   Copyright (C) 1997-2017 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_Renderer *screen = NULL;
    65 SDL_bool retval = SDL_FALSE;
    66 SDL_bool done = SDL_FALSE;
    67 SDL_Texture *background, *button, *axis;
    68 
    69 static SDL_Texture *
    70 LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
    71 {
    72     SDL_Surface *temp = NULL;
    73     SDL_Texture *texture = NULL;
    74 
    75     temp = SDL_LoadBMP(file);
    76     if (temp == NULL) {
    77         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
    78     } else {
    79         /* Set transparent pixel as the pixel at (0,0) */
    80         if (transparent) {
    81             if (temp->format->BytesPerPixel == 1) {
    82                 SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *)temp->pixels);
    83             }
    84         }
    85 
    86         texture = SDL_CreateTextureFromSurface(renderer, temp);
    87         if (!texture) {
    88             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
    89         }
    90     }
    91     if (temp) {
    92         SDL_FreeSurface(temp);
    93     }
    94     return texture;
    95 }
    96 
    97 void
    98 loop(void *arg)
    99 {
   100     SDL_Event event;
   101     int i;
   102     SDL_GameController *gamecontroller = (SDL_GameController *)arg;
   103 
   104     /* blank screen, set up for drawing this frame. */
   105     SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
   106     SDL_RenderClear(screen);
   107     SDL_RenderCopy(screen, background, NULL, NULL);
   108 
   109     while (SDL_PollEvent(&event)) {
   110         switch (event.type) {
   111         case SDL_CONTROLLERAXISMOTION:
   112             SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value);
   113             break;
   114         case SDL_CONTROLLERBUTTONDOWN:
   115         case SDL_CONTROLLERBUTTONUP:
   116             SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released");
   117             break;
   118         case SDL_KEYDOWN:
   119             if (event.key.keysym.sym != SDLK_ESCAPE) {
   120                 break;
   121             }
   122             /* Fall through to signal quit */
   123         case SDL_QUIT:
   124             done = SDL_TRUE;
   125             break;
   126         default:
   127             break;
   128         }
   129     }
   130 
   131     /* Update visual controller state */
   132     for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
   133         if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
   134             const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 };
   135             SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE);
   136         }
   137     }
   138 
   139     for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) {
   140         const Sint16 deadzone = 8000;  /* !!! FIXME: real deadzone */
   141         const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i));
   142         if (value < -deadzone) {
   143             const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
   144             const double angle = axis_positions[i].angle;
   145             SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
   146         } else if (value > deadzone) {
   147             const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
   148             const double angle = axis_positions[i].angle + 180.0;
   149             SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
   150         }
   151     }
   152 
   153     SDL_RenderPresent(screen);
   154 
   155     if (!SDL_GameControllerGetAttached(gamecontroller)) {
   156         done = SDL_TRUE;
   157         retval = SDL_TRUE;  /* keep going, wait for reattach. */
   158     }
   159 
   160 #ifdef __EMSCRIPTEN__
   161     if (done) {
   162         emscripten_cancel_main_loop();
   163     }
   164 #endif
   165 }
   166 
   167 SDL_bool
   168 WatchGameController(SDL_GameController * gamecontroller)
   169 {
   170     const char *name = SDL_GameControllerName(gamecontroller);
   171     const char *basetitle = "Game Controller Test: ";
   172     const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1;
   173     char *title = (char *)SDL_malloc(titlelen);
   174     SDL_Window *window = NULL;
   175 
   176     retval = SDL_FALSE;
   177     done = SDL_FALSE;
   178 
   179     if (title) {
   180         SDL_snprintf(title, titlelen, "%s%s", basetitle, name);
   181     }
   182 
   183     /* Create a window to display controller state */
   184     window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED,
   185                               SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
   186                               SCREEN_HEIGHT, 0);
   187     SDL_free(title);
   188     title = NULL;
   189     if (window == NULL) {
   190         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
   191         return SDL_FALSE;
   192     }
   193 
   194     screen = SDL_CreateRenderer(window, -1, 0);
   195     if (screen == NULL) {
   196         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
   197         SDL_DestroyWindow(window);
   198         return SDL_FALSE;
   199     }
   200 
   201     SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
   202     SDL_RenderClear(screen);
   203     SDL_RenderPresent(screen);
   204     SDL_RaiseWindow(window);
   205 
   206     /* scale for platforms that don't give you the window size you asked for. */
   207     SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
   208 
   209     background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
   210     button = LoadTexture(screen, "button.bmp", SDL_TRUE);
   211     axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
   212 
   213     if (!background || !button || !axis) {
   214         SDL_DestroyRenderer(screen);
   215         SDL_DestroyWindow(window);
   216         return SDL_FALSE;
   217     }
   218     SDL_SetTextureColorMod(button, 10, 255, 21);
   219     SDL_SetTextureColorMod(axis, 10, 255, 21);
   220 
   221     /* !!! FIXME: */
   222     /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/
   223 
   224     /* Print info about the controller we are watching */
   225     SDL_Log("Watching controller %s\n",  name ? name : "Unknown Controller");
   226 
   227     /* Loop, getting controller events! */
   228 #ifdef __EMSCRIPTEN__
   229     emscripten_set_main_loop_arg(loop, gamecontroller, 0, 1);
   230 #else
   231     while (!done) {
   232         loop(gamecontroller);
   233     }
   234 #endif
   235 
   236     SDL_DestroyRenderer(screen);
   237     screen = NULL;
   238     background = NULL;
   239     button = NULL;
   240     axis = NULL;
   241     SDL_DestroyWindow(window);
   242     return retval;
   243 }
   244 
   245 int
   246 main(int argc, char *argv[])
   247 {
   248     int i;
   249     int nController = 0;
   250     int retcode = 0;
   251     char guid[64];
   252     SDL_GameController *gamecontroller;
   253 
   254     /* Enable standard application logging */
   255     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
   256 
   257     /* Initialize SDL (Note: video is required to start event loop) */
   258     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER ) < 0) {
   259         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
   260         return 1;
   261     }
   262     
   263     SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
   264 
   265     /* Print information about the mappings */
   266     if (!argv[1]) {
   267         SDL_Log("Supported mappings:\n");
   268         for (i = 0; i < SDL_GameControllerNumMappings(); ++i) {
   269             char *mapping = SDL_GameControllerMappingForIndex(i);
   270             if (mapping) {
   271                 SDL_Log("\t%s\n", mapping);
   272                 SDL_free(mapping);
   273             }
   274         }
   275         SDL_Log("\n");
   276     }
   277 
   278     /* Print information about the controller */
   279     for (i = 0; i < SDL_NumJoysticks(); ++i) {
   280         const char *name;
   281         const char *description;
   282 
   283         SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i),
   284                                   guid, sizeof (guid));
   285 
   286         if ( SDL_IsGameController(i) )
   287         {
   288             nController++;
   289             name = SDL_GameControllerNameForIndex(i);
   290             description = "Controller";
   291         } else {
   292             name = SDL_JoystickNameForIndex(i);
   293             description = "Joystick";
   294         }
   295         SDL_Log("%s %d: %s (guid %s, VID 0x%.4x, PID 0x%.4x)\n",
   296             description, i, name ? name : "Unknown", guid,
   297             SDL_JoystickGetDeviceVendor(i), SDL_JoystickGetDeviceProduct(i));
   298     }
   299     SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", nController, SDL_NumJoysticks());
   300 
   301     if (argv[1]) {
   302         SDL_bool reportederror = SDL_FALSE;
   303         SDL_bool keepGoing = SDL_TRUE;
   304         SDL_Event event;
   305         int device = atoi(argv[1]);
   306         if (device >= SDL_NumJoysticks()) {
   307             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%i is an invalid joystick index.\n", device);
   308             retcode = 1;
   309         } else {
   310             SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(device),
   311                                       guid, sizeof (guid));
   312             SDL_Log("Attempting to open device %i, guid %s\n", device, guid);
   313             gamecontroller = SDL_GameControllerOpen(device);
   314 
   315             if (gamecontroller != NULL) {
   316                 SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
   317             }
   318 
   319             while (keepGoing) {
   320                 if (gamecontroller == NULL) {
   321                     if (!reportederror) {
   322                         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open gamecontroller %d: %s\n", device, SDL_GetError());
   323                         retcode = 1;
   324                         keepGoing = SDL_FALSE;
   325                         reportederror = SDL_TRUE;
   326                     }
   327                 } else {
   328                     reportederror = SDL_FALSE;
   329                     keepGoing = WatchGameController(gamecontroller);
   330                     SDL_GameControllerClose(gamecontroller);
   331                 }
   332 
   333                 gamecontroller = NULL;
   334                 if (keepGoing) {
   335                     SDL_Log("Waiting for attach\n");
   336                 }
   337                 while (keepGoing) {
   338                     SDL_WaitEvent(&event);
   339                     if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN)
   340                         || (event.type == SDL_MOUSEBUTTONDOWN)) {
   341                         keepGoing = SDL_FALSE;
   342                     } else if (event.type == SDL_CONTROLLERDEVICEADDED) {
   343                         gamecontroller = SDL_GameControllerOpen(event.cdevice.which);
   344                         if (gamecontroller != NULL) {
   345                             SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
   346                         }
   347                         break;
   348                     }
   349                 }
   350             }
   351         }
   352     }
   353 
   354     SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
   355 
   356     return retcode;
   357 }
   358 
   359 #else
   360 
   361 int
   362 main(int argc, char *argv[])
   363 {
   364     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n");
   365     exit(1);
   366 }
   367 
   368 #endif
   369 
   370 /* vi: set ts=4 sw=4 expandtab: */