| /* | 
 |   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org> | 
 |  | 
 |   This software is provided 'as-is', without any express or implied | 
 |   warranty.  In no event will the authors be held liable for any damages | 
 |   arising from the use of this software. | 
 |  | 
 |   Permission is granted to anyone to use this software for any purpose, | 
 |   including commercial applications, and to alter it and redistribute it | 
 |   freely. | 
 | */ | 
 |  | 
 | /* Simple program to test the SDL game controller routines */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include "SDL.h" | 
 |  | 
 | #ifdef __EMSCRIPTEN__ | 
 | #include <emscripten/emscripten.h> | 
 | #endif | 
 |  | 
 | #ifndef SDL_JOYSTICK_DISABLED | 
 |  | 
 | #ifdef __IPHONEOS__ | 
 | #define SCREEN_WIDTH    480 | 
 | #define SCREEN_HEIGHT    320 | 
 | #else | 
 | #define SCREEN_WIDTH    512 | 
 | #define SCREEN_HEIGHT   320 | 
 | #endif | 
 |  | 
 | /* This is indexed by SDL_GameControllerButton. */ | 
 | static const struct { int x; int y; } button_positions[] = { | 
 |     {387, 167},  /* A */ | 
 |     {431, 132},  /* B */ | 
 |     {342, 132},  /* X */ | 
 |     {389, 101},  /* Y */ | 
 |     {174, 132},  /* BACK */ | 
 |     {233, 132},  /* GUIDE */ | 
 |     {289, 132},  /* START */ | 
 |     {75,  154},  /* LEFTSTICK */ | 
 |     {305, 230},  /* RIGHTSTICK */ | 
 |     {77,  40},   /* LEFTSHOULDER */ | 
 |     {396, 36},   /* RIGHTSHOULDER */ | 
 |     {154, 188},  /* DPAD_UP */ | 
 |     {154, 249},  /* DPAD_DOWN */ | 
 |     {116, 217},  /* DPAD_LEFT */ | 
 |     {186, 217},  /* DPAD_RIGHT */ | 
 | }; | 
 |  | 
 | /* This is indexed by SDL_GameControllerAxis. */ | 
 | static const struct { int x; int y; double angle; } axis_positions[] = { | 
 |     {74,  153, 270.0},  /* LEFTX */ | 
 |     {74,  153, 0.0},  /* LEFTY */ | 
 |     {306, 231, 270.0},  /* RIGHTX */ | 
 |     {306, 231, 0.0},  /* RIGHTY */ | 
 |     {91, -20, 0.0},     /* TRIGGERLEFT */ | 
 |     {375, -20, 0.0},    /* TRIGGERRIGHT */ | 
 | }; | 
 |  | 
 | SDL_Renderer *screen = NULL; | 
 | SDL_bool retval = SDL_FALSE; | 
 | SDL_bool done = SDL_FALSE; | 
 | SDL_Texture *background, *button, *axis; | 
 |  | 
 | static SDL_Texture * | 
 | LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent) | 
 | { | 
 |     SDL_Surface *temp = NULL; | 
 |     SDL_Texture *texture = NULL; | 
 |  | 
 |     temp = SDL_LoadBMP(file); | 
 |     if (temp == NULL) { | 
 |         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError()); | 
 |     } else { | 
 |         /* Set transparent pixel as the pixel at (0,0) */ | 
 |         if (transparent) { | 
 |             if (temp->format->BytesPerPixel == 1) { | 
 |                 SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *)temp->pixels); | 
 |             } | 
 |         } | 
 |  | 
 |         texture = SDL_CreateTextureFromSurface(renderer, temp); | 
 |         if (!texture) { | 
 |             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError()); | 
 |         } | 
 |     } | 
 |     if (temp) { | 
 |         SDL_FreeSurface(temp); | 
 |     } | 
 |     return texture; | 
 | } | 
 |  | 
 | void | 
 | loop(void *arg) | 
 | { | 
 |     SDL_Event event; | 
 |     int i; | 
 |     SDL_GameController *gamecontroller = (SDL_GameController *)arg; | 
 |  | 
 |     /* blank screen, set up for drawing this frame. */ | 
 |     SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE); | 
 |     SDL_RenderClear(screen); | 
 |     SDL_RenderCopy(screen, background, NULL, NULL); | 
 |  | 
 |     while (SDL_PollEvent(&event)) { | 
 |         switch (event.type) { | 
 |         case SDL_CONTROLLERAXISMOTION: | 
 |             SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value); | 
 |             break; | 
 |         case SDL_CONTROLLERBUTTONDOWN: | 
 |         case SDL_CONTROLLERBUTTONUP: | 
 |             SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released"); | 
 |             /* First button triggers a 0.5 second full strength rumble */ | 
 |             if (event.type == SDL_CONTROLLERBUTTONDOWN && | 
 |                 event.cbutton.button == SDL_CONTROLLER_BUTTON_A) { | 
 |                 SDL_GameControllerRumble(gamecontroller, 0xFFFF, 0xFFFF, 500); | 
 |             } | 
 |             break; | 
 |         case SDL_KEYDOWN: | 
 |             if (event.key.keysym.sym != SDLK_ESCAPE) { | 
 |                 break; | 
 |             } | 
 |             /* Fall through to signal quit */ | 
 |         case SDL_QUIT: | 
 |             done = SDL_TRUE; | 
 |             break; | 
 |         default: | 
 |             break; | 
 |         } | 
 |     } | 
 |  | 
 |     /* Update visual controller state */ | 
 |     for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) { | 
 |         if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) { | 
 |             const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 }; | 
 |             SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE); | 
 |         } | 
 |     } | 
 |  | 
 |     for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) { | 
 |         const Sint16 deadzone = 8000;  /* !!! FIXME: real deadzone */ | 
 |         const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i)); | 
 |         if (value < -deadzone) { | 
 |             const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 }; | 
 |             const double angle = axis_positions[i].angle; | 
 |             SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE); | 
 |         } else if (value > deadzone) { | 
 |             const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 }; | 
 |             const double angle = axis_positions[i].angle + 180.0; | 
 |             SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE); | 
 |         } | 
 |     } | 
 |  | 
 |     SDL_RenderPresent(screen); | 
 |  | 
 |     if (!SDL_GameControllerGetAttached(gamecontroller)) { | 
 |         done = SDL_TRUE; | 
 |         retval = SDL_TRUE;  /* keep going, wait for reattach. */ | 
 |     } | 
 |  | 
 | #ifdef __EMSCRIPTEN__ | 
 |     if (done) { | 
 |         emscripten_cancel_main_loop(); | 
 |     } | 
 | #endif | 
 | } | 
 |  | 
 | SDL_bool | 
 | WatchGameController(SDL_GameController * gamecontroller) | 
 | { | 
 |     const char *name = SDL_GameControllerName(gamecontroller); | 
 |     const char *basetitle = "Game Controller Test: "; | 
 |     const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1; | 
 |     char *title = (char *)SDL_malloc(titlelen); | 
 |     SDL_Window *window = NULL; | 
 |  | 
 |     retval = SDL_FALSE; | 
 |     done = SDL_FALSE; | 
 |  | 
 |     if (title) { | 
 |         SDL_snprintf(title, titlelen, "%s%s", basetitle, name); | 
 |     } | 
 |  | 
 |     /* Create a window to display controller state */ | 
 |     window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, | 
 |                               SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, | 
 |                               SCREEN_HEIGHT, 0); | 
 |     SDL_free(title); | 
 |     title = NULL; | 
 |     if (window == NULL) { | 
 |         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); | 
 |         return SDL_FALSE; | 
 |     } | 
 |  | 
 |     screen = SDL_CreateRenderer(window, -1, 0); | 
 |     if (screen == NULL) { | 
 |         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); | 
 |         SDL_DestroyWindow(window); | 
 |         return SDL_FALSE; | 
 |     } | 
 |  | 
 |     SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); | 
 |     SDL_RenderClear(screen); | 
 |     SDL_RenderPresent(screen); | 
 |     SDL_RaiseWindow(window); | 
 |  | 
 |     /* scale for platforms that don't give you the window size you asked for. */ | 
 |     SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT); | 
 |  | 
 |     background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE); | 
 |     button = LoadTexture(screen, "button.bmp", SDL_TRUE); | 
 |     axis = LoadTexture(screen, "axis.bmp", SDL_TRUE); | 
 |  | 
 |     if (!background || !button || !axis) { | 
 |         SDL_DestroyRenderer(screen); | 
 |         SDL_DestroyWindow(window); | 
 |         return SDL_FALSE; | 
 |     } | 
 |     SDL_SetTextureColorMod(button, 10, 255, 21); | 
 |     SDL_SetTextureColorMod(axis, 10, 255, 21); | 
 |  | 
 |     /* !!! FIXME: */ | 
 |     /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/ | 
 |  | 
 |     /* Print info about the controller we are watching */ | 
 |     SDL_Log("Watching controller %s\n",  name ? name : "Unknown Controller"); | 
 |  | 
 |     /* Loop, getting controller events! */ | 
 | #ifdef __EMSCRIPTEN__ | 
 |     emscripten_set_main_loop_arg(loop, gamecontroller, 0, 1); | 
 | #else | 
 |     while (!done) { | 
 |         loop(gamecontroller); | 
 |     } | 
 | #endif | 
 |  | 
 |     SDL_DestroyRenderer(screen); | 
 |     screen = NULL; | 
 |     background = NULL; | 
 |     button = NULL; | 
 |     axis = NULL; | 
 |     SDL_DestroyWindow(window); | 
 |     return retval; | 
 | } | 
 |  | 
 | int | 
 | main(int argc, char *argv[]) | 
 | { | 
 |     int i; | 
 |     int nController = 0; | 
 |     int retcode = 0; | 
 |     char guid[64]; | 
 |     SDL_GameController *gamecontroller; | 
 |  | 
 |     /* Enable standard application logging */ | 
 |     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); | 
 |  | 
 |     /* Initialize SDL (Note: video is required to start event loop) */ | 
 |     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER ) < 0) { | 
 |         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); | 
 |         return 1; | 
 |     } | 
 |      | 
 |     SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); | 
 |  | 
 |     /* Print information about the mappings */ | 
 |     if (!argv[1]) { | 
 |         SDL_Log("Supported mappings:\n"); | 
 |         for (i = 0; i < SDL_GameControllerNumMappings(); ++i) { | 
 |             char *mapping = SDL_GameControllerMappingForIndex(i); | 
 |             if (mapping) { | 
 |                 SDL_Log("\t%s\n", mapping); | 
 |                 SDL_free(mapping); | 
 |             } | 
 |         } | 
 |         SDL_Log("\n"); | 
 |     } | 
 |  | 
 |     /* Print information about the controller */ | 
 |     for (i = 0; i < SDL_NumJoysticks(); ++i) { | 
 |         const char *name; | 
 |         const char *description; | 
 |  | 
 |         SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i), | 
 |                                   guid, sizeof (guid)); | 
 |  | 
 |         if ( SDL_IsGameController(i) ) | 
 |         { | 
 |             nController++; | 
 |             name = SDL_GameControllerNameForIndex(i); | 
 |             description = "Controller"; | 
 |         } else { | 
 |             name = SDL_JoystickNameForIndex(i); | 
 |             description = "Joystick"; | 
 |         } | 
 |         SDL_Log("%s %d: %s (guid %s, VID 0x%.4x, PID 0x%.4x)\n", | 
 |             description, i, name ? name : "Unknown", guid, | 
 |             SDL_JoystickGetDeviceVendor(i), SDL_JoystickGetDeviceProduct(i)); | 
 |     } | 
 |     SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", nController, SDL_NumJoysticks()); | 
 |  | 
 |     if (argv[1]) { | 
 |         SDL_bool reportederror = SDL_FALSE; | 
 |         SDL_bool keepGoing = SDL_TRUE; | 
 |         SDL_Event event; | 
 |         int device = atoi(argv[1]); | 
 |         if (device >= SDL_NumJoysticks()) { | 
 |             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%i is an invalid joystick index.\n", device); | 
 |             retcode = 1; | 
 |         } else { | 
 |             SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(device), | 
 |                                       guid, sizeof (guid)); | 
 |             SDL_Log("Attempting to open device %i, guid %s\n", device, guid); | 
 |             gamecontroller = SDL_GameControllerOpen(device); | 
 |  | 
 |             if (gamecontroller != NULL) { | 
 |                 SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller); | 
 |             } | 
 |  | 
 |             while (keepGoing) { | 
 |                 if (gamecontroller == NULL) { | 
 |                     if (!reportederror) { | 
 |                         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open gamecontroller %d: %s\n", device, SDL_GetError()); | 
 |                         retcode = 1; | 
 |                         keepGoing = SDL_FALSE; | 
 |                         reportederror = SDL_TRUE; | 
 |                     } | 
 |                 } else { | 
 |                     reportederror = SDL_FALSE; | 
 |                     keepGoing = WatchGameController(gamecontroller); | 
 |                     SDL_GameControllerClose(gamecontroller); | 
 |                 } | 
 |  | 
 |                 gamecontroller = NULL; | 
 |                 if (keepGoing) { | 
 |                     SDL_Log("Waiting for attach\n"); | 
 |                 } | 
 |                 while (keepGoing) { | 
 |                     SDL_WaitEvent(&event); | 
 |                     if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN) | 
 |                         || (event.type == SDL_MOUSEBUTTONDOWN)) { | 
 |                         keepGoing = SDL_FALSE; | 
 |                     } else if (event.type == SDL_CONTROLLERDEVICEADDED) { | 
 |                         gamecontroller = SDL_GameControllerOpen(event.cdevice.which); | 
 |                         if (gamecontroller != NULL) { | 
 |                             SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller); | 
 |                         } | 
 |                         break; | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); | 
 |  | 
 |     return retcode; | 
 | } | 
 |  | 
 | #else | 
 |  | 
 | int | 
 | main(int argc, char *argv[]) | 
 | { | 
 |     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n"); | 
 |     exit(1); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | /* vi: set ts=4 sw=4 expandtab: */ |