Atlas - gamepad-polling.c
Home / ext / SDL / examples / input / 03-gamepad-polling Lines: 1 | Size: 10260 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 * This example code looks for the current gamepad state once per frame, 3 * and draws a visual representation of it. See 01-joystick-polling for the 4 * equivalent example code for the lower-level joystick API. 5 * 6 * This code is public domain. Feel free to use it for any purpose! 7 */ 8 9/* Joysticks are low-level interfaces: there's something with a bunch of 10 buttons, axes and hats, in no understood order or position. This is 11 a flexible interface, but you'll need to build some sort of configuration 12 UI to let people tell you what button, etc, does what. On top of this 13 interface, SDL offers the "gamepad" API, which works with lots of devices, 14 and knows how to map arbitrary buttons and such to look like an 15 Xbox/PlayStation/etc gamepad. This is easier, and better, for many games, 16 but isn't necessarily a good fit for complex apps and hardware. A flight 17 simulator, a realistic racing game, etc, might want the joystick interface 18 instead of gamepads. */ 19 20/* SDL can handle multiple gamepads, but for simplicity, this program only 21 deals with the first gamepad it sees. */ 22 23#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ 24#include <SDL3/SDL.h> 25#include <SDL3/SDL_main.h> 26 27/* We will use this renderer to draw into this window every frame. */ 28static SDL_Window *window = NULL; 29static SDL_Renderer *renderer = NULL; 30static SDL_Texture *texture = NULL; 31static SDL_Gamepad *gamepad = NULL; 32 33#define WINDOW_WIDTH 640 34#define WINDOW_HEIGHT 480 35 36/* This function runs once at startup. */ 37SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) 38{ 39 char *png_path = NULL; 40 SDL_Surface *surface = NULL; 41 42 SDL_SetAppMetadata("Example Input Gamepad Polling", "1.0", "com.example.input-gamepad-polling"); 43 44 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) { 45 SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); 46 return SDL_APP_FAILURE; 47 } 48 49 if (!SDL_CreateWindowAndRenderer("examples/input/gamepad-polling", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE, &window, &renderer)) { 50 SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); 51 return SDL_APP_FAILURE; 52 } 53 54 if (!SDL_SetRenderLogicalPresentation(renderer, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_LOGICAL_PRESENTATION_STRETCH)) { 55 return SDL_APP_FAILURE; 56 } 57 58 /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 59 engines refer to these as "sprites." We'll do a static texture (upload once, draw many 60 times) with data from a bitmap file. */ 61 62 /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 63 Load a .png into a surface, move it to a texture from there. */ 64 SDL_asprintf(&png_path, "%sgamepad_front.png", SDL_GetBasePath()); /* allocate a string of the full file path */ 65 surface = SDL_LoadPNG(png_path); 66 if (!surface) { 67 SDL_Log("Couldn't load bitmap: %s", SDL_GetError()); 68 return SDL_APP_FAILURE; 69 } 70 71 SDL_free(png_path); /* done with this, the file is loaded. */ 72 73 texture = SDL_CreateTextureFromSurface(renderer, surface); 74 if (!texture) { 75 SDL_Log("Couldn't create static texture: %s", SDL_GetError()); 76 return SDL_APP_FAILURE; 77 } 78 79 SDL_DestroySurface(surface); /* done with this, the texture has a copy of the pixels now. */ 80 81 return SDL_APP_CONTINUE; /* carry on with the program! */ 82} 83 84/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ 85SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) 86{ 87 if (event->type == SDL_EVENT_QUIT) { 88 return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ 89 } else if (event->type == SDL_EVENT_GAMEPAD_ADDED) { 90 /* this event is sent for each hotplugged gamepad, but also each already-connected gamepad during SDL_Init(). */ 91 if (gamepad == NULL) { /* we don't have a stick yet and one was added, open it! */ 92 gamepad = SDL_OpenGamepad(event->gdevice.which); 93 if (!gamepad) { 94 SDL_Log("Failed to open gamepad ID %u: %s", (unsigned int) event->gdevice.which, SDL_GetError()); 95 } 96 } 97 } else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) { 98 if (gamepad && (SDL_GetGamepadID(gamepad) == event->gdevice.which)) { 99 SDL_CloseGamepad(gamepad); /* our controller was unplugged. */ 100 gamepad = NULL; 101 } 102 } 103 return SDL_APP_CONTINUE; /* carry on with the program! */ 104} 105 106/* This function runs once per frame, and is the heart of the program. */ 107SDL_AppResult SDL_AppIterate(void *appstate) 108{ 109 const char *text = "Plug in a gamepad, please."; 110 static Uint64 leftthumblast = 0xFFFFFFFF; 111 static Uint64 rightthumblast = 0xFFFFFFFF; 112 const Uint64 now = SDL_GetTicks(); 113 Sint16 axis_x, axis_y; 114 float x, y; 115 int i; 116 117 if (gamepad) { /* we have a stick opened? */ 118 text = SDL_GetGamepadName(gamepad); 119 } 120 121 SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); /* white */ 122 SDL_RenderClear(renderer); 123 124 /* note that you can get input as events, instead of polling, which is 125 better since it won't miss button presses if the system is lagging, 126 but often times checking the current state per-frame is good enough, 127 and maybe better if you'd rather _drop_ inputs due to lag. */ 128 129 if (gamepad) { /* we have a stick opened? */ 130 /* where to draw the buttons */ 131 const SDL_FRect buttons[] = { 132 { 497, 266, 38, 38 }, /* SDL_GAMEPAD_BUTTON_SOUTH */ 133 { 550, 217, 38, 38 }, /* SDL_GAMEPAD_BUTTON_EAST */ 134 { 445, 221, 38, 38 }, /* SDL_GAMEPAD_BUTTON_WEST */ 135 { 499, 173, 38, 38 }, /* SDL_GAMEPAD_BUTTON_NORTH */ 136 { 235, 228, 32, 29 }, /* SDL_GAMEPAD_BUTTON_BACK */ 137 { 287, 195, 69, 69 }, /* SDL_GAMEPAD_BUTTON_GUIDE */ 138 { 377, 228, 32, 29 }, /* SDL_GAMEPAD_BUTTON_START */ 139 { 91, 234, 63, 63 }, /* SDL_GAMEPAD_BUTTON_LEFT_STICK */ 140 { 381, 354, 63, 63 }, /* SDL_GAMEPAD_BUTTON_RIGHT_STICK */ 141 { 74, 73, 102, 29 }, /* SDL_GAMEPAD_BUTTON_LEFT_SHOULDER */ 142 { 468, 73, 102, 29 }, /* SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER */ 143 { 207, 316, 32, 32 }, /* SDL_GAMEPAD_BUTTON_DPAD_UP */ 144 { 207, 384, 32, 32 }, /* SDL_GAMEPAD_BUTTON_DPAD_DOWN */ 145 { 173, 351, 32, 32 }, /* SDL_GAMEPAD_BUTTON_DPAD_LEFT */ 146 { 242, 351, 32, 32 }, /* SDL_GAMEPAD_BUTTON_DPAD_RIGHT */ 147 { 310, 286, 23, 27 }, /* SDL_GAMEPAD_BUTTON_MISC1 */ 148 /* there are other buttons: paddles on the back of the gamepad, touchpads, etc, but this is good enough for now. */ 149 }; 150 151 SDL_RenderTexture(renderer, texture, NULL, NULL); /* draw the gamepad picture to the whole window. */ 152 153 /* draw green boxes over buttons that are currently pressed. */ 154 SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF); /* green */ 155 for (i = 0; i < SDL_arraysize(buttons); i++) { 156 if (SDL_GetGamepadButton(gamepad, (SDL_GamepadButton) i)) { 157 SDL_RenderFillRect(renderer, &buttons[i]); 158 } 159 } 160 161 SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0x00, 0xFF); /* yellow */ 162 163 /* left thumb axis. */ 164 axis_x = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); 165 axis_y = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); 166 if ((SDL_abs(axis_x) > 1000) || (SDL_abs(axis_y) > 1000)) { /* zero means centered, but it might be a little off zero... */ 167 leftthumblast = now; /* keep drawing, we're still moving. */ 168 } 169 if ((now - leftthumblast) < 500) { /* draw if there was movement in the last half-second. */ 170 const SDL_FRect box = { 107 + ((axis_x / 32767.0f) * 30.0f), 252 + ((axis_y / 32767.0f) * 30.0f), 30, 30 }; 171 SDL_RenderFillRect(renderer, &box); 172 } 173 174 /* right thumb axis. */ 175 axis_x = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX); 176 axis_y = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY); 177 if ((SDL_abs(axis_x) > 1000) || (SDL_abs(axis_y) > 1000)) { /* zero means centered, but it might be a little off zero... */ 178 rightthumblast = now; /* keep drawing, we're still moving. */ 179 } 180 if ((now - rightthumblast) < 500) { /* draw if there was movement in the last half-second. */ 181 const SDL_FRect box = { 397 + ((axis_x / 32767.0f) * 30.0f), 370 + ((axis_y / 32767.0f) * 30.0f), 30, 30 }; 182 SDL_RenderFillRect(renderer, &box); 183 } 184 185 /* left trigger. */ 186 axis_y = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER); 187 if (axis_y > 1000) { /* zero means unpressed, but it might be a little off zero... */ 188 const float height = ((axis_y / 32767.0f) * 65.0f); 189 const SDL_FRect box = { 127, 1 + (65.0f - height), 37, height }; 190 SDL_RenderFillRect(renderer, &box); 191 } 192 193 /* right trigger. */ 194 axis_y = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER); 195 if (axis_y > 1000) { /* zero means unpressed, but it might be a little off zero... */ 196 const float height = ((axis_y / 32767.0f) * 65.0f); 197 const SDL_FRect box = { 481, 1 + (65.0f - height), 37, height }; 198 SDL_RenderFillRect(renderer, &box); 199 } 200 } 201 202 x = (((float) WINDOW_WIDTH) - (SDL_strlen(text) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE)) / 2.0f; 203 if (gamepad) { 204 y = (float) (WINDOW_HEIGHT - (SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + 2)); 205 } else { 206 y = (((float) WINDOW_HEIGHT) - SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) / 2.0f; 207 } 208 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, 0xFF); /* blue */ 209 SDL_RenderDebugText(renderer, x, y, text); 210 SDL_RenderPresent(renderer); 211 212 return SDL_APP_CONTINUE; /* carry on with the program! */ 213} 214 215/* This function runs once at shutdown. */ 216void SDL_AppQuit(void *appstate, SDL_AppResult result) 217{ 218 SDL_DestroyTexture(texture); 219 SDL_CloseGamepad(gamepad); 220 /* SDL will clean up the window/renderer for us. */ 221} 222[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.