Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
3 | * It is copyright by its individual contributors, as recorded in the |
||
4 | * project's Git history. See COPYING.txt at the top level for license |
||
5 | * terms and a link to the Git history. |
||
6 | */ |
||
7 | /* |
||
8 | * |
||
9 | * SDL joystick support |
||
10 | * |
||
11 | */ |
||
12 | |||
13 | #include <memory> |
||
14 | #include <vector> |
||
15 | #include <tuple> |
||
16 | #include <type_traits> |
||
17 | #include "joy.h" |
||
18 | #include "key.h" |
||
19 | #include "dxxerror.h" |
||
20 | #include "timer.h" |
||
21 | #include "console.h" |
||
22 | #include "event.h" |
||
23 | #include "u_mem.h" |
||
24 | #include "playsave.h" |
||
25 | #include "kconfig.h" |
||
26 | #include "compiler-range_for.h" |
||
27 | |||
28 | #if DXX_MAX_JOYSTICKS |
||
29 | #include "compiler-cf_assert.h" |
||
30 | #include "d_enumerate.h" |
||
31 | #include "d_range.h" |
||
32 | #include "partial_range.h" |
||
33 | #include <utility> |
||
34 | |||
35 | namespace dcx { |
||
36 | |||
37 | namespace { |
||
38 | |||
39 | int num_joysticks = 0; |
||
40 | |||
41 | std::vector<unsigned> joy_key_map; |
||
42 | |||
43 | /* This struct is a "virtual" joystick, which includes all the axes |
||
44 | * and buttons of every joystick found. |
||
45 | */ |
||
46 | static struct joyinfo { |
||
47 | #if DXX_MAX_BUTTONS_PER_JOYSTICK |
||
48 | std::array<uint8_t, JOY_MAX_BUTTONS> button_state; |
||
49 | #endif |
||
50 | } Joystick; |
||
51 | |||
52 | struct d_event_joystickbutton : d_event |
||
53 | { |
||
54 | const unsigned button; |
||
55 | constexpr d_event_joystickbutton(const event_type t, const unsigned b) : |
||
56 | d_event(t), button(b) |
||
57 | { |
||
58 | } |
||
59 | }; |
||
60 | |||
61 | struct d_event_joystick_moved : d_event, d_event_joystick_axis_value |
||
62 | { |
||
63 | DXX_INHERIT_CONSTRUCTORS(d_event_joystick_moved, d_event); |
||
64 | }; |
||
65 | |||
66 | class SDL_Joystick_deleter |
||
67 | { |
||
68 | public: |
||
69 | void operator()(SDL_Joystick *j) const |
||
70 | { |
||
71 | SDL_JoystickClose(j); |
||
72 | } |
||
73 | }; |
||
74 | |||
75 | #ifndef DXX_USE_SIZE_SORTED_TUPLE |
||
76 | #define DXX_USE_SIZE_SORTED_TUPLE (__cplusplus > 201103L) |
||
77 | #endif |
||
78 | |||
79 | #if DXX_USE_SIZE_SORTED_TUPLE |
||
80 | template <typename T, std::size_t... Is, std::size_t... Js> |
||
81 | auto d_split_tuple(T &&t, std::index_sequence<Is...>, std::index_sequence<Js...>) -> |
||
82 | std::pair< |
||
83 | std::tuple<typename std::tuple_element<Is, T>::type...>, |
||
84 | std::tuple<typename std::tuple_element<sizeof...(Is) + Js, T>::type...> |
||
85 | >; |
||
86 | |||
87 | template <typename> |
||
88 | class d_size_sorted; |
||
89 | |||
90 | /* Given an input tuple T, define a public member `type` with the same |
||
91 | * members as T, but sorted such that sizeof(Ti) <= sizeof(Tj) for all i |
||
92 | * <= j. |
||
93 | */ |
||
94 | template <typename... Ts> |
||
95 | class d_size_sorted<std::tuple<Ts...>> |
||
96 | { |
||
97 | using split_tuple = decltype(d_split_tuple( |
||
98 | std::declval<std::tuple<Ts...>>(), |
||
99 | std::make_index_sequence<sizeof...(Ts) / 2>(), |
||
100 | std::make_index_sequence<(1 + sizeof...(Ts)) / 2>() |
||
101 | )); |
||
102 | using first_type = typename split_tuple::first_type; |
||
103 | using second_type = typename split_tuple::second_type; |
||
104 | public: |
||
105 | using type = typename std::conditional<(sizeof(first_type) < sizeof(second_type)), |
||
106 | decltype(std::tuple_cat(std::declval<first_type>(), std::declval<second_type>())), |
||
107 | decltype(std::tuple_cat(std::declval<second_type>(), std::declval<first_type>())) |
||
108 | >::type; |
||
109 | }; |
||
110 | |||
111 | template <typename T1> |
||
112 | class d_size_sorted<std::tuple<T1>> |
||
113 | { |
||
114 | public: |
||
115 | using type = std::tuple<T1>; |
||
116 | }; |
||
117 | #endif |
||
118 | |||
119 | template <typename> |
||
120 | class ignore_empty {}; |
||
121 | |||
122 | template <typename T> |
||
123 | using maybe_empty_array = typename std::conditional<T().empty(), ignore_empty<T>, T>::type; |
||
124 | |||
125 | /* This struct is an array, with one entry for each physical joystick |
||
126 | * found. |
||
127 | */ |
||
128 | class d_physical_joystick |
||
129 | { |
||
130 | #define for_each_tuple_item(HANDLE_VERB,VERB) \ |
||
131 | HANDLE_VERB(handle) \ |
||
132 | VERB(hat_map) \ |
||
133 | VERB(button_map) \ |
||
134 | VERB(axis_map) \ |
||
135 | VERB(axis_value) \ |
||
136 | VERB(axis_button_map) \ |
||
137 | |||
138 | #if DXX_USE_SIZE_SORTED_TUPLE |
||
139 | template <typename... Ts> |
||
140 | using tuple_type = typename d_size_sorted<std::tuple<Ts...>>::type; |
||
141 | #define define_handle_getter(N) \ |
||
142 | define_getter(N, tuple_member_type_##N) |
||
143 | #define define_array_getter(N) \ |
||
144 | define_getter(N, maybe_empty_array<tuple_member_type_##N>) |
||
145 | #else |
||
146 | template <typename... Ts> |
||
147 | using tuple_type = std::tuple<Ts...>; |
||
148 | enum |
||
149 | { |
||
150 | #define define_enum(V) tuple_item_##V, |
||
151 | for_each_tuple_item(define_enum, define_enum) |
||
152 | #undef define_enum |
||
153 | }; |
||
154 | /* std::get<index> does not handle the maybe_empty_array case, so |
||
155 | * reuse the same getter for both handle and array. |
||
156 | */ |
||
157 | #define define_handle_getter(N) \ |
||
158 | define_getter(N, tuple_item_##N) |
||
159 | #define define_array_getter define_handle_getter |
||
160 | #endif |
||
161 | #define define_getter(N,V) \ |
||
162 | auto &N() \ |
||
163 | { \ |
||
164 | return std::get<V>(t); \ |
||
165 | } |
||
166 | using tuple_member_type_handle = std::unique_ptr<SDL_Joystick, SDL_Joystick_deleter>; |
||
167 | //Note: Descent expects hats to be buttons, so these are indices into Joystick.buttons |
||
168 | struct tuple_member_type_hat_map : std::array<unsigned, DXX_MAX_HATS_PER_JOYSTICK> {}; |
||
169 | struct tuple_member_type_button_map : std::array<unsigned, DXX_MAX_BUTTONS_PER_JOYSTICK> {}; |
||
170 | struct tuple_member_type_axis_map : std::array<unsigned, DXX_MAX_AXES_PER_JOYSTICK> {}; |
||
171 | struct tuple_member_type_axis_value : std::array<int, DXX_MAX_AXES_PER_JOYSTICK> {}; |
||
172 | struct tuple_member_type_axis_button_map : std::array<unsigned, DXX_MAX_AXES_PER_JOYSTICK> {}; |
||
173 | tuple_type< |
||
174 | tuple_member_type_handle, |
||
175 | maybe_empty_array<tuple_member_type_hat_map>, |
||
176 | maybe_empty_array<tuple_member_type_button_map>, |
||
177 | maybe_empty_array<tuple_member_type_axis_map>, |
||
178 | maybe_empty_array<tuple_member_type_axis_value>, |
||
179 | maybe_empty_array<tuple_member_type_axis_button_map> |
||
180 | > t; |
||
181 | public: |
||
182 | for_each_tuple_item(define_handle_getter, define_array_getter); |
||
183 | #undef define_handle_getter |
||
184 | #undef define_array_getter |
||
185 | #undef define_getter |
||
186 | #undef for_each_tuple_item |
||
187 | }; |
||
188 | |||
189 | } |
||
190 | |||
191 | static std::array<d_physical_joystick, DXX_MAX_JOYSTICKS> SDL_Joysticks; |
||
192 | |||
193 | #if DXX_MAX_BUTTONS_PER_JOYSTICK |
||
194 | window_event_result joy_button_handler(const SDL_JoyButtonEvent *const jbe) |
||
195 | { |
||
196 | const unsigned button = SDL_Joysticks[jbe->which].button_map()[jbe->button]; |
||
197 | |||
198 | Joystick.button_state[button] = jbe->state; |
||
199 | |||
200 | const d_event_joystickbutton event{ |
||
201 | (jbe->type == SDL_JOYBUTTONDOWN) ? EVENT_JOYSTICK_BUTTON_DOWN : EVENT_JOYSTICK_BUTTON_UP, |
||
202 | button |
||
203 | }; |
||
204 | con_printf(CON_DEBUG, "Sending event %s, button %d", (jbe->type == SDL_JOYBUTTONDOWN) ? "EVENT_JOYSTICK_BUTTON_DOWN" : "EVENT_JOYSTICK_JOYSTICK_UP", event.button); |
||
205 | return event_send(event); |
||
206 | } |
||
207 | #endif |
||
208 | |||
209 | #if DXX_MAX_HATS_PER_JOYSTICK |
||
210 | window_event_result joy_hat_handler(const SDL_JoyHatEvent *const jhe) |
||
211 | { |
||
212 | int hat = SDL_Joysticks[jhe->which].hat_map()[jhe->hat]; |
||
213 | window_event_result highest_result(window_event_result::ignored); |
||
214 | //Save last state of the hat-button |
||
215 | |||
216 | //get current state of the hat-button |
||
217 | const auto jhe_value = jhe->value; |
||
218 | /* Every value must have exactly one bit set, and the union must |
||
219 | * cover the lower four bits. If any of these assertions fail, the |
||
220 | * loop will not work right. |
||
221 | */ |
||
222 | #define assert_hat_one_bit(M) \ |
||
223 | static_assert(!((SDL_HAT_##M) & ((SDL_HAT_##M) - 1)), "unexpected " #M " mask"); |
||
224 | assert_hat_one_bit(UP); |
||
225 | assert_hat_one_bit(RIGHT); |
||
226 | assert_hat_one_bit(DOWN); |
||
227 | assert_hat_one_bit(LEFT); |
||
228 | #undef assert_hat_one_bit |
||
229 | static_assert((SDL_HAT_UP | SDL_HAT_RIGHT | SDL_HAT_DOWN | SDL_HAT_LEFT) == 0xf, "unexpected hat mask"); |
||
230 | |||
231 | //determine if a hat-button up or down event based on state and last_state |
||
232 | range_for (const unsigned i, xrange(4u)) |
||
233 | { |
||
234 | const auto current_button_state = !!(jhe_value & (1 << i)); |
||
235 | auto &saved_button_state = Joystick.button_state[hat + i]; |
||
236 | if (saved_button_state == current_button_state) |
||
237 | // Same state as before |
||
238 | continue; |
||
239 | saved_button_state = current_button_state; |
||
240 | const d_event_joystickbutton event{current_button_state ? EVENT_JOYSTICK_BUTTON_DOWN : EVENT_JOYSTICK_BUTTON_UP, hat + i}; |
||
241 | if (current_button_state) //last_state up, current state down |
||
242 | { |
||
243 | con_printf(CON_DEBUG, "Sending event EVENT_JOYSTICK_BUTTON_DOWN, button %d", event.button); |
||
244 | } |
||
245 | else //last_state down, current state up |
||
246 | { |
||
247 | con_printf(CON_DEBUG, "Sending event EVENT_JOYSTICK_BUTTON_UP, button %d", event.button); |
||
248 | } |
||
249 | highest_result = std::max(event_send(event), highest_result); |
||
250 | } |
||
251 | |||
252 | return highest_result; |
||
253 | } |
||
254 | #endif |
||
255 | |||
256 | #if DXX_MAX_AXES_PER_JOYSTICK |
||
257 | #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK |
||
258 | static window_event_result send_axis_button_event(unsigned button, event_type e) |
||
259 | { |
||
260 | Joystick.button_state[button] = (e == EVENT_JOYSTICK_BUTTON_UP) ? 0 : 1; |
||
261 | const d_event_joystickbutton event{ e, button }; |
||
262 | con_printf(CON_DEBUG, "Sending event %s, button %d", (e == EVENT_JOYSTICK_BUTTON_UP) ? "EVENT_JOYSTICK_BUTTON_UP" : "EVENT_JOYSTICK_BUTTON_DOWN", event.button); |
||
263 | return event_send(event); |
||
264 | } |
||
265 | |||
266 | window_event_result joy_axisbutton_handler(const SDL_JoyAxisEvent *const jae) |
||
267 | { |
||
268 | auto &js = SDL_Joysticks[jae->which]; |
||
269 | auto axis_value = js.axis_value()[jae->axis]; |
||
270 | auto button = js.axis_button_map()[jae->axis]; |
||
271 | window_event_result highest_result(window_event_result::ignored); |
||
272 | |||
273 | // We have to hardcode a deadzone here. It's not mapped into the settings. |
||
274 | // We could add another deadzone slider called "axis button deadzone". |
||
275 | // I think it's safe to assume a 30% deadzone on analog button presses for now. |
||
276 | const decltype(axis_value) deadzone = 38; |
||
277 | auto prev_value = apply_deadzone(axis_value, deadzone); |
||
278 | auto new_value = apply_deadzone(jae->value/256, deadzone); |
||
279 | |||
280 | if (prev_value <= 0 && new_value >= 0) // positive pressed |
||
281 | { |
||
282 | if (prev_value < 0) // Do previous direction release first if the case |
||
283 | highest_result = std::max(send_axis_button_event(button + 1, EVENT_JOYSTICK_BUTTON_UP), highest_result); |
||
284 | if (new_value > 0) |
||
285 | highest_result = std::max(send_axis_button_event(button, EVENT_JOYSTICK_BUTTON_DOWN), highest_result); |
||
286 | } |
||
287 | else if (prev_value >= 0 && new_value <= 0) // negative pressed |
||
288 | { |
||
289 | if (prev_value > 0) // Do previous direction release first if the case |
||
290 | highest_result = std::max(send_axis_button_event(button, EVENT_JOYSTICK_BUTTON_UP), highest_result); |
||
291 | if (new_value < 0) |
||
292 | highest_result = std::max(send_axis_button_event(button + 1, EVENT_JOYSTICK_BUTTON_DOWN), highest_result); |
||
293 | } |
||
294 | |||
295 | return highest_result; |
||
296 | } |
||
297 | #endif |
||
298 | |||
299 | window_event_result joy_axis_handler(const SDL_JoyAxisEvent *const jae) |
||
300 | { |
||
301 | auto &js = SDL_Joysticks[jae->which]; |
||
302 | const auto axis = js.axis_map()[jae->axis]; |
||
303 | auto &axis_value = js.axis_value()[jae->axis]; |
||
304 | // inaccurate stick is inaccurate. SDL might send SDL_JoyAxisEvent even if the value is the same as before. |
||
305 | if (axis_value == jae->value/256) |
||
306 | return window_event_result::ignored; |
||
307 | |||
308 | d_event_joystick_moved event{EVENT_JOYSTICK_MOVED}; |
||
309 | event.value = axis_value = jae->value/256; |
||
310 | event.axis = axis; |
||
311 | con_printf(CON_DEBUG, "Sending event EVENT_JOYSTICK_MOVED, axis: %d, value: %d",event.axis, event.value); |
||
312 | |||
313 | return event_send(event); |
||
314 | } |
||
315 | #endif |
||
316 | |||
317 | |||
318 | /* ----------------------------------------------- */ |
||
319 | |||
320 | static unsigned check_warn_joy_support_limit(const unsigned n, const char *const desc, const unsigned MAX) |
||
321 | { |
||
322 | if (n <= MAX) |
||
323 | { |
||
324 | con_printf(CON_NORMAL, "sdl-joystick: %d %ss", n, desc); |
||
325 | return n; |
||
326 | } |
||
327 | Warning("sdl-joystick: found %d %ss, only %d supported.\n", n, desc, MAX); |
||
328 | return MAX; |
||
329 | } |
||
330 | |||
331 | void joy_init() |
||
332 | { |
||
333 | if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { |
||
334 | con_printf(CON_NORMAL, "sdl-joystick: initialisation failed: %s.",SDL_GetError()); |
||
335 | return; |
||
336 | } |
||
337 | |||
338 | Joystick = {}; |
||
339 | #if DXX_MAX_AXES_PER_JOYSTICK |
||
340 | joyaxis_text.clear(); |
||
341 | #endif |
||
342 | joybutton_text.clear(); |
||
343 | joy_key_map.clear(); |
||
344 | |||
345 | const auto n = check_warn_joy_support_limit(SDL_NumJoysticks(), "joystick", DXX_MAX_JOYSTICKS); |
||
346 | cf_assert(n <= DXX_MAX_JOYSTICKS); |
||
347 | unsigned joystick_n_buttons = 0, joystick_n_axes = 0; |
||
348 | range_for (const unsigned i, xrange(n)) |
||
349 | { |
||
350 | auto &joystick = SDL_Joysticks[num_joysticks]; |
||
351 | const auto handle = SDL_JoystickOpen(i); |
||
352 | joystick.handle().reset(handle); |
||
353 | #if SDL_MAJOR_VERSION == 1 |
||
354 | con_printf(CON_NORMAL, "sdl-joystick %d: %s", i, SDL_JoystickName(i)); |
||
355 | #else |
||
356 | con_printf(CON_NORMAL, "sdl-joystick %d: %s", i, SDL_JoystickName(handle)); |
||
357 | #endif |
||
358 | if (handle) |
||
359 | { |
||
360 | #if DXX_MAX_AXES_PER_JOYSTICK |
||
361 | const auto n_axes = check_warn_joy_support_limit(SDL_JoystickNumAxes(handle), "axe", DXX_MAX_AXES_PER_JOYSTICK); |
||
362 | |||
363 | joyaxis_text.resize(joyaxis_text.size() + n_axes); |
||
364 | range_for (auto &&e, enumerate(partial_range(joystick.axis_map(), n_axes), 1)) |
||
365 | { |
||
366 | cf_assert(e.idx <= DXX_MAX_AXES_PER_JOYSTICK); |
||
367 | auto &text = joyaxis_text[joystick_n_axes]; |
||
368 | e.value = joystick_n_axes++; |
||
369 | snprintf(&text[0], sizeof(text), "J%d A%u", i + 1, e.idx); |
||
370 | } |
||
371 | #else |
||
372 | const auto n_axes = 0; |
||
373 | #endif |
||
374 | |||
375 | const auto n_buttons = check_warn_joy_support_limit(SDL_JoystickNumButtons(handle), "button", DXX_MAX_BUTTONS_PER_JOYSTICK); |
||
376 | const auto n_hats = check_warn_joy_support_limit(SDL_JoystickNumHats(handle), "hat", DXX_MAX_HATS_PER_JOYSTICK); |
||
377 | |||
378 | const auto n_virtual_buttons = n_buttons + (4 * n_hats) + (2 * n_axes); |
||
379 | joybutton_text.resize(joybutton_text.size() + n_virtual_buttons); |
||
380 | joy_key_map.resize(joy_key_map.size() + n_virtual_buttons, 0); |
||
381 | #if DXX_MAX_BUTTONS_PER_JOYSTICK |
||
382 | range_for (auto &&e, enumerate(partial_range(joystick.button_map(), n_buttons), 1)) |
||
383 | { |
||
384 | switch (e.idx) { |
||
385 | case 1: joy_key_map[joystick_n_buttons] = KEY_ENTER; break; |
||
386 | case 2: joy_key_map[joystick_n_buttons] = KEY_ESC; break; |
||
387 | case 3: joy_key_map[joystick_n_buttons] = KEY_SPACEBAR; break; |
||
388 | case 4: joy_key_map[joystick_n_buttons] = KEY_DELETE; break; |
||
389 | default: break; |
||
390 | } |
||
391 | auto &text = joybutton_text[joystick_n_buttons]; |
||
392 | e.value = joystick_n_buttons++; |
||
393 | cf_assert(e.idx <= DXX_MAX_BUTTONS_PER_JOYSTICK); |
||
394 | snprintf(&text[0], sizeof(text), "J%u B%u", i + 1, e.idx); |
||
395 | } |
||
396 | #endif |
||
397 | #if DXX_MAX_HATS_PER_JOYSTICK |
||
398 | range_for (auto &&e, enumerate(partial_range(joystick.hat_map(), n_hats), 1)) |
||
399 | { |
||
400 | e.value = joystick_n_buttons; |
||
401 | cf_assert(e.idx <= DXX_MAX_HATS_PER_JOYSTICK); |
||
402 | //a hat counts as four buttons |
||
403 | joy_key_map[joystick_n_buttons] = KEY_UP; |
||
404 | snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0202); |
||
405 | joy_key_map[joystick_n_buttons] = KEY_RIGHT; |
||
406 | snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0177); |
||
407 | joy_key_map[joystick_n_buttons] = KEY_DOWN; |
||
408 | snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0200); |
||
409 | joy_key_map[joystick_n_buttons] = KEY_LEFT; |
||
410 | snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0201); |
||
411 | } |
||
412 | #endif |
||
413 | #if DXX_MAX_AXES_PER_JOYSTICK |
||
414 | range_for (auto &&e, enumerate(partial_range(joystick.axis_button_map(), n_axes), 1)) |
||
415 | { |
||
416 | e.value = joystick_n_buttons; |
||
417 | cf_assert(e.idx <= DXX_MAX_AXES_PER_JOYSTICK); |
||
418 | //an axis count as 2 buttons. negative - and positive + |
||
419 | joy_key_map[joystick_n_buttons] = (e.idx == 1) ? KEY_RIGHT : (e.idx == 2) ? KEY_DOWN : 0; |
||
420 | snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u -A%u", i + 1, e.idx); |
||
421 | joy_key_map[joystick_n_buttons] = (e.idx == 1) ? KEY_LEFT : (e.idx == 2) ? KEY_UP : 0; |
||
422 | snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u +A%u", i + 1, e.idx); |
||
423 | } |
||
424 | #endif |
||
425 | |||
426 | num_joysticks++; |
||
427 | } |
||
428 | else |
||
429 | con_puts(CON_NORMAL, "sdl-joystick: initialization failed!"); |
||
430 | |||
431 | con_printf(CON_NORMAL, "sdl-joystick: %d axes (total)", joystick_n_axes); |
||
432 | con_printf(CON_NORMAL, "sdl-joystick: %d buttons (total)", joystick_n_buttons); |
||
433 | } |
||
434 | } |
||
435 | |||
436 | void joy_close() |
||
437 | { |
||
438 | range_for (auto &j, SDL_Joysticks) |
||
439 | j.handle().reset(); |
||
440 | #if DXX_MAX_AXES_PER_JOYSTICK |
||
441 | joyaxis_text.clear(); |
||
442 | #endif |
||
443 | joybutton_text.clear(); |
||
444 | } |
||
445 | |||
446 | const d_event_joystick_axis_value &event_joystick_get_axis(const d_event &event) |
||
447 | { |
||
448 | auto &e = static_cast<const d_event_joystick_moved &>(event); |
||
449 | Assert(e.type == EVENT_JOYSTICK_MOVED); |
||
450 | return e; |
||
451 | } |
||
452 | |||
453 | void joy_flush() |
||
454 | { |
||
455 | if (!num_joysticks) |
||
456 | return; |
||
457 | |||
458 | static_assert(SDL_RELEASED == uint8_t(), "SDL_RELEASED not 0."); |
||
459 | #if DXX_MAX_BUTTONS_PER_JOYSTICK |
||
460 | Joystick.button_state = {}; |
||
461 | #endif |
||
462 | #if DXX_MAX_AXES_PER_JOYSTICK |
||
463 | range_for (auto &j, SDL_Joysticks) |
||
464 | j.axis_value() = {}; |
||
465 | #endif |
||
466 | } |
||
467 | |||
468 | int event_joystick_get_button(const d_event &event) |
||
469 | { |
||
470 | auto &e = static_cast<const d_event_joystickbutton &>(event); |
||
471 | Assert(e.type == EVENT_JOYSTICK_BUTTON_DOWN || e.type == EVENT_JOYSTICK_BUTTON_UP); |
||
472 | return e.button; |
||
473 | } |
||
474 | |||
475 | int apply_deadzone(int value, int deadzone) |
||
476 | { |
||
477 | if (value > deadzone) |
||
478 | return ((value - deadzone) * 128) / (128 - deadzone); |
||
479 | else if (value < -deadzone) |
||
480 | return ((value + deadzone) * 128) / (128 - deadzone); |
||
481 | else |
||
482 | return 0; |
||
483 | } |
||
484 | |||
485 | bool joy_translate_menu_key(const d_event &event) { |
||
486 | if (event.type != EVENT_JOYSTICK_BUTTON_DOWN) |
||
487 | return false; |
||
488 | #if DXX_MAX_JOYSTICKS |
||
489 | auto &e = static_cast<const d_event_joystickbutton &>(event); |
||
490 | assert(e.button < joy_key_map.size()); |
||
491 | auto key = joy_key_map[e.button]; |
||
492 | if (key) |
||
493 | { |
||
494 | event_keycommand_send(key); |
||
495 | return true; |
||
496 | } |
||
497 | return false; |
||
498 | #endif |
||
499 | } |
||
500 | |||
501 | } |
||
502 | #endif |