make menus controllable with joystick

In most menus, keyboard commands are synthesized
from controller buttons, leveraging the existing
axis-to-button translation.

Menu controls are currently fixed:
- button 0 (A) is confirm (Enter)
- button 1 (B) is cancel (Esc)
- button 2 (X) is switch (Space)
- button 3 (Y) is delete (Delete)
- axes 0 and 1 (main analog pad) maps to cursor keys
- all hats (D-pads) map to cursor keys

Title screens and credits can be confirmed
with any joystick button or axis motion too.
This commit is contained in:
Martin Fiedler 2020-06-28 17:08:26 +02:00
parent 096a678ff7
commit c24864b180
9 changed files with 91 additions and 0 deletions

View file

@ -14,6 +14,7 @@
#include <tuple>
#include <type_traits>
#include "joy.h"
#include "key.h"
#include "dxxerror.h"
#include "timer.h"
#include "console.h"
@ -466,5 +467,48 @@ int apply_deadzone(int value, int deadzone)
return 0;
}
bool joy_translate_menu_key(const d_event &event) {
if (event.type != EVENT_JOYSTICK_BUTTON_DOWN)
return false;
#if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK || DXX_MAX_AXES_PER_JOYSTICK
// Find out which button has been pressed by decoding the button
// description text. This might not be the ideal way to do things,
// but we currently don't have data structures that allow us to do
// such a mapping in a saner way, so we'll take what we can get.
auto &e = static_cast<const d_event_joystickbutton &>(event);
Assert((e.button >= 0) && (e.button < joybutton_text.size()));
const char* name = &joybutton_text[e.button][0];
Assert((name[0] == 'J') && name[1] && (name[2] == ' '));
name = &name[3]; // skip 'Jx ' prefix
int key = 0;
if (!strcmp(name, "B1")) { key = KEY_ENTER; }
else if (!strcmp(name, "B2")) { key = KEY_ESC; }
else if (!strcmp(name, "B3")) { key = KEY_SPACEBAR; }
else if (!strcmp(name, "B4")) { key = KEY_DELETE; }
else if (!strcmp(name, "+A1")) { key = KEY_LEFT; }
else if (!strcmp(name, "-A1")) { key = KEY_RIGHT; }
else if (!strcmp(name, "+A2")) { key = KEY_UP; }
else if (!strcmp(name, "-A2")) { key = KEY_DOWN; }
else if (name[0] == 'H')
{ // handle 'Hxx' / hat motion
Assert(name[1]);
switch (name[2])
{
case 0201: key = KEY_LEFT; break;
case 0177: key = KEY_RIGHT; break;
case 0202: key = KEY_UP; break;
case 0200: key = KEY_DOWN; break;
default: break;
}
}
if (key)
{
event_keycommand_send(key);
return true;
}
return false;
#endif
}
}
#endif

View file

@ -587,6 +587,11 @@ void key_flush()
restore_sticky_key(keystate, key);
}
void event_keycommand_send(int key) {
const d_event_keycommand event{EVENT_KEY_COMMAND, unsigned(key)};
event_send(event);
}
int event_key_get(const d_event &event)
{
auto &e = static_cast<const d_event_keycommand &>(event);

View file

@ -36,6 +36,7 @@ extern void joy_init();
extern void joy_close();
const d_event_joystick_axis_value &event_joystick_get_axis(const d_event &event);
extern void joy_flush();
extern bool joy_translate_menu_key(const d_event &event);
extern int event_joystick_get_button(const d_event &event);
extern int apply_deadzone(int value, int deadzone);

View file

@ -61,6 +61,7 @@ extern fix64 keyd_time_when_last_pressed;
extern std::array<unsigned char, KEY_BUFFER_SIZE> unicode_frame_buffer;
extern void key_flush(); // Clears the 256 char buffer
extern void event_keycommand_send(int key); // synthesize a key command event from a keycode
extern int event_key_get(const d_event &event); // Get the keycode from the EVENT_KEY_COMMAND event
extern int event_key_get_raw(const d_event &event); // same as above but without mod states
unsigned char key_ascii();

View file

@ -115,6 +115,9 @@ static window_event_result credits_handler(window *, const d_event &event, credi
}
break;
case EVENT_JOYSTICK_BUTTON_DOWN:
return window_event_result::close;
case EVENT_IDLE:
if (cr->done>NUM_LINES)
{

View file

@ -655,6 +655,9 @@ static window_event_result kconfig_key_command(window *, const d_event &event, k
namespace dsx {
static window_event_result kconfig_handler(window *wind,const d_event &event, kc_menu *menu)
{
if (!menu->changing && joy_translate_menu_key(event))
return window_event_result::handled;
switch (event.type)
{
case EVENT_WINDOW_ACTIVATED:

View file

@ -1491,6 +1491,9 @@ static window_event_result newmenu_draw(window *wind, newmenu *menu)
static window_event_result newmenu_handler(window *wind,const d_event &event, newmenu *menu)
{
if (joy_translate_menu_key(event))
return window_event_result::handled;
if (menu->subfunction)
{
int rval = (*menu->subfunction)(menu, event, menu->userdata);
@ -2110,6 +2113,9 @@ static window_event_result listbox_handler(window *wind,const d_event &event, li
return rval; // event handled
}
if (joy_translate_menu_key(event))
return window_event_result::handled;
switch (event.type)
{
case EVENT_WINDOW_ACTIVATED:

View file

@ -406,6 +406,9 @@ static window_event_result scores_handler(window *wind,const d_event &event, sco
}
break;
case EVENT_JOYSTICK_BUTTON_DOWN:
return window_event_result::close;
case EVENT_IDLE:
timer_delay2(50);
break;

View file

@ -141,6 +141,13 @@ static window_event_result title_handler(window *, const d_event &event, title_s
}
return result;
case EVENT_JOYSTICK_BUTTON_DOWN:
if (ts->allow_keys)
{
return window_event_result::close;
}
break;
case EVENT_IDLE:
timer_delay2(50);
@ -1529,6 +1536,24 @@ static window_event_result briefing_handler(window *, const d_event &event, brie
}
break;
case EVENT_JOYSTICK_BUTTON_DOWN:
// using joy_translate_menu_key doesn't work here for unclear
// reasons, so we build a reasonable facsimile right here
if (event_joystick_get_button(event) == 1)
return window_event_result::close;
if (br->new_screen)
{
if (!new_briefing_screen(br, 0))
{
return window_event_result::close;
}
}
else if (br->new_page)
init_new_page(br);
else
br->delay_count = 0;
return window_event_result::handled;
case EVENT_KEY_COMMAND:
{
int key = event_key_get(event);