gosu 0.15.2 → 1.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (234) hide show
  1. checksums.yaml +4 -4
  2. data/dependencies/SDL/include/SDL.h +138 -0
  3. data/dependencies/SDL/include/SDL_assert.h +293 -0
  4. data/dependencies/SDL/include/SDL_atomic.h +295 -0
  5. data/dependencies/SDL/include/SDL_audio.h +859 -0
  6. data/dependencies/SDL/include/SDL_bits.h +121 -0
  7. data/dependencies/SDL/include/SDL_blendmode.h +123 -0
  8. data/dependencies/SDL/include/SDL_clipboard.h +71 -0
  9. data/dependencies/SDL/include/SDL_config.h +55 -0
  10. data/dependencies/SDL/include/SDL_config_android.h +182 -0
  11. data/dependencies/SDL/include/SDL_config_iphoneos.h +207 -0
  12. data/dependencies/SDL/include/SDL_config_macosx.h +266 -0
  13. data/dependencies/SDL/include/SDL_config_minimal.h +85 -0
  14. data/dependencies/SDL/include/SDL_config_os2.h +188 -0
  15. data/dependencies/SDL/include/SDL_config_pandora.h +135 -0
  16. data/dependencies/SDL/include/SDL_config_psp.h +165 -0
  17. data/dependencies/SDL/include/SDL_config_windows.h +288 -0
  18. data/dependencies/SDL/include/SDL_config_winrt.h +243 -0
  19. data/dependencies/SDL/include/SDL_config_wiz.h +149 -0
  20. data/dependencies/SDL/include/SDL_copying.h +20 -0
  21. data/dependencies/SDL/include/SDL_cpuinfo.h +299 -0
  22. data/dependencies/SDL/include/SDL_egl.h +1676 -0
  23. data/dependencies/SDL/include/SDL_endian.h +263 -0
  24. data/dependencies/SDL/include/SDL_error.h +112 -0
  25. data/dependencies/SDL/include/SDL_events.h +827 -0
  26. data/dependencies/SDL/include/SDL_filesystem.h +136 -0
  27. data/dependencies/SDL/include/SDL_gamecontroller.h +541 -0
  28. data/dependencies/SDL/include/SDL_gesture.h +87 -0
  29. data/dependencies/SDL/include/SDL_haptic.h +1247 -0
  30. data/dependencies/SDL/include/SDL_hints.h +1578 -0
  31. data/dependencies/SDL/include/SDL_joystick.h +499 -0
  32. data/dependencies/SDL/include/SDL_keyboard.h +217 -0
  33. data/dependencies/SDL/include/SDL_keycode.h +351 -0
  34. data/dependencies/SDL/include/SDL_loadso.h +81 -0
  35. data/dependencies/SDL/include/SDL_locale.h +101 -0
  36. data/dependencies/SDL/include/SDL_log.h +211 -0
  37. data/dependencies/SDL/include/SDL_main.h +180 -0
  38. data/dependencies/SDL/include/SDL_messagebox.h +146 -0
  39. data/dependencies/SDL/include/SDL_metal.h +117 -0
  40. data/dependencies/SDL/include/SDL_misc.h +75 -0
  41. data/dependencies/SDL/include/SDL_mouse.h +302 -0
  42. data/dependencies/SDL/include/SDL_mutex.h +251 -0
  43. data/dependencies/SDL/include/SDL_name.h +33 -0
  44. data/dependencies/SDL/include/SDL_opengl.h +2183 -0
  45. data/dependencies/SDL/include/SDL_opengl_glext.h +11180 -0
  46. data/dependencies/SDL/include/SDL_opengles.h +39 -0
  47. data/dependencies/SDL/include/SDL_opengles2.h +52 -0
  48. data/dependencies/SDL/include/SDL_opengles2_gl2.h +621 -0
  49. data/dependencies/SDL/include/SDL_opengles2_gl2ext.h +2050 -0
  50. data/dependencies/SDL/include/SDL_opengles2_gl2platform.h +30 -0
  51. data/dependencies/SDL/include/SDL_opengles2_khrplatform.h +282 -0
  52. data/dependencies/SDL/include/SDL_pixels.h +479 -0
  53. data/dependencies/SDL/include/SDL_platform.h +198 -0
  54. data/dependencies/SDL/include/SDL_power.h +75 -0
  55. data/dependencies/SDL/include/SDL_quit.h +58 -0
  56. data/dependencies/SDL/include/SDL_rect.h +174 -0
  57. data/dependencies/SDL/include/SDL_render.h +1158 -0
  58. data/dependencies/SDL/include/SDL_revision.h +2 -0
  59. data/dependencies/SDL/include/SDL_rwops.h +283 -0
  60. data/dependencies/SDL/include/SDL_scancode.h +413 -0
  61. data/dependencies/SDL/include/SDL_sensor.h +267 -0
  62. data/dependencies/SDL/include/SDL_shape.h +144 -0
  63. data/dependencies/SDL/include/SDL_stdinc.h +647 -0
  64. data/dependencies/SDL/include/SDL_surface.h +563 -0
  65. data/dependencies/SDL/include/SDL_system.h +325 -0
  66. data/dependencies/SDL/include/SDL_syswm.h +354 -0
  67. data/dependencies/SDL/include/SDL_test.h +69 -0
  68. data/dependencies/SDL/include/SDL_test_assert.h +105 -0
  69. data/dependencies/SDL/include/SDL_test_common.h +218 -0
  70. data/dependencies/SDL/include/SDL_test_compare.h +69 -0
  71. data/dependencies/SDL/include/SDL_test_crc32.h +124 -0
  72. data/dependencies/SDL/include/SDL_test_font.h +81 -0
  73. data/dependencies/SDL/include/SDL_test_fuzzer.h +384 -0
  74. data/dependencies/SDL/include/SDL_test_harness.h +134 -0
  75. data/dependencies/SDL/include/SDL_test_images.h +78 -0
  76. data/dependencies/SDL/include/SDL_test_log.h +67 -0
  77. data/dependencies/SDL/include/SDL_test_md5.h +129 -0
  78. data/dependencies/SDL/include/SDL_test_memory.h +63 -0
  79. data/dependencies/SDL/include/SDL_test_random.h +115 -0
  80. data/dependencies/SDL/include/SDL_thread.h +366 -0
  81. data/dependencies/SDL/include/SDL_timer.h +115 -0
  82. data/dependencies/SDL/include/SDL_touch.h +102 -0
  83. data/dependencies/SDL/include/SDL_types.h +29 -0
  84. data/dependencies/SDL/include/SDL_version.h +162 -0
  85. data/dependencies/SDL/include/SDL_video.h +1282 -0
  86. data/dependencies/SDL/include/SDL_vulkan.h +276 -0
  87. data/dependencies/SDL/include/begin_code.h +166 -0
  88. data/dependencies/SDL/include/close_code.h +40 -0
  89. data/dependencies/SDL/lib/x64/libSDL2.dll.a +0 -0
  90. data/dependencies/SDL/lib/x86/libSDL2.dll.a +0 -0
  91. data/dependencies/SDL_sound/SDL_sound.c +795 -0
  92. data/dependencies/SDL_sound/SDL_sound.h +725 -0
  93. data/dependencies/SDL_sound/SDL_sound_aiff.c +537 -0
  94. data/dependencies/SDL_sound/SDL_sound_au.c +352 -0
  95. data/dependencies/SDL_sound/SDL_sound_coreaudio.c +747 -0
  96. data/dependencies/SDL_sound/SDL_sound_flac.c +182 -0
  97. data/dependencies/SDL_sound/SDL_sound_internal.h +304 -0
  98. data/dependencies/SDL_sound/SDL_sound_modplug.c +228 -0
  99. data/dependencies/SDL_sound/SDL_sound_mp3.c +184 -0
  100. data/dependencies/SDL_sound/SDL_sound_raw.c +164 -0
  101. data/dependencies/SDL_sound/SDL_sound_shn.c +1309 -0
  102. data/dependencies/SDL_sound/SDL_sound_voc.c +550 -0
  103. data/dependencies/SDL_sound/SDL_sound_vorbis.c +223 -0
  104. data/dependencies/SDL_sound/SDL_sound_wav.c +783 -0
  105. data/dependencies/SDL_sound/dr_flac.h +5906 -0
  106. data/dependencies/SDL_sound/dr_mp3.h +2832 -0
  107. data/dependencies/SDL_sound/libmodplug/fastmix.c +1748 -0
  108. data/dependencies/SDL_sound/libmodplug/libmodplug.h +1001 -0
  109. data/dependencies/SDL_sound/libmodplug/load_669.c +188 -0
  110. data/dependencies/SDL_sound/libmodplug/load_abc.c +4725 -0
  111. data/dependencies/SDL_sound/libmodplug/load_amf.c +403 -0
  112. data/dependencies/SDL_sound/libmodplug/load_ams.c +587 -0
  113. data/dependencies/SDL_sound/libmodplug/load_dbm.c +357 -0
  114. data/dependencies/SDL_sound/libmodplug/load_dmf.c +531 -0
  115. data/dependencies/SDL_sound/libmodplug/load_dsm.c +232 -0
  116. data/dependencies/SDL_sound/libmodplug/load_far.c +253 -0
  117. data/dependencies/SDL_sound/libmodplug/load_it.c +796 -0
  118. data/dependencies/SDL_sound/libmodplug/load_mdl.c +488 -0
  119. data/dependencies/SDL_sound/libmodplug/load_med.c +757 -0
  120. data/dependencies/SDL_sound/libmodplug/load_mid.c +1405 -0
  121. data/dependencies/SDL_sound/libmodplug/load_mod.c +269 -0
  122. data/dependencies/SDL_sound/libmodplug/load_mt2.c +546 -0
  123. data/dependencies/SDL_sound/libmodplug/load_mtm.c +142 -0
  124. data/dependencies/SDL_sound/libmodplug/load_okt.c +192 -0
  125. data/dependencies/SDL_sound/libmodplug/load_pat.c +1143 -0
  126. data/dependencies/SDL_sound/libmodplug/load_pat.h +25 -0
  127. data/dependencies/SDL_sound/libmodplug/load_psm.c +350 -0
  128. data/dependencies/SDL_sound/libmodplug/load_ptm.c +204 -0
  129. data/dependencies/SDL_sound/libmodplug/load_s3m.c +325 -0
  130. data/dependencies/SDL_sound/libmodplug/load_stm.c +180 -0
  131. data/dependencies/SDL_sound/libmodplug/load_ult.c +206 -0
  132. data/dependencies/SDL_sound/libmodplug/load_umx.c +51 -0
  133. data/dependencies/SDL_sound/libmodplug/load_xm.c +554 -0
  134. data/dependencies/SDL_sound/libmodplug/mmcmp.c +382 -0
  135. data/dependencies/SDL_sound/libmodplug/modplug.c +170 -0
  136. data/dependencies/SDL_sound/libmodplug/modplug.h +90 -0
  137. data/dependencies/SDL_sound/libmodplug/snd_dsp.c +301 -0
  138. data/dependencies/SDL_sound/libmodplug/snd_flt.c +63 -0
  139. data/dependencies/SDL_sound/libmodplug/snd_fx.c +2350 -0
  140. data/dependencies/SDL_sound/libmodplug/sndfile.c +1169 -0
  141. data/dependencies/SDL_sound/libmodplug/sndmix.c +1034 -0
  142. data/dependencies/SDL_sound/libmodplug/tables.h +371 -0
  143. data/{src/stb_vorbis.c → dependencies/SDL_sound/stb_vorbis.h} +143 -78
  144. data/dependencies/al_soft/AL/al.h +655 -0
  145. data/dependencies/al_soft/AL/alc.h +270 -0
  146. data/dependencies/al_soft/AL/alext.h +585 -0
  147. data/dependencies/al_soft/AL/efx-creative.h +3 -0
  148. data/dependencies/al_soft/AL/efx-presets.h +402 -0
  149. data/dependencies/al_soft/AL/efx.h +762 -0
  150. data/dependencies/al_soft/x64/libOpenAL32.dll.a +0 -0
  151. data/dependencies/al_soft/x86/libOpenAL32.dll.a +0 -0
  152. data/{src → dependencies/stb}/stb_image.h +330 -127
  153. data/{src → dependencies/stb}/stb_image_write.h +156 -85
  154. data/{src → dependencies/stb}/stb_truetype.h +192 -69
  155. data/{src → dependencies/utf8proc}/utf8proc.c +0 -0
  156. data/{src → dependencies/utf8proc}/utf8proc.h +0 -0
  157. data/{src → dependencies/utf8proc}/utf8proc_data.h +0 -0
  158. data/ext/gosu/extconf.rb +56 -22
  159. data/{Gosu → include/Gosu}/Audio.hpp +6 -8
  160. data/{Gosu → include/Gosu}/AutoLink.hpp +0 -0
  161. data/include/Gosu/Bitmap.hpp +100 -0
  162. data/{Gosu → include/Gosu}/Buttons.hpp +94 -35
  163. data/{Gosu → include/Gosu}/Channel.h +0 -0
  164. data/{Gosu → include/Gosu}/Color.h +0 -0
  165. data/{Gosu → include/Gosu}/Color.hpp +0 -0
  166. data/{Gosu → include/Gosu}/Directories.hpp +0 -0
  167. data/{Gosu → include/Gosu}/Font.h +0 -0
  168. data/{Gosu → include/Gosu}/Font.hpp +0 -0
  169. data/{Gosu → include/Gosu}/Fwd.hpp +0 -0
  170. data/{Gosu → include/Gosu}/Gosu.h +3 -0
  171. data/{Gosu → include/Gosu}/Gosu.hpp +0 -0
  172. data/{Gosu → include/Gosu}/Graphics.hpp +0 -0
  173. data/{Gosu → include/Gosu}/GraphicsBase.hpp +0 -0
  174. data/{Gosu → include/Gosu}/IO.hpp +0 -0
  175. data/{Gosu → include/Gosu}/Image.h +0 -0
  176. data/{Gosu → include/Gosu}/Image.hpp +7 -6
  177. data/{Gosu → include/Gosu}/ImageData.hpp +0 -0
  178. data/{Gosu → include/Gosu}/Input.hpp +30 -15
  179. data/{Gosu → include/Gosu}/Inspection.hpp +0 -0
  180. data/{Gosu → include/Gosu}/Math.hpp +0 -0
  181. data/{Gosu → include/Gosu}/Platform.hpp +0 -0
  182. data/{Gosu → include/Gosu}/Sample.h +0 -0
  183. data/{Gosu → include/Gosu}/Song.h +0 -0
  184. data/{Gosu → include/Gosu}/Text.hpp +0 -0
  185. data/{Gosu → include/Gosu}/TextInput.h +0 -0
  186. data/{Gosu → include/Gosu}/TextInput.hpp +0 -0
  187. data/{Gosu → include/Gosu}/Timing.hpp +0 -0
  188. data/{Gosu → include/Gosu}/Utility.hpp +1 -1
  189. data/{Gosu → include/Gosu}/Version.hpp +0 -0
  190. data/{Gosu → include/Gosu}/Window.h +2 -0
  191. data/{Gosu → include/Gosu}/Window.hpp +21 -13
  192. data/lib/OpenAL32.dll +0 -0
  193. data/lib/SDL2.dll +0 -0
  194. data/lib/gosu.rb +0 -3
  195. data/lib/gosu/patches.rb +0 -9
  196. data/lib/gosu/swig_patches.rb +3 -2
  197. data/lib/libmpg123.dll +0 -0
  198. data/lib/libsndfile.dll +0 -0
  199. data/lib64/OpenAL32.dll +0 -0
  200. data/lib64/SDL2.dll +0 -0
  201. data/lib64/libmpg123.dll +0 -0
  202. data/lib64/libsndfile.dll +0 -0
  203. data/rdoc/gosu.rb +95 -20
  204. data/src/Audio.cpp +50 -224
  205. data/src/AudioFile.hpp +17 -37
  206. data/src/AudioFileAudioToolbox.cpp +237 -0
  207. data/src/AudioFileSDLSound.cpp +147 -0
  208. data/src/AudioImpl.cpp +3 -12
  209. data/src/AudioImpl.hpp +3 -1
  210. data/src/Bitmap.cpp +85 -83
  211. data/src/BitmapIO.cpp +52 -58
  212. data/src/Constants.cpp +80 -33
  213. data/src/Font.cpp +3 -1
  214. data/src/GosuWrapper.cpp +19 -0
  215. data/src/Graphics.cpp +7 -4
  216. data/src/Image.cpp +13 -16
  217. data/src/Input.cpp +408 -159
  218. data/src/LargeImageData.cpp +1 -1
  219. data/src/MarkupParser.cpp +2 -1
  220. data/src/RubyGosu.cxx +349 -83
  221. data/src/RubyGosu.h +4 -2
  222. data/src/TexChunk.cpp +1 -1
  223. data/src/TextBuilder.cpp +3 -1
  224. data/src/Texture.cpp +1 -1
  225. data/src/TrueTypeFont.cpp +1 -1
  226. data/src/Utility.cpp +11 -7
  227. data/src/Window.cpp +30 -39
  228. data/src/WindowWrapper.cpp +28 -0
  229. metadata +207 -52
  230. data/Gosu/Bitmap.hpp +0 -113
  231. data/src/AudioToolboxFile.hpp +0 -210
  232. data/src/OggFile.hpp +0 -92
  233. data/src/SndFile.hpp +0 -174
  234. data/src/WinMain.cpp +0 -64
