Introduce wxUserInput class
This introduces a new abstraction for any user input. The long-term goal is to replace every usage of "custom" {key, mod, joy} triplets in the code base with this new class. This class implements comparison operators, allowing for faster access in a set or as a key in a map, compared to the vectors currently used. Issue: #745
This commit is contained in:
parent
d7abc27438
commit
4aebd0b802
|
@ -728,6 +728,7 @@ set(
|
|||
wxutil.cpp
|
||||
widgets/keyedit.cpp
|
||||
widgets/joyedit.cpp
|
||||
widgets/userinput.cpp
|
||||
widgets/sdljoy.cpp
|
||||
widgets/wxmisc.cpp
|
||||
# probably ought to be in common
|
||||
|
@ -761,6 +762,7 @@ set(
|
|||
widgets/wx/keyedit.h
|
||||
widgets/wx/joyedit.h
|
||||
widgets/wx/sdljoy.h
|
||||
widgets/wx/userinput.h
|
||||
widgets/wx/webupdatedef.h
|
||||
widgets/wx/wxmisc.h
|
||||
# probably ought to be in common
|
||||
|
|
139
src/wx/panel.cpp
139
src/wx/panel.cpp
|
@ -23,6 +23,8 @@
|
|||
#include "../sdl/text.h"
|
||||
#include "drawing.h"
|
||||
#include "filters.h"
|
||||
#include "wx/joyedit.h"
|
||||
#include "wx/userinput.h"
|
||||
#include "wxvbam.h"
|
||||
#include "wxutil.h"
|
||||
#include "wayland.h"
|
||||
|
@ -1217,7 +1219,7 @@ static uint32_t bmask[NUM_KEYS] = {
|
|||
KEYM_GS
|
||||
};
|
||||
|
||||
static wxJoyKeyBinding_v keys_pressed;
|
||||
static std::set<wxUserInput> keys_pressed;
|
||||
|
||||
static void clear_input_press()
|
||||
{
|
||||
|
@ -1236,6 +1238,7 @@ struct game_key {
|
|||
wxJoyKeyBinding_v& b;
|
||||
};
|
||||
|
||||
// Populates |vec| with the game keys currently pressed.
|
||||
static void game_keys_pressed(int key, int mod, int joy, std::vector<game_key>* vec)
|
||||
{
|
||||
for (int player = 0; player < 4; player++)
|
||||
|
@ -1248,56 +1251,32 @@ static void game_keys_pressed(int key, int mod, int joy, std::vector<game_key>*
|
|||
}
|
||||
}
|
||||
|
||||
static bool process_key_press(bool down, int key, int mod, int joy = 0)
|
||||
static bool process_user_input(bool down, const wxUserInput& user_input)
|
||||
{
|
||||
// modifier-only key releases do not set the modifier flag
|
||||
// so we set it here to match key release events to key press events
|
||||
switch (key) {
|
||||
case WXK_SHIFT:
|
||||
mod |= wxMOD_SHIFT;
|
||||
break;
|
||||
case WXK_ALT:
|
||||
mod |= wxMOD_ALT;
|
||||
break;
|
||||
case WXK_CONTROL:
|
||||
mod |= wxMOD_CONTROL;
|
||||
break;
|
||||
#ifdef __WXMAC__
|
||||
case WXK_RAW_CONTROL:
|
||||
mod |= wxMOD_RAW_CONTROL;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (joy == 0) mod = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// check if key is already pressed
|
||||
size_t kpno;
|
||||
|
||||
for (kpno = 0; kpno < keys_pressed.size(); kpno++)
|
||||
if (keys_pressed[kpno].key == key && keys_pressed[kpno].mod == mod && keys_pressed[kpno].joy == joy)
|
||||
break;
|
||||
int key = user_input.key();
|
||||
int mod = user_input.mod();
|
||||
int joy = user_input.joy();
|
||||
|
||||
std::vector<game_key> game_keys;
|
||||
game_keys_pressed(key, mod, joy, &game_keys);
|
||||
const bool is_game_key = !game_keys.empty();
|
||||
|
||||
const bool is_game_key = game_keys.size();
|
||||
|
||||
if (kpno < keys_pressed.size()) {
|
||||
// check if key is already pressed
|
||||
auto iter = keys_pressed.find(user_input);
|
||||
if (iter != keys_pressed.end()) {
|
||||
// double press is noop
|
||||
if (down)
|
||||
return is_game_key;
|
||||
|
||||
// if released, forget it
|
||||
keys_pressed.erase(keys_pressed.begin() + kpno);
|
||||
iter = keys_pressed.erase(iter);
|
||||
} else {
|
||||
// double release is noop
|
||||
if (!down)
|
||||
return is_game_key;
|
||||
|
||||
// otherwise remember it
|
||||
keys_pressed.push_back({key, mod, joy});
|
||||
keys_pressed.emplace(user_input);
|
||||
}
|
||||
|
||||
for (auto&& game_key : game_keys) {
|
||||
|
@ -1313,12 +1292,7 @@ static bool process_key_press(bool down, int key, int mod, int joy = 0)
|
|||
for (bind2 = 0; bind2 < game_key.b.size(); bind2++) {
|
||||
if ((size_t)game_key.bind_num == bind2 || (b[bind2].key == key && b[bind2].mod == mod && b[bind2].joy == joy))
|
||||
continue;
|
||||
|
||||
for (kpno = 0; kpno < keys_pressed.size(); kpno++)
|
||||
if (keys_pressed[kpno].key == b[bind2].key && keys_pressed[kpno].mod == b[bind2].mod && keys_pressed[kpno].joy == b[bind2].joy)
|
||||
break;
|
||||
}
|
||||
|
||||
if (bind2 == b.size()) {
|
||||
// release button
|
||||
joypress[game_key.player] &= ~bmask[game_key.key_num];
|
||||
|
@ -1338,42 +1312,63 @@ static void draw_black_background(wxWindow* win) {
|
|||
dc.DrawRectangle(0, 0, w, h);
|
||||
}
|
||||
|
||||
static bool is_key_pressed(wxKeyEvent& ev)
|
||||
static void process_keyboard_event(const wxKeyEvent& ev, bool down)
|
||||
{
|
||||
auto kc = ev.GetKeyCode();
|
||||
int kc = ev.GetKeyCode();
|
||||
|
||||
// Under Wayland or if the key is unicode, we can't use wxGetKeyState().
|
||||
if (IsItWayland() || kc == WXK_NONE)
|
||||
return true;
|
||||
|
||||
return wxGetKeyState(static_cast<wxKeyCode>(kc));
|
||||
if (!IsItWayland() && kc != WXK_NONE) {
|
||||
// Check if the key state corresponds to the event.
|
||||
if (down != wxGetKeyState(static_cast<wxKeyCode>(kc))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_key_released(wxKeyEvent& ev)
|
||||
{
|
||||
auto kc = ev.GetKeyCode();
|
||||
int key = getKeyboardKeyCode(ev);
|
||||
int mod = ev.GetModifiers();
|
||||
|
||||
// Under Wayland or if the key is unicode, we can't use wxGetKeyState().
|
||||
if (IsItWayland() || kc == WXK_NONE)
|
||||
return true;
|
||||
if (key == WXK_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
return !wxGetKeyState(static_cast<wxKeyCode>(kc));
|
||||
// modifier-only key releases do not set the modifier flag
|
||||
// so we set it here to match key release events to key press events
|
||||
switch (key) {
|
||||
case WXK_SHIFT:
|
||||
mod |= wxMOD_SHIFT;
|
||||
break;
|
||||
case WXK_ALT:
|
||||
mod |= wxMOD_ALT;
|
||||
break;
|
||||
case WXK_CONTROL:
|
||||
mod |= wxMOD_CONTROL;
|
||||
break;
|
||||
#ifdef __WXMAC__
|
||||
case WXK_RAW_CONTROL:
|
||||
mod |= wxMOD_RAW_CONTROL;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
// This resets mod for any non-modifier key. This has the side
|
||||
// effect of not being able to set up a modifier+key for a game
|
||||
// control.
|
||||
mod = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (process_user_input(down, wxUserInput::FromLegacyJoyKeyBinding({key, mod, 0}))) {
|
||||
wxWakeUpIdle();
|
||||
};
|
||||
}
|
||||
|
||||
void GameArea::OnKeyDown(wxKeyEvent& ev)
|
||||
{
|
||||
int kc = getKeyboardKeyCode(ev);
|
||||
if (is_key_pressed(ev) && process_key_press(true, kc, ev.GetModifiers())) {
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
process_keyboard_event(ev, true);
|
||||
}
|
||||
|
||||
void GameArea::OnKeyUp(wxKeyEvent& ev)
|
||||
{
|
||||
int kc = getKeyboardKeyCode(ev);
|
||||
if (is_key_released(ev) && process_key_press(false, kc, ev.GetModifiers())) {
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
process_keyboard_event(ev, false);
|
||||
}
|
||||
|
||||
// these three are forwarded to the DrawingPanel instance
|
||||
|
@ -1406,20 +1401,22 @@ void GameArea::OnSDLJoy(wxJoyEvent& ev)
|
|||
int joy = ev.joystick().player_index();
|
||||
|
||||
// mutually exclusive key types unpress their opposite
|
||||
// TODO: Move creation of these "ghost" events to wxJoyPoller.
|
||||
if (mod == WXJB_AXIS_PLUS) {
|
||||
process_key_press(false, key, WXJB_AXIS_MINUS, joy);
|
||||
process_key_press(ev.control_value() != 0, key, mod, joy);
|
||||
process_user_input(false, wxUserInput::FromLegacyJoyKeyBinding({key, WXJB_AXIS_MINUS, joy}));
|
||||
process_user_input(ev.control_value() != 0, wxUserInput::FromJoyEvent(ev));
|
||||
} else if (mod == WXJB_AXIS_MINUS) {
|
||||
process_key_press(false, key, WXJB_AXIS_PLUS, joy);
|
||||
process_key_press(ev.control_value() != 0, key, mod, joy);
|
||||
process_user_input(false, wxUserInput::FromLegacyJoyKeyBinding({key, WXJB_AXIS_PLUS, joy}));
|
||||
process_user_input(ev.control_value() != 0, wxUserInput::FromJoyEvent(ev));
|
||||
} else if (mod >= WXJB_HAT_FIRST && mod <= WXJB_HAT_LAST) {
|
||||
int value = ev.control_value();
|
||||
process_key_press(value & SDL_HAT_UP, key, WXJB_HAT_N, joy);
|
||||
process_key_press(value & SDL_HAT_DOWN, key, WXJB_HAT_S, joy);
|
||||
process_key_press(value & SDL_HAT_RIGHT, key, WXJB_HAT_E, joy);
|
||||
process_key_press(value & SDL_HAT_LEFT, key, WXJB_HAT_W, joy);
|
||||
} else
|
||||
process_key_press(ev.control_value() != 0, key, mod, joy);
|
||||
process_user_input(value & SDL_HAT_UP, wxUserInput::FromLegacyJoyKeyBinding({key, WXJB_HAT_N, joy}));
|
||||
process_user_input(value & SDL_HAT_DOWN, wxUserInput::FromLegacyJoyKeyBinding({key, WXJB_HAT_S, joy}));
|
||||
process_user_input(value & SDL_HAT_RIGHT, wxUserInput::FromLegacyJoyKeyBinding({key, WXJB_HAT_E, joy}));
|
||||
process_user_input(value & SDL_HAT_LEFT, wxUserInput::FromLegacyJoyKeyBinding({key, WXJB_HAT_W, joy}));
|
||||
} else {
|
||||
process_user_input(ev.control_value() != 0, wxUserInput::FromJoyEvent(ev));
|
||||
}
|
||||
|
||||
// tell Linux to turn off the screensaver/screen-blank if joystick button was pressed
|
||||
// this shouldn't be necessary of course
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#include <wx/tokenzr.h>
|
||||
#include "wx/joyedit.h"
|
||||
|
||||
#include <wx/tokenzr.h>
|
||||
|
||||
#include "strutils.h"
|
||||
#include "wx/userinput.h"
|
||||
|
||||
// FIXME: suppport analog/digital flag on per-axis basis
|
||||
|
||||
|
@ -17,7 +20,7 @@ wxJoyKeyBinding newWxJoyKeyBinding(int key, int mod, int joy)
|
|||
return tmp;
|
||||
}
|
||||
|
||||
int wxJoyKeyTextCtrl::DigitalButton(wxJoyEvent& event)
|
||||
int wxJoyKeyTextCtrl::DigitalButton(const wxJoyEvent& event)
|
||||
{
|
||||
int16_t sdlval = event.control_value();
|
||||
wxJoyControl sdltype = event.control();
|
||||
|
@ -72,24 +75,17 @@ void wxJoyKeyTextCtrl::OnJoy(wxJoyEvent& event)
|
|||
{
|
||||
static wxLongLong last_event = 0;
|
||||
|
||||
int16_t val = event.control_value();
|
||||
wxJoyControl type = event.control();
|
||||
|
||||
// Filter consecutive axis motions within 300ms, as this adds two bindings
|
||||
// +1/-1 instead of the one intended.
|
||||
if (type == wxJoyControl::Axis && wxGetUTCTimeMillis() - last_event < 300)
|
||||
if (event.control() == wxJoyControl::Axis && wxGetUTCTimeMillis() - last_event < 300)
|
||||
return;
|
||||
|
||||
last_event = wxGetUTCTimeMillis();
|
||||
|
||||
int mod = DigitalButton(event);
|
||||
uint8_t key = event.control_index();
|
||||
unsigned joy = event.joystick().player_index();
|
||||
|
||||
if (!val || mod < 0)
|
||||
if (!event.control_value() || DigitalButton(event) < 0)
|
||||
return;
|
||||
|
||||
wxString nv = ToString(mod, key, joy);
|
||||
wxString nv = wxUserInput::FromJoyEvent(event).ToString();
|
||||
|
||||
if (nv.empty())
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
#include "wx/userinput.h"
|
||||
|
||||
#include "wx/string.h"
|
||||
#include "wxutil.h"
|
||||
|
||||
// static
|
||||
wxUserInput wxUserInput::Invalid() {
|
||||
return wxUserInput(Device::Invalid, 0, 0, 0);
|
||||
}
|
||||
|
||||
// static
|
||||
wxUserInput wxUserInput::FromKeyEvent(const wxKeyEvent& event) {
|
||||
return wxUserInput(Device::Keyboard,
|
||||
event.GetModifiers(),
|
||||
getKeyboardKeyCode(event),
|
||||
0);
|
||||
}
|
||||
|
||||
// static
|
||||
wxUserInput wxUserInput::FromJoyEvent(const wxJoyEvent& event) {
|
||||
return wxUserInput(Device::Joystick,
|
||||
wxJoyKeyTextCtrl::DigitalButton(event),
|
||||
event.control_index(),
|
||||
event.joystick().player_index());
|
||||
}
|
||||
|
||||
// static
|
||||
wxUserInput wxUserInput::FromLegacyJoyKeyBinding(const wxJoyKeyBinding& binding) {
|
||||
return wxUserInput(binding.joy == 0 ? Device::Keyboard : Device::Joystick,
|
||||
binding.mod,
|
||||
binding.key,
|
||||
binding.joy);
|
||||
}
|
||||
|
||||
// static
|
||||
wxUserInput wxUserInput::FromString(const wxString& string) {
|
||||
// TODO: Move the implementation here once all callers have been updated.
|
||||
int mod = 0;
|
||||
int key = 0;
|
||||
int joy = 0;
|
||||
if (!wxJoyKeyTextCtrl::FromString(string, mod, key, joy)) {
|
||||
return wxUserInput::Invalid();
|
||||
}
|
||||
return wxUserInput::FromLegacyJoyKeyBinding({key, mod, joy});
|
||||
}
|
||||
|
||||
wxString wxUserInput::ToString() {
|
||||
if (!config_string_.IsNull()) {
|
||||
return config_string_;
|
||||
}
|
||||
|
||||
// TODO: Move the implementation here once all callers have been updated.
|
||||
config_string_ = wxJoyKeyTextCtrl::ToString(mod_, key_, joy_);
|
||||
return config_string_;
|
||||
}
|
||||
|
||||
bool wxUserInput::operator==(const wxUserInput& other) const {
|
||||
return device_ == other.device_ && mod_ == other.mod_ &&
|
||||
key_ == other.key_ && joy_ == other.joy_;
|
||||
}
|
||||
bool wxUserInput::operator!=(const wxUserInput& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
bool wxUserInput::operator<(const wxUserInput& other) const {
|
||||
if (device_ < other.device_) {
|
||||
return device_ < other.device_;
|
||||
}
|
||||
if (joy_ != other.joy_) {
|
||||
return joy_ < other.joy_;
|
||||
}
|
||||
if (key_ != other.key_) {
|
||||
return key_ < other.key_;
|
||||
}
|
||||
if (mod_ != other.mod_) {
|
||||
return mod_ < other.mod_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool wxUserInput::operator<=(const wxUserInput& other) const {
|
||||
return !(*this > other);
|
||||
}
|
||||
bool wxUserInput::operator>(const wxUserInput& other) const {
|
||||
return other < *this;
|
||||
}
|
||||
bool wxUserInput::operator>=(const wxUserInput& other) const {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
// Actual underlying constructor.
|
||||
wxUserInput::wxUserInput(Device device, int mod, uint8_t key, unsigned joy) :
|
||||
device_(device),
|
||||
mod_(mod),
|
||||
key_(key),
|
||||
joy_(joy) {}
|
|
@ -49,7 +49,7 @@ public:
|
|||
// key is event.GetControlIndex(), and joy is event.GetJoy() + 1
|
||||
// mod is derived from GetControlValue() and GetControlType():
|
||||
// convert wxJoyEvent's type+val into mod (WXJB_*)
|
||||
static int DigitalButton(wxJoyEvent& event);
|
||||
static int DigitalButton(const wxJoyEvent& event);
|
||||
// convert mod+key to accel string, separated by -
|
||||
static wxString ToString(int mod, int key, int joy, bool isConfig = false);
|
||||
// convert multiple keys, separated by multikey
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
#ifndef _WX_USER_INPUT_H_
|
||||
#define _WX_USER_INPUT_H_
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "wx/joyedit.h"
|
||||
#include "wx/sdljoy.h"
|
||||
|
||||
// Abstraction for a user input, which can come from a keyboard or a joystick.
|
||||
// This class implements comparison operators so it can be used in sets and as
|
||||
// a key in maps.
|
||||
//
|
||||
// TODO: Right now, this class is implemented as a thin wrapper around the key,
|
||||
// mod and joy user input representation used in many places in the code base.
|
||||
// This is to ease a transition away from the wxJoyKeyBinding type, which
|
||||
// wxUserInput will eventually replace.
|
||||
class wxUserInput {
|
||||
public:
|
||||
// The device type for a user control.
|
||||
enum class Device {
|
||||
Invalid = 0,
|
||||
Keyboard,
|
||||
Joystick,
|
||||
Last = Joystick
|
||||
};
|
||||
|
||||
// Invalid wxUserInput, mainly used for comparison.
|
||||
static wxUserInput Invalid();
|
||||
|
||||
// Constructor from a wxKeyEvent.
|
||||
static wxUserInput FromKeyEvent(const wxKeyEvent& event);
|
||||
|
||||
// Constructor from a wxJoyEvent.
|
||||
static wxUserInput FromJoyEvent(const wxJoyEvent& event);
|
||||
|
||||
// Constructor from a configuration string. Returns wxUserInput::Invalid()
|
||||
// on parsing failure.
|
||||
static wxUserInput FromString(const wxString& string);
|
||||
|
||||
// TODO: Remove this once all uses of wxJoyKeyBinding have been removed.
|
||||
static wxUserInput FromLegacyJoyKeyBinding(const wxJoyKeyBinding& binding);
|
||||
|
||||
// Converts to a configuration string. Computed on first call, and cached
|
||||
// for further calls.
|
||||
wxString ToString();
|
||||
|
||||
// TODO: Remove these accessors once all callers have been removed.
|
||||
int mod() const { return mod_; }
|
||||
int key() const { return key_; }
|
||||
int joy() const { return joy_; }
|
||||
|
||||
bool operator==(const wxUserInput& other) const;
|
||||
bool operator!=(const wxUserInput& other) const;
|
||||
bool operator<(const wxUserInput& other) const;
|
||||
bool operator<=(const wxUserInput& other) const;
|
||||
bool operator>(const wxUserInput& other) const;
|
||||
bool operator>=(const wxUserInput& other) const;
|
||||
|
||||
private:
|
||||
wxUserInput(Device device, int mod, uint8_t key, unsigned joy);
|
||||
|
||||
Device device_;
|
||||
int mod_;
|
||||
uint8_t key_;
|
||||
unsigned joy_;
|
||||
|
||||
wxString config_string_;
|
||||
};
|
||||
|
||||
|
||||
#endif /* _WX_USER_INPUT_H */
|
|
@ -2,7 +2,7 @@
|
|||
#include "../common/contains.h"
|
||||
|
||||
|
||||
int getKeyboardKeyCode(wxKeyEvent& event)
|
||||
int getKeyboardKeyCode(const wxKeyEvent& event)
|
||||
{
|
||||
int uc = event.GetUnicodeKey();
|
||||
if (uc != WXK_NONE) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <wx/event.h>
|
||||
|
||||
int getKeyboardKeyCode(wxKeyEvent& event);
|
||||
int getKeyboardKeyCode(const wxKeyEvent& event);
|
||||
|
||||
#include <wx/accel.h>
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
// The built-in vba-over.ini
|
||||
#include "builtin-over.h"
|
||||
#include "wx/userinput.h"
|
||||
|
||||
IMPLEMENT_APP(wxvbamApp)
|
||||
IMPLEMENT_DYNAMIC_CLASS(MainFrame, wxFrame)
|
||||
|
@ -881,10 +882,7 @@ int MainFrame::FilterEvent(wxEvent& event)
|
|||
{
|
||||
wxJoyEvent& je = (wxJoyEvent&)event;
|
||||
if (je.control_value() == 0) return -1; // joystick button UP
|
||||
uint8_t key = je.control_index();
|
||||
int mod = wxJoyKeyTextCtrl::DigitalButton(je);
|
||||
int joy = je.joystick().player_index();
|
||||
wxString label = wxJoyKeyTextCtrl::ToString(mod, key, joy);
|
||||
wxString label = wxUserInput::FromJoyEvent(je).ToString();
|
||||
wxAcceleratorEntry_v accels = wxGetApp().GetAccels();
|
||||
for (size_t i = 0; i < accels.size(); ++i)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue