2014-06-01 17:55:23 +00:00
|
|
|
/*
|
2018-09-02 00:57:29 +00:00
|
|
|
* This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
|
2014-06-01 17:55:23 +00:00
|
|
|
* It is copyright by its individual contributors, as recorded in the
|
|
|
|
* project's Git history. See COPYING.txt at the top level for license
|
|
|
|
* terms and a link to the Git history.
|
|
|
|
*/
|
2006-07-27 09:46:44 +00:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* SDL joystick support
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2015-11-01 21:15:39 +00:00
|
|
|
#include <memory>
|
|
|
|
#include <tuple>
|
2017-06-25 20:46:03 +00:00
|
|
|
#include <type_traits>
|
2006-07-27 09:46:44 +00:00
|
|
|
#include "joy.h"
|
2012-07-07 18:35:06 +00:00
|
|
|
#include "dxxerror.h"
|
2006-07-27 09:46:44 +00:00
|
|
|
#include "timer.h"
|
|
|
|
#include "console.h"
|
|
|
|
#include "event.h"
|
|
|
|
#include "u_mem.h"
|
2008-04-13 00:28:36 +00:00
|
|
|
#include "playsave.h"
|
2012-11-11 00:12:17 +00:00
|
|
|
#include "kconfig.h"
|
2015-09-29 10:02:49 +00:00
|
|
|
#include "compiler-range_for.h"
|
2006-07-27 09:46:44 +00:00
|
|
|
|
2016-09-25 04:52:48 +00:00
|
|
|
#if DXX_MAX_JOYSTICKS
|
2018-08-26 18:10:36 +00:00
|
|
|
#include "compiler-cf_assert.h"
|
2015-11-01 21:15:40 +00:00
|
|
|
#include "compiler-integer_sequence.h"
|
2016-11-19 17:24:52 +00:00
|
|
|
#include "d_enumerate.h"
|
2019-05-04 18:27:36 +00:00
|
|
|
#include "d_range.h"
|
2016-11-19 17:24:52 +00:00
|
|
|
#include "partial_range.h"
|
2015-11-01 21:15:40 +00:00
|
|
|
|
2015-12-13 18:00:49 +00:00
|
|
|
namespace dcx {
|
2015-12-05 22:57:24 +00:00
|
|
|
|
2015-08-12 03:11:46 +00:00
|
|
|
namespace {
|
|
|
|
|
2006-07-27 09:46:44 +00:00
|
|
|
int num_joysticks = 0;
|
|
|
|
|
|
|
|
/* This struct is a "virtual" joystick, which includes all the axes
|
|
|
|
* and buttons of every joystick found.
|
|
|
|
*/
|
|
|
|
static struct joyinfo {
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_BUTTONS_PER_JOYSTICK
|
2015-09-19 23:04:34 +00:00
|
|
|
array<uint8_t, JOY_MAX_BUTTONS> button_state;
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2006-07-27 09:46:44 +00:00
|
|
|
} Joystick;
|
|
|
|
|
2014-10-04 18:05:26 +00:00
|
|
|
struct d_event_joystickbutton : d_event
|
2011-02-02 00:36:43 +00:00
|
|
|
{
|
2018-05-12 18:24:19 +00:00
|
|
|
const unsigned button;
|
|
|
|
constexpr d_event_joystickbutton(const event_type t, const unsigned b) :
|
|
|
|
d_event(t), button(b)
|
|
|
|
{
|
|
|
|
}
|
2013-12-22 22:03:07 +00:00
|
|
|
};
|
2011-02-02 00:36:43 +00:00
|
|
|
|
2015-09-19 23:04:34 +00:00
|
|
|
struct d_event_joystick_moved : d_event, d_event_joystick_axis_value
|
2011-02-02 00:36:43 +00:00
|
|
|
{
|
2018-05-12 18:24:19 +00:00
|
|
|
DXX_INHERIT_CONSTRUCTORS(d_event_joystick_moved, d_event);
|
2013-12-22 22:03:07 +00:00
|
|
|
};
|
2011-02-02 00:36:43 +00:00
|
|
|
|
2015-09-29 02:41:22 +00:00
|
|
|
class SDL_Joystick_deleter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void operator()(SDL_Joystick *j) const
|
|
|
|
{
|
|
|
|
SDL_JoystickClose(j);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-11-01 21:15:40 +00:00
|
|
|
#ifndef DXX_USE_SIZE_SORTED_TUPLE
|
|
|
|
#define DXX_USE_SIZE_SORTED_TUPLE (__cplusplus > 201103L)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if DXX_USE_SIZE_SORTED_TUPLE
|
|
|
|
template <typename T, std::size_t... Is, std::size_t... Js>
|
|
|
|
auto d_split_tuple(T &&t, index_sequence<Is...>, index_sequence<Js...>) ->
|
|
|
|
std::pair<
|
|
|
|
std::tuple<typename std::tuple_element<Is, T>::type...>,
|
|
|
|
std::tuple<typename std::tuple_element<sizeof...(Is) + Js, T>::type...>
|
|
|
|
>;
|
|
|
|
|
|
|
|
template <typename>
|
|
|
|
class d_size_sorted;
|
|
|
|
|
|
|
|
/* Given an input tuple T, define a public member `type` with the same
|
|
|
|
* members as T, but sorted such that sizeof(Ti) <= sizeof(Tj) for all i
|
|
|
|
* <= j.
|
|
|
|
*/
|
|
|
|
template <typename... Ts>
|
|
|
|
class d_size_sorted<std::tuple<Ts...>>
|
|
|
|
{
|
|
|
|
using split_tuple = decltype(d_split_tuple(
|
|
|
|
std::declval<std::tuple<Ts...>>(),
|
|
|
|
make_tree_index_sequence<sizeof...(Ts) / 2>(),
|
|
|
|
make_tree_index_sequence<(1 + sizeof...(Ts)) / 2>()
|
|
|
|
));
|
|
|
|
using first_type = typename split_tuple::first_type;
|
|
|
|
using second_type = typename split_tuple::second_type;
|
|
|
|
public:
|
2016-11-05 21:22:43 +00:00
|
|
|
using type = typename std::conditional<(sizeof(first_type) < sizeof(second_type)),
|
2015-11-01 21:15:40 +00:00
|
|
|
decltype(std::tuple_cat(std::declval<first_type>(), std::declval<second_type>())),
|
|
|
|
decltype(std::tuple_cat(std::declval<second_type>(), std::declval<first_type>()))
|
|
|
|
>::type;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T1>
|
|
|
|
class d_size_sorted<std::tuple<T1>>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using type = std::tuple<T1>;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2015-11-01 21:15:40 +00:00
|
|
|
template <typename>
|
|
|
|
class ignore_empty {};
|
|
|
|
|
|
|
|
template <typename T>
|
2016-11-05 21:22:43 +00:00
|
|
|
using maybe_empty_array = typename std::conditional<T().empty(), ignore_empty<T>, T>::type;
|
2015-11-01 21:15:40 +00:00
|
|
|
|
2006-07-27 09:46:44 +00:00
|
|
|
/* This struct is an array, with one entry for each physical joystick
|
|
|
|
* found.
|
|
|
|
*/
|
2015-11-01 21:15:39 +00:00
|
|
|
class d_physical_joystick
|
|
|
|
{
|
2016-11-05 21:22:43 +00:00
|
|
|
#define for_each_tuple_item(HANDLE_VERB,VERB) \
|
|
|
|
HANDLE_VERB(handle) \
|
2015-11-01 21:15:39 +00:00
|
|
|
VERB(hat_map) \
|
|
|
|
VERB(button_map) \
|
|
|
|
VERB(axis_map) \
|
|
|
|
VERB(axis_value) \
|
2019-09-01 14:14:02 +00:00
|
|
|
VERB(axis_button_map) \
|
2015-11-01 21:15:39 +00:00
|
|
|
|
2015-11-01 21:15:40 +00:00
|
|
|
#if DXX_USE_SIZE_SORTED_TUPLE
|
|
|
|
template <typename... Ts>
|
|
|
|
using tuple_type = typename d_size_sorted<std::tuple<Ts...>>::type;
|
2016-11-05 21:22:43 +00:00
|
|
|
#define define_handle_getter(N) \
|
|
|
|
define_getter(N, tuple_member_type_##N)
|
|
|
|
#define define_array_getter(N) \
|
|
|
|
define_getter(N, maybe_empty_array<tuple_member_type_##N>)
|
2015-11-01 21:15:40 +00:00
|
|
|
#else
|
|
|
|
template <typename... Ts>
|
|
|
|
using tuple_type = std::tuple<Ts...>;
|
2015-11-01 21:15:39 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
#define define_enum(V) tuple_item_##V,
|
2016-11-05 21:22:43 +00:00
|
|
|
for_each_tuple_item(define_enum, define_enum)
|
2015-11-01 21:15:39 +00:00
|
|
|
#undef define_enum
|
|
|
|
};
|
2016-11-05 21:22:43 +00:00
|
|
|
/* std::get<index> does not handle the maybe_empty_array case, so
|
|
|
|
* reuse the same getter for both handle and array.
|
|
|
|
*/
|
|
|
|
#define define_handle_getter(N) \
|
|
|
|
define_getter(N, tuple_item_##N)
|
|
|
|
#define define_array_getter define_handle_getter
|
|
|
|
#endif
|
|
|
|
#define define_getter(N,V) \
|
|
|
|
auto &N() \
|
2015-11-01 21:15:39 +00:00
|
|
|
{ \
|
2016-11-05 21:22:43 +00:00
|
|
|
return std::get<V>(t); \
|
2015-11-01 21:15:39 +00:00
|
|
|
}
|
|
|
|
using tuple_member_type_handle = std::unique_ptr<SDL_Joystick, SDL_Joystick_deleter>;
|
|
|
|
//Note: Descent expects hats to be buttons, so these are indices into Joystick.buttons
|
2016-09-25 04:52:49 +00:00
|
|
|
struct tuple_member_type_hat_map : array<unsigned, DXX_MAX_HATS_PER_JOYSTICK> {};
|
2016-09-25 04:52:49 +00:00
|
|
|
struct tuple_member_type_button_map : array<unsigned, DXX_MAX_BUTTONS_PER_JOYSTICK> {};
|
2016-09-25 04:52:49 +00:00
|
|
|
struct tuple_member_type_axis_map : array<unsigned, DXX_MAX_AXES_PER_JOYSTICK> {};
|
|
|
|
struct tuple_member_type_axis_value : array<int, DXX_MAX_AXES_PER_JOYSTICK> {};
|
2019-09-01 14:14:02 +00:00
|
|
|
struct tuple_member_type_axis_button_map : array<unsigned, DXX_MAX_AXES_PER_JOYSTICK> {};
|
2015-11-01 21:15:40 +00:00
|
|
|
tuple_type<
|
2015-11-01 21:15:39 +00:00
|
|
|
tuple_member_type_handle,
|
2015-11-01 21:15:40 +00:00
|
|
|
maybe_empty_array<tuple_member_type_hat_map>,
|
|
|
|
maybe_empty_array<tuple_member_type_button_map>,
|
|
|
|
maybe_empty_array<tuple_member_type_axis_map>,
|
2019-09-01 14:14:02 +00:00
|
|
|
maybe_empty_array<tuple_member_type_axis_value>,
|
|
|
|
maybe_empty_array<tuple_member_type_axis_button_map>
|
2015-11-01 21:15:39 +00:00
|
|
|
> t;
|
|
|
|
public:
|
2016-11-05 21:22:43 +00:00
|
|
|
for_each_tuple_item(define_handle_getter, define_array_getter);
|
|
|
|
#undef define_handle_getter
|
|
|
|
#undef define_array_getter
|
2015-11-01 21:15:39 +00:00
|
|
|
#undef define_getter
|
|
|
|
#undef for_each_tuple_item
|
2015-08-12 03:11:46 +00:00
|
|
|
};
|
|
|
|
|
2015-08-12 03:11:46 +00:00
|
|
|
}
|
|
|
|
|
2016-09-25 04:52:48 +00:00
|
|
|
static array<d_physical_joystick, DXX_MAX_JOYSTICKS> SDL_Joysticks;
|
2006-07-27 09:46:44 +00:00
|
|
|
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_BUTTONS_PER_JOYSTICK
|
2016-10-28 06:43:20 +00:00
|
|
|
window_event_result joy_button_handler(SDL_JoyButtonEvent *jbe)
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2018-05-12 18:24:19 +00:00
|
|
|
const unsigned button = SDL_Joysticks[jbe->which].button_map()[jbe->button];
|
2006-07-27 09:46:44 +00:00
|
|
|
|
2011-02-02 00:36:43 +00:00
|
|
|
Joystick.button_state[button] = jbe->state;
|
2006-07-27 09:46:44 +00:00
|
|
|
|
2018-05-12 18:24:19 +00:00
|
|
|
const d_event_joystickbutton event{
|
|
|
|
(jbe->type == SDL_JOYBUTTONDOWN) ? EVENT_JOYSTICK_BUTTON_DOWN : EVENT_JOYSTICK_BUTTON_UP,
|
|
|
|
button
|
|
|
|
};
|
2013-12-07 00:47:27 +00:00
|
|
|
con_printf(CON_DEBUG, "Sending event %s, button %d", (jbe->type == SDL_JOYBUTTONDOWN) ? "EVENT_JOYSTICK_BUTTON_DOWN" : "EVENT_JOYSTICK_JOYSTICK_UP", event.button);
|
2016-10-28 06:43:20 +00:00
|
|
|
return event_send(event);
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2006-07-27 09:46:44 +00:00
|
|
|
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_HATS_PER_JOYSTICK
|
2016-10-28 06:43:20 +00:00
|
|
|
window_event_result joy_hat_handler(SDL_JoyHatEvent *jhe)
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2015-11-01 21:15:39 +00:00
|
|
|
int hat = SDL_Joysticks[jhe->which].hat_map()[jhe->hat];
|
2016-10-28 06:43:20 +00:00
|
|
|
window_event_result highest_result(window_event_result::ignored);
|
2006-07-27 09:46:44 +00:00
|
|
|
//Save last state of the hat-button
|
|
|
|
|
|
|
|
//get current state of the hat-button
|
2015-09-19 23:04:34 +00:00
|
|
|
const auto jhe_value = jhe->value;
|
|
|
|
/* Every value must have exactly one bit set, and the union must
|
|
|
|
* cover the lower four bits. If any of these assertions fail, the
|
|
|
|
* loop will not work right.
|
|
|
|
*/
|
|
|
|
#define assert_hat_one_bit(M) \
|
|
|
|
static_assert(!((SDL_HAT_##M) & ((SDL_HAT_##M) - 1)), "unexpected " #M " mask");
|
|
|
|
assert_hat_one_bit(UP);
|
|
|
|
assert_hat_one_bit(RIGHT);
|
|
|
|
assert_hat_one_bit(DOWN);
|
|
|
|
assert_hat_one_bit(LEFT);
|
|
|
|
#undef assert_hat_one_bit
|
|
|
|
static_assert((SDL_HAT_UP | SDL_HAT_RIGHT | SDL_HAT_DOWN | SDL_HAT_LEFT) == 0xf, "unexpected hat mask");
|
2006-07-27 09:46:44 +00:00
|
|
|
|
|
|
|
//determine if a hat-button up or down event based on state and last_state
|
2019-05-04 18:27:36 +00:00
|
|
|
range_for (const unsigned i, xrange(4u))
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2015-09-19 23:04:34 +00:00
|
|
|
const auto current_button_state = !!(jhe_value & (1 << i));
|
|
|
|
auto &saved_button_state = Joystick.button_state[hat + i];
|
|
|
|
if (saved_button_state == current_button_state)
|
|
|
|
// Same state as before
|
|
|
|
continue;
|
|
|
|
saved_button_state = current_button_state;
|
2018-05-12 18:24:19 +00:00
|
|
|
const d_event_joystickbutton event{current_button_state ? EVENT_JOYSTICK_BUTTON_DOWN : EVENT_JOYSTICK_BUTTON_UP, hat + i};
|
2015-09-19 23:04:34 +00:00
|
|
|
if (current_button_state) //last_state up, current state down
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2013-12-07 00:47:27 +00:00
|
|
|
con_printf(CON_DEBUG, "Sending event EVENT_JOYSTICK_BUTTON_DOWN, button %d", event.button);
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2015-09-19 23:04:34 +00:00
|
|
|
else //last_state down, current state up
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2013-12-07 00:47:27 +00:00
|
|
|
con_printf(CON_DEBUG, "Sending event EVENT_JOYSTICK_BUTTON_UP, button %d", event.button);
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2016-10-28 06:43:20 +00:00
|
|
|
highest_result = std::max(event_send(event), highest_result);
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2016-10-28 06:43:20 +00:00
|
|
|
|
|
|
|
return highest_result;
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2006-07-27 09:46:44 +00:00
|
|
|
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_AXES_PER_JOYSTICK
|
2019-09-01 14:14:02 +00:00
|
|
|
static window_event_result send_axis_button_event(unsigned button, event_type e)
|
|
|
|
{
|
|
|
|
Joystick.button_state[button] = (e == EVENT_JOYSTICK_BUTTON_UP) ? 0 : 1;
|
|
|
|
const d_event_joystickbutton event{ e, button };
|
|
|
|
con_printf(CON_DEBUG, "Sending event %s, button %d", (e == EVENT_JOYSTICK_BUTTON_UP) ? "EVENT_JOYSTICK_BUTTON_UP" : "EVENT_JOYSTICK_BUTTON_DOWN", event.button);
|
|
|
|
return event_send(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
window_event_result joy_axisbutton_handler(SDL_JoyAxisEvent *jae)
|
|
|
|
{
|
|
|
|
auto &js = SDL_Joysticks[jae->which];
|
|
|
|
auto axis_value = js.axis_value()[jae->axis];
|
|
|
|
auto button = js.axis_button_map()[jae->axis];
|
|
|
|
window_event_result highest_result(window_event_result::ignored);
|
|
|
|
|
|
|
|
// We have to hardcode a deadzone here. It's not mapped into the settings.
|
|
|
|
// We could add another deadzone slider called "axis button deadzone".
|
|
|
|
// I think it's safe to assume a 30% deadzone on analog button presses for now.
|
|
|
|
const decltype(axis_value) deadzone = 38;
|
|
|
|
auto prev_value = apply_deadzone(axis_value, deadzone);
|
|
|
|
auto new_value = apply_deadzone(jae->value/256, deadzone);
|
|
|
|
|
|
|
|
if (prev_value <= 0 && new_value >= 0) // positive pressed
|
|
|
|
{
|
|
|
|
if (prev_value < 0) // Do previous direction release first if the case
|
|
|
|
highest_result = std::max(send_axis_button_event(button + 1, EVENT_JOYSTICK_BUTTON_UP), highest_result);
|
|
|
|
if (new_value > 0)
|
|
|
|
highest_result = std::max(send_axis_button_event(button, EVENT_JOYSTICK_BUTTON_DOWN), highest_result);
|
|
|
|
}
|
|
|
|
else if (prev_value >= 0 && new_value <= 0) // negative pressed
|
|
|
|
{
|
|
|
|
if (prev_value > 0) // Do previous direction release first if the case
|
|
|
|
highest_result = std::max(send_axis_button_event(button, EVENT_JOYSTICK_BUTTON_UP), highest_result);
|
|
|
|
if (new_value < 0)
|
|
|
|
highest_result = std::max(send_axis_button_event(button + 1, EVENT_JOYSTICK_BUTTON_DOWN), highest_result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return highest_result;
|
|
|
|
}
|
|
|
|
|
2016-10-28 06:43:20 +00:00
|
|
|
window_event_result joy_axis_handler(SDL_JoyAxisEvent *jae)
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2015-09-19 23:04:35 +00:00
|
|
|
auto &js = SDL_Joysticks[jae->which];
|
2015-11-01 21:15:39 +00:00
|
|
|
const auto axis = js.axis_map()[jae->axis];
|
|
|
|
auto &axis_value = js.axis_value()[jae->axis];
|
2011-02-02 00:36:43 +00:00
|
|
|
// inaccurate stick is inaccurate. SDL might send SDL_JoyAxisEvent even if the value is the same as before.
|
2015-09-19 23:04:35 +00:00
|
|
|
if (axis_value == jae->value/256)
|
2016-10-28 06:43:20 +00:00
|
|
|
return window_event_result::ignored;
|
2011-02-02 00:36:43 +00:00
|
|
|
|
2018-05-12 18:24:19 +00:00
|
|
|
d_event_joystick_moved event{EVENT_JOYSTICK_MOVED};
|
2015-09-19 23:04:35 +00:00
|
|
|
event.value = axis_value = jae->value/256;
|
2011-02-02 00:36:43 +00:00
|
|
|
event.axis = axis;
|
2013-12-07 00:47:27 +00:00
|
|
|
con_printf(CON_DEBUG, "Sending event EVENT_JOYSTICK_MOVED, axis: %d, value: %d",event.axis, event.value);
|
2011-02-02 00:36:43 +00:00
|
|
|
|
2016-10-28 06:43:20 +00:00
|
|
|
return event_send(event);
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2006-07-27 09:46:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------- */
|
|
|
|
|
2016-10-29 23:16:15 +00:00
|
|
|
static unsigned check_warn_joy_support_limit(const unsigned n, const char *const desc, const unsigned MAX)
|
2015-07-14 02:42:12 +00:00
|
|
|
{
|
|
|
|
if (n <= MAX)
|
2015-09-13 21:02:19 +00:00
|
|
|
{
|
|
|
|
con_printf(CON_NORMAL, "sdl-joystick: %d %ss", n, desc);
|
2015-07-14 02:42:12 +00:00
|
|
|
return n;
|
2015-09-13 21:02:19 +00:00
|
|
|
}
|
2015-07-14 02:42:12 +00:00
|
|
|
Warning("sdl-joystick: found %d %ss, only %d supported.\n", n, desc, MAX);
|
|
|
|
return MAX;
|
|
|
|
}
|
|
|
|
|
2010-02-14 18:48:02 +00:00
|
|
|
void joy_init()
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
|
|
|
if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
|
2008-04-06 20:23:28 +00:00
|
|
|
con_printf(CON_NORMAL, "sdl-joystick: initialisation failed: %s.",SDL_GetError());
|
2010-02-14 18:48:02 +00:00
|
|
|
return;
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
|
|
|
|
2014-07-04 03:43:01 +00:00
|
|
|
Joystick = {};
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_AXES_PER_JOYSTICK
|
2013-11-09 23:17:28 +00:00
|
|
|
joyaxis_text.clear();
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2019-09-01 14:14:02 +00:00
|
|
|
#if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK || DXX_MAX_AXES_PER_JOYSTICK
|
2013-11-09 23:31:19 +00:00
|
|
|
joybutton_text.clear();
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2006-07-27 09:46:44 +00:00
|
|
|
|
2016-10-29 23:16:15 +00:00
|
|
|
const auto n = check_warn_joy_support_limit(SDL_NumJoysticks(), "joystick", DXX_MAX_JOYSTICKS);
|
2019-05-06 00:36:16 +00:00
|
|
|
cf_assert(n <= DXX_MAX_JOYSTICKS);
|
2015-09-29 02:41:22 +00:00
|
|
|
unsigned joystick_n_buttons = 0, joystick_n_axes = 0;
|
2019-05-06 00:36:16 +00:00
|
|
|
range_for (const unsigned i, xrange(n))
|
2018-08-26 18:10:36 +00:00
|
|
|
{
|
2015-07-14 02:42:12 +00:00
|
|
|
auto &joystick = SDL_Joysticks[num_joysticks];
|
2015-09-29 02:41:22 +00:00
|
|
|
const auto handle = SDL_JoystickOpen(i);
|
2015-11-01 21:15:39 +00:00
|
|
|
joystick.handle().reset(handle);
|
2015-07-14 02:42:12 +00:00
|
|
|
#if SDL_MAJOR_VERSION == 1
|
2013-12-07 00:47:27 +00:00
|
|
|
con_printf(CON_NORMAL, "sdl-joystick %d: %s", i, SDL_JoystickName(i));
|
2015-07-14 02:42:12 +00:00
|
|
|
#else
|
|
|
|
con_printf(CON_NORMAL, "sdl-joystick %d: %s", i, SDL_JoystickName(handle));
|
|
|
|
#endif
|
2015-07-14 02:42:12 +00:00
|
|
|
if (handle)
|
|
|
|
{
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_AXES_PER_JOYSTICK
|
2016-10-29 23:16:15 +00:00
|
|
|
const auto n_axes = check_warn_joy_support_limit(SDL_JoystickNumAxes(handle), "axe", DXX_MAX_AXES_PER_JOYSTICK);
|
2015-07-14 02:42:12 +00:00
|
|
|
|
|
|
|
joyaxis_text.resize(joyaxis_text.size() + n_axes);
|
2016-11-19 17:24:52 +00:00
|
|
|
range_for (auto &&e, enumerate(partial_range(joystick.axis_map(), n_axes), 1))
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2018-08-26 18:10:36 +00:00
|
|
|
cf_assert(e.idx <= DXX_MAX_AXES_PER_JOYSTICK);
|
2015-11-01 21:15:39 +00:00
|
|
|
auto &text = joyaxis_text[joystick_n_axes];
|
2016-11-19 17:24:52 +00:00
|
|
|
e.value = joystick_n_axes++;
|
|
|
|
snprintf(&text[0], sizeof(text), "J%d A%u", i + 1, e.idx);
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2019-09-01 14:14:02 +00:00
|
|
|
#else
|
|
|
|
const auto n_axes = 0;
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2015-11-01 21:15:39 +00:00
|
|
|
|
2019-09-01 14:14:02 +00:00
|
|
|
#if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK || DXX_MAX_AXES_PER_JOYSTICK
|
2016-10-29 23:16:15 +00:00
|
|
|
const auto n_buttons = check_warn_joy_support_limit(SDL_JoystickNumButtons(handle), "button", DXX_MAX_BUTTONS_PER_JOYSTICK);
|
|
|
|
const auto n_hats = check_warn_joy_support_limit(SDL_JoystickNumHats(handle), "hat", DXX_MAX_HATS_PER_JOYSTICK);
|
2015-11-01 21:15:39 +00:00
|
|
|
|
2019-09-01 14:14:02 +00:00
|
|
|
joybutton_text.resize(joybutton_text.size() + n_buttons + (4 * n_hats) + (2 * n_axes));
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_BUTTONS_PER_JOYSTICK
|
2016-11-19 17:24:52 +00:00
|
|
|
range_for (auto &&e, enumerate(partial_range(joystick.button_map(), n_buttons), 1))
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2015-11-01 21:15:39 +00:00
|
|
|
auto &text = joybutton_text[joystick_n_buttons];
|
2016-11-19 17:24:52 +00:00
|
|
|
e.value = joystick_n_buttons++;
|
2019-04-28 00:53:40 +00:00
|
|
|
cf_assert(e.idx <= DXX_MAX_BUTTONS_PER_JOYSTICK);
|
2019-04-13 18:00:07 +00:00
|
|
|
snprintf(&text[0], sizeof(text), "J%u B%u", i + 1, e.idx);
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_HATS_PER_JOYSTICK
|
2016-11-19 17:24:52 +00:00
|
|
|
range_for (auto &&e, enumerate(partial_range(joystick.hat_map(), n_hats), 1))
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2016-11-19 17:24:52 +00:00
|
|
|
e.value = joystick_n_buttons;
|
2018-08-26 18:10:36 +00:00
|
|
|
cf_assert(e.idx <= DXX_MAX_HATS_PER_JOYSTICK);
|
2006-07-27 09:46:44 +00:00
|
|
|
//a hat counts as four buttons
|
2019-04-13 18:00:07 +00:00
|
|
|
snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0202);
|
|
|
|
snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0177);
|
|
|
|
snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0200);
|
|
|
|
snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u H%u%c", i + 1, e.idx, 0201);
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2019-09-01 14:14:02 +00:00
|
|
|
#if DXX_MAX_AXES_PER_JOYSTICK
|
|
|
|
range_for (auto &&e, enumerate(partial_range(joystick.axis_button_map(), n_axes), 1))
|
|
|
|
{
|
|
|
|
e.value = joystick_n_buttons;
|
|
|
|
cf_assert(e.idx <= DXX_MAX_AXES_PER_JOYSTICK);
|
|
|
|
//an axis count as 2 buttons. negative - and positive +
|
|
|
|
snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u -A%u", i + 1, e.idx);
|
|
|
|
snprintf(&joybutton_text[joystick_n_buttons++][0], sizeof(joybutton_text[0]), "J%u +A%u", i + 1, e.idx);
|
|
|
|
}
|
|
|
|
#endif
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2006-07-27 09:46:44 +00:00
|
|
|
|
|
|
|
num_joysticks++;
|
|
|
|
}
|
|
|
|
else
|
2017-12-05 05:29:55 +00:00
|
|
|
con_puts(CON_NORMAL, "sdl-joystick: initialization failed!");
|
2006-07-27 09:46:44 +00:00
|
|
|
|
2015-09-29 02:41:22 +00:00
|
|
|
con_printf(CON_NORMAL, "sdl-joystick: %d axes (total)", joystick_n_axes);
|
|
|
|
con_printf(CON_NORMAL, "sdl-joystick: %d buttons (total)", joystick_n_buttons);
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void joy_close()
|
|
|
|
{
|
2015-09-29 02:41:22 +00:00
|
|
|
range_for (auto &j, SDL_Joysticks)
|
2015-11-01 21:15:39 +00:00
|
|
|
j.handle().reset();
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_AXES_PER_JOYSTICK
|
2013-11-09 23:17:28 +00:00
|
|
|
joyaxis_text.clear();
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2019-09-01 14:14:02 +00:00
|
|
|
#if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK || DXX_MAX_AXES_PER_JOYSTICK
|
2013-11-09 23:31:19 +00:00
|
|
|
joybutton_text.clear();
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 23:04:34 +00:00
|
|
|
const d_event_joystick_axis_value &event_joystick_get_axis(const d_event &event)
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2014-10-04 21:47:13 +00:00
|
|
|
auto &e = static_cast<const d_event_joystick_moved &>(event);
|
|
|
|
Assert(e.type == EVENT_JOYSTICK_MOVED);
|
2015-09-19 23:04:34 +00:00
|
|
|
return e;
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void joy_flush()
|
|
|
|
{
|
|
|
|
if (!num_joysticks)
|
|
|
|
return;
|
|
|
|
|
2015-09-19 23:04:34 +00:00
|
|
|
static_assert(SDL_RELEASED == uint8_t(), "SDL_RELEASED not 0.");
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_BUTTONS_PER_JOYSTICK
|
2015-09-19 23:04:34 +00:00
|
|
|
Joystick.button_state = {};
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2016-09-25 04:52:49 +00:00
|
|
|
#if DXX_MAX_AXES_PER_JOYSTICK
|
2015-09-29 02:41:22 +00:00
|
|
|
range_for (auto &j, SDL_Joysticks)
|
2015-11-01 21:15:39 +00:00
|
|
|
j.axis_value() = {};
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
|
|
|
|
2014-10-04 21:47:13 +00:00
|
|
|
int event_joystick_get_button(const d_event &event)
|
2006-07-27 09:46:44 +00:00
|
|
|
{
|
2014-10-04 21:47:13 +00:00
|
|
|
auto &e = static_cast<const d_event_joystickbutton &>(event);
|
|
|
|
Assert(e.type == EVENT_JOYSTICK_BUTTON_DOWN || e.type == EVENT_JOYSTICK_BUTTON_UP);
|
|
|
|
return e.button;
|
2006-07-27 09:46:44 +00:00
|
|
|
}
|
2015-12-05 22:57:24 +00:00
|
|
|
|
2019-09-01 14:14:02 +00:00
|
|
|
int apply_deadzone(int value, int deadzone)
|
|
|
|
{
|
|
|
|
if (value > deadzone)
|
|
|
|
return ((value - deadzone) * 128) / (128 - deadzone);
|
|
|
|
else if (value < -deadzone)
|
|
|
|
return ((value + deadzone) * 128) / (128 - deadzone);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-12-05 22:57:24 +00:00
|
|
|
}
|
2015-11-01 21:15:40 +00:00
|
|
|
#endif
|