@@ -47,7 +47,6 @@ unsigned Gosu_IF_TILEABLE_BOTTOM = Gosu::IF_TILEABLE_BOTTOM;
47
47
  unsigned Gosu_IF_TILEABLE = Gosu::IF_TILEABLE;
48
48
  unsigned Gosu_IF_RETRO = Gosu::IF_RETRO;
49
49
 
50
- unsigned Gosu_KB_RANGE_BEGIN = Gosu::KB_RANGE_BEGIN;
51
50
  unsigned Gosu_KB_ESCAPE = Gosu::KB_ESCAPE;
52
51
  unsigned Gosu_KB_F1 = Gosu::KB_F1;
53
52
  unsigned Gosu_KB_F2 = Gosu::KB_F2;
@@ -147,9 +146,7 @@ unsigned Gosu_KB_NUMPAD_PLUS = Gosu::KB_NUMPAD_PLUS;
147
146
  unsigned Gosu_KB_NUMPAD_MINUS = Gosu::KB_NUMPAD_MINUS;
148
147
  unsigned Gosu_KB_NUMPAD_MULTIPLY = Gosu::KB_NUMPAD_MULTIPLY;
149
148
  unsigned Gosu_KB_NUMPAD_DIVIDE = Gosu::KB_NUMPAD_DIVIDE;
150
- unsigned Gosu_KB_RANGE_END = Gosu::KB_RANGE_END;
151
149
 
152
- unsigned Gosu_MS_RANGE_BEGIN = Gosu::MS_RANGE_BEGIN;
153
150
  unsigned Gosu_MS_LEFT = Gosu::MS_LEFT;
154
151
  unsigned Gosu_MS_MIDDLE = Gosu::MS_MIDDLE;
