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 | /* Game controller mapping generator */ |
14 | /* Gabriel Jacobo <gabomdq@gmail.com> */ |
15 | |
16 | #include <stdio.h> |
17 | #include <stdlib.h> |
18 | #include <string.h> |
19 | |
20 | #include "SDL.h" |
21 | |
22 | #ifndef SDL_JOYSTICK_DISABLED |
23 | |
24 | /* Define this for verbose output while mapping controllers */ |
25 | #define DEBUG_CONTROLLERMAP |
26 | |
27 | #define SCREEN_WIDTH 512 |
28 | #define SCREEN_HEIGHT 320 |
29 | |
30 | #define MARKER_BUTTON 1 |
31 | #define MARKER_AXIS 2 |
32 | |
33 | enum |
34 | { |
35 | SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE, |
36 | SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE, |
37 | SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE, |
38 | SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE, |
39 | SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE, |
40 | SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE, |
41 | SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE, |
42 | SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE, |
43 | SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT, |
44 | SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT, |
45 | SDL_CONTROLLER_BINDING_AXIS_MAX, |
46 | }; |
47 | |
48 | #define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_MAX) |
49 | |
50 | static struct |
51 | { |
52 | int x, y; |
53 | double angle; |
54 | int marker; |
55 | |
56 | } s_arrBindingDisplay[] = { |
57 | { 387, 167, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_A */ |
58 | { 431, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_B */ |
59 | { 342, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_X */ |
60 | { 389, 101, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_Y */ |
61 | { 174, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_BACK */ |
62 | { 232, 128, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_GUIDE */ |
63 | { 289, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_START */ |
64 | { 75, 154, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSTICK */ |
65 | { 305, 230, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */ |
66 | { 77, 40, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */ |
67 | { 396, 36, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */ |
68 | { 154, 188, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_UP */ |
69 | { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */ |
70 | { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */ |
71 | { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */ |
72 | { 232, 174, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_MISC1 */ |
73 | { 132, 135, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_PADDLE1 */ |
74 | { 330, 135, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_PADDLE2 */ |
75 | { 132, 175, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_PADDLE3 */ |
76 | { 330, 175, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_PADDLE4 */ |
77 | { 0, 0, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_TOUCHPAD */ |
78 | { 74, 153, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE */ |
79 | { 74, 153, 90.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE */ |
80 | { 74, 153, 0.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE */ |
81 | { 74, 153, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE */ |
82 | { 306, 231, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE */ |
83 | { 306, 231, 90.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE */ |
84 | { 306, 231, 0.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE */ |
85 | { 306, 231, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE */ |
86 | { 91, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT */ |
87 | { 375, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT */ |
88 | }; |
89 | SDL_COMPILE_TIME_ASSERT(s_arrBindingDisplay, SDL_arraysize(s_arrBindingDisplay) == BINDING_COUNT); |
90 | |
91 | static int s_arrBindingOrder[] = { |
92 | SDL_CONTROLLER_BUTTON_A, |
93 | SDL_CONTROLLER_BUTTON_B, |
94 | SDL_CONTROLLER_BUTTON_Y, |
95 | SDL_CONTROLLER_BUTTON_X, |
96 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE, |
97 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE, |
98 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE, |
99 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE, |
100 | SDL_CONTROLLER_BUTTON_LEFTSTICK, |
101 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE, |
102 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE, |
103 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE, |
104 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE, |
105 | SDL_CONTROLLER_BUTTON_RIGHTSTICK, |
106 | SDL_CONTROLLER_BUTTON_LEFTSHOULDER, |
107 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT, |
108 | SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, |
109 | SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT, |
110 | SDL_CONTROLLER_BUTTON_DPAD_UP, |
111 | SDL_CONTROLLER_BUTTON_DPAD_RIGHT, |
112 | SDL_CONTROLLER_BUTTON_DPAD_DOWN, |
113 | SDL_CONTROLLER_BUTTON_DPAD_LEFT, |
114 | SDL_CONTROLLER_BUTTON_BACK, |
115 | SDL_CONTROLLER_BUTTON_GUIDE, |
116 | SDL_CONTROLLER_BUTTON_START, |
117 | SDL_CONTROLLER_BUTTON_MISC1, |
118 | SDL_CONTROLLER_BUTTON_PADDLE1, |
119 | SDL_CONTROLLER_BUTTON_PADDLE2, |
120 | SDL_CONTROLLER_BUTTON_PADDLE3, |
121 | SDL_CONTROLLER_BUTTON_PADDLE4, |
122 | -1, |
123 | }; |
124 | SDL_COMPILE_TIME_ASSERT(s_arrBindingOrder, SDL_arraysize(s_arrBindingOrder) == BINDING_COUNT); |
125 | |
126 | typedef struct |
127 | { |
128 | SDL_GameControllerBindType bindType; |
129 | union |
130 | { |
131 | int button; |
132 | |
133 | struct { |
134 | int axis; |
135 | int axis_min; |
136 | int axis_max; |
137 | } axis; |
138 | |
139 | struct { |
140 | int hat; |
141 | int hat_mask; |
142 | } hat; |
143 | |
144 | } value; |
145 | |
146 | SDL_bool committed; |
147 | |
148 | } SDL_GameControllerExtendedBind; |
149 | |
150 | static SDL_GameControllerExtendedBind s_arrBindings[BINDING_COUNT]; |
151 | |
152 | typedef struct |
153 | { |
154 | SDL_bool m_bMoving; |
155 | int m_nLastValue; |
156 | int m_nStartingValue; |
157 | int m_nFarthestValue; |
158 | } AxisState; |
159 | |
160 | static int s_nNumAxes; |
161 | static AxisState *s_arrAxisState; |
162 | |
163 | static int s_iCurrentBinding; |
164 | static Uint32 s_unPendingAdvanceTime; |
165 | static SDL_bool s_bBindingComplete; |
166 | |
167 | static SDL_Window *window; |
168 | static SDL_bool done = SDL_FALSE; |
169 | |
170 | SDL_Texture * |
171 | LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent) |
172 | { |
173 | SDL_Surface *temp; |
174 | SDL_Texture *texture; |
175 | |
176 | /* Load the sprite image */ |
177 | temp = SDL_LoadBMP(file); |
178 | if (temp == NULL) { |
179 | SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s" , file, SDL_GetError()); |
180 | return NULL; |
181 | } |
182 | |
183 | /* Set transparent pixel as the pixel at (0,0) */ |
184 | if (transparent) { |
185 | if (temp->format->palette) { |
186 | SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels); |
187 | } |
188 | } |
189 | |
190 | /* Create textures from the image */ |
191 | texture = SDL_CreateTextureFromSurface(renderer, temp); |
192 | if (!texture) { |
193 | SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n" , SDL_GetError()); |
194 | SDL_FreeSurface(temp); |
195 | return NULL; |
196 | } |
197 | SDL_FreeSurface(temp); |
198 | |
199 | /* We're ready to roll. :) */ |
200 | return texture; |
201 | } |
202 | |
203 | static int |
204 | StandardizeAxisValue(int nValue) |
205 | { |
206 | if (nValue > SDL_JOYSTICK_AXIS_MAX/2) { |
207 | return SDL_JOYSTICK_AXIS_MAX; |
208 | } else if (nValue < SDL_JOYSTICK_AXIS_MIN/2) { |
209 | return SDL_JOYSTICK_AXIS_MIN; |
210 | } else { |
211 | return 0; |
212 | } |
213 | } |
214 | |
215 | static void |
216 | SetCurrentBinding(int iBinding) |
217 | { |
218 | int iIndex; |
219 | SDL_GameControllerExtendedBind *pBinding; |
220 | |
221 | if (iBinding < 0) { |
222 | return; |
223 | } |
224 | |
225 | if (iBinding == BINDING_COUNT) { |
226 | s_bBindingComplete = SDL_TRUE; |
227 | return; |
228 | } |
229 | |
230 | if (s_arrBindingOrder[iBinding] == -1) |
231 | { |
232 | SetCurrentBinding(iBinding + 1); |
233 | return; |
234 | } |
235 | |
236 | s_iCurrentBinding = iBinding; |
237 | |
238 | pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]]; |
239 | SDL_zerop(pBinding); |
240 | |
241 | for (iIndex = 0; iIndex < s_nNumAxes; ++iIndex) { |
242 | s_arrAxisState[iIndex].m_nFarthestValue = s_arrAxisState[iIndex].m_nStartingValue; |
243 | } |
244 | |
245 | s_unPendingAdvanceTime = 0; |
246 | } |
247 | |
248 | static SDL_bool |
249 | BBindingContainsBinding(const SDL_GameControllerExtendedBind *pBindingA, const SDL_GameControllerExtendedBind *pBindingB) |
250 | { |
251 | if (pBindingA->bindType != pBindingB->bindType) |
252 | { |
253 | return SDL_FALSE; |
254 | } |
255 | switch (pBindingA->bindType) |
256 | { |
257 | case SDL_CONTROLLER_BINDTYPE_AXIS: |
258 | if (pBindingA->value.axis.axis != pBindingB->value.axis.axis) { |
259 | return SDL_FALSE; |
260 | } |
261 | if (!pBindingA->committed) { |
262 | return SDL_FALSE; |
263 | } |
264 | { |
265 | int minA = SDL_min(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max); |
266 | int maxA = SDL_max(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max); |
267 | int minB = SDL_min(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max); |
268 | int maxB = SDL_max(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max); |
269 | return (minA <= minB && maxA >= maxB); |
270 | } |
271 | /* Not reached */ |
272 | default: |
273 | return SDL_memcmp(pBindingA, pBindingB, sizeof(*pBindingA)) == 0; |
274 | } |
275 | } |
276 | |
277 | static void |
278 | ConfigureBinding(const SDL_GameControllerExtendedBind *pBinding) |
279 | { |
280 | SDL_GameControllerExtendedBind *pCurrent; |
281 | int iIndex; |
282 | int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding]; |
283 | |
284 | /* Do we already have this binding? */ |
285 | for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) { |
286 | pCurrent = &s_arrBindings[iIndex]; |
287 | if (BBindingContainsBinding(pCurrent, pBinding)) { |
288 | if (iIndex == SDL_CONTROLLER_BUTTON_A && iCurrentElement != SDL_CONTROLLER_BUTTON_B) { |
289 | /* Skip to the next binding */ |
290 | SetCurrentBinding(s_iCurrentBinding + 1); |
291 | return; |
292 | } |
293 | |
294 | if (iIndex == SDL_CONTROLLER_BUTTON_B) { |
295 | /* Go back to the previous binding */ |
296 | SetCurrentBinding(s_iCurrentBinding - 1); |
297 | return; |
298 | } |
299 | |
300 | /* Already have this binding, ignore it */ |
301 | return; |
302 | } |
303 | } |
304 | |
305 | #ifdef DEBUG_CONTROLLERMAP |
306 | switch ( pBinding->bindType ) |
307 | { |
308 | case SDL_CONTROLLER_BINDTYPE_NONE: |
309 | break; |
310 | case SDL_CONTROLLER_BINDTYPE_BUTTON: |
311 | SDL_Log("Configuring button binding for button %d\n" , pBinding->value.button); |
312 | break; |
313 | case SDL_CONTROLLER_BINDTYPE_AXIS: |
314 | SDL_Log("Configuring axis binding for axis %d %d/%d committed = %s\n" , pBinding->value.axis.axis, pBinding->value.axis.axis_min, pBinding->value.axis.axis_max, pBinding->committed ? "true" : "false" ); |
315 | break; |
316 | case SDL_CONTROLLER_BINDTYPE_HAT: |
317 | SDL_Log("Configuring hat binding for hat %d %d\n" , pBinding->value.hat.hat, pBinding->value.hat.hat_mask); |
318 | break; |
319 | } |
320 | #endif /* DEBUG_CONTROLLERMAP */ |
321 | |
322 | /* Should the new binding override the existing one? */ |
323 | pCurrent = &s_arrBindings[iCurrentElement]; |
324 | if (pCurrent->bindType != SDL_CONTROLLER_BINDTYPE_NONE) { |
325 | SDL_bool bNativeDPad, bCurrentDPad; |
326 | SDL_bool bNativeAxis, bCurrentAxis; |
327 | |
328 | bNativeDPad = (iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_UP || |
329 | iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_DOWN || |
330 | iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_LEFT || |
331 | iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_RIGHT); |
332 | bCurrentDPad = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_HAT); |
333 | if (bNativeDPad && bCurrentDPad) { |
334 | /* We already have a binding of the type we want, ignore the new one */ |
335 | return; |
336 | } |
337 | |
338 | bNativeAxis = (iCurrentElement >= SDL_CONTROLLER_BUTTON_MAX); |
339 | bCurrentAxis = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_AXIS); |
340 | if (bNativeAxis == bCurrentAxis && |
341 | (pBinding->bindType != SDL_CONTROLLER_BINDTYPE_AXIS || |
342 | pBinding->value.axis.axis != pCurrent->value.axis.axis)) { |
343 | /* We already have a binding of the type we want, ignore the new one */ |
344 | return; |
345 | } |
346 | } |
347 | |
348 | *pCurrent = *pBinding; |
349 | |
350 | if (pBinding->committed) { |
351 | s_unPendingAdvanceTime = SDL_GetTicks(); |
352 | } else { |
353 | s_unPendingAdvanceTime = 0; |
354 | } |
355 | } |
356 | |
357 | static SDL_bool |
358 | BMergeAxisBindings(int iIndex) |
359 | { |
360 | SDL_GameControllerExtendedBind *pBindingA = &s_arrBindings[iIndex]; |
361 | SDL_GameControllerExtendedBind *pBindingB = &s_arrBindings[iIndex+1]; |
362 | if (pBindingA->bindType == SDL_CONTROLLER_BINDTYPE_AXIS && |
363 | pBindingB->bindType == SDL_CONTROLLER_BINDTYPE_AXIS && |
364 | pBindingA->value.axis.axis == pBindingB->value.axis.axis) { |
365 | if (pBindingA->value.axis.axis_min == pBindingB->value.axis.axis_min) { |
366 | pBindingA->value.axis.axis_min = pBindingA->value.axis.axis_max; |
367 | pBindingA->value.axis.axis_max = pBindingB->value.axis.axis_max; |
368 | pBindingB->bindType = SDL_CONTROLLER_BINDTYPE_NONE; |
369 | return SDL_TRUE; |
370 | } |
371 | } |
372 | return SDL_FALSE; |
373 | } |
374 | |
375 | static void |
376 | WatchJoystick(SDL_Joystick * joystick) |
377 | { |
378 | SDL_Renderer *screen = NULL; |
379 | SDL_Texture *background_front, *background_back, *button, *axis, *marker; |
380 | const char *name = NULL; |
381 | SDL_Event event; |
382 | SDL_Rect dst; |
383 | Uint8 alpha=200, alpha_step = -1; |
384 | Uint32 alpha_ticks = 0; |
385 | SDL_JoystickID nJoystickID; |
386 | |
387 | screen = SDL_CreateRenderer(window, -1, 0); |
388 | if (screen == NULL) { |
389 | SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n" , SDL_GetError()); |
390 | return; |
391 | } |
392 | |
393 | background_front = LoadTexture(screen, "controllermap.bmp" , SDL_FALSE); |
394 | background_back = LoadTexture(screen, "controllermap_back.bmp" , SDL_FALSE); |
395 | button = LoadTexture(screen, "button.bmp" , SDL_TRUE); |
396 | axis = LoadTexture(screen, "axis.bmp" , SDL_TRUE); |
397 | SDL_RaiseWindow(window); |
398 | |
399 | /* scale for platforms that don't give you the window size you asked for. */ |
400 | SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT); |
401 | |
402 | /* Print info about the joystick we are watching */ |
403 | name = SDL_JoystickName(joystick); |
404 | SDL_Log("Watching joystick %d: (%s)\n" , SDL_JoystickInstanceID(joystick), |
405 | name ? name : "Unknown Joystick" ); |
406 | SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n" , |
407 | SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick), |
408 | SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick)); |
409 | |
410 | SDL_Log("\n\n\ |
411 | ====================================================================================\n\ |
412 | Press the buttons on your controller when indicated\n\ |
413 | (Your controller may look different than the picture)\n\ |
414 | If you want to correct a mistake, press backspace or the back button on your device\n\ |
415 | To skip a button, press SPACE or click/touch the screen\n\ |
416 | To exit, press ESC\n\ |
417 | ====================================================================================\n" ); |
418 | |
419 | nJoystickID = SDL_JoystickInstanceID(joystick); |
420 | |
421 | s_nNumAxes = SDL_JoystickNumAxes(joystick); |
422 | s_arrAxisState = (AxisState *)SDL_calloc(s_nNumAxes, sizeof(*s_arrAxisState)); |
423 | |
424 | /* Skip any spurious events at start */ |
425 | while (SDL_PollEvent(&event) > 0) { |
426 | continue; |
427 | } |
428 | |
429 | /* Loop, getting joystick events! */ |
430 | while (!done && !s_bBindingComplete) { |
431 | int iElement = s_arrBindingOrder[s_iCurrentBinding]; |
432 | |
433 | switch (s_arrBindingDisplay[iElement].marker) { |
434 | case MARKER_AXIS: |
435 | marker = axis; |
436 | break; |
437 | case MARKER_BUTTON: |
438 | marker = button; |
439 | break; |
440 | default: |
441 | break; |
442 | } |
443 | |
444 | dst.x = s_arrBindingDisplay[iElement].x; |
445 | dst.y = s_arrBindingDisplay[iElement].y; |
446 | SDL_QueryTexture(marker, NULL, NULL, &dst.w, &dst.h); |
447 | |
448 | if (SDL_GetTicks() - alpha_ticks > 5) { |
449 | alpha_ticks = SDL_GetTicks(); |
450 | alpha += alpha_step; |
451 | if (alpha == 255) { |
452 | alpha_step = -1; |
453 | } |
454 | if (alpha < 128) { |
455 | alpha_step = 1; |
456 | } |
457 | } |
458 | |
459 | SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE); |
460 | SDL_RenderClear(screen); |
461 | if (s_arrBindingOrder[s_iCurrentBinding] >= SDL_CONTROLLER_BUTTON_PADDLE1 && |
462 | s_arrBindingOrder[s_iCurrentBinding] <= SDL_CONTROLLER_BUTTON_PADDLE4) { |
463 | SDL_RenderCopy(screen, background_back, NULL, NULL); |
464 | } else { |
465 | SDL_RenderCopy(screen, background_front, NULL, NULL); |
466 | } |
467 | SDL_SetTextureAlphaMod(marker, alpha); |
468 | SDL_SetTextureColorMod(marker, 10, 255, 21); |
469 | SDL_RenderCopyEx(screen, marker, NULL, &dst, s_arrBindingDisplay[iElement].angle, NULL, SDL_FLIP_NONE); |
470 | SDL_RenderPresent(screen); |
471 | |
472 | while (SDL_PollEvent(&event) > 0) { |
473 | switch (event.type) { |
474 | case SDL_JOYDEVICEREMOVED: |
475 | if (event.jaxis.which == nJoystickID) { |
476 | done = SDL_TRUE; |
477 | } |
478 | break; |
479 | case SDL_JOYAXISMOTION: |
480 | if (event.jaxis.which == nJoystickID) { |
481 | const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 controller needed 96 */ |
482 | AxisState *pAxisState = &s_arrAxisState[event.jaxis.axis]; |
483 | int nValue = event.jaxis.value; |
484 | int nCurrentDistance, nFarthestDistance; |
485 | if (!pAxisState->m_bMoving) { |
486 | Sint16 nInitialValue; |
487 | pAxisState->m_bMoving = SDL_JoystickGetAxisInitialState(joystick, event.jaxis.axis, &nInitialValue); |
488 | pAxisState->m_nLastValue = nValue; |
489 | pAxisState->m_nStartingValue = nInitialValue; |
490 | pAxisState->m_nFarthestValue = nInitialValue; |
491 | } else if (SDL_abs(nValue - pAxisState->m_nLastValue) <= MAX_ALLOWED_JITTER) { |
492 | break; |
493 | } else { |
494 | pAxisState->m_nLastValue = nValue; |
495 | } |
496 | nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue); |
497 | nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); |
498 | if (nCurrentDistance > nFarthestDistance) { |
499 | pAxisState->m_nFarthestValue = nValue; |
500 | nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); |
501 | } |
502 | |
503 | #ifdef DEBUG_CONTROLLERMAP |
504 | SDL_Log("AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d\n" , event.jaxis.axis, nValue, nCurrentDistance, nFarthestDistance); |
505 | #endif |
506 | if (nFarthestDistance >= 16000) { |
507 | /* If we've gone out far enough and started to come back, let's bind this axis */ |
508 | SDL_bool bCommitBinding = (nCurrentDistance <= 10000) ? SDL_TRUE : SDL_FALSE; |
509 | SDL_GameControllerExtendedBind binding; |
510 | SDL_zero(binding); |
511 | binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; |
512 | binding.value.axis.axis = event.jaxis.axis; |
513 | binding.value.axis.axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue); |
514 | binding.value.axis.axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue); |
515 | binding.committed = bCommitBinding; |
516 | ConfigureBinding(&binding); |
517 | } |
518 | } |
519 | break; |
520 | case SDL_JOYHATMOTION: |
521 | if (event.jhat.which == nJoystickID) { |
522 | if (event.jhat.value != SDL_HAT_CENTERED) { |
523 | SDL_GameControllerExtendedBind binding; |
524 | |
525 | #ifdef DEBUG_CONTROLLERMAP |
526 | SDL_Log("HAT %d %d\n" , event.jhat.hat, event.jhat.value); |
527 | #endif |
528 | SDL_zero(binding); |
529 | binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT; |
530 | binding.value.hat.hat = event.jhat.hat; |
531 | binding.value.hat.hat_mask = event.jhat.value; |
532 | binding.committed = SDL_TRUE; |
533 | ConfigureBinding(&binding); |
534 | } |
535 | } |
536 | break; |
537 | case SDL_JOYBALLMOTION: |
538 | break; |
539 | case SDL_JOYBUTTONDOWN: |
540 | if (event.jbutton.which == nJoystickID) { |
541 | SDL_GameControllerExtendedBind binding; |
542 | |
543 | #ifdef DEBUG_CONTROLLERMAP |
544 | SDL_Log("BUTTON %d\n" , event.jbutton.button); |
545 | #endif |
546 | SDL_zero(binding); |
547 | binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; |
548 | binding.value.button = event.jbutton.button; |
549 | binding.committed = SDL_TRUE; |
550 | ConfigureBinding(&binding); |
551 | } |
552 | break; |
553 | case SDL_FINGERDOWN: |
554 | case SDL_MOUSEBUTTONDOWN: |
555 | /* Skip this step */ |
556 | SetCurrentBinding(s_iCurrentBinding + 1); |
557 | break; |
558 | case SDL_KEYDOWN: |
559 | if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) { |
560 | SetCurrentBinding(s_iCurrentBinding - 1); |
561 | break; |
562 | } |
563 | if (event.key.keysym.sym == SDLK_SPACE) { |
564 | SetCurrentBinding(s_iCurrentBinding + 1); |
565 | break; |
566 | } |
567 | |
568 | if ((event.key.keysym.sym != SDLK_ESCAPE)) { |
569 | break; |
570 | } |
571 | /* Fall through to signal quit */ |
572 | case SDL_QUIT: |
573 | done = SDL_TRUE; |
574 | break; |
575 | default: |
576 | break; |
577 | } |
578 | } |
579 | |
580 | SDL_Delay(15); |
581 | |
582 | /* Wait 100 ms for joystick events to stop coming in, |
583 | in case a controller sends multiple events for a single control (e.g. axis and button for trigger) |
584 | */ |
585 | if (s_unPendingAdvanceTime && SDL_GetTicks() - s_unPendingAdvanceTime >= 100) { |
586 | SetCurrentBinding(s_iCurrentBinding + 1); |
587 | } |
588 | } |
589 | |
590 | if (s_bBindingComplete) { |
591 | char mapping[1024]; |
592 | char trimmed_name[128]; |
593 | char *spot; |
594 | int iIndex; |
595 | char pszElement[12]; |
596 | |
597 | SDL_strlcpy(trimmed_name, name, SDL_arraysize(trimmed_name)); |
598 | while (SDL_isspace(trimmed_name[0])) { |
599 | SDL_memmove(&trimmed_name[0], &trimmed_name[1], SDL_strlen(trimmed_name)); |
600 | } |
601 | while (trimmed_name[0] && SDL_isspace(trimmed_name[SDL_strlen(trimmed_name) - 1])) { |
602 | trimmed_name[SDL_strlen(trimmed_name) - 1] = '\0'; |
603 | } |
604 | while ((spot = SDL_strchr(trimmed_name, ',')) != NULL) { |
605 | SDL_memmove(spot, spot + 1, SDL_strlen(spot)); |
606 | } |
607 | |
608 | /* Initialize mapping with GUID and name */ |
609 | SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), mapping, SDL_arraysize(mapping)); |
610 | SDL_strlcat(mapping, "," , SDL_arraysize(mapping)); |
611 | SDL_strlcat(mapping, trimmed_name, SDL_arraysize(mapping)); |
612 | SDL_strlcat(mapping, "," , SDL_arraysize(mapping)); |
613 | SDL_strlcat(mapping, "platform:" , SDL_arraysize(mapping)); |
614 | SDL_strlcat(mapping, SDL_GetPlatform(), SDL_arraysize(mapping)); |
615 | SDL_strlcat(mapping, "," , SDL_arraysize(mapping)); |
616 | |
617 | for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) { |
618 | SDL_GameControllerExtendedBind *pBinding = &s_arrBindings[iIndex]; |
619 | if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) { |
620 | continue; |
621 | } |
622 | |
623 | if (iIndex < SDL_CONTROLLER_BUTTON_MAX) { |
624 | SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex; |
625 | SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping)); |
626 | } else { |
627 | const char *pszAxisName; |
628 | switch (iIndex - SDL_CONTROLLER_BUTTON_MAX) { |
629 | case SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE: |
630 | if (!BMergeAxisBindings(iIndex)) { |
631 | SDL_strlcat(mapping, "-" , SDL_arraysize(mapping)); |
632 | } |
633 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX); |
634 | break; |
635 | case SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE: |
636 | SDL_strlcat(mapping, "+" , SDL_arraysize(mapping)); |
637 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX); |
638 | break; |
639 | case SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE: |
640 | if (!BMergeAxisBindings(iIndex)) { |
641 | SDL_strlcat(mapping, "-" , SDL_arraysize(mapping)); |
642 | } |
643 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY); |
644 | break; |
645 | case SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE: |
646 | SDL_strlcat(mapping, "+" , SDL_arraysize(mapping)); |
647 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY); |
648 | break; |
649 | case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE: |
650 | if (!BMergeAxisBindings(iIndex)) { |
651 | SDL_strlcat(mapping, "-" , SDL_arraysize(mapping)); |
652 | } |
653 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX); |
654 | break; |
655 | case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE: |
656 | SDL_strlcat(mapping, "+" , SDL_arraysize(mapping)); |
657 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX); |
658 | break; |
659 | case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE: |
660 | if (!BMergeAxisBindings(iIndex)) { |
661 | SDL_strlcat(mapping, "-" , SDL_arraysize(mapping)); |
662 | } |
663 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY); |
664 | break; |
665 | case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE: |
666 | SDL_strlcat(mapping, "+" , SDL_arraysize(mapping)); |
667 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY); |
668 | break; |
669 | case SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT: |
670 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT); |
671 | break; |
672 | case SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT: |
673 | pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT); |
674 | break; |
675 | } |
676 | SDL_strlcat(mapping, pszAxisName, SDL_arraysize(mapping)); |
677 | } |
678 | SDL_strlcat(mapping, ":" , SDL_arraysize(mapping)); |
679 | |
680 | pszElement[0] = '\0'; |
681 | switch (pBinding->bindType) { |
682 | case SDL_CONTROLLER_BINDTYPE_BUTTON: |
683 | SDL_snprintf(pszElement, sizeof(pszElement), "b%d" , pBinding->value.button); |
684 | break; |
685 | case SDL_CONTROLLER_BINDTYPE_AXIS: |
686 | if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MIN) { |
687 | /* The negative half axis */ |
688 | SDL_snprintf(pszElement, sizeof(pszElement), "-a%d" , pBinding->value.axis.axis); |
689 | } else if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MAX) { |
690 | /* The positive half axis */ |
691 | SDL_snprintf(pszElement, sizeof(pszElement), "+a%d" , pBinding->value.axis.axis); |
692 | } else { |
693 | SDL_snprintf(pszElement, sizeof(pszElement), "a%d" , pBinding->value.axis.axis); |
694 | if (pBinding->value.axis.axis_min > pBinding->value.axis.axis_max) { |
695 | /* Invert the axis */ |
696 | SDL_strlcat(pszElement, "~" , SDL_arraysize(pszElement)); |
697 | } |
698 | } |
699 | break; |
700 | case SDL_CONTROLLER_BINDTYPE_HAT: |
701 | SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d" , pBinding->value.hat.hat, pBinding->value.hat.hat_mask); |
702 | break; |
703 | default: |
704 | SDL_assert(!"Unknown bind type" ); |
705 | break; |
706 | } |
707 | SDL_strlcat(mapping, pszElement, SDL_arraysize(mapping)); |
708 | SDL_strlcat(mapping, "," , SDL_arraysize(mapping)); |
709 | } |
710 | |
711 | SDL_Log("Mapping:\n\n%s\n\n" , mapping); |
712 | /* Print to stdout as well so the user can cat the output somewhere */ |
713 | printf("%s\n" , mapping); |
714 | } |
715 | |
716 | SDL_free(s_arrAxisState); |
717 | s_arrAxisState = NULL; |
718 | |
719 | SDL_DestroyRenderer(screen); |
720 | } |
721 | |
722 | int |
723 | main(int argc, char *argv[]) |
724 | { |
725 | const char *name; |
726 | int i; |
727 | SDL_Joystick *joystick; |
728 | |
729 | SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0" ); |
730 | |
731 | /* Enable standard application logging */ |
732 | SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); |
733 | |
734 | /* Initialize SDL (Note: video is required to start event loop) */ |
735 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { |
736 | SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n" , SDL_GetError()); |
737 | exit(1); |
738 | } |
739 | |
740 | /* Create a window to display joystick axis position */ |
741 | window = SDL_CreateWindow("Game Controller Map" , SDL_WINDOWPOS_CENTERED, |
742 | SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, |
743 | SCREEN_HEIGHT, 0); |
744 | if (window == NULL) { |
745 | SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n" , SDL_GetError()); |
746 | return 2; |
747 | } |
748 | |
749 | while (SDL_NumJoysticks() == 0) { |
750 | SDL_Event event; |
751 | |
752 | while (SDL_PollEvent(&event) > 0) { |
753 | switch (event.type) { |
754 | case SDL_KEYDOWN: |
755 | if ((event.key.keysym.sym != SDLK_ESCAPE)) { |
756 | break; |
757 | } |
758 | /* Fall through to signal quit */ |
759 | case SDL_QUIT: |
760 | done = SDL_TRUE; |
761 | break; |
762 | default: |
763 | break; |
764 | } |
765 | } |
766 | } |
767 | |
768 | /* Print information about the joysticks */ |
769 | SDL_Log("There are %d joysticks attached\n" , SDL_NumJoysticks()); |
770 | for (i = 0; i < SDL_NumJoysticks(); ++i) { |
771 | name = SDL_JoystickNameForIndex(i); |
772 | SDL_Log("Joystick %d: %s\n" , i, name ? name : "Unknown Joystick" ); |
773 | joystick = SDL_JoystickOpen(i); |
774 | if (joystick == NULL) { |
775 | SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n" , i, |
776 | SDL_GetError()); |
777 | } else { |
778 | char guid[64]; |
779 | SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), |
780 | guid, sizeof (guid)); |
781 | SDL_Log(" axes: %d\n" , SDL_JoystickNumAxes(joystick)); |
782 | SDL_Log(" balls: %d\n" , SDL_JoystickNumBalls(joystick)); |
783 | SDL_Log(" hats: %d\n" , SDL_JoystickNumHats(joystick)); |
784 | SDL_Log(" buttons: %d\n" , SDL_JoystickNumButtons(joystick)); |
785 | SDL_Log("instance id: %d\n" , SDL_JoystickInstanceID(joystick)); |
786 | SDL_Log(" guid: %s\n" , guid); |
787 | SDL_Log(" VID/PID: 0x%.4x/0x%.4x\n" , SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick)); |
788 | SDL_JoystickClose(joystick); |
789 | } |
790 | } |
791 | |
792 | joystick = SDL_JoystickOpen(0); |
793 | if (joystick == NULL) { |
794 | SDL_Log("Couldn't open joystick 0: %s\n" , SDL_GetError()); |
795 | } else { |
796 | WatchJoystick(joystick); |
797 | SDL_JoystickClose(joystick); |
798 | } |
799 | |
800 | SDL_DestroyWindow(window); |
801 | |
802 | SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); |
803 | |
804 | return 0; |
805 | } |
806 | |
807 | #else |
808 | |
809 | int |
810 | main(int argc, char *argv[]) |
811 | { |
812 | SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n" ); |
813 | return 1; |
814 | } |
815 | |
816 | #endif |
817 | |
818 | /* vi: set ts=4 sw=4 expandtab: */ |
819 | |