1 /* 2 Copyright (C) 1997-2014 Sam Lantinga <slouken (at) 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 #ifndef SDL_JOYSTICK_DISABLED 22 23 #ifdef __IPHONEOS__ 24 #define SCREEN_WIDTH 320 25 #define SCREEN_HEIGHT 480 26 #else 27 #define SCREEN_WIDTH 512 28 #define SCREEN_HEIGHT 317 29 #endif 30 31 static const char * 32 ControllerAxisName(const SDL_GameControllerAxis axis) 33 { 34 switch (axis) 35 { 36 #define AXIS_CASE(ax) case SDL_CONTROLLER_AXIS_##ax: return #ax 37 AXIS_CASE(INVALID); 38 AXIS_CASE(LEFTX); 39 AXIS_CASE(LEFTY); 40 AXIS_CASE(RIGHTX); 41 AXIS_CASE(RIGHTY); 42 AXIS_CASE(TRIGGERLEFT); 43 AXIS_CASE(TRIGGERRIGHT); 44 #undef AXIS_CASE 45 default: return "???"; 46 } 47 } 48 49 static const char * 50 ControllerButtonName(const SDL_GameControllerButton button) 51 { 52 switch (button) 53 { 54 #define BUTTON_CASE(btn) case SDL_CONTROLLER_BUTTON_##btn: return #btn 55 BUTTON_CASE(INVALID); 56 BUTTON_CASE(A); 57 BUTTON_CASE(B); 58 BUTTON_CASE(X); 59 BUTTON_CASE(Y); 60 BUTTON_CASE(BACK); 61 BUTTON_CASE(GUIDE); 62 BUTTON_CASE(START); 63 BUTTON_CASE(LEFTSTICK); 64 BUTTON_CASE(RIGHTSTICK); 65 BUTTON_CASE(LEFTSHOULDER); 66 BUTTON_CASE(RIGHTSHOULDER); 67 BUTTON_CASE(DPAD_UP); 68 BUTTON_CASE(DPAD_DOWN); 69 BUTTON_CASE(DPAD_LEFT); 70 BUTTON_CASE(DPAD_RIGHT); 71 #undef BUTTON_CASE 72 default: return "???"; 73 } 74 } 75 76 static SDL_Texture * 77 LoadTexture(SDL_Renderer *renderer, char *file, SDL_bool transparent) 78 { 79 SDL_Surface *temp = NULL; 80 SDL_Texture *texture = NULL; 81 82 temp = SDL_LoadBMP(file); 83 if (temp == NULL) { 84 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError()); 85 } else { 86 /* Set transparent pixel as the pixel at (0,0) */ 87 if (transparent) { 88 SDL_assert(!temp->format->palette); 89 SDL_assert(temp->format->BitsPerPixel == 24); 90 SDL_SetColorKey(temp, SDL_TRUE, (*(Uint32 *) temp->pixels) & 0x00FFFFFF); 91 } 92 93 texture = SDL_CreateTextureFromSurface(renderer, temp); 94 if (!texture) { 95 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError()); 96 } 97 } 98 if (temp) { 99 SDL_FreeSurface(temp); 100 } 101 return texture; 102 } 103 104 SDL_bool 105 WatchGameController(SDL_GameController * gamecontroller) 106 { 107 /* This is indexed by SDL_GameControllerButton. */ 108 static const struct { int x; int y; } button_positions[] = { 109 {387, 167}, /* A */ 110 {431, 132}, /* B */ 111 {342, 132}, /* X */ 112 {389, 101}, /* Y */ 113 {174, 132}, /* BACK */ 114 {233, 132}, /* GUIDE */ 115 {289, 132}, /* START */ 116 {75, 154}, /* LEFTSTICK */ 117 {305, 230}, /* RIGHTSTICK */ 118 {77, 40}, /* LEFTSHOULDER */ 119 {396, 36}, /* RIGHTSHOULDER */ 120 {154, 188}, /* DPAD_UP */ 121 {154, 249}, /* DPAD_DOWN */ 122 {116, 217}, /* DPAD_LEFT */ 123 {186, 217}, /* DPAD_RIGHT */ 124 }; 125 126 /* This is indexed by SDL_GameControllerAxis. */ 127 static const struct { int x; int y; double angle; } axis_positions[] = { 128 {75, 154, 0.0}, /* LEFTX */ 129 {75, 154, 90.0}, /* LEFTY */ 130 {305, 230, 0.0}, /* RIGHTX */ 131 {305, 230, 90.0}, /* RIGHTY */ 132 {91, 0, 90.0}, /* TRIGGERLEFT */ 133 {375, 0, 90.0}, /* TRIGGERRIGHT */ 134 }; 135 136 const char *name = SDL_GameControllerName(gamecontroller); 137 const char *basetitle = "Game Controller Test: "; 138 const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1; 139 char *title = (char *)SDL_malloc(titlelen); 140 SDL_Texture *background, *button, *axis; 141 SDL_Window *window = NULL; 142 SDL_Renderer *screen = NULL; 143 SDL_bool retval = SDL_FALSE; 144 SDL_bool done = SDL_FALSE; 145 SDL_Event event; 146 int i; 147 148 if (title) { 149 SDL_snprintf(title, titlelen, "%s%s", basetitle, name); 150 } 151 152 /* Create a window to display controller state */ 153 window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, 154 SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, 155 SCREEN_HEIGHT, 0); 156 if (window == NULL) { 157 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); 158 return SDL_FALSE; 159 } 160 161 screen = SDL_CreateRenderer(window, -1, 0); 162 if (screen == NULL) { 163 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); 164 SDL_DestroyWindow(window); 165 return SDL_FALSE; 166 } 167 168 SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); 169 SDL_RenderClear(screen); 170 SDL_RenderPresent(screen); 171 SDL_RaiseWindow(window); 172 173 /* scale for platforms that don't give you the window size you asked for. */ 174 SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT); 175 176 background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE); 177 button = LoadTexture(screen, "button.bmp", SDL_TRUE); 178 axis = LoadTexture(screen, "axis.bmp", SDL_TRUE); 179 180 if (!background || !button || !axis) { 181 SDL_DestroyRenderer(screen); 182 SDL_DestroyWindow(window); 183 return SDL_FALSE; 184 } 185 SDL_SetTextureColorMod(button, 10, 255, 21); 186 SDL_SetTextureColorMod(axis, 10, 255, 21); 187 188 /* !!! FIXME: */ 189 /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/ 190 191 /* Print info about the controller we are watching */ 192 SDL_Log("Watching controller %s\n", name ? name : "Unknown Controller"); 193 194 /* Loop, getting controller events! */ 195 while (!done) { 196 /* blank screen, set up for drawing this frame. */ 197 SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE); 198 SDL_RenderClear(screen); 199 SDL_RenderCopy(screen, background, NULL, NULL); 200 201 while (SDL_PollEvent(&event)) { 202 switch (event.type) { 203 case SDL_KEYDOWN: 204 if (event.key.keysym.sym != SDLK_ESCAPE) { 205 break; 206 } 207 /* Fall through to signal quit */ 208 case SDL_QUIT: 209 done = SDL_TRUE; 210 break; 211 default: 212 break; 213 } 214 } 215 216 /* Update visual controller state */ 217 for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) { 218 if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) { 219 const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 }; 220 SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, 0); 221 } 222 } 223 224 for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) { 225 const Sint16 deadzone = 8000; /* !!! FIXME: real deadzone */ 226 const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i)); 227 if (value < -deadzone) { 228 const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 }; 229 const double angle = axis_positions[i].angle; 230 SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, 0); 231 } else if (value > deadzone) { 232 const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 }; 233 const double angle = axis_positions[i].angle + 180.0; 234 SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, 0); 235 } 236 } 237 238 SDL_RenderPresent(screen); 239 240 if (!SDL_GameControllerGetAttached(gamecontroller)) { 241 done = SDL_TRUE; 242 retval = SDL_TRUE; /* keep going, wait for reattach. */ 243 } 244 } 245 246 SDL_DestroyRenderer(screen); 247 SDL_DestroyWindow(window); 248 return retval; 249 } 250 251 int 252 main(int argc, char *argv[]) 253 { 254 int i; 255 int nController = 0; 256 int retcode = 0; 257 char guid[64]; 258 SDL_GameController *gamecontroller; 259 260 /* Enable standard application logging */ 261 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); 262 263 /* Initialize SDL (Note: video is required to start event loop) */ 264 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER ) < 0) { 265 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); 266 return 1; 267 } 268 269 SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); 270 271 /* Print information about the controller */ 272 for (i = 0; i < SDL_NumJoysticks(); ++i) { 273 const char *name; 274 const char *description; 275 276 SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i), 277 guid, sizeof (guid)); 278 279 if ( SDL_IsGameController(i) ) 280 { 281 nController++; 282 name = SDL_GameControllerNameForIndex(i); 283 description = "Controller"; 284 } else { 285 name = SDL_JoystickNameForIndex(i); 286 description = "Joystick"; 287 } 288 SDL_Log("%s %d: %s (guid %s)\n", description, i, name ? name : "Unknown", guid); 289 } 290 SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", nController, SDL_NumJoysticks()); 291 292 if (argv[1]) { 293 SDL_bool reportederror = SDL_FALSE; 294 SDL_bool keepGoing = SDL_TRUE; 295 SDL_Event event; 296 int device = atoi(argv[1]); 297 if (device >= SDL_NumJoysticks()) { 298 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%i is an invalid joystick index.\n", device); 299 retcode = 1; 300 } else { 301 SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(device), 302 guid, sizeof (guid)); 303 SDL_Log("Attempting to open device %i, guid %s\n", device, guid); 304 gamecontroller = SDL_GameControllerOpen(device); 305 while (keepGoing) { 306 if (gamecontroller == NULL) { 307 if (!reportederror) { 308 if (gamecontroller == NULL) { 309 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open gamecontroller %d: %s\n", device, SDL_GetError()); 310 retcode = 1; 311 } 312 keepGoing = SDL_FALSE; 313 reportederror = SDL_TRUE; 314 } 315 } else { 316 reportederror = SDL_FALSE; 317 keepGoing = WatchGameController(gamecontroller); 318 SDL_GameControllerClose(gamecontroller); 319 } 320 321 gamecontroller = NULL; 322 if (keepGoing) { 323 SDL_Log("Waiting for attach\n"); 324 } 325 while (keepGoing) { 326 SDL_WaitEvent(&event); 327 if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN) 328 || (event.type == SDL_MOUSEBUTTONDOWN)) { 329 keepGoing = SDL_FALSE; 330 } else if (event.type == SDL_CONTROLLERDEVICEADDED) { 331 gamecontroller = SDL_GameControllerOpen(event.cdevice.which); 332 break; 333 } 334 } 335 } 336 } 337 } 338 339 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); 340 341 return retcode; 342 } 343 344 #else 345 346 int 347 main(int argc, char *argv[]) 348 { 349 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n"); 350 exit(1); 351 } 352 353 #endif 354