155
152
  unsigned Gosu_MS_RIGHT = Gosu::MS_RIGHT;
@@ -163,13 +160,11 @@ unsigned Gosu_MS_OTHER_4 = Gosu::MS_OTHER_4;
163
160
  unsigned Gosu_MS_OTHER_5 = Gosu::MS_OTHER_5;
164
161
  unsigned Gosu_MS_OTHER_6 = Gosu::MS_OTHER_6;
165
162
  unsigned Gosu_MS_OTHER_7 = Gosu::MS_OTHER_7;
166
- unsigned Gosu_MS_RANGE_END = Gosu::MS_RANGE_END;
167
163
 
168
- unsigned Gosu_GP_RANGE_BEGIN = Gosu::GP_RANGE_BEGIN;
169
- unsigned Gosu_GP_LEFT = Gosu::GP_LEFT;
170
- unsigned Gosu_GP_RIGHT = Gosu::GP_RIGHT;
171
- unsigned Gosu_GP_UP = Gosu::GP_UP;
172
- unsigned Gosu_GP_DOWN = Gosu::GP_DOWN;
164
+ unsigned Gosu_GP_DPAD_LEFT = Gosu::GP_DPAD_LEFT;
165
+ unsigned Gosu_GP_DPAD_RIGHT = Gosu::GP_DPAD_RIGHT;
166
+ unsigned Gosu_GP_DPAD_UP = Gosu::GP_DPAD_UP;
167
+ unsigned Gosu_GP_DPAD_DOWN = Gosu::GP_DPAD_DOWN;
173
168
  unsigned Gosu_GP_BUTTON_0 = Gosu::GP_BUTTON_0;
174
169
  unsigned Gosu_GP_BUTTON_1 = Gosu::GP_BUTTON_1;
175
170
  unsigned Gosu_GP_BUTTON_2 = Gosu::GP_BUTTON_2;
@@ -187,10 +182,10 @@ unsigned Gosu_GP_BUTTON_13 = Gosu::GP_BUTTON_13;
187
182
  unsigned Gosu_GP_BUTTON_14 = Gosu::GP_BUTTON_14;
188
183
  unsigned Gosu_GP_BUTTON_15 = Gosu::GP_BUTTON_15;
189
184
 
190
- unsigned Gosu_GP_0_LEFT = Gosu::GP_0_LEFT;
191
- unsigned Gosu_GP_0_RIGHT = Gosu::GP_0_RIGHT;
192
- unsigned Gosu_GP_0_UP = Gosu::GP_0_UP;
193
- unsigned Gosu_GP_0_DOWN = Gosu::GP_0_DOWN;
185
+ unsigned Gosu_GP_0_DPAD_LEFT = Gosu::GP_0_DPAD_LEFT;
186
+ unsigned Gosu_GP_0_DPAD_RIGHT = Gosu::GP_0_DPAD_RIGHT;
187
+ unsigned Gosu_GP_0_DPAD_UP = Gosu::GP_0_DPAD_UP;
188
+ unsigned Gosu_GP_0_DPAD_DOWN = Gosu::GP_0_DPAD_DOWN;
194
189
  unsigned Gosu_GP_0_BUTTON_0 = Gosu::GP_0_BUTTON_0;
195
190
  unsigned Gosu_GP_0_BUTTON_1 = Gosu::GP_0_BUTTON_1;
196
191
  unsigned Gosu_GP_0_BUTTON_2 = Gosu::GP_0_BUTTON_2;
@@ -208,10 +203,10 @@ unsigned Gosu_GP_0_BUTTON_13 = Gosu::GP_0_BUTTON_13;
208
203
  unsigned Gosu_GP_0_BUTTON_14 = Gosu::GP_0_BUTTON_14;
209
204
  unsigned Gosu_GP_0_BUTTON_15 = Gosu::GP_0_BUTTON_15;
210
205
 
211
- unsigned Gosu_GP_1_LEFT = Gosu::GP_1_LEFT;
212
- unsigned Gosu_GP_1_RIGHT = Gosu::GP_1_RIGHT;
213
- unsigned Gosu_GP_1_UP = Gosu::GP_1_UP;
214
- unsigned Gosu_GP_1_DOWN = Gosu::GP_1_DOWN;
206
+ unsigned Gosu_GP_1_DPAD_LEFT = Gosu::GP_1_DPAD_LEFT;
207
+ unsigned Gosu_GP_1_DPAD_RIGHT = Gosu::GP_1_DPAD_RIGHT;
208
+ unsigned Gosu_GP_1_DPAD_UP = Gosu::GP_1_DPAD_UP;
209
+ unsigned Gosu_GP_1_DPAD_DOWN = Gosu::GP_1_DPAD_DOWN;
215
210
  unsigned Gosu_GP_1_BUTTON_0 = Gosu::GP_1_BUTTON_0;
216
211
  unsigned Gosu_GP_1_BUTTON_1 = Gosu::GP_1_BUTTON_1;
217
212
  unsigned Gosu_GP_1_BUTTON_2 = Gosu::GP_1_BUTTON_2;
@@ -229,10 +224,10 @@ unsigned Gosu_GP_1_BUTTON_13 = Gosu::GP_1_BUTTON_13;
229
224
  unsigned Gosu_GP_1_BUTTON_14 = Gosu::GP_1_BUTTON_14;
230
225
  unsigned Gosu_GP_1_BUTTON_15 = Gosu::GP_1_BUTTON_15;
231
226
 
232
- unsigned Gosu_GP_2_LEFT = Gosu::GP_2_LEFT;
233
- unsigned Gosu_GP_2_RIGHT = Gosu::GP_2_RIGHT;
234
- unsigned Gosu_GP_2_UP = Gosu::GP_2_UP;
235
- unsigned Gosu_GP_2_DOWN = Gosu::GP_2_DOWN;
227
+ unsigned Gosu_GP_2_DPAD_LEFT = Gosu::GP_2_DPAD_LEFT;
228
+ unsigned Gosu_GP_2_DPAD_RIGHT = Gosu::GP_2_DPAD_RIGHT;
229
+ unsigned Gosu_GP_2_DPAD_UP = Gosu::GP_2_DPAD_UP;
230
+ unsigned Gosu_GP_2_DPAD_DOWN = Gosu::GP_2_DPAD_DOWN;
236
231
  unsigned Gosu_GP_2_BUTTON_0 = Gosu::GP_2_BUTTON_0;
237
232
  unsigned Gosu_GP_2_BUTTON_1 = Gosu::GP_2_BUTTON_1;
238
233
  unsigned Gosu_GP_2_BUTTON_2 = Gosu::GP_2_BUTTON_2;
@@ -250,10 +245,10 @@ unsigned Gosu_GP_2_BUTTON_13 = Gosu::GP_2_BUTTON_13;
250
245
  unsigned Gosu_GP_2_BUTTON_14 = Gosu::GP_2_BUTTON_14;
251
246
  unsigned Gosu_GP_2_BUTTON_15 = Gosu::GP_2_BUTTON_15;
252
247
 
253
- unsigned Gosu_GP_3_LEFT = Gosu::GP_3_LEFT;
254
- unsigned Gosu_GP_3_RIGHT = Gosu::GP_3_RIGHT;
255
- unsigned Gosu_GP_3_UP = Gosu::GP_3_UP;
256
- unsigned Gosu_GP_3_DOWN = Gosu::GP_3_DOWN;
248
+ unsigned Gosu_GP_3_DPAD_LEFT = Gosu::GP_3_DPAD_LEFT;
249
+ unsigned Gosu_GP_3_DPAD_RIGHT = Gosu::GP_3_DPAD_RIGHT;
250
+ unsigned Gosu_GP_3_DPAD_UP = Gosu::GP_3_DPAD_UP;
251
+ unsigned Gosu_GP_3_DPAD_DOWN = Gosu::GP_3_DPAD_DOWN;
257
252
  unsigned Gosu_GP_3_BUTTON_0 = Gosu::GP_3_BUTTON_0;
258
253
  unsigned Gosu_GP_3_BUTTON_1 = Gosu::GP_3_BUTTON_1;
259
254
  unsigned Gosu_GP_3_BUTTON_2 = Gosu::GP_3_BUTTON_2;
@@ -271,17 +266,69 @@ unsigned Gosu_GP_3_BUTTON_13 = Gosu::GP_3_BUTTON_13;
271
266
  unsigned Gosu_GP_3_BUTTON_14 = Gosu::GP_3_BUTTON_14;
272
267
  unsigned Gosu_GP_3_BUTTON_15 = Gosu::GP_3_BUTTON_15;
273
268
 
