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