1 /*
2   Copyright (C) 1997-2021 Sam Lantinga <slouken@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 joystick routines */
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include "SDL.h"
20 
21 #ifdef __EMSCRIPTEN__
22 #include <emscripten/emscripten.h>
23 #endif
24 
25 #ifndef SDL_JOYSTICK_DISABLED
26 
27 #ifdef __IPHONEOS__
28 #define SCREEN_WIDTH    320
29 #define SCREEN_HEIGHT   480
30 #else
31 #define SCREEN_WIDTH    640
32 #define SCREEN_HEIGHT   480
33 #endif
34 
35 static SDL_Window *window = NULL;
36 static SDL_Renderer *screen = NULL;
37 static SDL_Joystick *joystick = NULL;
38 static SDL_bool done = SDL_FALSE;
39 
40 static void
PrintJoystick(SDL_Joystick * joy)41 PrintJoystick(SDL_Joystick *joy)
42 {
43     const char *type;
44     char guid[64];
45 
46     SDL_assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joy)) == joy);
47     SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, sizeof (guid));
48     switch (SDL_JoystickGetType(joy)) {
49     case SDL_JOYSTICK_TYPE_GAMECONTROLLER:
50         type = "Game Controller";
51         break;
52     case SDL_JOYSTICK_TYPE_WHEEL:
53         type = "Wheel";
54         break;
55     case SDL_JOYSTICK_TYPE_ARCADE_STICK:
56         type = "Arcade Stick";
57         break;
58     case SDL_JOYSTICK_TYPE_FLIGHT_STICK:
59         type = "Flight Stick";
60         break;
61     case SDL_JOYSTICK_TYPE_DANCE_PAD:
62         type = "Dance Pad";
63         break;
64     case SDL_JOYSTICK_TYPE_GUITAR:
65         type = "Guitar";
66         break;
67     case SDL_JOYSTICK_TYPE_DRUM_KIT:
68         type = "Drum Kit";
69         break;
70     case SDL_JOYSTICK_TYPE_ARCADE_PAD:
71         type = "Arcade Pad";
72         break;
73     case SDL_JOYSTICK_TYPE_THROTTLE:
74         type = "Throttle";
75         break;
76     default:
77         type = "Unknown";
78         break;
79     }
80     SDL_Log("Joystick\n");
81     SDL_Log("          name: %s\n", SDL_JoystickName(joy));
82     SDL_Log("          type: %s\n", type);
83     SDL_Log("           LED: %s\n", SDL_JoystickHasLED(joy) ? "yes" : "no");
84     SDL_Log("        rumble: %s\n", SDL_JoystickHasRumble(joy) ? "yes" : "no");
85     SDL_Log("trigger rumble: %s\n", SDL_JoystickHasRumbleTriggers(joy) ? "yes" : "no");
86     SDL_Log("          axes: %d\n", SDL_JoystickNumAxes(joy));
87     SDL_Log("         balls: %d\n", SDL_JoystickNumBalls(joy));
88     SDL_Log("          hats: %d\n", SDL_JoystickNumHats(joy));
89     SDL_Log("       buttons: %d\n", SDL_JoystickNumButtons(joy));
90     SDL_Log("   instance id: %d\n", SDL_JoystickInstanceID(joy));
91     SDL_Log("          guid: %s\n", guid);
92     SDL_Log("       VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joy), SDL_JoystickGetProduct(joy));
93 }
94 
95 static void
DrawRect(SDL_Renderer * r,const int x,const int y,const int w,const int h)96 DrawRect(SDL_Renderer *r, const int x, const int y, const int w, const int h)
97 {
98     SDL_Rect area;
99     area.x = x;
100     area.y = y;
101     area.w = w;
102     area.h = h;
103     SDL_RenderFillRect(r, &area);
104 }
105 
106 void
loop(void * arg)107 loop(void *arg)
108 {
109     SDL_Event event;
110     int i;
111 
112     /* blank screen, set up for drawing this frame. */
113     SDL_SetRenderDrawColor(screen, 0x0, 0x0, 0x0, SDL_ALPHA_OPAQUE);
114     SDL_RenderClear(screen);
115 
116     while (SDL_PollEvent(&event)) {
117         switch (event.type) {
118 
119         case SDL_JOYDEVICEADDED:
120             SDL_Log("Joystick device %d added.\n", (int) event.jdevice.which);
121             if (!joystick) {
122                 joystick = SDL_JoystickOpen(event.jdevice.which);
123                 if (joystick) {
124                     PrintJoystick(joystick);
125                 } else {
126                     SDL_Log("Couldn't open joystick: %s\n", SDL_GetError());
127                 }
128             }
129             break;
130 
131         case SDL_JOYDEVICEREMOVED:
132             SDL_Log("Joystick device %d removed.\n", (int) event.jdevice.which);
133             if (event.jdevice.which == SDL_JoystickInstanceID(joystick)) {
134                 SDL_JoystickClose(joystick);
135                 joystick = SDL_JoystickOpen(0);
136             }
137             break;
138 
139         case SDL_JOYAXISMOTION:
140             SDL_Log("Joystick %d axis %d value: %d\n",
141                    event.jaxis.which,
142                    event.jaxis.axis, event.jaxis.value);
143             break;
144         case SDL_JOYHATMOTION:
145             SDL_Log("Joystick %d hat %d value:",
146                    event.jhat.which, event.jhat.hat);
147             if (event.jhat.value == SDL_HAT_CENTERED)
148                 SDL_Log(" centered");
149             if (event.jhat.value & SDL_HAT_UP)
150                 SDL_Log(" up");
151             if (event.jhat.value & SDL_HAT_RIGHT)
152                 SDL_Log(" right");
153             if (event.jhat.value & SDL_HAT_DOWN)
154                 SDL_Log(" down");
155             if (event.jhat.value & SDL_HAT_LEFT)
156                 SDL_Log(" left");
157             SDL_Log("\n");
158             break;
159         case SDL_JOYBALLMOTION:
160             SDL_Log("Joystick %d ball %d delta: (%d,%d)\n",
161                    event.jball.which,
162                    event.jball.ball, event.jball.xrel, event.jball.yrel);
163             break;
164         case SDL_JOYBUTTONDOWN:
165             SDL_Log("Joystick %d button %d down\n",
166                    event.jbutton.which, event.jbutton.button);
167             /* First button triggers a 0.5 second full strength rumble */
168             if (event.jbutton.button == 0) {
169                 SDL_JoystickRumble(joystick, 0xFFFF, 0xFFFF, 500);
170             }
171             break;
172         case SDL_JOYBUTTONUP:
173             SDL_Log("Joystick %d button %d up\n",
174                    event.jbutton.which, event.jbutton.button);
175             break;
176         case SDL_KEYDOWN:
177             /* Press the L key to lag for 3 seconds, to see what happens
178                 when SDL doesn't service the event loop quickly. */
179             if (event.key.keysym.sym == SDLK_l) {
180                 SDL_Log("Lagging for 3 seconds...\n");
181                 SDL_Delay(3000);
182                 break;
183             }
184 
185             if ((event.key.keysym.sym != SDLK_ESCAPE) &&
186                 (event.key.keysym.sym != SDLK_AC_BACK)) {
187                 break;
188             }
189             SDL_FALLTHROUGH;
190         case SDL_FINGERDOWN:
191         case SDL_MOUSEBUTTONDOWN:
192         case SDL_QUIT:
193             done = SDL_TRUE;
194             break;
195         default:
196             break;
197         }
198     }
199 
200     if (joystick) {
201 
202         /* Update visual joystick state */
203         SDL_SetRenderDrawColor(screen, 0x00, 0xFF, 0x00, SDL_ALPHA_OPAQUE);
204         for (i = 0; i < SDL_JoystickNumButtons(joystick); ++i) {
205             if (SDL_JoystickGetButton(joystick, i) == SDL_PRESSED) {
206                 DrawRect(screen, (i%20) * 34, SCREEN_HEIGHT - 68 + (i/20) * 34, 32, 32);
207             }
208         }
209 
210         SDL_SetRenderDrawColor(screen, 0xFF, 0x00, 0x00, SDL_ALPHA_OPAQUE);
211         for (i = 0; i < SDL_JoystickNumAxes(joystick); ++i) {
212             /* Draw the X/Y axis */
213             int x, y;
214             x = (((int) SDL_JoystickGetAxis(joystick, i)) + 32768);
215             x *= SCREEN_WIDTH;
216             x /= 65535;
217             if (x < 0) {
218                 x = 0;
219             } else if (x > (SCREEN_WIDTH - 16)) {
220                 x = SCREEN_WIDTH - 16;
221             }
222             ++i;
223             if (i < SDL_JoystickNumAxes(joystick)) {
224                 y = (((int) SDL_JoystickGetAxis(joystick, i)) + 32768);
225             } else {
226                 y = 32768;
227             }
228             y *= SCREEN_HEIGHT;
229             y /= 65535;
230             if (y < 0) {
231                 y = 0;
232             } else if (y > (SCREEN_HEIGHT - 16)) {
233                 y = SCREEN_HEIGHT - 16;
234             }
235 
236             DrawRect(screen, x, y, 16, 16);
237         }
238 
239         SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0xFF, SDL_ALPHA_OPAQUE);
240         for (i = 0; i < SDL_JoystickNumHats(joystick); ++i) {
241             /* Derive the new position */
242             int x = SCREEN_WIDTH/2;
243             int y = SCREEN_HEIGHT/2;
244             const Uint8 hat_pos = SDL_JoystickGetHat(joystick, i);
245 
246             if (hat_pos & SDL_HAT_UP) {
247                 y = 0;
248             } else if (hat_pos & SDL_HAT_DOWN) {
249                 y = SCREEN_HEIGHT-8;
250             }
251 
252             if (hat_pos & SDL_HAT_LEFT) {
253                 x = 0;
254             } else if (hat_pos & SDL_HAT_RIGHT) {
255                 x = SCREEN_WIDTH-8;
256             }
257 
258             DrawRect(screen, x, y, 8, 8);
259         }
260     }
261 
262     SDL_RenderPresent(screen);
263 
264 #ifdef __EMSCRIPTEN__
265     if (done) {
266         emscripten_cancel_main_loop();
267     }
268 #endif
269 }
270 
271 int
main(int argc,char * argv[])272 main(int argc, char *argv[])
273 {
274     SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
275 
276     /* Enable standard application logging */
277     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
278 
279     /* Initialize SDL (Note: video is required to start event loop) */
280     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
281         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
282         exit(1);
283     }
284 
285     /* Create a window to display joystick axis position */
286     window = SDL_CreateWindow("Joystick Test", SDL_WINDOWPOS_CENTERED,
287                               SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
288                               SCREEN_HEIGHT, 0);
289     if (window == NULL) {
290         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
291         return SDL_FALSE;
292     }
293 
294     screen = SDL_CreateRenderer(window, -1, 0);
295     if (screen == NULL) {
296         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
297         SDL_DestroyWindow(window);
298         return SDL_FALSE;
299     }
300 
301     SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
302     SDL_RenderClear(screen);
303     SDL_RenderPresent(screen);
304 
305     /* Loop, getting joystick events! */
306 #ifdef __EMSCRIPTEN__
307     emscripten_set_main_loop_arg(loop, NULL, 0, 1);
308 #else
309     while (!done) {
310         loop(NULL);
311     }
312 #endif
313 
314     SDL_DestroyRenderer(screen);
315     SDL_DestroyWindow(window);
316 
317     SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
318 
319     return 0;
320 }
321 
322 #else
323 
324 int
main(int argc,char * argv[])325 main(int argc, char *argv[])
326 {
327     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n");
328     return 1;
329 }
330 
331 #endif
332 
333 /* vi: set ts=4 sw=4 expandtab: */
334