274
- unsigned Gosu_GP_RANGE_END = Gosu::GP_RANGE_END;
269
+ unsigned Gosu_GP_LEFT = Gosu::GP_LEFT;
270
+ unsigned Gosu_GP_RIGHT = Gosu::GP_RIGHT;
271
+ unsigned Gosu_GP_UP = Gosu::GP_UP;
272
+ unsigned Gosu_GP_DOWN = Gosu::GP_DOWN;
273
+
274
+ unsigned Gosu_GP_0_LEFT = Gosu::GP_0_LEFT;
275
+ unsigned Gosu_GP_0_RIGHT = Gosu::GP_0_RIGHT;
276
+ unsigned Gosu_GP_0_UP = Gosu::GP_0_UP;
277
+ unsigned Gosu_GP_0_DOWN = Gosu::GP_0_DOWN;
278
+
279
+ unsigned Gosu_GP_1_LEFT = Gosu::GP_1_LEFT;
280
+ unsigned Gosu_GP_1_RIGHT = Gosu::GP_1_RIGHT;
281
+ unsigned Gosu_GP_1_UP = Gosu::GP_1_UP;
282
+ unsigned Gosu_GP_1_DOWN = Gosu::GP_1_DOWN;
283
+
284
+ unsigned Gosu_GP_2_LEFT = Gosu::GP_2_LEFT;
285
+ unsigned Gosu_GP_2_RIGHT = Gosu::GP_2_RIGHT;
286
+ unsigned Gosu_GP_2_UP = Gosu::GP_2_UP;
287
+ unsigned Gosu_GP_2_DOWN = Gosu::GP_2_DOWN;
288
+
289
+ unsigned Gosu_GP_3_LEFT = Gosu::GP_3_LEFT;
290
+ unsigned Gosu_GP_3_RIGHT = Gosu::GP_3_RIGHT;
291
+ unsigned Gosu_GP_3_UP = Gosu::GP_3_UP;
292
+ unsigned Gosu_GP_3_DOWN = Gosu::GP_3_DOWN;
293
+
294
+ unsigned Gosu_GP_LEFT_STICK_X_AXIS = Gosu::GP_LEFT_STICK_Y_AXIS;
295
+ unsigned Gosu_GP_LEFT_STICK_Y_AXIS = Gosu::GP_LEFT_STICK_Y_AXIS;
296
+ unsigned Gosu_GP_RIGHT_STICK_X_AXIS = Gosu::GP_RIGHT_STICK_X_AXIS;
297
+ unsigned Gosu_GP_RIGHT_STICK_Y_AXIS = Gosu::GP_RIGHT_STICK_Y_AXIS;
298
+ unsigned Gosu_GP_LEFT_TRIGGER_AXIS = Gosu::GP_LEFT_TRIGGER_AXIS;
299
+ unsigned Gosu_GP_RIGHT_TRIGGER_AXIS = Gosu::GP_RIGHT_TRIGGER_AXIS;
300
+
301
+ unsigned Gosu_GP_0_LEFT_STICK_X_AXIS = Gosu::GP_0_LEFT_STICK_Y_AXIS;
302
+ unsigned Gosu_GP_0_LEFT_STICK_Y_AXIS = Gosu::GP_0_LEFT_STICK_Y_AXIS;
303
+ unsigned Gosu_GP_0_RIGHT_STICK_X_AXIS = Gosu::GP_0_RIGHT_STICK_X_AXIS;
304
+ unsigned Gosu_GP_0_RIGHT_STICK_Y_AXIS = Gosu::GP_0_RIGHT_STICK_Y_AXIS;
305
+ unsigned Gosu_GP_0_LEFT_TRIGGER_AXIS = Gosu::GP_0_LEFT_TRIGGER_AXIS;
306
+ unsigned Gosu_GP_0_RIGHT_TRIGGER_AXIS = Gosu::GP_0_RIGHT_TRIGGER_AXIS;
307
+
308
+ unsigned Gosu_GP_1_LEFT_STICK_X_AXIS = Gosu::GP_1_LEFT_STICK_Y_AXIS;
309
+ unsigned Gosu_GP_1_LEFT_STICK_Y_AXIS = Gosu::GP_1_LEFT_STICK_Y_AXIS;
310
+ unsigned Gosu_GP_1_RIGHT_STICK_X_AXIS = Gosu::GP_1_RIGHT_STICK_X_AXIS;
311
+ unsigned Gosu_GP_1_RIGHT_STICK_Y_AXIS = Gosu::GP_1_RIGHT_STICK_Y_AXIS;
312
+ unsigned Gosu_GP_1_LEFT_TRIGGER_AXIS = Gosu::GP_1_LEFT_TRIGGER_AXIS;
313
+ unsigned Gosu_GP_1_RIGHT_TRIGGER_AXIS = Gosu::GP_1_RIGHT_TRIGGER_AXIS;
314
+
315
+ unsigned Gosu_GP_2_LEFT_STICK_X_AXIS = Gosu::GP_2_LEFT_STICK_Y_AXIS;
316
+ unsigned Gosu_GP_2_LEFT_STICK_Y_AXIS = Gosu::GP_2_LEFT_STICK_Y_AXIS;
317
+ unsigned Gosu_GP_2_RIGHT_STICK_X_AXIS = Gosu::GP_2_RIGHT_STICK_X_AXIS;
318
+ unsigned Gosu_GP_2_RIGHT_STICK_Y_AXIS = Gosu::GP_2_RIGHT_STICK_Y_AXIS;
319
+ unsigned Gosu_GP_2_LEFT_TRIGGER_AXIS = Gosu::GP_2_LEFT_TRIGGER_AXIS;
320
+ unsigned Gosu_GP_2_RIGHT_TRIGGER_AXIS = Gosu::GP_2_RIGHT_TRIGGER_AXIS;
321
+
322
+ unsigned Gosu_GP_3_LEFT_STICK_X_AXIS = Gosu::GP_3_LEFT_STICK_Y_AXIS;
323
+ unsigned Gosu_GP_3_LEFT_STICK_Y_AXIS = Gosu::GP_3_LEFT_STICK_Y_AXIS;
324
+ unsigned Gosu_GP_3_RIGHT_STICK_X_AXIS = Gosu::GP_3_RIGHT_STICK_X_AXIS;
325
+ unsigned Gosu_GP_3_RIGHT_STICK_Y_AXIS = Gosu::GP_3_RIGHT_STICK_Y_AXIS;
326
+ unsigned Gosu_GP_3_LEFT_TRIGGER_AXIS = Gosu::GP_3_LEFT_TRIGGER_AXIS;
327
+ unsigned Gosu_GP_3_RIGHT_TRIGGER_AXIS = Gosu::GP_3_RIGHT_TRIGGER_AXIS;
275
328
 
276
- unsigned Gosu_NUM_BUTTONS = Gosu::NUM_BUTTONS;
277
329
  unsigned Gosu_NUM_GAMEPADS = Gosu::NUM_GAMEPADS;
278
330
  unsigned Gosu_NO_BUTTON = Gosu::NO_BUTTON;
279
331
 
280
- unsigned Gosu_KB_NUM = Gosu::KB_NUM;
281
- unsigned Gosu_MS_NUM = Gosu::MS_NUM;
282
- unsigned Gosu_GP_NUM = Gosu::GP_NUM;
283
- unsigned Gosu_GP_NUM_PER_GAMEPAD = Gosu::GP_NUM_PER_GAMEPAD;
284
-
285
332
  #ifdef __cplusplus
286
333
  }
287
- #endif
334
+ #endif
@@ -6,7 +6,9 @@
6
6
  #include <Gosu/Math.hpp>
7
7
  #include <Gosu/Text.hpp>
8
8
  #include <Gosu/Utility.hpp>
9
- #include "utf8proc.h"
9
+
10
+ #include <utf8proc.h>
11
+
10
12
  #include <array>
11
13
  #include <cassert>
12
14
  #include <map>
@@ -203,6 +203,25 @@ unsigned Gosu_button_char_to_id(const char *btn)
203
203
  return Gosu::Input::char_to_id(btn).id();
204
204
  }
205
205
 
206
+ const char *Gosu_button_name(int btn)
207
+ {
208
+ static thread_local std::string name;
209
+ name = Gosu::Input::button_name((Gosu::ButtonName)btn);
210
+ return name.empty() ? nullptr : name.c_str();
211
+ }
212
+
213
+ const char *Gosu_gamepad_name(int id)
214
+ {
215
+ static thread_local std::string name;
216
+ name = Gosu::Input::gamepad_name(id);
217
+ return name.empty() ? nullptr : name.c_str();
218
+ }
219
+
220
+ double Gosu_axis(int btn)
221
+ {
222
+ return Gosu::Input::axis((Gosu::ButtonName)btn);
223
+ }
224
+
206
225
  int Gosu_fps()
