Skip to content

Commit def0918

Browse files
committed
Add Gamepad support+example for SDL3, todo are other backends
1 parent 4d4d5f2 commit def0918

File tree

5 files changed

+286
-0
lines changed

5 files changed

+286
-0
lines changed

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ add_example(core_rendertexture)
6666
#add_example(pipeline_basic)
6767
add_example(pipeline_settings)
6868
add_example(input_keys)
69+
add_example(input_gamepad)
6970
add_example(input_multitouch_gesture)
7071
add_example(input_text)
7172
#add_example(models_cube)

examples/input_gamepad.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include <raygpu.h>
2+
3+
const int screenWidth = 800;
4+
const int screenHeight = 450;
5+
6+
Vector2 ballPosition = { 400, 225 };
7+
Color ballColor = {0, 82, 172, 255};
8+
9+
void setup(void){
10+
SetTargetFPS(60);
11+
}
12+
13+
void render(void) {
14+
if (IsGamepadAvailable(0)) {
15+
16+
17+
ballPosition.x += GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) * 5.0f;
18+
ballPosition.y += GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) * 5.0f;
19+
if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_SOUTH))
20+
ballColor = GREEN;
21+
else if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_EAST)){
22+
ballColor = RED;
23+
}
24+
else if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_WEST)){
25+
ballColor = BLUE;
26+
}
27+
else if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_NORTH)){
28+
ballColor = YELLOW;
29+
}
30+
else ballColor = DARKBLUE;
31+
}
32+
33+
BeginDrawing();
34+
ClearBackground(RAYWHITE);
35+
36+
if (IsGamepadAvailable(0)) {
37+
DrawText(GetGamepadName(0), 10, 10, 20, GRAY);
38+
DrawText("Left stick to move, face buttons to change color", 10, 40, 20, GRAY);
39+
40+
DrawText(TextFormat("Left X: %.2f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X)), 10, 80, 20, BLACK);
41+
DrawText(TextFormat("Left Y: %.2f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y)), 10, 110, 20, BLACK);
42+
DrawText(TextFormat("Right X: %.2f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X)), 10, 140, 20, BLACK);
43+
DrawText(TextFormat("Right Y: %.2f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y)), 10, 170, 20, BLACK);
44+
DrawText(TextFormat("L Trigger: %.2f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER)), 10, 200, 20, BLACK);
45+
DrawText(TextFormat("R Trigger: %.2f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER)), 10, 230, 20, BLACK);
46+
47+
DrawCircleV(ballPosition, 50, ballColor);
48+
} else {
49+
DrawText("No gamepad detected", screenWidth/2 - 100, screenHeight/2, 20, GRAY);
50+
DrawText("Connect a controller and it will be detected automatically", screenWidth/2 - 220, screenHeight/2 + 30, 20, LIGHTGRAY);
51+
}
52+
53+
EndDrawing();
54+
}
55+
56+
int main(void) {
57+
ProgramInfo programInfo = {
58+
.windowWidth = screenWidth,
59+
.windowHeight = screenHeight,
60+
.windowTitle = "Gamepad Input Example",
61+
.setupFunction = setup,
62+
.renderFunction = render
63+
};
64+
65+
InitProgram(programInfo);
66+
67+
while (!WindowShouldClose()) {
68+
render();
69+
}
70+
71+
return 0;
72+
}

include/raygpu.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,47 @@ typedef enum {
960960
KEY_VOLUME_DOWN = 25 // Key: Android volume down button
961961
} KeyboardKey;
962962

963+
// Gamepad buttons (matching SDL3 gamepad button layout)
964+
typedef enum {
965+
GAMEPAD_BUTTON_UNKNOWN = -1,
966+
GAMEPAD_BUTTON_SOUTH = 0, // Xbox A, PlayStation Cross
967+
GAMEPAD_BUTTON_EAST = 1, // Xbox B, PlayStation Circle
968+
GAMEPAD_BUTTON_WEST = 2, // Xbox X, PlayStation Square
969+
GAMEPAD_BUTTON_NORTH = 3, // Xbox Y, PlayStation Triangle
970+
GAMEPAD_BUTTON_BACK = 4, // Xbox Back, PlayStation Select
971+
GAMEPAD_BUTTON_GUIDE = 5, // Xbox Guide, PlayStation PS
972+
GAMEPAD_BUTTON_START = 6, // Xbox Start, PlayStation Start
973+
GAMEPAD_BUTTON_LEFT_STICK = 7, // Left stick click
974+
GAMEPAD_BUTTON_RIGHT_STICK = 8, // Right stick click
975+
GAMEPAD_BUTTON_LEFT_SHOULDER = 9, // Left bumper/shoulder
976+
GAMEPAD_BUTTON_RIGHT_SHOULDER = 10, // Right bumper/shoulder
977+
GAMEPAD_BUTTON_DPAD_UP = 11,
978+
GAMEPAD_BUTTON_DPAD_DOWN = 12,
979+
GAMEPAD_BUTTON_DPAD_LEFT = 13,
980+
GAMEPAD_BUTTON_DPAD_RIGHT = 14,
981+
GAMEPAD_BUTTON_MISC1 = 15, // Xbox Series X share, PS5 microphone
982+
GAMEPAD_BUTTON_RIGHT_PADDLE1 = 16,
983+
GAMEPAD_BUTTON_LEFT_PADDLE1 = 17,
984+
GAMEPAD_BUTTON_RIGHT_PADDLE2 = 18,
985+
GAMEPAD_BUTTON_LEFT_PADDLE2 = 19,
986+
GAMEPAD_BUTTON_TOUCHPAD = 20, // PlayStation touchpad button
987+
GAMEPAD_BUTTON_MISC2 = 21,
988+
GAMEPAD_BUTTON_MISC3 = 22,
989+
GAMEPAD_BUTTON_MISC4 = 23,
990+
GAMEPAD_BUTTON_MISC5 = 24,
991+
GAMEPAD_BUTTON_MISC6 = 25,
992+
} GamepadButton;
993+
994+
// Gamepad axes (matching SDL3 gamepad axis layout)
995+
typedef enum {
996+
GAMEPAD_AXIS_INVALID = -1,
997+
GAMEPAD_AXIS_LEFT_X = 0, // Left stick X axis
998+
GAMEPAD_AXIS_LEFT_Y = 1, // Left stick Y axis
999+
GAMEPAD_AXIS_RIGHT_X = 2, // Right stick X axis
1000+
GAMEPAD_AXIS_RIGHT_Y = 3, // Right stick Y axis
1001+
GAMEPAD_AXIS_LEFT_TRIGGER = 4, // Left trigger
1002+
GAMEPAD_AXIS_RIGHT_TRIGGER = 5, // Right trigger
1003+
} GamepadAxis;
9631004

9641005
/**
9651006
* @brief Limit enum to describe WebGPU requested device limits
@@ -1254,6 +1295,7 @@ typedef struct GamepadState{
12541295
bool connected;
12551296
float axis[GAMEPAD_AXIS_MAX];
12561297
uint8_t buttons[GAMEPAD_BUTTON_MAX];
1298+
uint8_t buttonsPrevious[GAMEPAD_BUTTON_MAX];
12571299
} GamepadState;
12581300

12591301
typedef struct window_input_state{
@@ -1357,6 +1399,24 @@ RGAPI Vector2 GetMouseWheelMoveV(void); // Get mouse wheel movement for both X a
13571399
RGAPI bool IsMouseButtonPressed(int button);
13581400
RGAPI bool IsMouseButtonDown(int button);
13591401
RGAPI bool IsMouseButtonReleased(int button);
1402+
// Gamepad input functions (generic - dispatch to platform backend)
1403+
RGAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is connected
1404+
RGAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name
1405+
RGAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once
1406+
RGAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed
1407+
RGAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once
1408+
RGAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed
1409+
RGAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad
1410+
RGAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis
1411+
// Gamepad input functions (SDL3-specific)
1412+
RGAPI bool IsGamepadAvailable_SDL3(int gamepad);
1413+
RGAPI const char *GetGamepadName_SDL3(int gamepad);
1414+
RGAPI bool IsGamepadButtonPressed_SDL3(int gamepad, int button);
1415+
RGAPI bool IsGamepadButtonDown_SDL3(int gamepad, int button);
1416+
RGAPI bool IsGamepadButtonReleased_SDL3(int gamepad, int button);
1417+
RGAPI int GetGamepadButtonPressed_SDL3(void);
1418+
RGAPI int GetGamepadAxisCount_SDL3(int gamepad);
1419+
RGAPI float GetGamepadAxisMovement_SDL3(int gamepad, int axis);
13601420
RGAPI void ShowCursor(cwoid); // Shows cursor
13611421
RGAPI void HideCursor(cwoid); // Hides cursor
13621422
RGAPI bool IsCursorHidden(cwoid); // Check if cursor is not visible

src/platform/InitWindow_SDL3.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,4 +638,62 @@ void DisableCursor_SDL3(void* window){
638638
SDL_SetWindowRelativeMouseMode((SDL_Window*)window, true);
639639
}
640640

641+
// Gamepad input functions
642+
bool IsGamepadAvailable_SDL3(int gamepad) {
643+
if (gamepad < 0 || gamepad >= GAMEPAD_MAX) return false;
644+
if (!g_renderstate.mainWindow) return false;
645+
return g_renderstate.mainWindow->input_state.gamepads[gamepad].connected;
646+
}
647+
648+
const char *GetGamepadName_SDL3(int gamepad) {
649+
if (!IsGamepadAvailable_SDL3(gamepad)) return NULL;
650+
SDL_Gamepad *gp = (SDL_Gamepad *)g_renderstate.mainWindow->input_state.gamepads[gamepad].handle;
651+
return SDL_GetGamepadName(gp);
652+
}
653+
654+
bool IsGamepadButtonPressed_SDL3(int gamepad, int button) {
655+
if (!IsGamepadAvailable_SDL3(gamepad)) return false;
656+
if (button < 0 || button >= GAMEPAD_BUTTON_MAX) return false;
657+
GamepadState *state = &g_renderstate.mainWindow->input_state.gamepads[gamepad];
658+
return state->buttons[button] && !state->buttonsPrevious[button];
659+
}
660+
661+
bool IsGamepadButtonDown_SDL3(int gamepad, int button) {
662+
if (!IsGamepadAvailable_SDL3(gamepad)) return false;
663+
if (button < 0 || button >= GAMEPAD_BUTTON_MAX) return false;
664+
return g_renderstate.mainWindow->input_state.gamepads[gamepad].buttons[button];
665+
}
666+
667+
bool IsGamepadButtonReleased_SDL3(int gamepad, int button) {
668+
if (!IsGamepadAvailable_SDL3(gamepad)) return false;
669+
if (button < 0 || button >= GAMEPAD_BUTTON_MAX) return false;
670+
GamepadState *state = &g_renderstate.mainWindow->input_state.gamepads[gamepad];
671+
return !state->buttons[button] && state->buttonsPrevious[button];
672+
}
673+
674+
int GetGamepadButtonPressed_SDL3(void) {
675+
if (!g_renderstate.mainWindow) return GAMEPAD_BUTTON_UNKNOWN;
676+
for (int gp = 0; gp < GAMEPAD_MAX; gp++) {
677+
if (!g_renderstate.mainWindow->input_state.gamepads[gp].connected) continue;
678+
GamepadState *state = &g_renderstate.mainWindow->input_state.gamepads[gp];
679+
for (int btn = 0; btn < GAMEPAD_BUTTON_MAX; btn++) {
680+
if (state->buttons[btn] && !state->buttonsPrevious[btn]) {
681+
return btn;
682+
}
683+
}
684+
}
685+
return GAMEPAD_BUTTON_UNKNOWN;
686+
}
687+
688+
int GetGamepadAxisCount_SDL3(int gamepad) {
689+
if (!IsGamepadAvailable_SDL3(gamepad)) return 0;
690+
return GAMEPAD_AXIS_MAX;
691+
}
692+
693+
float GetGamepadAxisMovement_SDL3(int gamepad, int axis) {
694+
if (!IsGamepadAvailable_SDL3(gamepad)) return 0.0f;
695+
if (axis < 0 || axis >= GAMEPAD_AXIS_MAX) return 0.0f;
696+
return g_renderstate.mainWindow->input_state.gamepads[gamepad].axis[axis];
697+
}
698+
641699
// end file src/InitWindow_SDL3.c

src/raygpu.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,9 @@ RGAPI void EndDrawing(){
12121212
ipstate->scrollPreviousFrame = ipstate->scrollThisFrame;
12131213
ipstate->scrollThisFrame = CLITERAL(Vector2){0, 0};
12141214
memcpy(ipstate->mouseButtonDownPrevious, ipstate->mouseButtonDown, MOUSEBTN_MAX);
1215+
for (int gp = 0; gp < GAMEPAD_MAX; gp++) {
1216+
memcpy(ipstate->gamepads[gp].buttonsPrevious, ipstate->gamepads[gp].buttons, GAMEPAD_BUTTON_MAX);
1217+
}
12151218
for(size_t i = 0;i < g_renderstate.createdSubwindows.current_capacity;i++){
12161219
CreatedWindowMap_kv_pair* iter = g_renderstate.createdSubwindows.table + i;
12171220
if(iter->key != PHM_EMPTY_SLOT_KEY && iter->key != PHM_DELETED_SLOT_KEY){
@@ -1817,6 +1820,95 @@ bool IsMouseButtonDown(int button){
18171820
bool IsMouseButtonReleased(int button){
18181821
return !CreatedWindowMap_get(&g_renderstate.createdSubwindows, GetActiveWindowHandle())->input_state.mouseButtonDown[button] && CreatedWindowMap_get(&g_renderstate.createdSubwindows, GetActiveWindowHandle())->input_state.mouseButtonDownPrevious[button];
18191822
}
1823+
bool IsGamepadAvailable_SDL3(int gamepad);
1824+
const char *GetGamepadName_SDL3(int gamepad);
1825+
bool IsGamepadButtonPressed_SDL3(int gamepad, int button);
1826+
bool IsGamepadButtonDown_SDL3(int gamepad, int button);
1827+
bool IsGamepadButtonReleased_SDL3(int gamepad, int button);
1828+
int GetGamepadButtonPressed_SDL3(void);
1829+
int GetGamepadAxisCount_SDL3(int gamepad);
1830+
float GetGamepadAxisMovement_SDL3(int gamepad, int axis);
1831+
// Generic gamepad functions with backend dispatch
1832+
RGAPI bool IsGamepadAvailable(int gamepad) {
1833+
if (!g_renderstate.mainWindow) return false;
1834+
switch (g_renderstate.mainWindow->type) {
1835+
#if SUPPORT_SDL3 == 1
1836+
case windowType_sdl3: return IsGamepadAvailable_SDL3(gamepad);
1837+
#endif
1838+
default: return false;
1839+
}
1840+
}
1841+
1842+
RGAPI const char *GetGamepadName(int gamepad) {
1843+
if (!g_renderstate.mainWindow) return NULL;
1844+
switch (g_renderstate.mainWindow->type) {
1845+
#if SUPPORT_SDL3 == 1
1846+
case windowType_sdl3: return GetGamepadName_SDL3(gamepad);
1847+
#endif
1848+
default: return NULL;
1849+
}
1850+
}
1851+
1852+
RGAPI bool IsGamepadButtonPressed(int gamepad, int button) {
1853+
if (!g_renderstate.mainWindow) return false;
1854+
switch (g_renderstate.mainWindow->type) {
1855+
#if SUPPORT_SDL3 == 1
1856+
case windowType_sdl3: return IsGamepadButtonPressed_SDL3(gamepad, button);
1857+
#endif
1858+
default: return false;
1859+
}
1860+
}
1861+
1862+
RGAPI bool IsGamepadButtonDown(int gamepad, int button) {
1863+
if (!g_renderstate.mainWindow) return false;
1864+
switch (g_renderstate.mainWindow->type) {
1865+
#if SUPPORT_SDL3 == 1
1866+
case windowType_sdl3: return IsGamepadButtonDown_SDL3(gamepad, button);
1867+
#endif
1868+
default: return false;
1869+
}
1870+
}
1871+
1872+
RGAPI bool IsGamepadButtonReleased(int gamepad, int button) {
1873+
if (!g_renderstate.mainWindow) return false;
1874+
switch (g_renderstate.mainWindow->type) {
1875+
#if SUPPORT_SDL3 == 1
1876+
case windowType_sdl3: return IsGamepadButtonReleased_SDL3(gamepad, button);
1877+
#endif
1878+
default: return false;
1879+
}
1880+
}
1881+
1882+
RGAPI int GetGamepadButtonPressed(void) {
1883+
if (!g_renderstate.mainWindow) return GAMEPAD_BUTTON_UNKNOWN;
1884+
switch (g_renderstate.mainWindow->type) {
1885+
#if SUPPORT_SDL3 == 1
1886+
case windowType_sdl3: return GetGamepadButtonPressed_SDL3();
1887+
#endif
1888+
default: return GAMEPAD_BUTTON_UNKNOWN;
1889+
}
1890+
}
1891+
1892+
RGAPI int GetGamepadAxisCount(int gamepad) {
1893+
if (!g_renderstate.mainWindow) return 0;
1894+
switch (g_renderstate.mainWindow->type) {
1895+
#if SUPPORT_SDL3 == 1
1896+
case windowType_sdl3: return GetGamepadAxisCount_SDL3(gamepad);
1897+
#endif
1898+
default: return 0;
1899+
}
1900+
}
1901+
1902+
RGAPI float GetGamepadAxisMovement(int gamepad, int axis) {
1903+
if (!g_renderstate.mainWindow) return 0.0f;
1904+
switch (g_renderstate.mainWindow->type) {
1905+
#if SUPPORT_SDL3 == 1
1906+
case windowType_sdl3: return GetGamepadAxisMovement_SDL3(gamepad, axis);
1907+
#endif
1908+
default: return 0.0f;
1909+
}
1910+
}
1911+
18201912
bool IsCursorOnScreen(cwoid){
18211913
return CreatedWindowMap_get(&g_renderstate.createdSubwindows, GetActiveWindowHandle())->input_state.cursorInWindow;
18221914
}
@@ -2858,6 +2950,9 @@ RGAPI void EndWindowMode(){
28582950
ipstate->scrollPreviousFrame = ipstate->scrollThisFrame;
28592951
ipstate->scrollThisFrame = CLITERAL(Vector2){0, 0};
28602952
memcpy(ipstate->mouseButtonDownPrevious, ipstate->mouseButtonDown, MOUSEBTN_MAX);
2953+
for (int gp = 0; gp < GAMEPAD_MAX; gp++) {
2954+
memcpy(ipstate->gamepads[gp].buttonsPrevious, ipstate->gamepads[gp].buttons, GAMEPAD_BUTTON_MAX);
2955+
}
28612956
g_renderstate.activeSubWindow = NULL;
28622957
return;
28632958
}

0 commit comments

Comments
 (0)