Atlas - gamepad-events.c

Home / ext / SDL / examples / input / 04-gamepad-events Lines: 1 | Size: 8257 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 * This example code looks for gamepad input in the event handler, and 3 * reports any changes as a flood of info. 4 * 5 * This code is public domain. Feel free to use it for any purpose! 6 */ 7 8/* Joysticks are low-level interfaces: there's something with a bunch of 9 buttons, axes and hats, in no understood order or position. This is 10 a flexible interface, but you'll need to build some sort of configuration 11 UI to let people tell you what button, etc, does what. On top of this 12 interface, SDL offers the "gamepad" API, which works with lots of devices, 13 and knows how to map arbitrary buttons and such to look like an 14 Xbox/PlayStation/etc gamepad. This is easier, and better, for many games, 15 but isn't necessarily a good fit for complex apps and hardware. A flight 16 simulator, a realistic racing game, etc, might want the joystick interface 17 instead of gamepads. */ 18 19#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ 20#include <SDL3/SDL.h> 21#include <SDL3/SDL_main.h> 22 23/* We will use this renderer to draw into this window every frame. */ 24static SDL_Window *window = NULL; 25static SDL_Renderer *renderer = NULL; 26static SDL_Color colors[64]; 27 28#define MOTION_EVENT_COOLDOWN 40 29 30typedef struct EventMessage 31{ 32 char *str; 33 SDL_Color color; 34 Uint64 start_ticks; 35 struct EventMessage *next; 36} EventMessage; 37 38static EventMessage messages; 39static EventMessage *messages_tail = &messages; 40 41static const char *battery_state_string(SDL_PowerState state) 42{ 43 switch (state) { 44 case SDL_POWERSTATE_ERROR: return "ERROR"; 45 case SDL_POWERSTATE_UNKNOWN: return "UNKNOWN"; 46 case SDL_POWERSTATE_ON_BATTERY: return "ON BATTERY"; 47 case SDL_POWERSTATE_NO_BATTERY: return "NO BATTERY"; 48 case SDL_POWERSTATE_CHARGING: return "CHARGING"; 49 case SDL_POWERSTATE_CHARGED: return "CHARGED"; 50 default: break; 51 } 52 return "UNKNOWN"; 53} 54 55static void add_message(SDL_JoystickID jid, const char *fmt, ...) 56{ 57 const SDL_Color *color = &colors[((size_t) jid) % SDL_arraysize(colors)]; 58 EventMessage *msg = NULL; 59 char *str = NULL; 60 va_list ap; 61 62 msg = (EventMessage *) SDL_calloc(1, sizeof (*msg)); 63 if (!msg) { 64 return; // oh well. 65 } 66 67 va_start(ap, fmt); 68 SDL_vasprintf(&str, fmt, ap); 69 va_end(ap); 70 if (!str) { 71 SDL_free(msg); 72 return; // oh well. 73 } 74 75 msg->str = str; 76 SDL_copyp(&msg->color, color); 77 msg->start_ticks = SDL_GetTicks(); 78 msg->next = NULL; 79 80 messages_tail->next = msg; 81 messages_tail = msg; 82} 83 84 85/* This function runs once at startup. */ 86SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) 87{ 88 int i; 89 90 SDL_SetAppMetadata("Example Input Gamepad Events", "1.0", "com.example.input-gamepad-events"); 91 92 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) { 93 SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); 94 return SDL_APP_FAILURE; 95 } 96 97 if (!SDL_CreateWindowAndRenderer("examples/input/gamepad-events", 640, 480, SDL_WINDOW_RESIZABLE, &window, &renderer)) { 98 SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); 99 return SDL_APP_FAILURE; 100 } 101 102 colors[0].r = colors[0].g = colors[0].b = colors[0].a = 255; 103 for (i = 1; i < SDL_arraysize(colors); i++) { 104 colors[i].r = SDL_rand(255); 105 colors[i].g = SDL_rand(255); 106 colors[i].b = SDL_rand(255); 107 colors[i].a = 255; 108 } 109 110 add_message(0, "Please plug in a gamepad."); 111 112 return SDL_APP_CONTINUE; /* carry on with the program! */ 113} 114 115/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ 116SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) 117{ 118 if (event->type == SDL_EVENT_QUIT) { 119 return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ 120 } else if (event->type == SDL_EVENT_GAMEPAD_ADDED) { 121 /* this event is sent for each hotplugged stick, but also each already-connected gamepad during SDL_Init(). */ 122 const SDL_JoystickID which = event->gdevice.which; 123 SDL_Gamepad *gamepad = SDL_OpenGamepad(which); 124 if (!gamepad) { 125 add_message(which, "Gamepad #%u add, but not opened: %s", (unsigned int) which, SDL_GetError()); 126 } else { 127 char *mapping = SDL_GetGamepadMapping(gamepad); 128 add_message(which, "Gamepad #%u ('%s') added", (unsigned int) which, SDL_GetGamepadName(gamepad)); 129 if (mapping) { 130 add_message(which, "Gamepad #%u mapping: %s", (unsigned int) which, mapping); 131 SDL_free(mapping); 132 } 133 } 134 } else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) { 135 const SDL_JoystickID which = event->gdevice.which; 136 SDL_Gamepad *gamepad = SDL_GetGamepadFromID(which); 137 if (gamepad) { 138 SDL_CloseGamepad(gamepad); /* the gamepad was unplugged. */ 139 } 140 add_message(which, "Gamepad #%u removed", (unsigned int) which); 141 } else if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) { 142 static Uint64 axis_motion_cooldown_time = 0; /* these are spammy, only show every X milliseconds. */ 143 const Uint64 now = SDL_GetTicks(); 144 if (now >= axis_motion_cooldown_time) { 145 const SDL_JoystickID which = event->gaxis.which; 146 axis_motion_cooldown_time = now + MOTION_EVENT_COOLDOWN; 147 add_message(which, "Gamepad #%u axis %s -> %d", (unsigned int) which, SDL_GetGamepadStringForAxis((SDL_GamepadAxis) event->gaxis.axis), (int) event->gaxis.value); 148 } 149 } else if ((event->type == SDL_EVENT_GAMEPAD_BUTTON_UP) || (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN)) { 150 const SDL_JoystickID which = event->gbutton.which; 151 add_message(which, "Gamepad #%u button %s -> %s", (unsigned int) which, SDL_GetGamepadStringForButton((SDL_GamepadButton) event->gbutton.button), event->gbutton.down ? "PRESSED" : "RELEASED"); 152 } else if (event->type == SDL_EVENT_JOYSTICK_BATTERY_UPDATED) { 153 const SDL_JoystickID which = event->jbattery.which; 154 if (SDL_IsGamepad(which)) { /* this is only reported for joysticks, so make sure this joystick is _actually_ a gamepad. */ 155 add_message(which, "Gamepad #%u battery -> %s - %d%%", (unsigned int) which, battery_state_string(event->jbattery.state), event->jbattery.percent); 156 } 157 } 158 159 return SDL_APP_CONTINUE; /* carry on with the program! */ 160} 161 162/* This function runs once per frame, and is the heart of the program. */ 163SDL_AppResult SDL_AppIterate(void *appstate) 164{ 165 const Uint64 now = SDL_GetTicks(); 166 const float msg_lifetime = 3500.0f; /* milliseconds a message lives for. */ 167 EventMessage *msg = messages.next; 168 float prev_y = 0.0f; 169 int winw = 640, winh = 480; 170 171 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); 172 SDL_RenderClear(renderer); 173 SDL_GetWindowSize(window, &winw, &winh); 174 175 while (msg) { 176 float x, y; 177 const float life_percent = ((float) (now - msg->start_ticks)) / msg_lifetime; 178 if (life_percent >= 1.0f) { /* msg is done. */ 179 messages.next = msg->next; 180 if (messages_tail == msg) { 181 messages_tail = &messages; 182 } 183 SDL_free(msg->str); 184 SDL_free(msg); 185 msg = messages.next; 186 continue; 187 } 188 x = (((float) winw) - (SDL_strlen(msg->str) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE)) / 2.0f; 189 y = ((float) winh) * life_percent; 190 if ((prev_y != 0.0f) && ((prev_y - y) < ((float) SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE))) { 191 msg->start_ticks = now; 192 break; // wait for the previous message to tick up a little. 193 } 194 195 SDL_SetRenderDrawColor(renderer, msg->color.r, msg->color.g, msg->color.b, (Uint8) (((float) msg->color.a) * (1.0f - life_percent))); 196 SDL_RenderDebugText(renderer, x, y, msg->str); 197 198 prev_y = y; 199 msg = msg->next; 200 } 201 202 SDL_RenderPresent(renderer); 203 204 return SDL_APP_CONTINUE; /* carry on with the program! */ 205} 206 207/* This function runs once at shutdown. */ 208void SDL_AppQuit(void *appstate, SDL_AppResult result) 209{ 210 SDL_Quit(); 211 /* SDL will clean up the window/renderer for us. We let the gamepads leak. */ 212} 213
[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.