207
226
  {
208
227
  return Gosu::fps();
@@ -287,7 +287,9 @@ Gosu::Image Gosu::Graphics::render(int width, int height, const function<void ()
287
287
 
288
288
  // This is the actual render-to-texture step.
289
289
  Image result = OffScreenTarget(width, height, image_flags).render([&] {
290
+ #ifndef GOSU_IS_OPENGLES
290
291
  glPushAttrib(GL_ALL_ATTRIB_BITS);
292
+ #endif
291
293
  glClearColor(0, 0, 0, 0);
292
294
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
293
295
  glEnable(GL_BLEND);
@@ -296,7 +298,9 @@ Gosu::Image Gosu::Graphics::render(int width, int height, const function<void ()
296
298
  queues.back().perform_draw_ops_and_code();
297
299
  queues.pop_back();
298
300
  glFlush();
301
+ #ifndef GOSU_IS_OPENGLES
299
302
  glPopAttrib();
303
+ #endif
300
304
  });
301
305
 
302
306
  // Restore previous matrix and glViewport.
@@ -434,7 +438,7 @@ unique_ptr<Gosu::ImageData> Gosu::Graphics::create_image(const Bitmap& src,
434
438
  }
435
439
  else {
436
440
  Bitmap bmp(src_width, src_height);
437
- bmp.insert(src, 0, 0, src_x, src_y, src_width, src_height);
441
+ bmp.insert(0, 0, src, src_x, src_y, src_width, src_height);
438
442
  data = texture->try_alloc(bmp, 0);
439
443
  }
440
444
 
@@ -445,14 +449,13 @@ unique_ptr<Gosu::ImageData> Gosu::Graphics::create_image(const Bitmap& src,
445
449
  // Too large to fit on a single texture.
446
450
  if (src_width > max_size - 2 || src_height > max_size - 2) {
447
451
  Bitmap bmp(src_width, src_height);
448
- bmp.insert(src, 0, 0, src_x, src_y, src_width, src_height);
452
+ bmp.insert(0, 0, src, src_x, src_y, src_width, src_height);
449
453
  unique_ptr<ImageData> lidi;
450
454
  lidi.reset(new LargeImageData(bmp, max_size - 2, max_size - 2, flags));
451
455
  return lidi;
452
456
  }
453
457
 
454
- Bitmap bmp;
455
- apply_border_flags(bmp, src, src_x, src_y, src_width, src_height, flags);
458
+ Bitmap bmp = apply_border_flags(flags, src, src_x, src_y, src_width, src_height);
456
459
 
457
460
  // Try to put the bitmap into one of the already allocated textures.
458
461
  for (const auto& texture : textures) {
@@ -13,32 +13,30 @@ Gosu::Image::Image()
13
13
  {
14
14
  }
15
15
 
16
- Gosu::Image::Image(const string& filename, unsigned flags)
16
+ Gosu::Image::Image(const string& filename, unsigned image_flags)
17
17
  {
18
18
  // Forward.
19
- Bitmap bmp;
20
- load_image_file(bmp, filename);
21
- Image(bmp, flags).data_.swap(data_);
19
+ Bitmap bitmap = load_image_file(filename);
20
+ Image{bitmap, image_flags}.data_.swap(data_);
22
21
  }
23
22
 
24
- Gosu::Image::Image(const string& filename, unsigned src_x, unsigned src_y,
25
- unsigned src_width, unsigned src_height, unsigned flags)
23
+ Gosu::Image::Image(const string& filename, int src_x, int src_y, int src_width, int src_height,
24
+ unsigned image_flags)
26
25
  {
27
26
  // Forward.
28
- Bitmap bmp;
29
- load_image_file(bmp, filename);
30
- Image(bmp, src_x, src_y, src_width, src_height, flags).data_.swap(data_);
27
+ Bitmap bitmap = load_image_file(filename);
28
+ Image{bitmap, src_x, src_y, src_width, src_height, image_flags}.data_.swap(data_);
31
29
  }
32
30
 
33
- Gosu::Image::Image(const Bitmap& source, unsigned flags)
31
+ Gosu::Image::Image(const Bitmap& source, unsigned image_flags)
34
32
  {
35
33
  // Forward.
36
- Image(source, 0, 0, source.width(), source.height(), flags).data_.swap(data_);
34
+ Image{source, 0, 0, source.width(), source.height(), image_flags}.data_.swap(data_);
37
35
  }
38
36
 
39
- Gosu::Image::Image(const Bitmap& source, unsigned src_x, unsigned src_y,
40
- unsigned src_width, unsigned src_height, unsigned flags)
41
- : data_(Graphics::create_image(source, src_x, src_y, src_width, src_height, flags))
37
+ Gosu::Image::Image(const Bitmap& source, int src_x, int src_y, int src_width, int src_height,
38
+ unsigned image_flags)
39
+ : data_(Graphics::create_image(source, src_x, src_y, src_width, src_height, image_flags))
42
40
  {
43
41
  }
44
42
 
@@ -141,7 +139,6 @@ vector<Gosu::Image> Gosu::load_tiles(const Bitmap& bmp, int tile_width, int tile
141
139
  vector<Gosu::Image> Gosu::load_tiles(const string& filename, int tile_width, int tile_height,
142
140
  unsigned flags)
143
141
  {
144
- Bitmap bmp;
145
- load_image_file(bmp, filename);
142
+ Bitmap bmp = load_image_file(filename);
146
143
  return load_tiles(bmp, tile_width, tile_height, flags);
147
144
  }
@@ -6,89 +6,80 @@
6
6
  #include <Gosu/Utility.hpp>
7
7
 
8
8
  #include <SDL.h>
9
- #include "utf8proc.h"
9
+ #include <utf8proc.h>
10
10
 
11
11
  #include <cwctype>
12
12
  #include <cstdlib>
13
13
  #include <algorithm>
14
14
  #include <array>
15
+ #include <mutex>
15
16
  using namespace std;
16
17
 
17
- // Workaround for broken SDL_GetGlobalMouseState, see below.
18
- #ifdef GOSU_IS_MAC
19
- #import <CoreGraphics/CoreGraphics.h>
20
- #import <AppKit/AppKit.h>
21
- #endif
22
-
23
18
  static void require_sdl_video()
24
19
  {
25
- static bool initialized = false;
26
- if (!initialized) {
20
+ static std::once_flag initialized;
21
+
22
+ std::call_once(initialized, [] {
27
23
  SDL_InitSubSystem(SDL_INIT_VIDEO);
28
- initialized = true;
29
- atexit([] { SDL_QuitSubSystem(SDL_INIT_VIDEO); });
30
- }
24
+ });
31
25
  }
32
26
 
33
- static array<bool, Gosu::NUM_BUTTONS> button_states = { { false } };
27
+ static const unsigned NUM_BUTTONS_PER_GAMEPAD =
28
+ (Gosu::GP_RANGE_END - Gosu::GP_RANGE_BEGIN + 1 - 4) / (Gosu::NUM_GAMEPADS + 1) - 3;
29
+ static const unsigned NUM_AXES_PER_GAMEPAD =
30
+ (Gosu::GP_AXES_RANGE_END - Gosu::GP_AXES_RANGE_BEGIN + 1) / (Gosu::NUM_GAMEPADS + 1);
31
+ static const unsigned NUM_BUTTONS = Gosu::GP_RANGE_END + 1;
32
+
33
+ static array<bool, NUM_BUTTONS> button_states = {false};
34
+ static array<double, NUM_AXES_PER_GAMEPAD * (Gosu::NUM_GAMEPADS + 1)> axis_states = {0.0};
35
+ static vector<shared_ptr<SDL_Joystick>> open_joysticks;
36
+ static vector<shared_ptr<SDL_GameController>> open_game_controllers;
37
+ // Stores joystick instance id or -1 if empty
38
+ static array<int, Gosu::NUM_GAMEPADS> gamepad_slots = {-1, -1, -1, -1};
34
39
 
35
40
  struct Gosu::Input::Impl
36
41
  {
42
+ struct InputEvent
43
+ {
44
+ enum {
45
+ ButtonUp,
46
+ ButtonDown,
47
+ GamepadConnected,
48
+ GamepadDisconnected
49
+ } type;
50
+ int id = -1;
51
+ int gamepad_instance_id = -1;
52
+ };
53
+
37
54
  Input& input;
38
55
  SDL_Window* window;
39
-
56
+
40
57
  TextInput* text_input = nullptr;
41
58
  double mouse_x, mouse_y;
42
59
  double mouse_scale_x = 1;
43
60
  double mouse_scale_y = 1;
44
61
  double mouse_offset_x = 0;
45
62
  double mouse_offset_y = 0;
46
-
63
+
47
64
  Impl(Input& input, SDL_Window* window)
48
65
  : input(input), window(window)
49
66
  {
50
67
  require_sdl_video();
51
-
68
+
52
69
  SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
53
-
54
- int num_gamepads = min<int>(Gosu::NUM_GAMEPADS, SDL_NumJoysticks());
55
-
56
- for (int i = 0; i < num_gamepads; ++i) {
57
- // Prefer the SDL_GameController API...
58
- if (SDL_IsGameController(i)) {
59
- if (SDL_GameController* game_controller = SDL_GameControllerOpen(i)) {
60
- game_controllers.push_back(game_controller);
61
- continue;
62
- }
63
- }
64
- // ...but fall back on the good, old SDL_Joystick API.
65
- if (SDL_Joystick* joystick = SDL_JoystickOpen(i)) {
66
- joysticks.push_back(joystick);
67
- }
68
- }
69
70
  }
70
-
71
+
71
72
  ~Impl()
72
73
  {
73
- for_each(joysticks.begin(), joysticks.end(), &SDL_JoystickClose);
74
- joysticks.clear();
75
- for_each(game_controllers.begin(), game_controllers.end(), &SDL_GameControllerClose);
76
- game_controllers.clear();
77
-
74
+ open_joysticks.clear();
75
+ open_game_controllers.clear();
76
+
78
77
  SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
79
78
  }
80
-
79
+
81
80
  void update_mouse_position()
82
81
  {
83
- #if defined(GOSU_IS_MAC)
84
- // Avoid SDL_GetGlobalMouseState on macOS until this bug is fixed:
85
- // https://bugzilla.libsdl.org/show_bug.cgi?id=4255
86
- int window_x, window_y;
87
- SDL_GetWindowPosition(window, &window_x, &window_y);
88
- auto mouse_position = NSEvent.mouseLocation;
89
- mouse_x = mouse_position.x - window_x;
90
- mouse_y = (CGDisplayPixelsHigh(kCGDirectMainDisplay) - mouse_position.y) - window_y;
91
- #elif SDL_VERSION_ATLEAST(2, 0, 5)
82
+ #if SDL_VERSION_ATLEAST(2, 0, 5)
92
83
  // SDL_GetGlobalMouseState was added in SDL 2.0.4, but it only started using the same
93
84
  // coordinate system as SDL_GetWindowPosition on X11 in 2.0.5.
94
85
  int x, y, window_x, window_y;
@@ -103,7 +94,7 @@ struct Gosu::Input::Impl
103
94
  mouse_y = y;
104
95
  #endif
105
96
  }
106
-
97
+
107
98
  void set_mouse_position(double x, double y)
108
99
  {
109
100
  SDL_WarpMouseInWindow(window,
@@ -120,13 +111,13 @@ struct Gosu::Input::Impl
120
111
  mouse_x = x, mouse_y = y;
121
112
  #endif
122
113
  }
123
-
114
+
124
115
  bool feed_sdl_event(const SDL_Event* e)
125
116
  {
126
117
  switch (e->type) {
127
118
  case SDL_KEYDOWN:
128
119
  case SDL_KEYUP: {
129
- if (e->key.repeat == 0 && e->key.keysym.scancode <= KB_RANGE_END) {
120
+ if (e->key.repeat == 0 && e->key.keysym.scancode <= static_cast<int>(KB_RANGE_END)) {
130
121
  enqueue_event(e->key.keysym.scancode, e->type == SDL_KEYDOWN);
131
122
  return true;
132
123
  }
@@ -153,160 +144,377 @@ struct Gosu::Input::Impl
153
144
  }
154
145
  break;
155
146
  }
147
+ case SDL_JOYDEVICEADDED: {
148
+ if (available_gamepad_slot_index() == -1) {
149
+ break;
150
+ }
151
+ int gamepad_slot = -1;
152
+ int joystick_instance_id = -1;
153
+
154
+ // Loop through attached gamepads as e->jdevice.which cannot be trusted (always 0)
155
+ for (int i = 0; i < SDL_NumJoysticks(); i++) {
156
+ // Prefer the SDL_GameController API...
157
+ if (SDL_IsGameController(i)) {
158
+ if (SDL_GameController *game_controller = SDL_GameControllerOpen(i)) {
159
+ gamepad_slot = available_gamepad_slot_index();
160
+ joystick_instance_id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(game_controller));
161
+ if (gamepad_instance_id_is_known(joystick_instance_id)) {
162
+ continue;
163
+ }
164
+ open_game_controllers.emplace_back(
165
+ shared_ptr<SDL_GameController>(game_controller, SDL_GameControllerClose)
166
+ );
167
+ }
168
+ }
169
+ // ...but fall back on the good, old SDL_Joystick API.
170
+ else if (SDL_Joystick *joystick = SDL_JoystickOpen(i)) {
171
+ gamepad_slot = available_gamepad_slot_index();
172
+ joystick_instance_id = SDL_JoystickInstanceID(joystick);
173
+ if (gamepad_instance_id_is_known(joystick_instance_id)) {
174
+ continue;
175
+ }
176
+ open_joysticks.emplace_back(
177
+ shared_ptr<SDL_Joystick>(joystick, SDL_JoystickClose)
178
+ );
179
+ }
180
+
181
+ // Reserve gamepad slot and issue gamepad connection event
182
+ if (gamepad_slot >= 0 && joystick_instance_id >= 0) {
183
+ gamepad_slots[gamepad_slot] = joystick_instance_id;
184
+ enqueue_gamepad_connection_event(gamepad_slot, true, -1);
185
+ }
186
+ }
187
+ break;
188
+ }
189
+ case SDL_JOYDEVICEREMOVED: {
190
+ int gamepad_slot = gamepad_slot_index(e->jdevice.which);
191
+ if (gamepad_slot >= 0) {
192
+ enqueue_gamepad_connection_event(gamepad_slot, false, e->jdevice.which);
193
+ }
194
+ break;
195
+ }
156
196
  }
157
197
  return false;
158
198
  }
159
-
160
- typedef array<bool, GP_NUM_PER_GAMEPAD> GamepadBuffer;
161
-
199
+
200
+ double scale_axis(double value)
201
+ {
202
+ return value >= 0 ? value / SDL_JOYSTICK_AXIS_MAX : -value / SDL_JOYSTICK_AXIS_MIN;
203
+ }
204
+
205
+ // returns the gamepad slot index (0..NUM_GAMEPADS - 1) for the joystick instance id or -1 if not found
206
+ int gamepad_slot_index(int joystick_instance_id) const
207
+ {
208
+ for (int i = 0; i < gamepad_slots.size(); i++) {
209
+ if (gamepad_slots[i] == joystick_instance_id) {
210
+ return i;
211
+ }
212
+ }
213
+
214
+ return -1;
215
+ }
216
+
217
+ // returns first available gamepad slot or -1 if non are available
218
+ int available_gamepad_slot_index() const
219
+ {
220
+ for (int i = 0; i < gamepad_slots.size(); i++) {
221
+ if (gamepad_slots[i] == -1) {
222
+ return i;
223
+ }
224
+ }
225
+
226
+ return -1;
227
+ }
228
+
229
+ // returns whether the gamepad using the joystick instance id is known
230
+ bool gamepad_instance_id_is_known(int id)
231
+ {
232
+ for (int j = 0; j < gamepad_slots.size(); j++) {
233
+ if (gamepad_slots[j] == id) {
234
+ return true;
235
+ }
236
+ }
237
+
238
+ return false;
239
+ }
240
+
241
+ // frees the gamepad slot associated with this joystick instance id
242
+ // and frees the SDL_GameController/SDL_Joystick
243
+ void free_gamepad_slot(int instance_id)
244
+ {
245
+ int index = gamepad_slot_index(instance_id);
246
+
247
+ for (int i = 0; i < open_game_controllers.size(); i++) {
248
+ SDL_Joystick* joystick = SDL_GameControllerGetJoystick(open_game_controllers[i].get());
249
+ if (SDL_JoystickInstanceID(joystick) == instance_id) {
250
+ open_game_controllers.erase(open_game_controllers.begin() + i);
251
+ gamepad_slots[index] = -1;
252
+ return;
253
+ }
254
+ }
255
+
256
+ for (int i = 0; i < open_joysticks.size(); i++) {
257
+ if (SDL_JoystickInstanceID(open_joysticks[i].get()) == instance_id) {
258
+ open_joysticks.erase(open_joysticks.begin() + i);
259
+ gamepad_slots[index] = -1;
260
+ return;
261
+ }
262
+ }
263
+ }
264
+
265
+ struct GamepadBuffer
266
+ {
267
+ array<bool, NUM_BUTTONS_PER_GAMEPAD> buttons = { false };
268
+ array<bool, 4> directions = { false };
269
+ array<double, NUM_AXES_PER_GAMEPAD> axes = { 0.0 };
270
+ };
271
+
162
272
  void poll_gamepads()
163
273
  {
164
- // This gamepad is an OR-ed version of all the other gamepads. If button
165
- // 3 is pressed on any attached gamepad, down(GP_BUTTON_3) will return
166
- // true. This is handy for singleplayer games.
167
- GamepadBuffer any_gamepad = { false };
168
-
169
- size_t available_gamepads = game_controllers.size() + joysticks.size();
170
-
171
- for (int i = 0; i < available_gamepads; ++i) {
172
- GamepadBuffer current_gamepad = { false };
173
-
274
+ // This gamepad is an OR-ed version of all the other gamepads.
275
+ // If button3 is pressed on any attached gamepad, down(GP_BUTTON_3) will return true.
276
+ // This is handy for singleplayer games where you don't care which gamepad that player uses.
277
+ GamepadBuffer any_gamepad;
278
+
279
+ // Reset all axes values, they will be recalculated below.
280
+ axis_states.fill(0.0);
281
+
282
+ for (int i = 0; i < gamepad_slots.size(); ++i) {
283
+ if (gamepad_slots[i] == -1) {
284
+ continue;
285
+ }
286
+
174
287
  // Poll data from SDL, using either of two API interfaces.
175
- if (i < game_controllers.size()) {
176
- SDL_GameController* game_controller = game_controllers[i];
177
- poll_game_controller(game_controller, current_gamepad);
288
+ GamepadBuffer current_gamepad;
289
+ if (SDL_GameController* game_controller = SDL_GameControllerFromInstanceID(gamepad_slots[i])) {
290
+ current_gamepad = poll_game_controller(game_controller);
178
291
  }
179
292
  else {
180
- SDL_Joystick* joystick = joysticks[i - game_controllers.size()];
181
- poll_joystick(joystick, current_gamepad);
293
+ current_gamepad = poll_joystick(SDL_JoystickFromInstanceID(gamepad_slots[i]));
294
+ }
295
+
296
+ int axis_offset = NUM_AXES_PER_GAMEPAD * (i + 1);
297
+ for (int a = 0; a < NUM_AXES_PER_GAMEPAD; ++a) {
298
+ // Transfer the axes values into the global axis_state array..
299
+ axis_states[a + axis_offset] = current_gamepad.axes[a];
300
+ // Also transfer the gamepad-specific axes values into the "any gamepad" slots.
301
+ // (Values with a higher amplitude override smaller ones.)
302
+ if (abs(current_gamepad.axes[a]) > abs(axis_states[a])) {
303
+ axis_states[a] = current_gamepad.axes[a];
304
+ }
182
305
  }
183
-
306
+
184
307
  // Now at the same time, enqueue all events for this particular
185
308
  // gamepad, and OR the keyboard state into any_gamepad.
186
- int offset = GP_RANGE_BEGIN + GP_NUM_PER_GAMEPAD * (i + 1);
187
- for (int j = 0; j < current_gamepad.size(); ++j) {
188
- any_gamepad[j] = any_gamepad[j] || current_gamepad[j];
189
-
190
- if (current_gamepad[j] && !button_states[j + offset]) {
309
+ int offset = GP_RANGE_BEGIN + NUM_BUTTONS_PER_GAMEPAD * (i + 1);
310
+ for (int j = 0; j < current_gamepad.buttons.size(); ++j) {
311
+ any_gamepad.buttons[j] = any_gamepad.buttons[j] || current_gamepad.buttons[j];
312
+
313
+ if (current_gamepad.buttons[j] && !button_states[j + offset]) {
191
314
  button_states[j + offset] = true;
192
315
  enqueue_event(j + offset, true);
193
316
  }
194
- else if (!current_gamepad[j] && button_states[j + offset]) {
317
+ else if (!current_gamepad.buttons[j] && button_states[j + offset]) {
195
318
  button_states[j + offset] = false;
196
319
  enqueue_event(j + offset, false);
197
320
  }
198
321
  }
322
+ int direction_offset = GP_LEFT + 4 * (i + 1);
323
+ for (int d = 0; d < 4; ++d) {
324
+ any_gamepad.directions[d] = any_gamepad.directions[d] || current_gamepad.directions[d];
325
+
326
+ if (current_gamepad.directions[d] && !button_states[d + direction_offset]) {
327
+ button_states[d + direction_offset] = true;
328
+ enqueue_event(d + direction_offset, true);
329
+ }
330
+ else if (!current_gamepad.directions[d] && button_states[d + direction_offset]) {
331
+ button_states[d + direction_offset] = false;
332
+ enqueue_event(d + direction_offset, false);
333
+ }
334
+ }
199
335
  }
200
-
336
+
201
337
  // And lastly, enqueue events for the virtual "any" gamepad.
202
- for (int j = 0; j < any_gamepad.size(); ++j) {
203
- if (any_gamepad[j] && !button_states[j + GP_RANGE_BEGIN]) {
338
+ for (int j = 0; j < any_gamepad.buttons.size(); ++j) {
339
+ if (any_gamepad.buttons[j] && !button_states[j + GP_RANGE_BEGIN]) {
204
340
  button_states[j + GP_RANGE_BEGIN] = true;
205
341
  enqueue_event(j + GP_RANGE_BEGIN, true);
206
342
  }
207
- else if (!any_gamepad[j] && button_states[j + GP_RANGE_BEGIN]) {
343
+ else if (!any_gamepad.buttons[j] && button_states[j + GP_RANGE_BEGIN]) {
208
344
  button_states[j + GP_RANGE_BEGIN] = false;
209
345
  enqueue_event(j + GP_RANGE_BEGIN, false);
210
346
  }
211
347
  }
348
+ for (int d = 0; d < 4; ++d) {
349
+ if (any_gamepad.directions[d] && !button_states[d + GP_LEFT]) {
350
+ button_states[d + GP_LEFT] = true;
351
+ enqueue_event(d + GP_LEFT, true);
352
+ }
353
+ else if (!any_gamepad.directions[d] && button_states[d + GP_LEFT]) {
354
+ button_states[d + GP_LEFT] = false;
355
+ enqueue_event(d + GP_LEFT, false);
356
+ }
357
+ }
212
358
  }
213
-
359
+
214
360
  void dispatch_enqueued_events()
215
361
  {
216
- for (int event : event_queue) {
217
- bool down = (event >= 0);
218
- Button button(down ? event : ~event);
219
-
220
- button_states[button.id()] = down;
221
- if (down && input.on_button_down) {
222
- input.on_button_down(button);
223
- }
224
- else if (!down && input.on_button_up) {
225
- input.on_button_up(button);
362
+ for (const InputEvent& event : event_queue) {
363
+ switch (event.type) {
364
+ case InputEvent::ButtonDown:
365
+ button_states[event.id] = true;
366
+ if (input.on_button_down) {
367
+ input.on_button_down(Button(event.id));
368
+ }
369
+ break;
370
+ case InputEvent::ButtonUp:
371
+ button_states[event.id] = false;
372
+ if (input.on_button_up) {
373
+ input.on_button_up(Button(event.id));
374
+ }
375
+ break;
376
+ case InputEvent::GamepadConnected:
377
+ if (input.on_gamepad_connected) {
378
+ input.on_gamepad_connected(event.id);
379
+ }
380
+ break;
381
+ case InputEvent::GamepadDisconnected:
382
+ if (input.on_gamepad_disconnected) {
383
+ input.on_gamepad_disconnected(event.id);
384
+ }
385
+ free_gamepad_slot(event.gamepad_instance_id);
386
+ break;
226
387
  }
227
388
  }
228
389
  event_queue.clear();
229
390
  }
230
-
391
+
231
392
  private:
232
393
  // For button down event: Button name value (>= 0)
233
394
  // For button up event: ~Button name value (< 0)
234
- vector<int> event_queue;
395
+ vector<InputEvent> event_queue;
235
396
 
236
- void enqueue_event(int id, bool down)
397
+ void enqueue_event(unsigned id, bool down)
237
398
  {
238
- event_queue.push_back(down ? id : ~id);
399
+ InputEvent event;
400
+ event.type = down ? InputEvent::ButtonDown : InputEvent::ButtonUp;
401
+ event.id = id;
402
+ event_queue.push_back(event);
239
403
  }
240
-
241
- vector<SDL_Joystick*> joysticks;
242
- vector<SDL_GameController*> game_controllers;
243
-
244
- // SDL returns axis values in the range -2^15 through 2^15-1, so we consider -2^14 through
245
- // 2^14 (half of that range) the dead zone.
246
- enum { DEAD_ZONE = (1 << 14) };
247
-
248
- void poll_game_controller(SDL_GameController* controller, GamepadBuffer& gamepad)
404
+
405
+ void enqueue_gamepad_connection_event(int gamepad_index_id, bool connected, int instance_id)
406
+ {
407
+ InputEvent event;
408
+ event.type = connected ? InputEvent::GamepadConnected : InputEvent::GamepadDisconnected;
409
+ event.id = gamepad_index_id;
410
+ event.gamepad_instance_id = instance_id;
411
+ event_queue.push_back(event);
412
+ }
413
+
414
+ GamepadBuffer poll_game_controller(SDL_GameController* controller)
249
415
  {
250
- gamepad[GP_LEFT - GP_RANGE_BEGIN] =
251
- SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT) ||
252
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) < -DEAD_ZONE ||
253
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) < -DEAD_ZONE;
254
-
255
- gamepad[GP_RIGHT - GP_RANGE_BEGIN] =
256
- SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT) ||
257
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) > +DEAD_ZONE ||
258
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) > +DEAD_ZONE;
259
-
260
- gamepad[GP_UP - GP_RANGE_BEGIN] =
261
- SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP) ||
262
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) < -DEAD_ZONE ||
263
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) < -DEAD_ZONE;
264
-
265
- gamepad[GP_DOWN - GP_RANGE_BEGIN] =
266
- SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN) ||
267
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) > +DEAD_ZONE ||
268
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) > +DEAD_ZONE;
269
-
416
+ GamepadBuffer gamepad;
417
+
418
+ // Poll axes first.
419
+ gamepad.axes[GP_LEFT_STICK_X_AXIS - GP_AXES_RANGE_BEGIN] =
420
+ scale_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX));
421
+ gamepad.axes[GP_LEFT_STICK_Y_AXIS - GP_AXES_RANGE_BEGIN] =
422
+ scale_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY));
423
+ gamepad.axes[GP_RIGHT_STICK_X_AXIS - GP_AXES_RANGE_BEGIN] =
424
+ scale_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX));
425
+ gamepad.axes[GP_RIGHT_STICK_Y_AXIS - GP_AXES_RANGE_BEGIN] =
426
+ scale_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY));
427
+ gamepad.axes[GP_LEFT_TRIGGER_AXIS - GP_AXES_RANGE_BEGIN] =
428
+ scale_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT));
429
+ gamepad.axes[GP_RIGHT_TRIGGER_AXIS - GP_AXES_RANGE_BEGIN] =
430
+ scale_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT));
431
+
432
+ gamepad.buttons[GP_DPAD_LEFT - GP_RANGE_BEGIN] =
433
+ SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
434
+ gamepad.buttons[GP_DPAD_RIGHT - GP_RANGE_BEGIN] =
435
+ SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
436
+ gamepad.buttons[GP_DPAD_UP - GP_RANGE_BEGIN] =
437
+ SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP);
438
+ gamepad.buttons[GP_DPAD_DOWN - GP_RANGE_BEGIN] =
439
+ SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
440
+
270
441
  int button = 0;
