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