SDL: examples: Added input/05-gamepad-rumble

From dc8b1894919788650c4b01c4c21b3df37fff70b4 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 31 May 2026 08:32:17 -0700
Subject: [PATCH] examples: Added input/05-gamepad-rumble

---
 .../05-gamepad-rumble.vcxproj                 |  12 +++
 examples/CMakeLists.txt                       |   1 +
 examples/input/05-gamepad-rumble/README.txt   |   1 +
 .../input/05-gamepad-rumble/gamepad-rumble.c  |  83 ++++++++++++++++++
 .../input/05-gamepad-rumble/onmouseover.webp  | Bin 0 -> 1862 bytes
 .../input/05-gamepad-rumble/thumbnail.png     | Bin 0 -> 362 bytes
 6 files changed, 97 insertions(+)
 create mode 100644 VisualC/examples/input/05-gamepad-rumble/05-gamepad-rumble.vcxproj
 create mode 100644 examples/input/05-gamepad-rumble/README.txt
 create mode 100644 examples/input/05-gamepad-rumble/gamepad-rumble.c
 create mode 100644 examples/input/05-gamepad-rumble/onmouseover.webp
 create mode 100644 examples/input/05-gamepad-rumble/thumbnail.png

diff --git a/VisualC/examples/input/05-gamepad-rumble/05-gamepad-rumble.vcxproj b/VisualC/examples/input/05-gamepad-rumble/05-gamepad-rumble.vcxproj
new file mode 100644
index 0000000000000..ba30e01ef8300
--- /dev/null
+++ b/VisualC/examples/input/05-gamepad-rumble/05-gamepad-rumble.vcxproj
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{5F4CC3F4-265F-4295-98E3-19BD09108B39}</ProjectGuid>
+  </PropertyGroup>
+  <Import Project="$(SolutionDir)\examples\Examples.props" />
+  <ItemGroup>
+    <None Include="$(SolutionDir)\..\examples\input\05-gamepad-rumble\README.txt" />
+    <ClCompile Include="$(SolutionDir)\..\examples\input\05-gamepad-rumble\gamepad-rumble.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
\ No newline at end of file
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 752eb640e3de1..1e6b086d7e041 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -152,6 +152,7 @@ add_sdl_example_executable(input-joystick-polling SOURCES input/01-joystick-poll
 add_sdl_example_executable(input-joystick-events SOURCES input/02-joystick-events/joystick-events.c)
 add_sdl_example_executable(input-gamepad-polling SOURCES input/03-gamepad-polling/gamepad-polling.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/gamepad_front.png)
 add_sdl_example_executable(input-gamepad-events SOURCES input/04-gamepad-events/gamepad-events.c)
+add_sdl_example_executable(input-gamepad-rumble SOURCES input/05-gamepad-rumble/gamepad-rumble.c)
 add_sdl_example_executable(camera-read-and-draw SOURCES camera/01-read-and-draw/read-and-draw.c)
 add_sdl_example_executable(pen-drawing-lines SOURCES pen/01-drawing-lines/drawing-lines.c)
 add_sdl_example_executable(asyncio-load-bitmaps SOURCES asyncio/01-load-bitmaps/load-bitmaps.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.png ${CMAKE_CURRENT_SOURCE_DIR}/../test/gamepad_front.png ${CMAKE_CURRENT_SOURCE_DIR}/../test/speaker.png ${CMAKE_CURRENT_SOURCE_DIR}/../test/icon2x.png)
diff --git a/examples/input/05-gamepad-rumble/README.txt b/examples/input/05-gamepad-rumble/README.txt
new file mode 100644
index 0000000000000..b720fbfd6e849
--- /dev/null
+++ b/examples/input/05-gamepad-rumble/README.txt
@@ -0,0 +1 @@
+This example code rumbles gamepads when buttons are pressed.
diff --git a/examples/input/05-gamepad-rumble/gamepad-rumble.c b/examples/input/05-gamepad-rumble/gamepad-rumble.c
new file mode 100644
index 0000000000000..e49037a3d8dfd
--- /dev/null
+++ b/examples/input/05-gamepad-rumble/gamepad-rumble.c
@@ -0,0 +1,83 @@
+/*
+ * This example rumbles gamepads on button presses.
+ *
+ * This code is public domain. Feel free to use it for any purpose!
+ */
+
+#define SDL_MAIN_USE_CALLBACKS 1  /* use the callbacks instead of main() */
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_main.h>
+
+/* We will use this renderer to draw into this window every frame. */
+static SDL_Window *window = NULL;
+static SDL_Renderer *renderer = NULL;
+
+/* This function runs once at startup. */
+SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
+{
+    SDL_SetAppMetadata("Example Input Gamepad Rumble", "1.0", "com.example.input-gamepad-rumble");
+
+    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
+        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    if (!SDL_CreateWindowAndRenderer("examples/input/gamepad-rumble", 640, 480, SDL_WINDOW_RESIZABLE, &window, &renderer)) {
+        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    return SDL_APP_CONTINUE;  /* carry on with the program! */
+}
+
+/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
+SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
+{
+    if (event->type == SDL_EVENT_QUIT) {
+        return SDL_APP_SUCCESS;  /* end the program, reporting success to the OS. */
+    } else if (event->type == SDL_EVENT_GAMEPAD_ADDED) {
+        /* this event is sent for each hotplugged stick, but also each already-connected gamepad during SDL_Init(). */
+        SDL_OpenGamepad(event->gdevice.which);
+    } else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) {
+        SDL_Gamepad *gamepad = SDL_GetGamepadFromID(event->gdevice.which);
+        SDL_CloseGamepad(gamepad);
+    } else if (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
+        SDL_Gamepad *gamepad = SDL_GetGamepadFromID(event->gbutton.which);
+        switch (event->gbutton.button) {
+        case SDL_GAMEPAD_BUTTON_SOUTH:
+            SDL_RumbleGamepad(gamepad, 0xFFFF, 0x0000, 5000);
+            break;
+        case SDL_GAMEPAD_BUTTON_EAST:
+            SDL_RumbleGamepad(gamepad, 0x0000, 0xFFFF, 5000);
+            break;
+        default:
+            break;
+        }
+    } else if (event->type == SDL_EVENT_GAMEPAD_BUTTON_UP) {
+        SDL_Gamepad *gamepad = SDL_GetGamepadFromID(event->gbutton.which);
+        SDL_RumbleGamepad(gamepad, 0x0000, 0x0000, 0);
+    }
+
+    return SDL_APP_CONTINUE;  /* carry on with the program! */
+}
+
+/* This function runs once per frame, and is the heart of the program. */
+SDL_AppResult SDL_AppIterate(void *appstate)
+{
+    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+    SDL_RenderClear(renderer);
+
+    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
+    SDL_RenderDebugText(renderer, 8, 8, "Connect gamepad and press buttons to rumble");
+
+    SDL_RenderPresent(renderer);
+
+    return SDL_APP_CONTINUE;  /* carry on with the program! */
+}
+
+/* This function runs once at shutdown. */
+void SDL_AppQuit(void *appstate, SDL_AppResult result)
+{
+    SDL_Quit();
+    /* SDL will clean up the window/renderer for us. We let the gamepads leak. */
+}
diff --git a/examples/input/05-gamepad-rumble/onmouseover.webp b/examples/input/05-gamepad-rumble/onmouseover.webp
new file mode 100644
index 0000000000000000000000000000000000000000..10d0eeffa47df48398c2864f55fea07993865494
GIT binary patch
literal 1862
zcmeH@_dDAO0EfRpxKc$+FPsoXt%jN{+Jx9@<ZMBcS~Xf~*Oe!+TdP(PHCwaQT(tI8
z615Mt5|ki%XSAi%PL15%KX8A*J@@(PeV^z3>0@VMYKq_k0A~{v+6jFXrtvqUaX<-3
znZU&YA=V??oRn&+Wy{+NL6@PA{DY2Sh)ZA4^kd)zw6xEXPU6HLU**Uj1&)?(s{%}c
zgQ!^~3-7hX{Z^WW1WqdUs<yxfX5U68N;b1V2Q}M5Y$yv87dcWSy;lm*fusG;6^GI%
zR2Vi7Z^Y?3iDL1wf8D_!N|UAbP8yK7>qM$Al0!Ha=FJZkFc_=#tuI>W5)1&B^B=Y-
z!@RLgBYS}<xW=iMNSb|Sd|*-s#Vsw|7-dq<zrLmiA5*$nquuq(TN)g&y9Rh;qdHlc
zu90>bGPl`=xZNK-N5MG>x3ZI^sTJ9T_VIMt0$F9Y;5~sSRN=|JTpJxpTc*hpA+V0Z
zH#5yW$Sc|^+&pAILHVe5iQB{a>xC_vHhuno`7eK<wF#UU5{hEi_nThFgEupa1Q$nw
z@iMJ&lTYV^-#kBybv&iFFV{;g{jALQfKR59uX<yvxG;g(F@=+8+0MQab1q>yP&+7y
z0zr_Ce2KyA{_r@nTW@mhtt>3271@D|RneKlD_zIC1?a$u2&-0G%fxX_fD>gaou*&8
z%uf=-R4%w^yi&)dG8D+;s?G7@jFn(!zYrQn|CVSjR@a$)eytKT@G<VpY%!5}{ZiYm
z_yJ=TuV!8Db&*0EN*Y__r$tPrT1QJdBooC4r&rr%$GGnt;=)RfD2?28c`cUv#`)9!
z-jg|}dGhFtwT6tC(IXVoe&oRNw(fn*3@U|OO+c-~-nGtkn)Q-2j-K5p%$W;?Fgez)
zBzImHh-I)j3qJ(ajJe+QeezPpVx=tU0sN>^rPD_>F?vE5g$Nz~l$E?(rSO}h$BOrW
z{U6$h-hO&mD7A62dkgzO8iy<JL?JVsnYQ?@p8NV>#V1eQ6C59(o=!NBt8mQF$~Cm{
zMMR%*W&rJ>t6U*yD=AfPzVsZ+*Z-u&*6n=`2|BP%e}Y{kKLWG+3s{Gu;{|sicD6!I
zk>{9Flz^K_hkgM-V|a>A^z3So5b@PRa0AuTXyc^-uX0Act5KJ^nfsnw8dE9~<1p&s
z`_(IKS4XS3u(m_^^B8`m0rHg01ydR6A2k2y$~D&~4*K+CSSBbek)T8@Wh*`%1lM-w
z_Cl;eR#bXkWY48P=TlZ|k}ty+6sLXj<VQ6MeSu=Kwj{~p$FXsCY8+Zk{xch&+#<b#
zg+d-IC(agbTs)|t6#<XOsADoIF=^vDJ@Tjy*DPQ6cJMv<<#(h4XZS*oSYmW7-IG0D
za5p9VodVEFBaHJpi<QJ!*S{Ii<%P6FkR_@&PU-b!cE7*qR|GQ-EYmhfDIEi2r%gp*
zP92Hi;`KXO0^PQ$LiB)GxE$n^+;Hs7akJ>1ea45Z_G?;^AGVuBQl<E{d~rV|%`>uP
z<0G%s2E%9;`gI|xcfDSQnrVgf_>@wH>jp=ucLfd3XJ+%<e7*LVWk2|{g2uow^ad_R
zWv6N0RkOvPr7ol<ENXa__{=X(gcQTzl6qj|zjzGF=RGHrX4PslC>f=^>w?CHbs(0l
zK}|fX9EbMDqFMJu@O1^dM-Ys^rI?E767t|8bV^Y;yIJLhU%iC*DqCh7sy(arXldSO
zBLrELXa7W>fc`z!O$yL-R@tLWkuL9q4ZYf0K9N%X{+{l6Myop+7e|67o1K;aUgV@}
zVAZ&tJKvXYC*Pbtx}PFA=~gw16DGGmr1|taN|Yt}3A(X8AXFu@Yr20%T~J4sDb)^$
Wj2gWkzC+%bdo(j-S|tkn`~CvVUX8Z^

literal 0
HcmV?d00001

diff --git a/examples/input/05-gamepad-rumble/thumbnail.png b/examples/input/05-gamepad-rumble/thumbnail.png
new file mode 100644
index 0000000000000000000000000000000000000000..ffc13aa1e6b9624a8449344170ca32d3ac1522f8
GIT binary patch
literal 362
zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&H3>4v)_gfC6*aCb)T!Hle|NocXoPQT6
zE$`{#7*cWT?PbNjCk6tn7pxo{4zpErG`CgHP<ZS*v5{ZgWW|RC4VF%a8^W^ZhIu<1
z=Pa%D*HT|PX<w3hXG8tL_t&c`%lF?a{n2&$Y4x|S&r=d=*FL@L``+qI;HmF9dg{#A
zx4mEcH6m);THlM`G`Fq&Dp&mN-Z#0uN2gg%FEcXWS1qban_J~`bFtpebr&~JIb-T|
zY|?o<z2(gEuQ}eFcYkY{WRz9AMeM8SjlKi3&PU0qpD$opS1Ii{|FxjmtrwrIe3OfI
zr}`D1-uU}`)V0a^JA0RBT%R$m-%S7Xjk`jhYh`kBPJg{s)Sun``_sm)-!`95moKfq
ifB&y9Fcb)a|K0mT@{4_EO?tuyQtRpJ=d#Wzp$PzCs+lAJ

literal 0
HcmV?d00001