[config] Move wxUserInput to the config namespace

* Remove the wx prefix and make UserInput a class in the config
  namespace.
* Make UserInput constexpr-constructible. This should reduce program
  startup time by having the compiler pre-allocate UserInput objects.
* Change initialization of UserInput objects to be made via
  constructor. A previous approach would have limited creation of
  UserInput objects to a few places but that proved unworkable in the
  long run.
This commit is contained in:
Fabrice de Gans 2022-09-24 16:55:12 -07:00 committed by Rafael Kitover
parent 1d4eb48a68
commit b027e64753
14 changed files with 201 additions and 221 deletions

View File

@ -739,10 +739,10 @@ set(
wayland.cpp
strutils.cpp
wxutil.cpp
config/user-input.cpp
widgets/gamecontrol.cpp
widgets/keyedit.cpp
widgets/joyedit.cpp
widgets/userinput.cpp
widgets/sdljoy.cpp
widgets/wxmisc.cpp
# probably ought to be in common
@ -775,11 +775,11 @@ set(
wxhead.h
wayland.h
wxutil.h
config/user-input.h
widgets/wx/gamecontrol.h
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

View File

@ -0,0 +1,58 @@
#include "config/user-input.h"
#include "strutils.h"
#include "wx/joyedit.h"
namespace config {
UserInput::UserInput(const wxKeyEvent& event)
: UserInput(Device::Keyboard,
event.GetModifiers(),
getKeyboardKeyCode(event),
0) {}
UserInput::UserInput(const wxJoyEvent& event)
: UserInput(Device::Joystick,
event.control(),
event.control_index(),
event.joystick().player_index()) {}
// static
std::set<UserInput> UserInput::FromString(const wxString& string) {
std::set<UserInput> user_inputs;
if (string.empty()) {
return user_inputs;
}
for (const auto& token : strutils::split_with_sep(string, wxT(","))) {
int mod, key, joy;
if (!wxJoyKeyTextCtrl::ParseString(token, token.size(), mod, key,
joy)) {
user_inputs.clear();
return user_inputs;
}
user_inputs.emplace(UserInput(key, mod, joy));
}
return user_inputs;
}
// static
wxString UserInput::SpanToString(const std::set<UserInput>& user_inputs,
bool is_config) {
wxString config_string;
if (user_inputs.empty()) {
return config_string;
}
for (const UserInput& user_input : user_inputs) {
config_string += user_input.ToString(is_config) + wxT(',');
}
return config_string.SubString(0, config_string.size() - 2);
}
wxString UserInput::ToString(bool is_config) const {
// TODO: Move the implementation here once all callers have been updated.
return wxJoyKeyTextCtrl::ToString(mod_, key_, joy_, is_config);
}
} // namespace config

108
src/wx/config/user-input.h Normal file
View File

@ -0,0 +1,108 @@
#ifndef VBAM_WX_CONFIG_USER_INPUT_H_
#define VBAM_WX_CONFIG_USER_INPUT_H_
#include <wx/event.h>
#include <wx/string.h>
#include <set>
#include "widgets/wx/sdljoy.h"
namespace config {
// 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 key, mod, joy triplet, which
// UserInput will eventually replace.
class UserInput {
public:
// The device type for a user control.
enum class Device { Invalid = 0, Keyboard, Joystick, Last = Joystick };
// Constructor from a configuration string. Returns empty set on failure.
static std::set<UserInput> FromString(const wxString& string);
// Converts a set of UserInput into a configuration string. This
// recomputes the configuration string every time and should not be used
// for comparison purposes.
// TODO: Replace std::set with std::span when the code base uses C++20.
static wxString SpanToString(const std::set<UserInput>& user_inputs,
bool is_config = false);
// Invalid UserInput, mainly used for comparison.
constexpr UserInput() : UserInput(Device::Invalid, 0, 0, 0) {}
// Constructor from a wxKeyEvent.
UserInput(const wxKeyEvent& event);
// Constructor from a wxJoyEvent.
UserInput(const wxJoyEvent& event);
// TODO: Remove this once all uses have been removed.
constexpr UserInput(int key = 0, int mod = 0, int joy = 0)
: UserInput(joy == 0 ? Device::Keyboard : Device::Joystick,
mod,
key,
joy) {}
// Converts to a configuration string.
wxString ToString(bool is_config = false) const;
wxJoystick joystick() const { return joystick_; }
constexpr bool is_valid() const { return device_ != Device::Invalid; }
constexpr operator bool() const { return is_valid(); }
constexpr bool operator==(const UserInput& other) const {
return device_ == other.device_ && mod_ == other.mod_ &&
key_ == other.key_ && joy_ == other.joy_;
}
constexpr bool operator!=(const UserInput& other) const {
return !(*this == other);
}
constexpr bool operator<(const UserInput& 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;
}
constexpr bool operator<=(const UserInput& other) const {
return !(*this > other);
}
constexpr bool operator>(const UserInput& other) const {
return other < *this;
}
constexpr bool operator>=(const UserInput& other) const {
return !(*this < other);
}
private:
constexpr UserInput(Device device, int mod, int key, unsigned joy)
: device_(device),
joystick_(joy == 0 ? wxJoystick::Invalid()
: wxJoystick::FromLegacyPlayerIndex(joy)),
mod_(mod),
key_(key),
joy_(joy) {}
Device device_;
wxJoystick joystick_;
int mod_;
int key_;
unsigned joy_;
};
} // namespace config
#endif // VBAM_WX_CONFIG_USER_INPUT_H_

View File

@ -23,10 +23,10 @@
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include "config/user-input.h"
#include "opts.h"
#include "vbam-options.h"
#include "wx/gamecontrol.h"
#include "wx/userinput.h"
#include "../gba/CheatSearch.h"
#if defined(__WXGTK__)
@ -1691,7 +1691,7 @@ public:
tc->SetValue(wxEmptyString);
} else {
tc->SetValue(
wxUserInput::SpanToString(
config::UserInput::SpanToString(
kDefaultBindings.find(
wxGameControl(0, game_key))->second));
}

View File

@ -2,6 +2,7 @@
#include <vector>
#include <algorithm>
#include <wx/log.h>
#include <wx/display.h>
@ -18,7 +19,7 @@
-p/--profile=hz
*/
#define WJKB wxUserInput::FromLegacyKeyModJoy
#define WJKB config::UserInput
opts_t gopts;
@ -114,7 +115,7 @@ const wxAcceleratorEntryUnicode default_accels[] = {
};
const int num_def_accels = sizeof(default_accels) / sizeof(default_accels[0]);
const std::map<wxGameControl, std::set<wxUserInput>> kDefaultBindings = {
const std::map<wxGameControl, std::set<config::UserInput>> kDefaultBindings = {
{ wxGameControl(0, wxGameKey::Up), {
WJKB(wxT('W')),
WJKB(11, wxJoyControl::Button, 1),
@ -488,12 +489,12 @@ void load_opts()
for (auto& iter : gopts.game_control_bindings) {
const wxString optname = iter.first.ToString();
if (cfg->Read(optname, &s)) {
iter.second = wxUserInput::FromString(s);
iter.second = config::UserInput::FromString(s);
if (!s.empty() && iter.second.empty()) {
wxLogWarning(_("Invalid key binding %s for %s"), s.c_str(), optname.c_str());
}
} else {
s = wxUserInput::SpanToString(iter.second);
s = config::UserInput::SpanToString(iter.second);
cfg->Write(optname, s);
}
}
@ -571,18 +572,18 @@ void update_opts()
}
}
// For joypad, compare the wxUserInput sets. Since wxUserInput guarantees a
// For joypad, compare the UserInput sets. Since UserInput guarantees a
// certain ordering, it is possible that the user control in the panel shows
// a different ordering than the one that will be eventually saved, but this
// is nothing to worry about.
bool game_bindings_changed = false;
for (auto &iter : gopts.game_control_bindings) {
wxString option_name = iter.first.ToString();
std::set<wxUserInput> saved_config =
wxUserInput::FromString(cfg->Read(option_name, ""));
std::set<config::UserInput> saved_config =
config::UserInput::FromString(cfg->Read(option_name, ""));
if (saved_config != iter.second) {
game_bindings_changed = true;
cfg->Write(option_name, wxUserInput::SpanToString(iter.second));
cfg->Write(option_name, config::UserInput::SpanToString(iter.second));
}
}
if (game_bindings_changed) {
@ -760,7 +761,7 @@ void opt_set(const wxString& name, const wxString& val) {
gopts.game_control_bindings[game_control.value()].clear();
} else {
gopts.game_control_bindings[game_control.value()] =
wxUserInput::FromString(val);
config::UserInput::FromString(val);
}
return;
}

View File

@ -8,13 +8,13 @@
#include "wx/gamecontrol.h"
#include "wx/keyedit.h"
#include "wx/userinput.h"
#include "config/user-input.h"
// Forward declaration.
class wxFileHistory;
// Default joystick bindings.
extern const std::map<wxGameControl, std::set<wxUserInput>> kDefaultBindings;
extern const std::map<wxGameControl, std::set<config::UserInput>> kDefaultBindings;
extern struct opts_t {
opts_t();
@ -64,7 +64,7 @@ extern struct opts_t {
int statusbar;
/// Joypad
std::map<wxGameControl, std::set<wxUserInput>> game_control_bindings;
std::map<wxGameControl, std::set<config::UserInput>> game_control_bindings;
int autofire_rate;
int default_stick;

View File

@ -23,11 +23,11 @@
#include "../gba/RTC.h"
#include "../gba/agbprint.h"
#include "../sdl/text.h"
#include "config/user-input.h"
#include "drawing.h"
#include "filters.h"
#include "wx/joyedit.h"
#include "wx/gamecontrol.h"
#include "wx/userinput.h"
#include "wxvbam.h"
#include "wxutil.h"
#include "wayland.h"
@ -1208,7 +1208,7 @@ void GameArea::OnIdle(wxIdleEvent& event)
}
}
static bool process_user_input(bool down, const wxUserInput& user_input)
static bool process_user_input(bool down, const config::UserInput& user_input)
{
if (down)
return wxGameControlState::Instance().OnInputPressed(user_input);
@ -1269,7 +1269,7 @@ static void process_keyboard_event(const wxKeyEvent& ev, bool down)
break;
}
if (process_user_input(down, wxUserInput::FromLegacyKeyModJoy(key, mod, 0))) {
if (process_user_input(down, config::UserInput(key, mod, 0))) {
wxWakeUpIdle();
};
}
@ -1316,7 +1316,7 @@ void GameArea::OnSize(wxSizeEvent& ev)
void GameArea::OnSDLJoy(wxJoyEvent& ev)
{
process_user_input(ev.pressed(), wxUserInput::FromJoyEvent(ev));
process_user_input(ev.pressed(), config::UserInput(ev));
// tell Linux to turn off the screensaver/screen-blank if joystick button was pressed
// this shouldn't be necessary of course

View File

@ -198,7 +198,7 @@ wxGameControlState& wxGameControlState::Instance() {
wxGameControlState::wxGameControlState() : joypads_({0, 0, 0, 0}) {}
wxGameControlState::~wxGameControlState() = default;
bool wxGameControlState::OnInputPressed(const wxUserInput& user_input) {
bool wxGameControlState::OnInputPressed(const config::UserInput& user_input) {
assert(user_input);
const auto& game_keys = input_bindings_.find(user_input);
@ -226,7 +226,7 @@ bool wxGameControlState::OnInputPressed(const wxUserInput& user_input) {
return true;
}
bool wxGameControlState::OnInputReleased(const wxUserInput& user_input) {
bool wxGameControlState::OnInputReleased(const config::UserInput& user_input) {
assert(user_input);
const auto& game_keys = input_bindings_.find(user_input);

View File

@ -2,10 +2,10 @@
#include <wx/tokenzr.h>
#include "config/user-input.h"
#include "opts.h"
#include "strutils.h"
#include "wx/sdljoy.h"
#include "wx/userinput.h"
// FIXME: suppport analog/digital flag on per-axis basis
@ -33,7 +33,7 @@ void wxJoyKeyTextCtrl::OnJoy(wxJoyEvent& event)
if (!event.pressed())
return;
wxString nv = wxUserInput::FromJoyEvent(event).ToString();
wxString nv = config::UserInput(event).ToString();
if (nv.empty())
return;
@ -246,7 +246,7 @@ bool wxJoyKeyValidator::TransferToWindow()
if (!jk)
return false;
jk->SetValue(wxUserInput::SpanToString(gopts.game_control_bindings[val_]));
jk->SetValue(config::UserInput::SpanToString(gopts.game_control_bindings[val_]));
return true;
}
@ -257,6 +257,6 @@ bool wxJoyKeyValidator::TransferFromWindow()
if (!jk)
return false;
gopts.game_control_bindings[val_] = wxUserInput::FromString(jk->GetValue());
gopts.game_control_bindings[val_] = config::UserInput::FromString(jk->GetValue());
return true;
}

View File

@ -1,112 +0,0 @@
#include "wx/userinput.h"
#include "wx/joyedit.h"
#include "wx/sdljoy.h"
#include "wx/string.h"
#include "wxutil.h"
#include "../../wx/strutils.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,
event.control(),
event.control_index(),
event.joystick().player_index());
}
// static
wxUserInput wxUserInput::FromLegacyKeyModJoy(int key, int mod, int joy) {
return wxUserInput(joy == 0 ? Device::Keyboard : Device::Joystick,
mod,
key,
joy);
}
// static
std::set<wxUserInput> wxUserInput::FromString(const wxString& string) {
std::set<wxUserInput> user_inputs;
if (string.empty()) {
return user_inputs;
}
for (const auto& token : strutils::split_with_sep(string, wxT(","))) {
int mod, key, joy;
if (!wxJoyKeyTextCtrl::ParseString(token, token.size(), mod, key, joy)) {
user_inputs.clear();
return user_inputs;
}
user_inputs.emplace(FromLegacyKeyModJoy(key, mod, joy));
}
return user_inputs;
}
// static
wxString wxUserInput::SpanToString(const std::set<wxUserInput>& user_inputs, bool is_config) {
wxString config_string;
if (user_inputs.empty()) {
return config_string;
}
for (const wxUserInput& user_input : user_inputs) {
config_string += user_input.ToString(is_config) + wxT(',');
}
return config_string.SubString(0, config_string.size() - 2);
}
wxString wxUserInput::ToString(bool is_config) const {
// TODO: Move the implementation here once all callers have been updated.
return wxJoyKeyTextCtrl::ToString(mod_, key_, joy_, is_config);
}
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),
joystick_(joy == 0 ? wxJoystick::Invalid() : wxJoystick::FromLegacyPlayerIndex(joy)),
mod_(mod),
key_(key),
joy_(joy) {}

View File

@ -7,7 +7,7 @@
#include <set>
#include <wx/string.h>
#include "wx/userinput.h"
#include "config/user-input.h"
// Forward declaration.
class wxGameControlState;
@ -114,8 +114,8 @@ public:
// Processes `user_input` and updates the internal tracking state.
// Returns true if `user_input` corresponds to a game input.
bool OnInputPressed(const wxUserInput& user_input);
bool OnInputReleased(const wxUserInput& user_input);
bool OnInputPressed(const config::UserInput& user_input);
bool OnInputReleased(const config::UserInput& user_input);
// Clears all input.
void Reset();
@ -130,9 +130,9 @@ private:
wxGameControlState();
~wxGameControlState();
std::map<wxUserInput, std::set<wxGameControl>> input_bindings_;
std::map<wxGameControl, std::set<wxUserInput>> active_controls_;
std::set<wxUserInput> keys_pressed_;
std::map<config::UserInput, std::set<wxGameControl>> input_bindings_;
std::map<wxGameControl, std::set<config::UserInput>> active_controls_;
std::set<config::UserInput> keys_pressed_;
std::array<uint32_t, kNbJoypads> joypads_;
};

View File

@ -8,7 +8,6 @@
#include "wx/gamecontrol.h"
#include "wx/keyedit.h"
#include "wx/sdljoy.h"
#include "wx/userinput.h"
class wxJoyKeyTextCtrl : public wxKeyTextCtrl {
public:

View File

@ -1,74 +0,0 @@
#ifndef _WX_USER_INPUT_H_
#define _WX_USER_INPUT_H_
#include <set>
#include <wx/event.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 key, mod, joy triplet, 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 empty set on failure.
static std::set<wxUserInput> FromString(const wxString& string);
// TODO: Remove this once all uses have been removed.
static wxUserInput FromLegacyKeyModJoy(int key = 0, int mod = 0, int joy = 0);
// Converts a set of wxUserInput into a configuration string. This
// recomputes the configuration string every time and should not be used
// for comparison purposes.
// TODO: Replace std::set with std::span when the code base uses C++20.
static wxString SpanToString(
const std::set<wxUserInput>& user_inputs, bool is_config = false);
// Converts to a configuration string.
wxString ToString(bool is_config = false) const;
wxJoystick joystick() const { return joystick_; }
bool is_valid() const { return device_ != Device::Invalid; }
operator bool() const { return is_valid(); }
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);
const Device device_;
const wxJoystick joystick_;
const int mod_;
const uint8_t key_;
const unsigned joy_;
};
#endif /* _WX_USER_INPUT_H */

View File

@ -26,11 +26,11 @@
// The built-in vba-over.ini
#include "builtin-over.h"
#include "config/user-input.h"
#include "strutils.h"
#include "vbam-options.h"
#include "wayland.h"
#include "wx/gamecontrol.h"
#include "wx/userinput.h"
IMPLEMENT_APP(wxvbamApp)
IMPLEMENT_DYNAMIC_CLASS(MainFrame, wxFrame)
@ -877,7 +877,7 @@ int MainFrame::FilterEvent(wxEvent& event)
{
wxJoyEvent& je = (wxJoyEvent&)event;
if (!je.pressed()) return -1; // joystick button UP
wxString label = wxUserInput::FromJoyEvent(je).ToString();
wxString label = config::UserInput(je).ToString();
wxAcceleratorEntry_v accels = wxGetApp().GetAccels();
for (size_t i = 0; i < accels.size(); ++i)
{