271
442
  for (; button < SDL_CONTROLLER_BUTTON_DPAD_UP; ++button) {
272
- gamepad[GP_BUTTON_0 + button - GP_RANGE_BEGIN] =
443
+ gamepad.buttons[GP_BUTTON_0 + button - GP_RANGE_BEGIN] =
273
444
  SDL_GameControllerGetButton(controller, (SDL_GameControllerButton) button);
274
445
  }
275
- gamepad[GP_BUTTON_0 + button++ - GP_RANGE_BEGIN] =
276
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) > +DEAD_ZONE;
277
- gamepad[GP_BUTTON_0 + button++ - GP_RANGE_BEGIN] =
278
- SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) > +DEAD_ZONE;
446
+ // Represent the triggers as buttons in addition to them being axes.
447
+ gamepad.buttons[GP_BUTTON_0 + button++ - GP_RANGE_BEGIN] =
448
+ gamepad.axes[GP_LEFT_TRIGGER_AXIS] >= 0.5;
449
+ gamepad.buttons[GP_BUTTON_0 + button++ - GP_RANGE_BEGIN] =
450
+ gamepad.axes[GP_RIGHT_TRIGGER_AXIS] >= 0.5;
451
+
452
+ merge_directions(gamepad);
453
+ return gamepad;
279
454
  }
