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 |