280
-
281
- void poll_joystick(SDL_Joystick* joystick, GamepadBuffer& gamepad)
455
+
456
+ GamepadBuffer poll_joystick(SDL_Joystick* joystick)
282
457
  {
458
+ GamepadBuffer gamepad;
459
+
460
+ // Just guess that the first four axes are equivalent to two analog sticks.
283
461
  int axes = SDL_JoystickNumAxes(joystick);
284
- for (int axis = 0; axis < axes; ++axis) {
285
- Sint16 value = SDL_JoystickGetAxis(joystick, axis);
286
-
287
- if (value < -DEAD_ZONE) {
288
- gamepad[(axis % 2 ? GP_UP : GP_LEFT) - GP_RANGE_BEGIN] = true;
289
- }
290
- else if (value > +DEAD_ZONE) {
291
- gamepad[(axis % 2 ? GP_DOWN : GP_RIGHT) - GP_RANGE_BEGIN] = true;
292
- }
462
+ if (axes > 0) {
463
+ gamepad.axes[GP_LEFT_STICK_X_AXIS - GP_AXES_RANGE_BEGIN] =
464
+ scale_axis(SDL_JoystickGetAxis(joystick, 0));
465
+ }
466
+ if (axes > 1) {
467
+ gamepad.axes[GP_LEFT_STICK_Y_AXIS - GP_AXES_RANGE_BEGIN] =
468
+ scale_axis(SDL_JoystickGetAxis(joystick, 1));
293
469
  }
294
-
470
+ if (axes > 2) {
471
+ gamepad.axes[GP_RIGHT_STICK_X_AXIS - GP_AXES_RANGE_BEGIN] =
472
+ scale_axis(SDL_JoystickGetAxis(joystick, 2));
473
+ }
474
+ if (axes > 3) {
475
+ gamepad.axes[GP_RIGHT_STICK_Y_AXIS - GP_AXES_RANGE_BEGIN] =
476
+ scale_axis(SDL_JoystickGetAxis(joystick, 3));
477
+ }
478
+
479
+ // All hats are merged into the DPad.
295
480
  int hats = SDL_JoystickNumHats(joystick);
296
481
  for (int hat = 0; hat < hats; ++hat) {
297
482
  Uint8 value = SDL_JoystickGetHat(joystick, hat);
298
- if (value & SDL_HAT_LEFT) gamepad[GP_LEFT - GP_RANGE_BEGIN] = true;
299
- if (value & SDL_HAT_RIGHT) gamepad[GP_RIGHT - GP_RANGE_BEGIN] = true;
300
- if (value & SDL_HAT_UP) gamepad[GP_UP - GP_RANGE_BEGIN] = true;
301
- if (value & SDL_HAT_DOWN) gamepad[GP_DOWN - GP_RANGE_BEGIN] = true;
483
+ if (value & SDL_HAT_LEFT) gamepad.buttons[GP_DPAD_LEFT - GP_RANGE_BEGIN] = true;
484
+ if (value & SDL_HAT_RIGHT) gamepad.buttons[GP_DPAD_RIGHT - GP_RANGE_BEGIN] = true;
485
+ if (value & SDL_HAT_UP) gamepad.buttons[GP_DPAD_UP - GP_RANGE_BEGIN] = true;
486
+ if (value & SDL_HAT_DOWN) gamepad.buttons[GP_DPAD_DOWN - GP_RANGE_BEGIN] = true;
302
487
  }
303
-
304
- int buttons = min<int>(GP_NUM_PER_GAMEPAD - 4, SDL_JoystickNumButtons(joystick));
488
+
489
+ int buttons = min<int>(NUM_BUTTONS_PER_GAMEPAD - 4, SDL_JoystickNumButtons(joystick));
305
490
  for (int button = 0; button < buttons; ++button) {
306
491
  if (SDL_JoystickGetButton(joystick, button)) {
307
- gamepad[GP_BUTTON_0 + button - GP_RANGE_BEGIN] = true;
492
+ gamepad.buttons[GP_BUTTON_0 + button - GP_RANGE_BEGIN] = true;
308
493
  }
309
494
  }
495
+
496
+ merge_directions(gamepad);
497
+ return gamepad;
498
+ }
499
+
500
+ void merge_directions(GamepadBuffer& gamepad)
501
+ {
502
+ gamepad.directions[0] =
503
+ gamepad.buttons[GP_DPAD_LEFT - GP_RANGE_BEGIN] ||
504
+ gamepad.axes[GP_LEFT_STICK_X_AXIS - GP_AXES_RANGE_BEGIN] <= -0.5 ||
505
+ gamepad.axes[GP_RIGHT_STICK_X_AXIS - GP_AXES_RANGE_BEGIN] <= -0.5;
506
+ gamepad.directions[1] =
507
+ gamepad.buttons[GP_DPAD_RIGHT - GP_RANGE_BEGIN] ||
508
+ gamepad.axes[GP_LEFT_STICK_X_AXIS - GP_AXES_RANGE_BEGIN] >= +0.5 ||
509
+ gamepad.axes[GP_RIGHT_STICK_X_AXIS - GP_AXES_RANGE_BEGIN] >= +0.5;
510
+ gamepad.directions[2] =
511
+ gamepad.buttons[GP_DPAD_UP - GP_RANGE_BEGIN] ||
512
+ gamepad.axes[GP_LEFT_STICK_Y_AXIS - GP_AXES_RANGE_BEGIN] <= -0.5 ||
513
+ gamepad.axes[GP_RIGHT_STICK_Y_AXIS - GP_AXES_RANGE_BEGIN] <= -0.5;
514
+ gamepad.directions[3] =
515
+ gamepad.buttons[GP_DPAD_DOWN - GP_RANGE_BEGIN] ||
516
+ gamepad.axes[GP_LEFT_STICK_Y_AXIS - GP_AXES_RANGE_BEGIN] >= +0.5 ||
517
+ gamepad.axes[GP_RIGHT_STICK_Y_AXIS - GP_AXES_RANGE_BEGIN] >= +0.5;
310
518
  }
311
519
  };
312
520
 
@@ -328,23 +536,23 @@ bool Gosu::Input::feed_sdl_event(void* event)
328
536
  string Gosu::Input::id_to_char(Button btn)
329
537
  {
330
538
  require_sdl_video();
331
-
539
+
332
540
  if (btn.id() > KB_RANGE_END) return "";
333
-
541
+
334
542
  // SDL_GetKeyName returns "Space" for this value, but we want the character value.
335
543
  if (btn.id() == KB_SPACE) return " ";
336
-
544
+
337
545
  SDL_Keycode keycode = SDL_GetKeyFromScancode(static_cast<SDL_Scancode>(btn.id()));
338
546
  if (keycode == SDLK_UNKNOWN) return "";
339
-
547
+
340
548
  const char* name = SDL_GetKeyName(keycode);
341
549
  if (name == nullptr) return "";
342
-
550
+
343
551
  u32string codepoints = utf8_to_composed_utc4(name);
344
-
552
+
345
553
  // Filter out names that are more than one logical character.
346
554
  if (codepoints.length() != 1) return "";
347
-
555
+
348
556
  // Always return lower case to be consistent with previous versions of Gosu.
349
557
  codepoints[0] = utf8proc_tolower(codepoints[0]);
350
558
  // Convert back to UTF-8.
@@ -356,18 +564,59 @@ string Gosu::Input::id_to_char(Button btn)
356
564
  Gosu::Button Gosu::Input::char_to_id(string ch)
357
565
  {
358
566
  require_sdl_video();
359
-
567
+
360
568
  SDL_Keycode keycode = SDL_GetKeyFromName(ch.c_str());
361
569
  return keycode == SDLK_UNKNOWN ? NO_BUTTON : Button(SDL_GetScancodeFromKey(keycode));
362
570
  }
363
571
 
572
+ std::string Gosu::Input::button_name(Button btn)
573
+ {
574
+ require_sdl_video();
575
+
576
+ SDL_Keycode keycode = SDL_GetKeyFromScancode(static_cast<SDL_Scancode>(btn.id()));
577
+ return SDL_GetKeyName(keycode);
578
+ }
579
+
580
+ std::string Gosu::Input::gamepad_name(int index)
581
+ {
582
+ if (index < 0 || index > gamepad_slots.size() - 1) {
583
+ return "";
584
+ }
585
+
586
+ int instance_id = gamepad_slots[index];
587
+
588
+ if (instance_id == -1) {
589
+ return "";
590
+ }
591
+
592
+ if (SDL_GameController* game_controller = SDL_GameControllerFromInstanceID(instance_id)) {
593
+ return SDL_GameControllerName(game_controller);
594
+ }
595
+ else if (SDL_Joystick* joystick = SDL_JoystickFromInstanceID(instance_id)) {
596
+ return SDL_JoystickName(joystick);
597
+ }
598
+
599
+ return "";
600
+ }
601
+
364
602
  bool Gosu::Input::down(Gosu::Button btn)
365
603
  {
366
604
  if (btn == NO_BUTTON || btn.id() >= NUM_BUTTONS) return false;
367
-
605
+
368
606
  return button_states[btn.id()];
369
607
  }
370
608
 
609
+ double Gosu::Input::axis(Gosu::Button btn)
610
+ {
611
+ unsigned axis_id = btn.id() - GP_LEFT_STICK_X_AXIS;
612
+
613
+ if (axis_id >= axis_states.size()) {
614
+ throw std::out_of_range("Invalid axis ID: " + std::to_string(btn.id()));
615
+ }
616
+
617
+ return axis_states[axis_id];
618
+ }
619
+
371
620
  double Gosu::Input::mouse_x() const
372
621
  {
373
622
  return pimpl->mouse_x * pimpl->mouse_scale_x + pimpl->mouse_offset_x;
@@ -395,7 +644,7 @@ void Gosu::Input::set_mouse_factors(double scale_x, double scale_y,
395
644
  const Gosu::Touches& Gosu::Input::current_touches() const
396
645
  {
397
646
  // We could use the SDL 2 touch API to implement this.
398
-
647
+
399
648
  static Gosu::Touches none;
400
649
  return none;
401
650
  }
@@ -435,7 +684,7 @@ void Gosu::Input::set_text_input(TextInput* text_input)
435
684
  else if (pimpl->text_input == nullptr && text_input) {
436
685
  SDL_StartTextInput();
437
686
  }
438
-
687
+
439
688
  pimpl->text_input = text_input;
440
689
  }
441
690