[Input] Unify UserInput event handling
This adds a custom UserInputEvent for handling joypad and keyboard input for both accelerators and emulator control configuration. These changes fix a number of issues with the wxWidgets implementation of key down / up event handling. In particular, every "down" event now has a corresponding "up" event. All of the special handling that was done in multiple places to handle shortcuts is now done in a new class, `UserInputEventSender`, simplifying multiple call sites. This is another step towards complete unification of UserInput handling, which will prevent double assignment between shortcuts and emulator controls. Bug: #745
This commit is contained in:
parent
cc65ef2849
commit
1e1a369c8d
|
@ -78,8 +78,10 @@ set(VBAM_WX_COMMON
|
||||||
widgets/render-plugin.h
|
widgets/render-plugin.h
|
||||||
widgets/user-input-ctrl.cpp
|
widgets/user-input-ctrl.cpp
|
||||||
widgets/user-input-ctrl.h
|
widgets/user-input-ctrl.h
|
||||||
widgets/sdljoy.cpp
|
widgets/user-input-event.cpp
|
||||||
widgets/sdljoy.h
|
widgets/user-input-event.h
|
||||||
|
widgets/sdl-poller.cpp
|
||||||
|
widgets/sdl-poller.h
|
||||||
widgets/utils.cpp
|
widgets/utils.cpp
|
||||||
widgets/utils.h
|
widgets/utils.h
|
||||||
widgets/webupdatedef.h
|
widgets/webupdatedef.h
|
||||||
|
@ -87,8 +89,6 @@ set(VBAM_WX_COMMON
|
||||||
widgets/wxmisc.cpp
|
widgets/wxmisc.cpp
|
||||||
wxhead.h
|
wxhead.h
|
||||||
wxlogdebug.h
|
wxlogdebug.h
|
||||||
wxutil.cpp
|
|
||||||
wxutil.h
|
|
||||||
wxvbam.cpp
|
wxvbam.cpp
|
||||||
wxvbam.h
|
wxvbam.h
|
||||||
x11keymap.h
|
x11keymap.h
|
||||||
|
|
|
@ -547,17 +547,17 @@ wxThread::ExitCode BackgroundInput::CheckKeyboard()
|
||||||
// virtual key "i" is pressed
|
// virtual key "i" is pressed
|
||||||
if ((bits & 0x8000) && (previousState[i] & 0x8000) == 0) {
|
if ((bits & 0x8000) && (previousState[i] & 0x8000) == 0) {
|
||||||
if (handler && !wxWindow::FindFocus()) {
|
if (handler && !wxWindow::FindFocus()) {
|
||||||
wxKeyEvent ev(wxEVT_KEY_DOWN);
|
wxKeyEvent* event = new wxKeyEvent(wxEVT_KEY_DOWN);
|
||||||
ev.m_keyCode = xKeySym;
|
event->m_keyCode = xKeySym;
|
||||||
handler->AddPendingEvent(ev);
|
handler->QueueEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// virtual key "i" is released
|
// virtual key "i" is released
|
||||||
else if (((bits & 0x8000) == 0) && (previousState[i] & 0x8000)) {
|
else if (((bits & 0x8000) == 0) && (previousState[i] & 0x8000)) {
|
||||||
if (handler && !wxWindow::FindFocus()) {
|
if (handler && !wxWindow::FindFocus()) {
|
||||||
wxKeyEvent ev(wxEVT_KEY_UP);
|
wxKeyEvent* event = new wxKeyEvent(wxEVT_KEY_UP);
|
||||||
ev.m_keyCode = xKeySym;
|
event->m_keyCode = xKeySym;
|
||||||
handler->AddPendingEvent(ev);
|
handler->QueueEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
previousState[i] = bits;
|
previousState[i] = bits;
|
||||||
|
|
|
@ -1184,7 +1184,7 @@ EVT_HANDLER(AllowKeyboardBackgroundInput, "Allow keyboard background input (togg
|
||||||
disableKeyboardBackgroundInput();
|
disableKeyboardBackgroundInput();
|
||||||
if (OPTION(kUIAllowKeyboardBackgroundInput)) {
|
if (OPTION(kUIAllowKeyboardBackgroundInput)) {
|
||||||
if (panel && panel->panel) {
|
if (panel && panel->panel) {
|
||||||
enableKeyboardBackgroundInput(panel->panel->GetWindow());
|
enableKeyboardBackgroundInput(panel->panel->GetWindow()->GetEventHandler());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2173,44 +2173,17 @@ EVT_HANDLER(EmulatorDirectories, "Directories...")
|
||||||
|
|
||||||
EVT_HANDLER(JoypadConfigure, "Joypad options...")
|
EVT_HANDLER(JoypadConfigure, "Joypad options...")
|
||||||
{
|
{
|
||||||
joy.PollAllJoysticks();
|
|
||||||
|
|
||||||
auto frame = wxGetApp().frame;
|
|
||||||
bool joy_timer = frame->IsJoyPollTimerRunning();
|
|
||||||
|
|
||||||
if (!joy_timer) {
|
|
||||||
frame->StartJoyPollTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ShowModal(GetXRCDialog("JoypadConfig")) == wxID_OK) {
|
if (ShowModal(GetXRCDialog("JoypadConfig")) == wxID_OK) {
|
||||||
update_joypad_opts();
|
update_joypad_opts();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!joy_timer) {
|
|
||||||
frame->StopJoyPollTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
SetJoystick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_HANDLER(Customize, "Customize UI...")
|
EVT_HANDLER(Customize, "Customize UI...")
|
||||||
{
|
{
|
||||||
wxDialog* dlg = GetXRCDialog("AccelConfig");
|
if (ShowModal(GetXRCDialog("AccelConfig")) == wxID_OK) {
|
||||||
joy.PollAllJoysticks();
|
|
||||||
|
|
||||||
auto frame = wxGetApp().frame;
|
|
||||||
bool joy_timer = frame->IsJoyPollTimerRunning();
|
|
||||||
|
|
||||||
if (!joy_timer) frame->StartJoyPollTimer();
|
|
||||||
|
|
||||||
if (ShowModal(dlg) == wxID_OK) {
|
|
||||||
update_shortcut_opts();
|
update_shortcut_opts();
|
||||||
ResetMenuAccelerators();
|
ResetMenuAccelerators();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!joy_timer) frame->StopJoyPollTimer();
|
|
||||||
|
|
||||||
SetJoystick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_ONLINEUPDATES
|
#ifndef NO_ONLINEUPDATES
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "wx/opts.h"
|
#include "wx/opts.h"
|
||||||
#include "wx/strutils.h"
|
#include "wx/strutils.h"
|
||||||
#include "wx/wxlogdebug.h"
|
|
||||||
|
|
||||||
namespace config {
|
namespace config {
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef VBAM_WX_CONFIG_GAME_CONTROL_H_
|
#ifndef VBAM_WX_CONFIG_GAME_CONTROL_H_
|
||||||
#define VBAM_WX_CONFIG_GAME_CONTROL_H_
|
#define VBAM_WX_CONFIG_GAME_CONTROL_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
|
@ -87,16 +87,6 @@ int Shortcuts::CommandForInput(const UserInput& input) const {
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<wxJoystick> Shortcuts::Joysticks() const {
|
|
||||||
std::set<wxJoystick> output;
|
|
||||||
for (const auto& iter : command_to_inputs_) {
|
|
||||||
for (const UserInput& user_input : iter.second) {
|
|
||||||
output.insert(user_input.joystick());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shortcuts Shortcuts::Clone() const {
|
Shortcuts Shortcuts::Clone() const {
|
||||||
return Shortcuts(this->command_to_inputs_, this->input_to_command_, this->disabled_defaults_);
|
return Shortcuts(this->command_to_inputs_, this->input_to_command_, this->disabled_defaults_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#include "wx/config/user-input.h"
|
#include "wx/config/user-input.h"
|
||||||
|
|
||||||
// by default, only 9 recent items
|
// wxWidgets only goes up to `wxID_FILE9` but we want 10 recent files.
|
||||||
#define wxID_FILE10 (wxID_FILE9 + 1)
|
#define wxID_FILE10 (wxID_FILE9 + 1)
|
||||||
|
|
||||||
namespace config {
|
namespace config {
|
||||||
|
@ -45,9 +45,6 @@ public:
|
||||||
// Returns the command currently assigned to `input` or nullptr if none.
|
// Returns the command currently assigned to `input` or nullptr if none.
|
||||||
int CommandForInput(const UserInput& input) const;
|
int CommandForInput(const UserInput& input) const;
|
||||||
|
|
||||||
// Returns the set of joysticks used by shortcuts.
|
|
||||||
std::set<wxJoystick> Joysticks() const;
|
|
||||||
|
|
||||||
// Returns a copy of this object. This can be an expensive operation and
|
// Returns a copy of this object. This can be an expensive operation and
|
||||||
// should only be used to modify the currently active shortcuts
|
// should only be used to modify the currently active shortcuts
|
||||||
// configuration.
|
// configuration.
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include <wx/translation.h>
|
#include <wx/translation.h>
|
||||||
|
|
||||||
#include "wx/strutils.h"
|
#include "wx/strutils.h"
|
||||||
#include "wx/wxutil.h"
|
|
||||||
|
|
||||||
namespace config {
|
namespace config {
|
||||||
|
|
||||||
|
@ -202,26 +201,26 @@ UserInput StringToUserInput(const wxString& string) {
|
||||||
if (kAxisRegex.Matches(remainder)) {
|
if (kAxisRegex.Matches(remainder)) {
|
||||||
kAxisRegex.GetMatch(&start, &length, 1);
|
kAxisRegex.GetMatch(&start, &length, 1);
|
||||||
const int key = StringToInt(remainder.Mid(start, length));
|
const int key = StringToInt(remainder.Mid(start, length));
|
||||||
const int mod =
|
const JoyControl control =
|
||||||
remainder[start + length] == '+' ? wxJoyControl::AxisPlus : wxJoyControl::AxisMinus;
|
remainder[start + length] == '+' ? JoyControl::AxisPlus : JoyControl::AxisMinus;
|
||||||
return UserInput(key, mod, joy);
|
return UserInput(key, control, JoyId(joy - 1));
|
||||||
}
|
}
|
||||||
if (kButtonRegex.Matches(remainder)) {
|
if (kButtonRegex.Matches(remainder)) {
|
||||||
kButtonRegex.GetMatch(&start, &length, 1);
|
kButtonRegex.GetMatch(&start, &length, 1);
|
||||||
const int key = StringToInt(remainder.Mid(start, length));
|
const int key = StringToInt(remainder.Mid(start, length));
|
||||||
return UserInput(key, wxJoyControl::Button, joy);
|
return UserInput(key, JoyControl::Button, JoyId(joy - 1));
|
||||||
}
|
}
|
||||||
if (kHatRegex.Matches(remainder)) {
|
if (kHatRegex.Matches(remainder)) {
|
||||||
kHatRegex.GetMatch(&start, &length, 1);
|
kHatRegex.GetMatch(&start, &length, 1);
|
||||||
const int key = StringToInt(remainder.Mid(start, length));
|
const int key = StringToInt(remainder.Mid(start, length));
|
||||||
if (kHatRegex.GetMatch(remainder, 3).Length()) {
|
if (kHatRegex.GetMatch(remainder, 3).Length()) {
|
||||||
return UserInput(key, wxJoyControl::HatNorth, joy);
|
return UserInput(key, JoyControl::HatNorth, JoyId(joy - 1));
|
||||||
} else if (kHatRegex.GetMatch(remainder, 4).Length()) {
|
} else if (kHatRegex.GetMatch(remainder, 4).Length()) {
|
||||||
return UserInput(key, wxJoyControl::HatSouth, joy);
|
return UserInput(key, JoyControl::HatSouth, JoyId(joy - 1));
|
||||||
} else if (kHatRegex.GetMatch(remainder, 5).Length()) {
|
} else if (kHatRegex.GetMatch(remainder, 5).Length()) {
|
||||||
return UserInput(key, wxJoyControl::HatEast, joy);
|
return UserInput(key, JoyControl::HatEast, JoyId(joy - 1));
|
||||||
} else if (kHatRegex.GetMatch(remainder, 6).Length()) {
|
} else if (kHatRegex.GetMatch(remainder, 6).Length()) {
|
||||||
return UserInput(key, wxJoyControl::HatWest, joy);
|
return UserInput(key, JoyControl::HatWest, JoyId(joy - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,14 +266,47 @@ UserInput StringToUserInput(const wxString& string) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
UserInput::UserInput(const wxKeyEvent& event)
|
// static
|
||||||
: UserInput(Device::Keyboard, event.GetModifiers(), getKeyboardKeyCode(event), 0) {}
|
JoyId JoyId::Invalid() {
|
||||||
|
static constexpr int kInvalidSdlIndex = -1;
|
||||||
|
return JoyId(kInvalidSdlIndex);
|
||||||
|
}
|
||||||
|
|
||||||
UserInput::UserInput(const wxJoyEvent& event)
|
wxString JoyId::ToString() {
|
||||||
|
return wxString::Format("Joy%d", sdl_index_ + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JoyId::operator==(const JoyId& other) const {
|
||||||
|
return sdl_index_ == other.sdl_index_;
|
||||||
|
}
|
||||||
|
bool JoyId::operator!=(const JoyId& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
bool JoyId::operator<(const JoyId& other) const {
|
||||||
|
return sdl_index_ < other.sdl_index_;
|
||||||
|
}
|
||||||
|
bool JoyId::operator<=(const JoyId& other) const {
|
||||||
|
return !(*this > other);
|
||||||
|
}
|
||||||
|
bool JoyId::operator>(const JoyId& other) const {
|
||||||
|
return other < *this;
|
||||||
|
}
|
||||||
|
bool JoyId::operator>=(const JoyId& other) const {
|
||||||
|
return !(*this < other);
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyId::JoyId(int sdl_index) : sdl_index_(sdl_index) {}
|
||||||
|
|
||||||
|
UserInput::UserInput(uint8_t control_index, JoyControl control, JoyId joystick)
|
||||||
: UserInput(Device::Joystick,
|
: UserInput(Device::Joystick,
|
||||||
event.control(),
|
static_cast<int>(control),
|
||||||
event.control_index(),
|
control_index,
|
||||||
event.joystick().player_index()) {}
|
joystick.sdl_index_ + 1) {}
|
||||||
|
|
||||||
|
UserInput::UserInput(wxKeyCode key, wxKeyModifier mod)
|
||||||
|
: UserInput(Device::Keyboard, mod, key, 0) {}
|
||||||
|
|
||||||
|
UserInput::UserInput(char c, wxKeyModifier mod) : UserInput(Device::Keyboard, mod, c, 0) {}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
std::set<UserInput> UserInput::FromConfigString(const wxString& string) {
|
std::set<UserInput> UserInput::FromConfigString(const wxString& string) {
|
||||||
|
@ -315,26 +347,26 @@ wxString UserInput::ToConfigString() const {
|
||||||
return KeyboardInputToConfigString(mod_, key_);
|
return KeyboardInputToConfigString(mod_, key_);
|
||||||
case Device::Joystick:
|
case Device::Joystick:
|
||||||
wxString key;
|
wxString key;
|
||||||
switch (mod_) {
|
switch (static_cast<JoyControl>(mod_)) {
|
||||||
case wxJoyControl::AxisPlus:
|
case JoyControl::AxisPlus:
|
||||||
key = wxString::Format(("Axis%d+"), key_);
|
key = wxString::Format(("Axis%d+"), key_);
|
||||||
break;
|
break;
|
||||||
case wxJoyControl::AxisMinus:
|
case JoyControl::AxisMinus:
|
||||||
key = wxString::Format(("Axis%d-"), key_);
|
key = wxString::Format(("Axis%d-"), key_);
|
||||||
break;
|
break;
|
||||||
case wxJoyControl::Button:
|
case JoyControl::Button:
|
||||||
key = wxString::Format(("Button%d"), key_);
|
key = wxString::Format(("Button%d"), key_);
|
||||||
break;
|
break;
|
||||||
case wxJoyControl::HatNorth:
|
case JoyControl::HatNorth:
|
||||||
key = wxString::Format(("Hat%dN"), key_);
|
key = wxString::Format(("Hat%dN"), key_);
|
||||||
break;
|
break;
|
||||||
case wxJoyControl::HatSouth:
|
case JoyControl::HatSouth:
|
||||||
key = wxString::Format(("Hat%dS"), key_);
|
key = wxString::Format(("Hat%dS"), key_);
|
||||||
break;
|
break;
|
||||||
case wxJoyControl::HatWest:
|
case JoyControl::HatWest:
|
||||||
key = wxString::Format(("Hat%dW"), key_);
|
key = wxString::Format(("Hat%dW"), key_);
|
||||||
break;
|
break;
|
||||||
case wxJoyControl::HatEast:
|
case JoyControl::HatEast:
|
||||||
key = wxString::Format(("Hat%dE"), key_);
|
key = wxString::Format(("Hat%dE"), key_);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,56 @@
|
||||||
#ifndef VBAM_WX_CONFIG_USER_INPUT_H_
|
#ifndef VBAM_WX_CONFIG_USER_INPUT_H_
|
||||||
#define VBAM_WX_CONFIG_USER_INPUT_H_
|
#define VBAM_WX_CONFIG_USER_INPUT_H_
|
||||||
|
|
||||||
#include <wx/event.h>
|
#include <cstdint>
|
||||||
#include <wx/string.h>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "wx/widgets/sdljoy.h"
|
#include <wx/event.h>
|
||||||
|
#include <wx/log.h>
|
||||||
|
#include <wx/string.h>
|
||||||
|
|
||||||
namespace config {
|
namespace config {
|
||||||
|
|
||||||
|
// Forward declaration.
|
||||||
|
class UserInput;
|
||||||
|
|
||||||
|
// One of the possible joystick controls.
|
||||||
|
enum class JoyControl {
|
||||||
|
AxisPlus = 0,
|
||||||
|
AxisMinus,
|
||||||
|
Button,
|
||||||
|
HatNorth,
|
||||||
|
HatSouth,
|
||||||
|
HatWest,
|
||||||
|
HatEast,
|
||||||
|
Last = HatEast
|
||||||
|
};
|
||||||
|
|
||||||
|
// Abstraction for a single joystick. In the current implementation, this
|
||||||
|
// encapsulates an `sdl_index_`.
|
||||||
|
class JoyId {
|
||||||
|
public:
|
||||||
|
static JoyId Invalid();
|
||||||
|
|
||||||
|
explicit JoyId(int sdl_index);
|
||||||
|
virtual ~JoyId() = default;
|
||||||
|
|
||||||
|
wxString ToString();
|
||||||
|
|
||||||
|
bool operator==(const JoyId& other) const;
|
||||||
|
bool operator!=(const JoyId& other) const;
|
||||||
|
bool operator<(const JoyId& other) const;
|
||||||
|
bool operator<=(const JoyId& other) const;
|
||||||
|
bool operator>(const JoyId& other) const;
|
||||||
|
bool operator>=(const JoyId& other) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
JoyId() = delete;
|
||||||
|
|
||||||
|
int sdl_index_;
|
||||||
|
|
||||||
|
friend class UserInput;
|
||||||
|
};
|
||||||
|
|
||||||
// Abstraction for a user input, which can come from a keyboard or a joystick.
|
// 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
|
// This class implements comparison operators so it can be used in sets and as
|
||||||
// a key in maps.
|
// a key in maps.
|
||||||
|
@ -34,14 +76,15 @@ public:
|
||||||
// Invalid UserInput, mainly used for comparison.
|
// Invalid UserInput, mainly used for comparison.
|
||||||
UserInput() : UserInput(Device::Invalid, 0, 0, 0) {}
|
UserInput() : UserInput(Device::Invalid, 0, 0, 0) {}
|
||||||
|
|
||||||
// Constructor from a wxKeyEvent.
|
// Constructor for a joystick input.
|
||||||
UserInput(const wxKeyEvent& event);
|
UserInput(uint8_t control_index, JoyControl control, JoyId joystick);
|
||||||
|
|
||||||
// Constructor from a wxJoyEvent.
|
// Constructors for a keyboard input.
|
||||||
UserInput(const wxJoyEvent& event);
|
UserInput(wxKeyCode key, wxKeyModifier mod = wxMOD_NONE);
|
||||||
|
UserInput(char c, wxKeyModifier mod = wxMOD_NONE);
|
||||||
|
|
||||||
// TODO: Remove this once all uses have been removed.
|
// TODO: Remove this once all uses have been removed.
|
||||||
UserInput(int key, int mod = 0, int joy = 0)
|
explicit UserInput(int key, int mod = 0, int joy = 0)
|
||||||
: UserInput(joy == 0 ? Device::Keyboard : Device::Joystick,
|
: UserInput(joy == 0 ? Device::Keyboard : Device::Joystick,
|
||||||
mod,
|
mod,
|
||||||
key,
|
key,
|
||||||
|
@ -55,14 +98,27 @@ public:
|
||||||
// Converts to a localized string for display.
|
// Converts to a localized string for display.
|
||||||
wxString ToLocalizedString() const;
|
wxString ToLocalizedString() const;
|
||||||
|
|
||||||
wxJoystick joystick() const { return joystick_; }
|
JoyId joystick() const { return joystick_; }
|
||||||
constexpr bool is_valid() const { return device_ != Device::Invalid; }
|
constexpr bool is_valid() const { return device_ != Device::Invalid; }
|
||||||
constexpr operator bool() const { return is_valid(); }
|
constexpr operator bool() const { return is_valid(); }
|
||||||
|
|
||||||
|
bool is_joystick() const { return device_ == Device::Joystick; }
|
||||||
|
bool is_keyboard() const { return device_ == Device::Keyboard; }
|
||||||
|
|
||||||
int key() const { return key_; }
|
int key() const { return key_; }
|
||||||
int mod() const { return mod_; }
|
int mod() const { return mod_; }
|
||||||
unsigned joy() const { return joy_; }
|
unsigned joy() const { return joy_; }
|
||||||
|
|
||||||
|
JoyControl joy_control() const {
|
||||||
|
assert(is_joystick());
|
||||||
|
return static_cast<JoyControl>(mod_);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxKeyCode key_code() const {
|
||||||
|
assert(is_keyboard());
|
||||||
|
return static_cast<wxKeyCode>(key_);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr bool operator==(const UserInput& other) const {
|
constexpr bool operator==(const UserInput& other) const {
|
||||||
return device_ == other.device_ && mod_ == other.mod_ &&
|
return device_ == other.device_ && mod_ == other.mod_ &&
|
||||||
key_ == other.key_ && joy_ == other.joy_;
|
key_ == other.key_ && joy_ == other.joy_;
|
||||||
|
@ -98,14 +154,14 @@ public:
|
||||||
private:
|
private:
|
||||||
UserInput(Device device, int mod, int key, unsigned joy)
|
UserInput(Device device, int mod, int key, unsigned joy)
|
||||||
: device_(device),
|
: device_(device),
|
||||||
joystick_(joy == 0 ? wxJoystick::Invalid()
|
joystick_(joy == 0 ? JoyId::Invalid()
|
||||||
: wxJoystick::FromLegacyPlayerIndex(joy)),
|
: JoyId(joy - 1)),
|
||||||
mod_(mod),
|
mod_(mod),
|
||||||
key_(key),
|
key_(key),
|
||||||
joy_(joy) {}
|
joy_(joy) {}
|
||||||
|
|
||||||
Device device_;
|
Device device_;
|
||||||
wxJoystick joystick_;
|
JoyId joystick_;
|
||||||
int mod_;
|
int mod_;
|
||||||
int key_;
|
int key_;
|
||||||
unsigned joy_;
|
unsigned joy_;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
#include "wx/dialogs/joypad-config.h"
|
#include "wx/dialogs/joypad-config.h"
|
||||||
|
|
||||||
#include <wx/xrc/xmlres.h>
|
#include <wx/checkbox.h>
|
||||||
|
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
#include "wx/dialogs/base-dialog.h"
|
#include "wx/dialogs/base-dialog.h"
|
||||||
|
#include "wx/opts.h"
|
||||||
#include "wx/widgets/option-validator.h"
|
#include "wx/widgets/option-validator.h"
|
||||||
#include "wx/widgets/user-input-ctrl.h"
|
#include "wx/widgets/user-input-ctrl.h"
|
||||||
#include "wx/widgets/utils.h"
|
#include "wx/widgets/utils.h"
|
||||||
#include "wx/wxvbam.h"
|
|
||||||
|
|
||||||
namespace dialogs {
|
namespace dialogs {
|
||||||
|
|
||||||
|
@ -87,7 +87,6 @@ void JoypadConfig::ToggleSDLGameControllerMode() {
|
||||||
OPTION(kSDLGameControllerMode) =
|
OPTION(kSDLGameControllerMode) =
|
||||||
GetValidatedChild<wxCheckBox>("SDLGameControllerMode")->IsChecked();
|
GetValidatedChild<wxCheckBox>("SDLGameControllerMode")->IsChecked();
|
||||||
ClearAllJoypads();
|
ClearAllJoypads();
|
||||||
wxGetApp().frame->PollAllJoysticks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoypadConfig::ClearAllJoypads() {
|
void JoypadConfig::ClearAllJoypads() {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define VBAM_WX_DIALOGS_JOYPAD_CONFIG_H_
|
#define VBAM_WX_DIALOGS_JOYPAD_CONFIG_H_
|
||||||
|
|
||||||
#include "wx/dialogs/base-dialog.h"
|
#include "wx/dialogs/base-dialog.h"
|
||||||
|
|
||||||
namespace dialogs {
|
namespace dialogs {
|
||||||
|
|
||||||
// Manages the Joypad configuration dialog.
|
// Manages the Joypad configuration dialog.
|
||||||
|
|
247
src/wx/opts.cpp
247
src/wx/opts.cpp
|
@ -105,142 +105,151 @@ uint32_t LoadUnsignedOption(wxConfigBase* cfg,
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#define WJKB config::UserInput
|
|
||||||
|
|
||||||
opts_t gopts;
|
opts_t gopts;
|
||||||
|
|
||||||
const std::map<config::GameControl, std::set<config::UserInput>> kDefaultBindings = {
|
const std::map<config::GameControl, std::set<config::UserInput>> kDefaultBindings = {
|
||||||
{ config::GameControl(0, config::GameKey::Up), {
|
{config::GameControl(0, config::GameKey::Up),
|
||||||
WJKB('W'),
|
{
|
||||||
WJKB(11, wxJoyControl::Button, 1),
|
config::UserInput('W'),
|
||||||
WJKB(1, wxJoyControl::AxisMinus, 1),
|
config::UserInput(11, config::JoyControl::Button, config::JoyId(0)),
|
||||||
WJKB(3, wxJoyControl::AxisMinus, 1),
|
config::UserInput(1, config::JoyControl::AxisMinus, config::JoyId(0)),
|
||||||
|
config::UserInput(3, config::JoyControl::AxisMinus, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::Down), {
|
{config::GameControl(0, config::GameKey::Down),
|
||||||
WJKB('S'),
|
{
|
||||||
WJKB(12, wxJoyControl::Button, 1),
|
config::UserInput('S'),
|
||||||
WJKB(1, wxJoyControl::AxisPlus, 1),
|
config::UserInput(12, config::JoyControl::Button, config::JoyId(0)),
|
||||||
WJKB(3, wxJoyControl::AxisPlus, 1),
|
config::UserInput(1, config::JoyControl::AxisPlus, config::JoyId(0)),
|
||||||
|
config::UserInput(3, config::JoyControl::AxisPlus, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::Left), {
|
{config::GameControl(0, config::GameKey::Left),
|
||||||
WJKB('A'),
|
{
|
||||||
WJKB(13, wxJoyControl::Button, 1),
|
config::UserInput('A'),
|
||||||
WJKB(0, wxJoyControl::AxisMinus, 1),
|
config::UserInput(13, config::JoyControl::Button, config::JoyId(0)),
|
||||||
WJKB(2, wxJoyControl::AxisMinus, 1),
|
config::UserInput(0, config::JoyControl::AxisMinus, config::JoyId(0)),
|
||||||
|
config::UserInput(2, config::JoyControl::AxisMinus, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::Right), {
|
{config::GameControl(0, config::GameKey::Right),
|
||||||
WJKB('D'),
|
{
|
||||||
WJKB(14, wxJoyControl::Button, 1),
|
config::UserInput('D'),
|
||||||
WJKB(0, wxJoyControl::AxisPlus, 1),
|
config::UserInput(14, config::JoyControl::Button, config::JoyId(0)),
|
||||||
WJKB(2, wxJoyControl::AxisPlus, 1),
|
config::UserInput(0, config::JoyControl::AxisPlus, config::JoyId(0)),
|
||||||
|
config::UserInput(2, config::JoyControl::AxisPlus, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::A), {
|
{config::GameControl(0, config::GameKey::A),
|
||||||
WJKB('L'),
|
{
|
||||||
WJKB(0, wxJoyControl::Button, 1),
|
config::UserInput('L'),
|
||||||
|
config::UserInput(0, config::JoyControl::Button, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::B), {
|
{config::GameControl(0, config::GameKey::B),
|
||||||
WJKB('K'),
|
{
|
||||||
WJKB(1, wxJoyControl::Button, 1),
|
config::UserInput('K'),
|
||||||
|
config::UserInput(1, config::JoyControl::Button, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::L), {
|
{config::GameControl(0, config::GameKey::L),
|
||||||
WJKB('I'),
|
{
|
||||||
WJKB(2, wxJoyControl::Button, 1),
|
config::UserInput('I'),
|
||||||
WJKB(9, wxJoyControl::Button, 1),
|
config::UserInput(2, config::JoyControl::Button, config::JoyId(0)),
|
||||||
WJKB(4, wxJoyControl::AxisPlus, 1),
|
config::UserInput(9, config::JoyControl::Button, config::JoyId(0)),
|
||||||
|
config::UserInput(4, config::JoyControl::AxisPlus, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::R), {
|
{config::GameControl(0, config::GameKey::R),
|
||||||
WJKB('O'),
|
{
|
||||||
WJKB(3, wxJoyControl::Button, 1),
|
config::UserInput('O'),
|
||||||
WJKB(10, wxJoyControl::Button, 1),
|
config::UserInput(3, config::JoyControl::Button, config::JoyId(0)),
|
||||||
WJKB(5, wxJoyControl::AxisPlus, 1),
|
config::UserInput(10, config::JoyControl::Button, config::JoyId(0)),
|
||||||
|
config::UserInput(5, config::JoyControl::AxisPlus, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::Select), {
|
{config::GameControl(0, config::GameKey::Select),
|
||||||
WJKB(WXK_BACK),
|
{
|
||||||
WJKB(4, wxJoyControl::Button, 1),
|
config::UserInput(WXK_BACK),
|
||||||
|
config::UserInput(4, config::JoyControl::Button, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::Start), {
|
{config::GameControl(0, config::GameKey::Start),
|
||||||
WJKB(WXK_RETURN),
|
{
|
||||||
WJKB(6, wxJoyControl::Button, 1),
|
config::UserInput(WXK_RETURN),
|
||||||
|
config::UserInput(6, config::JoyControl::Button, config::JoyId(0)),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::MotionUp), {}},
|
{config::GameControl(0, config::GameKey::MotionUp), {}},
|
||||||
{ config::GameControl(0, config::GameKey::MotionDown), {}},
|
{config::GameControl(0, config::GameKey::MotionDown), {}},
|
||||||
{ config::GameControl(0, config::GameKey::MotionLeft), {}},
|
{config::GameControl(0, config::GameKey::MotionLeft), {}},
|
||||||
{ config::GameControl(0, config::GameKey::MotionRight), {}},
|
{config::GameControl(0, config::GameKey::MotionRight), {}},
|
||||||
{ config::GameControl(0, config::GameKey::MotionIn), {}},
|
{config::GameControl(0, config::GameKey::MotionIn), {}},
|
||||||
{ config::GameControl(0, config::GameKey::MotionOut), {}},
|
{config::GameControl(0, config::GameKey::MotionOut), {}},
|
||||||
{ config::GameControl(0, config::GameKey::AutoA), {}},
|
{config::GameControl(0, config::GameKey::AutoA), {}},
|
||||||
{ config::GameControl(0, config::GameKey::AutoB), {}},
|
{config::GameControl(0, config::GameKey::AutoB), {}},
|
||||||
{ config::GameControl(0, config::GameKey::Speed), {
|
{config::GameControl(0, config::GameKey::Speed),
|
||||||
WJKB(WXK_SPACE),
|
{
|
||||||
|
config::UserInput(WXK_SPACE),
|
||||||
}},
|
}},
|
||||||
{ config::GameControl(0, config::GameKey::Capture), {}},
|
{config::GameControl(0, config::GameKey::Capture), {}},
|
||||||
{ config::GameControl(0, config::GameKey::Gameshark), {}},
|
{config::GameControl(0, config::GameKey::Gameshark), {}},
|
||||||
|
|
||||||
{ config::GameControl(1, config::GameKey::Up), {}},
|
{config::GameControl(1, config::GameKey::Up), {}},
|
||||||
{ config::GameControl(1, config::GameKey::Down), {}},
|
{config::GameControl(1, config::GameKey::Down), {}},
|
||||||
{ config::GameControl(1, config::GameKey::Left), {}},
|
{config::GameControl(1, config::GameKey::Left), {}},
|
||||||
{ config::GameControl(1, config::GameKey::Right), {}},
|
{config::GameControl(1, config::GameKey::Right), {}},
|
||||||
{ config::GameControl(1, config::GameKey::A), {}},
|
{config::GameControl(1, config::GameKey::A), {}},
|
||||||
{ config::GameControl(1, config::GameKey::B), {}},
|
{config::GameControl(1, config::GameKey::B), {}},
|
||||||
{ config::GameControl(1, config::GameKey::L), {}},
|
{config::GameControl(1, config::GameKey::L), {}},
|
||||||
{ config::GameControl(1, config::GameKey::R), {}},
|
{config::GameControl(1, config::GameKey::R), {}},
|
||||||
{ config::GameControl(1, config::GameKey::Select), {}},
|
{config::GameControl(1, config::GameKey::Select), {}},
|
||||||
{ config::GameControl(1, config::GameKey::Start), {}},
|
{config::GameControl(1, config::GameKey::Start), {}},
|
||||||
{ config::GameControl(1, config::GameKey::MotionUp), {}},
|
{config::GameControl(1, config::GameKey::MotionUp), {}},
|
||||||
{ config::GameControl(1, config::GameKey::MotionDown), {}},
|
{config::GameControl(1, config::GameKey::MotionDown), {}},
|
||||||
{ config::GameControl(1, config::GameKey::MotionLeft), {}},
|
{config::GameControl(1, config::GameKey::MotionLeft), {}},
|
||||||
{ config::GameControl(1, config::GameKey::MotionRight), {}},
|
{config::GameControl(1, config::GameKey::MotionRight), {}},
|
||||||
{ config::GameControl(1, config::GameKey::MotionIn), {}},
|
{config::GameControl(1, config::GameKey::MotionIn), {}},
|
||||||
{ config::GameControl(1, config::GameKey::MotionOut), {}},
|
{config::GameControl(1, config::GameKey::MotionOut), {}},
|
||||||
{ config::GameControl(1, config::GameKey::AutoA), {}},
|
{config::GameControl(1, config::GameKey::AutoA), {}},
|
||||||
{ config::GameControl(1, config::GameKey::AutoB), {}},
|
{config::GameControl(1, config::GameKey::AutoB), {}},
|
||||||
{ config::GameControl(1, config::GameKey::Speed), {}},
|
{config::GameControl(1, config::GameKey::Speed), {}},
|
||||||
{ config::GameControl(1, config::GameKey::Capture), {}},
|
{config::GameControl(1, config::GameKey::Capture), {}},
|
||||||
{ config::GameControl(1, config::GameKey::Gameshark), {}},
|
{config::GameControl(1, config::GameKey::Gameshark), {}},
|
||||||
|
|
||||||
{ config::GameControl(2, config::GameKey::Up), {}},
|
{config::GameControl(2, config::GameKey::Up), {}},
|
||||||
{ config::GameControl(2, config::GameKey::Down), {}},
|
{config::GameControl(2, config::GameKey::Down), {}},
|
||||||
{ config::GameControl(2, config::GameKey::Left), {}},
|
{config::GameControl(2, config::GameKey::Left), {}},
|
||||||
{ config::GameControl(2, config::GameKey::Right), {}},
|
{config::GameControl(2, config::GameKey::Right), {}},
|
||||||
{ config::GameControl(2, config::GameKey::A), {}},
|
{config::GameControl(2, config::GameKey::A), {}},
|
||||||
{ config::GameControl(2, config::GameKey::B), {}},
|
{config::GameControl(2, config::GameKey::B), {}},
|
||||||
{ config::GameControl(2, config::GameKey::L), {}},
|
{config::GameControl(2, config::GameKey::L), {}},
|
||||||
{ config::GameControl(2, config::GameKey::R), {}},
|
{config::GameControl(2, config::GameKey::R), {}},
|
||||||
{ config::GameControl(2, config::GameKey::Select), {}},
|
{config::GameControl(2, config::GameKey::Select), {}},
|
||||||
{ config::GameControl(2, config::GameKey::Start), {}},
|
{config::GameControl(2, config::GameKey::Start), {}},
|
||||||
{ config::GameControl(2, config::GameKey::MotionUp), {}},
|
{config::GameControl(2, config::GameKey::MotionUp), {}},
|
||||||
{ config::GameControl(2, config::GameKey::MotionDown), {}},
|
{config::GameControl(2, config::GameKey::MotionDown), {}},
|
||||||
{ config::GameControl(2, config::GameKey::MotionLeft), {}},
|
{config::GameControl(2, config::GameKey::MotionLeft), {}},
|
||||||
{ config::GameControl(2, config::GameKey::MotionRight), {}},
|
{config::GameControl(2, config::GameKey::MotionRight), {}},
|
||||||
{ config::GameControl(2, config::GameKey::MotionIn), {}},
|
{config::GameControl(2, config::GameKey::MotionIn), {}},
|
||||||
{ config::GameControl(2, config::GameKey::MotionOut), {}},
|
{config::GameControl(2, config::GameKey::MotionOut), {}},
|
||||||
{ config::GameControl(2, config::GameKey::AutoA), {}},
|
{config::GameControl(2, config::GameKey::AutoA), {}},
|
||||||
{ config::GameControl(2, config::GameKey::AutoB), {}},
|
{config::GameControl(2, config::GameKey::AutoB), {}},
|
||||||
{ config::GameControl(2, config::GameKey::Speed), {}},
|
{config::GameControl(2, config::GameKey::Speed), {}},
|
||||||
{ config::GameControl(2, config::GameKey::Capture), {}},
|
{config::GameControl(2, config::GameKey::Capture), {}},
|
||||||
{ config::GameControl(2, config::GameKey::Gameshark), {}},
|
{config::GameControl(2, config::GameKey::Gameshark), {}},
|
||||||
|
|
||||||
{ config::GameControl(3, config::GameKey::Up), {}},
|
{config::GameControl(3, config::GameKey::Up), {}},
|
||||||
{ config::GameControl(3, config::GameKey::Down), {}},
|
{config::GameControl(3, config::GameKey::Down), {}},
|
||||||
{ config::GameControl(3, config::GameKey::Left), {}},
|
{config::GameControl(3, config::GameKey::Left), {}},
|
||||||
{ config::GameControl(3, config::GameKey::Right), {}},
|
{config::GameControl(3, config::GameKey::Right), {}},
|
||||||
{ config::GameControl(3, config::GameKey::A), {}},
|
{config::GameControl(3, config::GameKey::A), {}},
|
||||||
{ config::GameControl(3, config::GameKey::B), {}},
|
{config::GameControl(3, config::GameKey::B), {}},
|
||||||
{ config::GameControl(3, config::GameKey::L), {}},
|
{config::GameControl(3, config::GameKey::L), {}},
|
||||||
{ config::GameControl(3, config::GameKey::R), {}},
|
{config::GameControl(3, config::GameKey::R), {}},
|
||||||
{ config::GameControl(3, config::GameKey::Select), {}},
|
{config::GameControl(3, config::GameKey::Select), {}},
|
||||||
{ config::GameControl(3, config::GameKey::Start), {}},
|
{config::GameControl(3, config::GameKey::Start), {}},
|
||||||
{ config::GameControl(3, config::GameKey::MotionUp), {}},
|
{config::GameControl(3, config::GameKey::MotionUp), {}},
|
||||||
{ config::GameControl(3, config::GameKey::MotionDown), {}},
|
{config::GameControl(3, config::GameKey::MotionDown), {}},
|
||||||
{ config::GameControl(3, config::GameKey::MotionLeft), {}},
|
{config::GameControl(3, config::GameKey::MotionLeft), {}},
|
||||||
{ config::GameControl(3, config::GameKey::MotionRight), {}},
|
{config::GameControl(3, config::GameKey::MotionRight), {}},
|
||||||
{ config::GameControl(3, config::GameKey::MotionIn), {}},
|
{config::GameControl(3, config::GameKey::MotionIn), {}},
|
||||||
{ config::GameControl(3, config::GameKey::MotionOut), {}},
|
{config::GameControl(3, config::GameKey::MotionOut), {}},
|
||||||
{ config::GameControl(3, config::GameKey::AutoA), {}},
|
{config::GameControl(3, config::GameKey::AutoA), {}},
|
||||||
{ config::GameControl(3, config::GameKey::AutoB), {}},
|
{config::GameControl(3, config::GameKey::AutoB), {}},
|
||||||
{ config::GameControl(3, config::GameKey::Speed), {}},
|
{config::GameControl(3, config::GameKey::Speed), {}},
|
||||||
{ config::GameControl(3, config::GameKey::Capture), {}},
|
{config::GameControl(3, config::GameKey::Capture), {}},
|
||||||
{ config::GameControl(3, config::GameKey::Gameshark), {}},
|
{config::GameControl(3, config::GameKey::Gameshark), {}},
|
||||||
};
|
};
|
||||||
|
|
||||||
// This constructor only works with globally allocated gopts.
|
// This constructor only works with globally allocated gopts.
|
||||||
|
|
157
src/wx/panel.cpp
157
src/wx/panel.cpp
|
@ -1,10 +1,8 @@
|
||||||
|
#include "wx/wxvbam.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "components/filters_agb/filters_agb.h"
|
|
||||||
#include "components/filters_interframe/interframe.h"
|
|
||||||
#include "core/base/system.h"
|
|
||||||
#include "wx/config/option-id.h"
|
|
||||||
|
|
||||||
#ifdef __WXGTK__
|
#ifdef __WXGTK__
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
@ -22,13 +20,14 @@
|
||||||
|
|
||||||
#include <wx/dcbuffer.h>
|
#include <wx/dcbuffer.h>
|
||||||
#include <wx/menu.h>
|
#include <wx/menu.h>
|
||||||
#include <SDL_joystick.h>
|
|
||||||
|
|
||||||
#include "background-input.h"
|
|
||||||
#include "components/draw_text/draw_text.h"
|
#include "components/draw_text/draw_text.h"
|
||||||
#include "components/filters/filters.h"
|
#include "components/filters/filters.h"
|
||||||
|
#include "components/filters_agb/filters_agb.h"
|
||||||
|
#include "components/filters_interframe/interframe.h"
|
||||||
#include "core/base/file_util.h"
|
#include "core/base/file_util.h"
|
||||||
#include "core/base/patch.h"
|
#include "core/base/patch.h"
|
||||||
|
#include "core/base/system.h"
|
||||||
#include "core/base/version.h"
|
#include "core/base/version.h"
|
||||||
#include "core/gb/gb.h"
|
#include "core/gb/gb.h"
|
||||||
#include "core/gb/gbCheats.h"
|
#include "core/gb/gbCheats.h"
|
||||||
|
@ -41,15 +40,15 @@
|
||||||
#include "core/gba/gbaPrint.h"
|
#include "core/gba/gbaPrint.h"
|
||||||
#include "core/gba/gbaRtc.h"
|
#include "core/gba/gbaRtc.h"
|
||||||
#include "core/gba/gbaSound.h"
|
#include "core/gba/gbaSound.h"
|
||||||
|
#include "wx/background-input.h"
|
||||||
#include "wx/config/game-control.h"
|
#include "wx/config/game-control.h"
|
||||||
|
#include "wx/config/option-id.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
#include "wx/config/option.h"
|
#include "wx/config/option.h"
|
||||||
#include "wx/config/user-input.h"
|
|
||||||
#include "wx/drawing.h"
|
#include "wx/drawing.h"
|
||||||
#include "wx/wayland.h"
|
#include "wx/wayland.h"
|
||||||
#include "wx/widgets/render-plugin.h"
|
#include "wx/widgets/render-plugin.h"
|
||||||
#include "wx/wxutil.h"
|
#include "wx/widgets/user-input-event.h"
|
||||||
#include "wx/wxvbam.h"
|
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -165,6 +164,7 @@ GameArea::GameArea()
|
||||||
config::OptionID::kSoundBuffers, config::OptionID::kSoundDSoundHWAccel,
|
config::OptionID::kSoundBuffers, config::OptionID::kSoundDSoundHWAccel,
|
||||||
config::OptionID::kSoundUpmix},
|
config::OptionID::kSoundUpmix},
|
||||||
[&](config::Option*) { schedule_audio_restart_ = true; }) {
|
[&](config::Option*) { schedule_audio_restart_ = true; }) {
|
||||||
|
this->SetClientObject(new widgets::UserInputEventSender(this));
|
||||||
SetSizer(new wxBoxSizer(wxVERTICAL));
|
SetSizer(new wxBoxSizer(wxVERTICAL));
|
||||||
// all renderers prefer 32-bit
|
// all renderers prefer 32-bit
|
||||||
// well, "simple" prefers 24-bit, but that's not available for filters
|
// well, "simple" prefers 24-bit, but that's not available for filters
|
||||||
|
@ -440,8 +440,6 @@ void GameArea::LoadGame(const wxString& name)
|
||||||
was_paused = true;
|
was_paused = true;
|
||||||
schedule_audio_restart_ = false;
|
schedule_audio_restart_ = false;
|
||||||
MainFrame* mf = wxGetApp().frame;
|
MainFrame* mf = wxGetApp().frame;
|
||||||
mf->StopJoyPollTimer();
|
|
||||||
mf->SetJoystick();
|
|
||||||
mf->cmd_enable &= ~(CMDEN_GB | CMDEN_GBA);
|
mf->cmd_enable &= ~(CMDEN_GB | CMDEN_GBA);
|
||||||
mf->cmd_enable |= ONLOAD_CMDEN;
|
mf->cmd_enable |= ONLOAD_CMDEN;
|
||||||
mf->cmd_enable |= loaded == IMAGE_GB ? CMDEN_GB : (CMDEN_GBA | CMDEN_NGDB_GBA);
|
mf->cmd_enable |= loaded == IMAGE_GB ? CMDEN_GB : (CMDEN_GBA | CMDEN_NGDB_GBA);
|
||||||
|
@ -697,8 +695,6 @@ void GameArea::UnloadGame(bool destruct)
|
||||||
mf->cmd_enable &= UNLOAD_CMDEN_KEEP;
|
mf->cmd_enable &= UNLOAD_CMDEN_KEEP;
|
||||||
mf->update_state_ts(true);
|
mf->update_state_ts(true);
|
||||||
mf->enable_menus();
|
mf->enable_menus();
|
||||||
mf->StartJoyPollTimer();
|
|
||||||
mf->SetJoystick();
|
|
||||||
mf->ResetCheatSearch();
|
mf->ResetCheatSearch();
|
||||||
|
|
||||||
if (rewind_mem)
|
if (rewind_mem)
|
||||||
|
@ -1087,8 +1083,6 @@ void GameArea::Pause()
|
||||||
|
|
||||||
if (loaded != IMAGE_UNKNOWN)
|
if (loaded != IMAGE_UNKNOWN)
|
||||||
soundPause();
|
soundPause();
|
||||||
|
|
||||||
wxGetApp().frame->StartJoyPollTimer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameArea::Resume()
|
void GameArea::Resume()
|
||||||
|
@ -1103,8 +1097,6 @@ void GameArea::Resume()
|
||||||
if (loaded != IMAGE_UNKNOWN)
|
if (loaded != IMAGE_UNKNOWN)
|
||||||
soundResume();
|
soundResume();
|
||||||
|
|
||||||
wxGetApp().frame->StopJoyPollTimer();
|
|
||||||
|
|
||||||
SetFocus();
|
SetFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1184,31 +1176,32 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
||||||
wxWindow* w = panel->GetWindow();
|
wxWindow* w = panel->GetWindow();
|
||||||
|
|
||||||
// set up event handlers
|
// set up event handlers
|
||||||
w->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GameArea::OnKeyDown), NULL, this);
|
w->Bind(VBAM_EVT_USER_INPUT_DOWN, &GameArea::OnUserInputDown, this);
|
||||||
w->Connect(wxEVT_KEY_UP, wxKeyEventHandler(GameArea::OnKeyUp), NULL, this);
|
w->Bind(VBAM_EVT_USER_INPUT_UP, &GameArea::OnUserInputUp, this);
|
||||||
w->Connect(wxEVT_PAINT, wxPaintEventHandler(GameArea::PaintEv), NULL, this);
|
w->Bind(wxEVT_PAINT, &GameArea::PaintEv, this);
|
||||||
w->Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(GameArea::EraseBackground), NULL, this);
|
w->Bind(wxEVT_ERASE_BACKGROUND, &GameArea::EraseBackground, this);
|
||||||
|
|
||||||
// set userdata so we know it's the panel and not the frame being resized
|
// set userdata so we know it's the panel and not the frame being resized
|
||||||
// the userdata is freed on disconnect/destruction
|
// the userdata is freed on disconnect/destruction
|
||||||
this->Connect(wxEVT_SIZE, wxSizeEventHandler(GameArea::OnSize), NULL, this);
|
this->Bind(wxEVT_SIZE, &GameArea::OnSize, this);
|
||||||
|
|
||||||
// We need to check if the buttons stayed pressed when focus the panel.
|
// We need to check if the buttons stayed pressed when focus the panel.
|
||||||
w->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(GameArea::OnKillFocus), NULL, this);
|
w->Bind(wxEVT_KILL_FOCUS, &GameArea::OnKillFocus, this);
|
||||||
|
|
||||||
// Update mouse last-used timers on mouse events etc..
|
// Update mouse last-used timers on mouse events etc..
|
||||||
w->Connect(wxEVT_MOTION, wxMouseEventHandler(GameArea::MouseEvent), NULL, this);
|
w->Bind(wxEVT_MOTION, &GameArea::MouseEvent, this);
|
||||||
w->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(GameArea::MouseEvent), NULL, this);
|
w->Bind(wxEVT_LEFT_DOWN, &GameArea::MouseEvent, this);
|
||||||
w->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(GameArea::MouseEvent), NULL, this);
|
w->Bind(wxEVT_RIGHT_DOWN, &GameArea::MouseEvent, this);
|
||||||
w->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(GameArea::MouseEvent), NULL, this);
|
w->Bind(wxEVT_MIDDLE_DOWN, &GameArea::MouseEvent, this);
|
||||||
w->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(GameArea::MouseEvent), NULL, this);
|
w->Bind(wxEVT_MOUSEWHEEL, &GameArea::MouseEvent, this);
|
||||||
|
|
||||||
w->SetBackgroundStyle(wxBG_STYLE_CUSTOM);
|
w->SetBackgroundStyle(wxBG_STYLE_CUSTOM);
|
||||||
w->SetSize(wxSize(basic_width, basic_height));
|
w->SetSize(wxSize(basic_width, basic_height));
|
||||||
|
|
||||||
// Allow input while on background
|
// Allow input while on background
|
||||||
if (OPTION(kUIAllowKeyboardBackgroundInput))
|
if (OPTION(kUIAllowKeyboardBackgroundInput)) {
|
||||||
enableKeyboardBackgroundInput(w);
|
enableKeyboardBackgroundInput(w->GetEventHandler());
|
||||||
|
}
|
||||||
|
|
||||||
if (gopts.max_scale)
|
if (gopts.max_scale)
|
||||||
w->SetMaxSize(wxSize(basic_width * gopts.max_scale,
|
w->SetMaxSize(wxSize(basic_width * gopts.max_scale,
|
||||||
|
@ -1251,8 +1244,6 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
||||||
UpdateLcdFilter();
|
UpdateLcdFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
mf->PollJoysticks();
|
|
||||||
|
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
HidePointer();
|
HidePointer();
|
||||||
HideMenuBar();
|
HideMenuBar();
|
||||||
|
@ -1323,14 +1314,6 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool process_user_input(bool down, const config::UserInput& user_input)
|
|
||||||
{
|
|
||||||
if (down)
|
|
||||||
return config::GameControlState::Instance().OnInputPressed(user_input);
|
|
||||||
else
|
|
||||||
return config::GameControlState::Instance().OnInputReleased(user_input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw_black_background(wxWindow* win) {
|
static void draw_black_background(wxWindow* win) {
|
||||||
wxClientDC dc(win);
|
wxClientDC dc(win);
|
||||||
wxCoord w, h;
|
wxCoord w, h;
|
||||||
|
@ -1340,70 +1323,32 @@ static void draw_black_background(wxWindow* win) {
|
||||||
dc.DrawRectangle(0, 0, w, h);
|
dc.DrawRectangle(0, 0, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_keyboard_event(const wxKeyEvent& ev, bool down)
|
|
||||||
{
|
|
||||||
int kc = ev.GetKeyCode();
|
|
||||||
|
|
||||||
// Under Wayland or if the key is unicode, we can't use wxGetKeyState().
|
|
||||||
if (!IsWayland() && kc != WXK_NONE) {
|
|
||||||
// Check if the key state corresponds to the event.
|
|
||||||
if (down != wxGetKeyState(static_cast<wxKeyCode>(kc))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int key = getKeyboardKeyCode(ev);
|
|
||||||
int mod = ev.GetModifiers();
|
|
||||||
|
|
||||||
if (key == WXK_NONE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, config::UserInput(key, mod, 0))) {
|
|
||||||
wxWakeUpIdle();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __WXGTK__
|
#ifdef __WXGTK__
|
||||||
static Display* GetX11Display()
|
static Display* GetX11Display() {
|
||||||
{
|
|
||||||
return GDK_WINDOW_XDISPLAY(gtk_widget_get_window(wxGetApp().frame->GetHandle()));
|
return GDK_WINDOW_XDISPLAY(gtk_widget_get_window(wxGetApp().frame->GetHandle()));
|
||||||
}
|
}
|
||||||
#endif // __WXGTK__
|
#endif // __WXGTK__
|
||||||
|
|
||||||
void GameArea::OnKeyDown(wxKeyEvent& ev)
|
void GameArea::OnUserInputDown(widgets::UserInputEvent& event) {
|
||||||
{
|
if (config::GameControlState::Instance().OnInputPressed(event.input())) {
|
||||||
process_keyboard_event(ev, true);
|
wxWakeUpIdle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameArea::OnKeyUp(wxKeyEvent& ev)
|
void GameArea::OnUserInputUp(widgets::UserInputEvent& event) {
|
||||||
{
|
if (config::GameControlState::Instance().OnInputReleased(event.input())) {
|
||||||
process_keyboard_event(ev, false);
|
wxWakeUpIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tell Linux to turn off the screensaver/screen-blank if joystick button was pressed
|
||||||
|
// this shouldn't be necessary of course
|
||||||
|
#if defined(__WXGTK__) && defined(HAVE_X11) && !defined(HAVE_XSS)
|
||||||
|
if (event.input().is_joystick() && !wxGetApp().UsingWayland()) {
|
||||||
|
auto display = GetX11Display();
|
||||||
|
XResetScreenSaver(display);
|
||||||
|
XFlush(display);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// these three are forwarded to the DrawingPanel instance
|
// these three are forwarded to the DrawingPanel instance
|
||||||
|
@ -1430,24 +1375,8 @@ void GameArea::OnSize(wxSizeEvent& ev)
|
||||||
ev.Skip();
|
ev.Skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameArea::OnSDLJoy(wxJoyEvent& 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
|
|
||||||
#if defined(__WXGTK__) && defined(HAVE_X11) && !defined(HAVE_XSS)
|
|
||||||
if (!wxGetApp().UsingWayland()) {
|
|
||||||
auto display = GetX11Display();
|
|
||||||
XResetScreenSaver(display);
|
|
||||||
XFlush(display);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(GameArea, wxPanel)
|
BEGIN_EVENT_TABLE(GameArea, wxPanel)
|
||||||
EVT_IDLE(GameArea::OnIdle)
|
EVT_IDLE(GameArea::OnIdle)
|
||||||
EVT_SDLJOY(GameArea::OnSDLJoy)
|
|
||||||
// FIXME: wxGTK does not generate motion events in MainFrame (not sure
|
// FIXME: wxGTK does not generate motion events in MainFrame (not sure
|
||||||
// what to do about it)
|
// what to do about it)
|
||||||
EVT_MOUSE_EVENTS(GameArea::MouseEvent)
|
EVT_MOUSE_EVENTS(GameArea::MouseEvent)
|
||||||
|
@ -1525,6 +1454,7 @@ DrawingPanel::DrawingPanel(wxWindow* parent, int _width, int _height)
|
||||||
, wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetClientSize(),
|
, wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetClientSize(),
|
||||||
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
||||||
{
|
{
|
||||||
|
this->SetClientData(new widgets::UserInputEventSender(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawingPanelBase::DrawingPanelInit()
|
void DrawingPanelBase::DrawingPanelInit()
|
||||||
|
@ -2256,6 +2186,7 @@ GLDrawingPanel::GLDrawingPanel(wxWindow* parent, int _width, int _height)
|
||||||
, wxglc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetClientSize(),
|
, wxglc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetClientSize(),
|
||||||
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
||||||
{
|
{
|
||||||
|
this->SetClientData(new widgets::UserInputEventSender(this));
|
||||||
widgets::RequestHighResolutionOpenGlSurfaceForWindow(this);
|
widgets::RequestHighResolutionOpenGlSurfaceForWindow(this);
|
||||||
SetContext();
|
SetContext();
|
||||||
}
|
}
|
||||||
|
|
|
@ -596,7 +596,7 @@ uint32_t systemGetClock()
|
||||||
|
|
||||||
void systemCartridgeRumble(bool b)
|
void systemCartridgeRumble(bool b)
|
||||||
{
|
{
|
||||||
wxGetApp().frame->SetJoystickRumble(b);
|
wxGetApp().sdl_poller()->SetRumble(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t sensorDarkness = 0xE8; // total darkness (including daylight on rainy days)
|
static uint8_t sensorDarkness = 0xE8; // total darkness (including daylight on rainy days)
|
||||||
|
|
|
@ -1,10 +1,38 @@
|
||||||
#include "wx/viewsupt.h"
|
#include "wx/viewsupt.h"
|
||||||
|
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
#include "wx/wxutil.h"
|
|
||||||
#include "wx/wxvbam.h"
|
#include "wx/wxvbam.h"
|
||||||
|
|
||||||
namespace Viewers {
|
namespace Viewers {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
int getKeyboardKeyCode(const wxKeyEvent& event) {
|
||||||
|
const int key_code = event.GetKeyCode();
|
||||||
|
if (key_code > WXK_START) {
|
||||||
|
return key_code;
|
||||||
|
}
|
||||||
|
int uc = event.GetUnicodeKey();
|
||||||
|
if (uc != WXK_NONE) {
|
||||||
|
if (uc < 32) { // not all control chars
|
||||||
|
switch (uc) {
|
||||||
|
case WXK_BACK:
|
||||||
|
case WXK_TAB:
|
||||||
|
case WXK_RETURN:
|
||||||
|
case WXK_ESCAPE:
|
||||||
|
return uc;
|
||||||
|
default:
|
||||||
|
return WXK_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uc;
|
||||||
|
} else {
|
||||||
|
return event.GetKeyCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void Viewer::CloseDlg(wxCloseEvent& ev)
|
void Viewer::CloseDlg(wxCloseEvent& ev)
|
||||||
{
|
{
|
||||||
(void)ev; // unused params
|
(void)ev; // unused params
|
||||||
|
|
|
@ -0,0 +1,439 @@
|
||||||
|
#include "wx/widgets/sdl-poller.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <SDL_events.h>
|
||||||
|
#include <wx/timer.h>
|
||||||
|
#include <wx/toplevel.h>
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include "wx/config/option-id.h"
|
||||||
|
#include "wx/config/option-observer.h"
|
||||||
|
#include "wx/config/option-proxy.h"
|
||||||
|
#include "wx/config/option.h"
|
||||||
|
#include "wx/config/user-input.h"
|
||||||
|
#include "wx/widgets/user-input-event.h"
|
||||||
|
|
||||||
|
namespace widgets {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum class JoyAxisStatus { Neutral = 0, Plus, Minus };
|
||||||
|
|
||||||
|
JoyAxisStatus AxisValueToStatus(const int16_t& x) {
|
||||||
|
if (x > 0x1fff)
|
||||||
|
return JoyAxisStatus::Plus;
|
||||||
|
if (x < -0x1fff)
|
||||||
|
return JoyAxisStatus::Minus;
|
||||||
|
return JoyAxisStatus::Neutral;
|
||||||
|
}
|
||||||
|
|
||||||
|
config::JoyControl AxisStatusToJoyControl(const JoyAxisStatus& status) {
|
||||||
|
switch (status) {
|
||||||
|
case JoyAxisStatus::Plus:
|
||||||
|
return config::JoyControl::AxisPlus;
|
||||||
|
case JoyAxisStatus::Minus:
|
||||||
|
return config::JoyControl::AxisMinus;
|
||||||
|
case JoyAxisStatus::Neutral:
|
||||||
|
default:
|
||||||
|
// This should never happen.
|
||||||
|
assert(false);
|
||||||
|
return config::JoyControl::AxisPlus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config::JoyControl HatStatusToJoyControl(const uint8_t status) {
|
||||||
|
switch (status) {
|
||||||
|
case SDL_HAT_UP:
|
||||||
|
return config::JoyControl::HatNorth;
|
||||||
|
case SDL_HAT_DOWN:
|
||||||
|
return config::JoyControl::HatSouth;
|
||||||
|
case SDL_HAT_LEFT:
|
||||||
|
return config::JoyControl::HatWest;
|
||||||
|
case SDL_HAT_RIGHT:
|
||||||
|
return config::JoyControl::HatEast;
|
||||||
|
default:
|
||||||
|
// This should never happen.
|
||||||
|
assert(false);
|
||||||
|
return config::JoyControl::HatNorth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Represents the current state of a joystick. This class takes care of
|
||||||
|
// initializing and destroying SDL resources on construction and destruction so
|
||||||
|
// every associated SDL state for a joystick dies with this object.
|
||||||
|
class JoyState : public wxTimer {
|
||||||
|
public:
|
||||||
|
JoyState(bool enable_game_controller, int sdl_index);
|
||||||
|
~JoyState() override;
|
||||||
|
|
||||||
|
// Move constructor and assignment.
|
||||||
|
JoyState(JoyState&& other);
|
||||||
|
JoyState& operator=(JoyState&& other);
|
||||||
|
|
||||||
|
// Disable copy constructor and assignment.
|
||||||
|
JoyState(const JoyState&) = delete;
|
||||||
|
JoyState& operator=(const JoyState&) = delete;
|
||||||
|
|
||||||
|
// Returns true if this object was properly initialized.
|
||||||
|
bool IsValid() const;
|
||||||
|
|
||||||
|
// Returns true if this object is a game controller.
|
||||||
|
bool is_game_controller() const { return !!game_controller_; }
|
||||||
|
|
||||||
|
// Processes the corresponding events.
|
||||||
|
std::vector<UserInputEvent> ProcessAxisEvent(const uint8_t index, const JoyAxisStatus status);
|
||||||
|
std::vector<UserInputEvent> ProcessButtonEvent(const uint8_t index, const bool pressed);
|
||||||
|
std::vector<UserInputEvent> ProcessHatEvent(const uint8_t index, const uint8_t status);
|
||||||
|
|
||||||
|
// Activates or deactivates rumble.
|
||||||
|
void SetRumble(bool activate_rumble);
|
||||||
|
|
||||||
|
SDL_JoystickID joystick_id() const { return joystick_id_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// wxTimer implementation.
|
||||||
|
// Used to rumble on a timer.
|
||||||
|
void Notify() override;
|
||||||
|
|
||||||
|
// The Joystick abstraction for UI events.
|
||||||
|
config::JoyId wx_joystick_;
|
||||||
|
|
||||||
|
// SDL Joystick ID used for events.
|
||||||
|
SDL_JoystickID joystick_id_;
|
||||||
|
|
||||||
|
// The SDL GameController instance.
|
||||||
|
SDL_GameController* game_controller_ = nullptr;
|
||||||
|
|
||||||
|
// The SDL Joystick instance.
|
||||||
|
SDL_Joystick* sdl_joystick_ = nullptr;
|
||||||
|
|
||||||
|
// Current state of Joystick axis.
|
||||||
|
std::unordered_map<uint8_t, JoyAxisStatus> axis_{};
|
||||||
|
|
||||||
|
// Current state of Joystick buttons.
|
||||||
|
std::unordered_map<uint8_t, bool> buttons_{};
|
||||||
|
|
||||||
|
// Current state of Joystick HAT. Unused for GameControllers.
|
||||||
|
std::unordered_map<uint8_t, uint8_t> hats_{};
|
||||||
|
|
||||||
|
// Set to true to activate joystick rumble.
|
||||||
|
bool rumbling_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
JoyState::JoyState(bool enable_game_controller, int sdl_index) : wx_joystick_(sdl_index) {
|
||||||
|
if (enable_game_controller && SDL_IsGameController(sdl_index)) {
|
||||||
|
game_controller_ = SDL_GameControllerOpen(sdl_index);
|
||||||
|
if (game_controller_)
|
||||||
|
sdl_joystick_ = SDL_GameControllerGetJoystick(game_controller_);
|
||||||
|
} else {
|
||||||
|
sdl_joystick_ = SDL_JoystickOpen(sdl_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sdl_joystick_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
joystick_id_ = SDL_JoystickInstanceID(sdl_joystick_);
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyState::~JoyState() {
|
||||||
|
// Nothing to do if this object is not initialized.
|
||||||
|
if (!sdl_joystick_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (game_controller_) {
|
||||||
|
SDL_GameControllerClose(game_controller_);
|
||||||
|
} else {
|
||||||
|
SDL_JoystickClose(sdl_joystick_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyState::JoyState(JoyState&& other) : wx_joystick_(other.wx_joystick_) {
|
||||||
|
if (this == &other) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdl_joystick_ = other.sdl_joystick_;
|
||||||
|
game_controller_ = other.game_controller_;
|
||||||
|
joystick_id_ = other.joystick_id_;
|
||||||
|
axis_ = std::move(other.axis_);
|
||||||
|
buttons_ = std::move(other.buttons_);
|
||||||
|
hats_ = std::move(other.hats_);
|
||||||
|
rumbling_ = other.rumbling_;
|
||||||
|
|
||||||
|
other.sdl_joystick_ = nullptr;
|
||||||
|
other.game_controller_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyState& JoyState::operator=(JoyState&& other) {
|
||||||
|
if (this == &other) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdl_joystick_ = other.sdl_joystick_;
|
||||||
|
game_controller_ = other.game_controller_;
|
||||||
|
joystick_id_ = other.joystick_id_;
|
||||||
|
axis_ = std::move(other.axis_);
|
||||||
|
buttons_ = std::move(other.buttons_);
|
||||||
|
hats_ = std::move(other.hats_);
|
||||||
|
rumbling_ = other.rumbling_;
|
||||||
|
|
||||||
|
other.sdl_joystick_ = nullptr;
|
||||||
|
other.game_controller_ = nullptr;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JoyState::IsValid() const {
|
||||||
|
return sdl_joystick_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UserInputEvent> JoyState::ProcessAxisEvent(const uint8_t index,
|
||||||
|
const JoyAxisStatus status) {
|
||||||
|
const JoyAxisStatus previous_status = axis_[index];
|
||||||
|
std::vector<UserInputEvent> events;
|
||||||
|
|
||||||
|
// Nothing to do if no-op.
|
||||||
|
if (status == previous_status) {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the value.
|
||||||
|
axis_[index] = status;
|
||||||
|
|
||||||
|
if (previous_status != JoyAxisStatus::Neutral) {
|
||||||
|
// Send the "unpressed" event.
|
||||||
|
events.push_back(UserInputEvent(
|
||||||
|
config::UserInput(index, AxisStatusToJoyControl(previous_status), wx_joystick_),
|
||||||
|
false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We already sent the "unpressed" event so nothing more to do.
|
||||||
|
if (status == JoyAxisStatus::Neutral) {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the "pressed" event.
|
||||||
|
events.push_back(UserInputEvent(
|
||||||
|
config::UserInput(index, AxisStatusToJoyControl(status), wx_joystick_), true));
|
||||||
|
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UserInputEvent> JoyState::ProcessButtonEvent(const uint8_t index, const bool status) {
|
||||||
|
const bool previous_status = buttons_[index];
|
||||||
|
std::vector<UserInputEvent> events;
|
||||||
|
|
||||||
|
// Nothing to do if no-op.
|
||||||
|
if (status == previous_status) {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the value.
|
||||||
|
buttons_[index] = status;
|
||||||
|
|
||||||
|
// Send the event.
|
||||||
|
events.push_back(
|
||||||
|
UserInputEvent(config::UserInput(index, config::JoyControl::Button, wx_joystick_), status));
|
||||||
|
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UserInputEvent> JoyState::ProcessHatEvent(const uint8_t index, const uint8_t status) {
|
||||||
|
const uint16_t previous_status = hats_[index];
|
||||||
|
std::vector<UserInputEvent> events;
|
||||||
|
|
||||||
|
// Nothing to do if no-op.
|
||||||
|
if (status == previous_status) {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the value.
|
||||||
|
hats_[index] = status;
|
||||||
|
|
||||||
|
// For HATs, the status value is a bit field, where each bit corresponds to
|
||||||
|
// a direction. These are parsed here to send the corresponding "pressed"
|
||||||
|
// and "unpressed" events.
|
||||||
|
for (uint8_t bit = 0x01; bit != 0x10; bit <<= 1) {
|
||||||
|
const bool old_control_pressed = (previous_status & bit) != 0;
|
||||||
|
const bool new_control_pressed = (status & bit) != 0;
|
||||||
|
if (old_control_pressed && !new_control_pressed) {
|
||||||
|
// Send the "unpressed" event.
|
||||||
|
events.push_back(UserInputEvent(
|
||||||
|
config::UserInput(index, HatStatusToJoyControl(bit), wx_joystick_), false));
|
||||||
|
}
|
||||||
|
if (!old_control_pressed && new_control_pressed) {
|
||||||
|
// Send the "pressed" event.
|
||||||
|
events.push_back(UserInputEvent(
|
||||||
|
config::UserInput(index, HatStatusToJoyControl(bit), wx_joystick_), true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoyState::SetRumble(bool activate_rumble) {
|
||||||
|
rumbling_ = activate_rumble;
|
||||||
|
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 9)
|
||||||
|
if (!game_controller_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (rumbling_) {
|
||||||
|
SDL_GameControllerRumble(game_controller_, 0xFFFF, 0xFFFF, 300);
|
||||||
|
if (!IsRunning()) {
|
||||||
|
Start(150);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SDL_GameControllerRumble(game_controller_, 0, 0, 0);
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoyState::Notify() {
|
||||||
|
SetRumble(rumbling_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SdlPoller::SdlPoller(const EventHandlerProvider handler_provider)
|
||||||
|
: enable_game_controller_(OPTION(kSDLGameControllerMode)),
|
||||||
|
handler_provider_(handler_provider),
|
||||||
|
game_controller_enabled_observer_(
|
||||||
|
config::OptionID::kSDLGameControllerMode,
|
||||||
|
[this](config::Option* option) { ReconnectControllers(option->GetBool()); }) {
|
||||||
|
wxTimer::Start(50);
|
||||||
|
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS);
|
||||||
|
SDL_GameControllerEventState(SDL_ENABLE);
|
||||||
|
SDL_JoystickEventState(SDL_ENABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
SdlPoller::~SdlPoller() {
|
||||||
|
wxTimer::Stop();
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_EVENTS);
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlPoller::SetRumble(bool rumble) {
|
||||||
|
if (rumble) {
|
||||||
|
if (!joystick_states_.empty()) {
|
||||||
|
auto it = joystick_states_.begin();
|
||||||
|
it->second.SetRumble(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!joystick_states_.empty()) {
|
||||||
|
auto it = joystick_states_.begin();
|
||||||
|
it->second.SetRumble(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlPoller::ReconnectControllers(bool enable_game_controller) {
|
||||||
|
enable_game_controller_ = enable_game_controller;
|
||||||
|
RemapControllers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlPoller::Notify() {
|
||||||
|
SDL_Event sdl_event;
|
||||||
|
|
||||||
|
while (SDL_PollEvent(&sdl_event)) {
|
||||||
|
std::vector<UserInputEvent> events;
|
||||||
|
JoyState* joy_state = nullptr;
|
||||||
|
switch (sdl_event.type) {
|
||||||
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
|
case SDL_CONTROLLERBUTTONUP:
|
||||||
|
joy_state = FindJoyState(sdl_event.cbutton.which);
|
||||||
|
if (joy_state) {
|
||||||
|
events = joy_state->ProcessButtonEvent(sdl_event.cbutton.button,
|
||||||
|
sdl_event.cbutton.state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
|
joy_state = FindJoyState(sdl_event.caxis.which);
|
||||||
|
if (joy_state) {
|
||||||
|
events = joy_state->ProcessAxisEvent(sdl_event.caxis.axis,
|
||||||
|
AxisValueToStatus(sdl_event.caxis.value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_CONTROLLERDEVICEADDED:
|
||||||
|
case SDL_CONTROLLERDEVICEREMOVED:
|
||||||
|
// Do nothing. This will be handled with JOYDEVICEADDED and
|
||||||
|
// JOYDEVICEREMOVED events.
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Joystick events for non-GameControllers.
|
||||||
|
case SDL_JOYBUTTONDOWN:
|
||||||
|
case SDL_JOYBUTTONUP:
|
||||||
|
joy_state = FindJoyState(sdl_event.jbutton.which);
|
||||||
|
if (joy_state && !joy_state->is_game_controller()) {
|
||||||
|
events = joy_state->ProcessButtonEvent(sdl_event.jbutton.button,
|
||||||
|
sdl_event.jbutton.state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_JOYAXISMOTION:
|
||||||
|
joy_state = FindJoyState(sdl_event.jaxis.which);
|
||||||
|
if (joy_state && !joy_state->is_game_controller()) {
|
||||||
|
events = joy_state->ProcessAxisEvent(sdl_event.jaxis.axis,
|
||||||
|
AxisValueToStatus(sdl_event.jaxis.value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_JOYHATMOTION:
|
||||||
|
joy_state = FindJoyState(sdl_event.jhat.which);
|
||||||
|
if (joy_state && !joy_state->is_game_controller()) {
|
||||||
|
events = joy_state->ProcessHatEvent(sdl_event.jhat.hat, sdl_event.jhat.value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_JOYDEVICEADDED:
|
||||||
|
// Always remap all controllers.
|
||||||
|
RemapControllers();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_JOYDEVICEREMOVED:
|
||||||
|
joystick_states_.erase(sdl_event.jdevice.which);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!events.empty()) {
|
||||||
|
wxEvtHandler* handler = handler_provider_();
|
||||||
|
if (handler) {
|
||||||
|
for (const auto& user_input_event : events) {
|
||||||
|
handler->QueueEvent(user_input_event.Clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyState* SdlPoller::FindJoyState(const SDL_JoystickID& joy_id) {
|
||||||
|
auto it = joystick_states_.find(joy_id);
|
||||||
|
if (it == joystick_states_.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlPoller::RemapControllers() {
|
||||||
|
// Clear the current joystick states.
|
||||||
|
joystick_states_.clear();
|
||||||
|
|
||||||
|
// Reconnect all controllers.
|
||||||
|
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
||||||
|
JoyState joy_state(enable_game_controller_, i);
|
||||||
|
if (joy_state.IsValid()) {
|
||||||
|
joystick_states_.insert({joy_state.joystick_id(), std::move(joy_state)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widgets
|
|
@ -0,0 +1,70 @@
|
||||||
|
#ifndef WX_WIDGETS_SDL_POLLER_H_
|
||||||
|
#define WX_WIDGETS_SDL_POLLER_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <wx/timer.h>
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include "wx/config/option-observer.h"
|
||||||
|
|
||||||
|
// Forward declarations.
|
||||||
|
class wxEvtHandler;
|
||||||
|
|
||||||
|
namespace widgets {
|
||||||
|
|
||||||
|
// Forward declaration.
|
||||||
|
class JoyState;
|
||||||
|
|
||||||
|
// Provider for the event handler to send the events to.
|
||||||
|
using EventHandlerProvider = std::function<wxEvtHandler*()>;
|
||||||
|
|
||||||
|
// The SDL worker is responsible for handling SDL events and firing the
|
||||||
|
// appropriate `UserInputEvent` for joysticks.
|
||||||
|
// It is used to fire `UserInputEvent` for joysticks. This class should be kept
|
||||||
|
// as a singleton owned by the application object.
|
||||||
|
class SdlPoller final : public wxTimer {
|
||||||
|
public:
|
||||||
|
explicit SdlPoller(const EventHandlerProvider handler_provider);
|
||||||
|
~SdlPoller() final;
|
||||||
|
|
||||||
|
// Disable copy and copy assignment.
|
||||||
|
SdlPoller(const SdlPoller&) = delete;
|
||||||
|
SdlPoller& operator=(const SdlPoller&) = delete;
|
||||||
|
|
||||||
|
// Sets or unsets the controller rumble.
|
||||||
|
void SetRumble(bool rumble);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Helper method to find a joystick state from a joystick ID.
|
||||||
|
// Returns nullptr if not present. Called from the SDL worker thread.
|
||||||
|
JoyState* FindJoyState(const SDL_JoystickID& joy_id);
|
||||||
|
|
||||||
|
// Remap all controllers. Called from the SDL worker thread.
|
||||||
|
void RemapControllers();
|
||||||
|
|
||||||
|
// Reconnects all controllers.
|
||||||
|
void ReconnectControllers(bool enable_game_controller);
|
||||||
|
|
||||||
|
// wxTimer implementation.
|
||||||
|
void Notify() final;
|
||||||
|
|
||||||
|
// Map of SDL joystick ID to joystick state. Only contains active joysticks.
|
||||||
|
// Only accessed from the SDL worker thread.
|
||||||
|
std::map<SDL_JoystickID, JoyState> joystick_states_;
|
||||||
|
|
||||||
|
// Set to true to enable the game controller API.
|
||||||
|
bool enable_game_controller_ = false;
|
||||||
|
|
||||||
|
// The provider of event handlers to send the events to.
|
||||||
|
const EventHandlerProvider handler_provider_;
|
||||||
|
|
||||||
|
// Observer for the game controller enabled option.
|
||||||
|
const config::OptionsObserver game_controller_enabled_observer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widgets
|
||||||
|
|
||||||
|
#endif // WX_WIDGETS_SDL_POLLER_H_
|
|
@ -1,493 +0,0 @@
|
||||||
#include "wx/widgets/sdljoy.h"
|
|
||||||
|
|
||||||
#include <wx/timer.h>
|
|
||||||
#include <SDL.h>
|
|
||||||
|
|
||||||
#include "wx/config/option-proxy.h"
|
|
||||||
#include "wx/config/option.h"
|
|
||||||
#include "wx/wxvbam.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
enum class wxAxisStatus {
|
|
||||||
Neutral = 0,
|
|
||||||
Plus,
|
|
||||||
Minus
|
|
||||||
};
|
|
||||||
|
|
||||||
wxAxisStatus AxisValueToStatus(const int16_t& x) {
|
|
||||||
if (x > 0x1fff)
|
|
||||||
return wxAxisStatus::Plus;
|
|
||||||
if (x < -0x1fff)
|
|
||||||
return wxAxisStatus::Minus;
|
|
||||||
return wxAxisStatus::Neutral;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxJoyControl AxisStatusToJoyControl(const wxAxisStatus& status) {
|
|
||||||
switch (status) {
|
|
||||||
case wxAxisStatus::Plus:
|
|
||||||
return wxJoyControl::AxisPlus;
|
|
||||||
case wxAxisStatus::Minus:
|
|
||||||
return wxJoyControl::AxisMinus;
|
|
||||||
case wxAxisStatus::Neutral:
|
|
||||||
default:
|
|
||||||
// This should never happen.
|
|
||||||
assert(false);
|
|
||||||
return wxJoyControl::AxisPlus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wxJoyControl HatStatusToJoyControl(const uint8_t status) {
|
|
||||||
switch (status) {
|
|
||||||
case SDL_HAT_UP:
|
|
||||||
return wxJoyControl::HatNorth;
|
|
||||||
case SDL_HAT_DOWN:
|
|
||||||
return wxJoyControl::HatSouth;
|
|
||||||
case SDL_HAT_LEFT:
|
|
||||||
return wxJoyControl::HatWest;
|
|
||||||
case SDL_HAT_RIGHT:
|
|
||||||
return wxJoyControl::HatEast;
|
|
||||||
default:
|
|
||||||
// This should never happen.
|
|
||||||
assert(false);
|
|
||||||
return wxJoyControl::HatNorth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// For testing a GameController as a Joystick:
|
|
||||||
//#define SDL_IsGameController(x) false
|
|
||||||
|
|
||||||
// static
|
|
||||||
wxJoystick wxJoystick::Invalid() {
|
|
||||||
return wxJoystick(kInvalidSdlIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
wxJoystick wxJoystick::FromLegacyPlayerIndex(unsigned player_index) {
|
|
||||||
assert(player_index != 0);
|
|
||||||
return wxJoystick(player_index - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString wxJoystick::ToString() {
|
|
||||||
return wxString::Format("Joy%d", sdl_index_ + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wxJoystick::operator==(const wxJoystick& other) const {
|
|
||||||
return sdl_index_ == other.sdl_index_;
|
|
||||||
}
|
|
||||||
bool wxJoystick::operator!=(const wxJoystick& other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
bool wxJoystick::operator<(const wxJoystick& other) const {
|
|
||||||
return sdl_index_ < other.sdl_index_;
|
|
||||||
}
|
|
||||||
bool wxJoystick::operator<=(const wxJoystick& other) const {
|
|
||||||
return !(*this > other);
|
|
||||||
}
|
|
||||||
bool wxJoystick::operator>(const wxJoystick& other) const {
|
|
||||||
return other < *this;
|
|
||||||
}
|
|
||||||
bool wxJoystick::operator>=(const wxJoystick& other) const {
|
|
||||||
return !(*this < other);
|
|
||||||
}
|
|
||||||
|
|
||||||
wxJoystick::wxJoystick(int sdl_index) : sdl_index_(sdl_index) {}
|
|
||||||
|
|
||||||
wxJoyEvent::wxJoyEvent(
|
|
||||||
wxJoystick joystick,
|
|
||||||
wxJoyControl control,
|
|
||||||
uint8_t control_index,
|
|
||||||
bool pressed) :
|
|
||||||
wxCommandEvent(wxEVT_JOY),
|
|
||||||
joystick_(joystick),
|
|
||||||
control_(control),
|
|
||||||
control_index_(control_index),
|
|
||||||
pressed_(pressed) {}
|
|
||||||
|
|
||||||
wxDEFINE_EVENT(wxEVT_JOY, wxJoyEvent);
|
|
||||||
|
|
||||||
// Represents the current state of a joystick. This class takes care of
|
|
||||||
// initializing and destroying SDL resources on construction and destruction so
|
|
||||||
// every associated SDL state for a joystick dies with this object.
|
|
||||||
class wxSDLJoyState : public wxTimer {
|
|
||||||
public:
|
|
||||||
explicit wxSDLJoyState(int sdl_index);
|
|
||||||
explicit wxSDLJoyState(wxJoystick joystick);
|
|
||||||
~wxSDLJoyState() override;
|
|
||||||
|
|
||||||
// Disable copy constructor and assignment. This is to prevent double
|
|
||||||
// closure of the SDL objects.
|
|
||||||
wxSDLJoyState(const wxSDLJoyState&) = delete;
|
|
||||||
wxSDLJoyState& operator=(const wxSDLJoyState&) = delete;
|
|
||||||
|
|
||||||
// Returns true if this object was properly initialized.
|
|
||||||
bool IsValid() const;
|
|
||||||
|
|
||||||
// Returns true if this object is a game controller.
|
|
||||||
bool is_game_controller() const { return !!game_controller_; }
|
|
||||||
|
|
||||||
// Processes the corresponding events.
|
|
||||||
void ProcessAxisEvent(const uint8_t index, const wxAxisStatus status);
|
|
||||||
void ProcessButtonEvent(const uint8_t index, const bool pressed);
|
|
||||||
void ProcessHatEvent(const uint8_t index, const uint8_t status);
|
|
||||||
|
|
||||||
// Activates or deactivates rumble.
|
|
||||||
void SetRumble(bool activate_rumble);
|
|
||||||
|
|
||||||
SDL_JoystickID joystick_id() const { return joystick_id_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// wxTimer implementation.
|
|
||||||
// Used to rumble on a timer.
|
|
||||||
void Notify() override;
|
|
||||||
|
|
||||||
// The Joystick abstraction for UI events.
|
|
||||||
wxJoystick wx_joystick_;
|
|
||||||
|
|
||||||
// SDL Joystick ID used for events.
|
|
||||||
SDL_JoystickID joystick_id_;
|
|
||||||
|
|
||||||
// The SDL GameController instance.
|
|
||||||
SDL_GameController* game_controller_ = nullptr;
|
|
||||||
|
|
||||||
// The SDL Joystick instance.
|
|
||||||
SDL_Joystick* sdl_joystick_ = nullptr;
|
|
||||||
|
|
||||||
// Current state of Joystick axis.
|
|
||||||
std::unordered_map<uint8_t, wxAxisStatus> axis_{};
|
|
||||||
|
|
||||||
// Current state of Joystick buttons.
|
|
||||||
std::unordered_map<uint8_t, bool> buttons_{};
|
|
||||||
|
|
||||||
// Current state of Joystick HAT. Unused for GameControllers.
|
|
||||||
std::unordered_map<uint8_t, uint8_t> hats_{};
|
|
||||||
|
|
||||||
// Set to true to activate joystick rumble.
|
|
||||||
bool rumbling_ = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
wxSDLJoyState::wxSDLJoyState(int sdl_index)
|
|
||||||
: wxSDLJoyState(wxJoystick(sdl_index)) {}
|
|
||||||
|
|
||||||
wxSDLJoyState::wxSDLJoyState(wxJoystick joystick) : wx_joystick_(joystick) {
|
|
||||||
int sdl_index = wx_joystick_.sdl_index_;
|
|
||||||
if (OPTION(kSDLGameControllerMode) && SDL_IsGameController(sdl_index)) {
|
|
||||||
game_controller_ = SDL_GameControllerOpen(sdl_index);
|
|
||||||
if (game_controller_)
|
|
||||||
sdl_joystick_ = SDL_GameControllerGetJoystick(game_controller_);
|
|
||||||
} else {
|
|
||||||
sdl_joystick_ = SDL_JoystickOpen(sdl_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sdl_joystick_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
joystick_id_ = SDL_JoystickInstanceID(sdl_joystick_);
|
|
||||||
systemScreenMessage(
|
|
||||||
wxString::Format(_("Connected %s: %s"),
|
|
||||||
wx_joystick_.ToString(), SDL_JoystickNameForIndex(sdl_index)));
|
|
||||||
}
|
|
||||||
|
|
||||||
wxSDLJoyState::~wxSDLJoyState() {
|
|
||||||
// Nothing to do if this object is not initialized.
|
|
||||||
if (!sdl_joystick_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (game_controller_)
|
|
||||||
SDL_GameControllerClose(game_controller_);
|
|
||||||
else
|
|
||||||
SDL_JoystickClose(sdl_joystick_);
|
|
||||||
|
|
||||||
systemScreenMessage(
|
|
||||||
wxString::Format(_("Disconnected %s"), wx_joystick_.ToString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wxSDLJoyState::IsValid() const {
|
|
||||||
return sdl_joystick_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxSDLJoyState::ProcessAxisEvent(const uint8_t index, const wxAxisStatus status) {
|
|
||||||
auto handler = wxGetApp().frame->GetJoyEventHandler();
|
|
||||||
const wxAxisStatus previous_status = axis_[index];
|
|
||||||
|
|
||||||
// Nothing to do if no-op.
|
|
||||||
if (status == previous_status) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the value.
|
|
||||||
axis_[index] = status;
|
|
||||||
|
|
||||||
// Nothing more to do if we can't send events.
|
|
||||||
if (!handler) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxLogDebug("Got Axis motion: %s ctrl_idx:%d val:%d prev_val:%d",
|
|
||||||
wx_joystick_.ToString(), index, status, previous_status);
|
|
||||||
|
|
||||||
if (previous_status != wxAxisStatus::Neutral) {
|
|
||||||
// Send the "unpressed" event.
|
|
||||||
wxQueueEvent(handler, new wxJoyEvent(
|
|
||||||
wx_joystick_,
|
|
||||||
AxisStatusToJoyControl(previous_status),
|
|
||||||
index,
|
|
||||||
false));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We already sent the "unpressed" event so nothing more to do.
|
|
||||||
if (status == wxAxisStatus::Neutral) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the "pressed" event.
|
|
||||||
wxQueueEvent(handler, new wxJoyEvent(
|
|
||||||
wx_joystick_,
|
|
||||||
AxisStatusToJoyControl(status),
|
|
||||||
index,
|
|
||||||
true));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxSDLJoyState::ProcessButtonEvent(const uint8_t index, const bool status) {
|
|
||||||
auto handler = wxGetApp().frame->GetJoyEventHandler();
|
|
||||||
const bool previous_status = buttons_[index];
|
|
||||||
|
|
||||||
// Nothing to do if no-op.
|
|
||||||
if (status == previous_status) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the value.
|
|
||||||
buttons_[index] = status;
|
|
||||||
|
|
||||||
// Nothing more to do if we can't send events.
|
|
||||||
if (!handler) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxLogDebug("Got Button event: %s ctrl_idx:%d val:%d prev_val:%d",
|
|
||||||
wx_joystick_.ToString(), index, status, previous_status);
|
|
||||||
|
|
||||||
// Send the event.
|
|
||||||
wxQueueEvent(handler, new wxJoyEvent(
|
|
||||||
wx_joystick_, wxJoyControl::Button, index, status));
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxSDLJoyState::ProcessHatEvent(const uint8_t index, const uint8_t status) {
|
|
||||||
auto handler = wxGetApp().frame->GetJoyEventHandler();
|
|
||||||
const uint16_t previous_status = hats_[index];
|
|
||||||
|
|
||||||
// Nothing to do if no-op.
|
|
||||||
if (status == previous_status) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the value.
|
|
||||||
hats_[index] = status;
|
|
||||||
|
|
||||||
// Nothing more to do if we can't send events.
|
|
||||||
if (!handler) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxLogDebug("Got Hat event: %s ctrl_idx:%d val:%d prev_val:%d",
|
|
||||||
wx_joystick_.ToString(), index, status, previous_status);
|
|
||||||
|
|
||||||
// For HATs, the status value is a bit field, where each bit corresponds to
|
|
||||||
// a direction. These are parsed here to send the corresponding "pressed"
|
|
||||||
// and "unpressed" events.
|
|
||||||
for (uint8_t bit = 0x01; bit != 0x10; bit <<= 1) {
|
|
||||||
const bool old_control_pressed = (previous_status & bit) != 0;
|
|
||||||
const bool new_control_pressed = (status & bit) != 0;
|
|
||||||
if (old_control_pressed && !new_control_pressed) {
|
|
||||||
// Send the "unpressed" event.
|
|
||||||
wxQueueEvent(handler, new wxJoyEvent(
|
|
||||||
wx_joystick_, HatStatusToJoyControl(bit), index, false));
|
|
||||||
}
|
|
||||||
if (!old_control_pressed && new_control_pressed) {
|
|
||||||
// Send the "pressed" event.
|
|
||||||
wxQueueEvent(handler, new wxJoyEvent(
|
|
||||||
wx_joystick_, HatStatusToJoyControl(bit), index, true));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxSDLJoyState::SetRumble(bool activate_rumble) {
|
|
||||||
rumbling_ = activate_rumble;
|
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 9)
|
|
||||||
if (!game_controller_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (rumbling_) {
|
|
||||||
SDL_GameControllerRumble(game_controller_, 0xFFFF, 0xFFFF, 300);
|
|
||||||
if (!IsRunning())
|
|
||||||
Start(150);
|
|
||||||
} else {
|
|
||||||
SDL_GameControllerRumble(game_controller_, 0, 0, 0);
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxSDLJoyState::Notify() {
|
|
||||||
SetRumble(rumbling_);
|
|
||||||
}
|
|
||||||
|
|
||||||
wxJoyPoller::wxJoyPoller() {
|
|
||||||
// Start up joystick if not already started
|
|
||||||
// FIXME: check for errors
|
|
||||||
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
|
|
||||||
SDL_GameControllerEventState(SDL_ENABLE);
|
|
||||||
SDL_JoystickEventState(SDL_ENABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
wxJoyPoller::~wxJoyPoller() {
|
|
||||||
// It is necessary to free all SDL resources before quitting SDL.
|
|
||||||
joystick_states_.clear();
|
|
||||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
|
||||||
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxJoyPoller::Poll() {
|
|
||||||
SDL_Event e;
|
|
||||||
wxSDLJoyState* joy_state = nullptr;
|
|
||||||
|
|
||||||
while (SDL_PollEvent(&e)) {
|
|
||||||
switch (e.type) {
|
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
|
||||||
case SDL_CONTROLLERBUTTONUP:
|
|
||||||
joy_state = FindJoyState(e.cbutton.which);
|
|
||||||
if (joy_state) {
|
|
||||||
joy_state->ProcessButtonEvent(
|
|
||||||
e.cbutton.button, e.cbutton.state);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_CONTROLLERAXISMOTION:
|
|
||||||
joy_state = FindJoyState(e.caxis.which);
|
|
||||||
if (joy_state) {
|
|
||||||
joy_state->ProcessAxisEvent(
|
|
||||||
e.caxis.axis, AxisValueToStatus(e.caxis.value));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
|
||||||
// Do nothing. This will be handled with JOYDEVICEADDED and
|
|
||||||
// JOYDEVICEREMOVED events.
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Joystick events for non-GameControllers.
|
|
||||||
case SDL_JOYBUTTONDOWN:
|
|
||||||
case SDL_JOYBUTTONUP:
|
|
||||||
joy_state = FindJoyState(e.jbutton.which);
|
|
||||||
if (joy_state && !joy_state->is_game_controller()) {
|
|
||||||
joy_state->ProcessButtonEvent(
|
|
||||||
e.jbutton.button, e.jbutton.state);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_JOYAXISMOTION:
|
|
||||||
joy_state = FindJoyState(e.jaxis.which);
|
|
||||||
if (joy_state && !joy_state->is_game_controller()) {
|
|
||||||
joy_state->ProcessAxisEvent(
|
|
||||||
e.jaxis.axis, AxisValueToStatus(e.jaxis.value));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_JOYHATMOTION:
|
|
||||||
joy_state = FindJoyState(e.jhat.which);
|
|
||||||
if (joy_state && !joy_state->is_game_controller()) {
|
|
||||||
joy_state->ProcessHatEvent(
|
|
||||||
e.jhat.hat, e.jhat.value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_JOYDEVICEADDED:
|
|
||||||
// Always remap all controllers.
|
|
||||||
RemapControllers();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_JOYDEVICEREMOVED:
|
|
||||||
joystick_states_.erase(e.jdevice.which);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxJoyPoller::RemapControllers() {
|
|
||||||
if (!is_polling_active_) {
|
|
||||||
// Nothing to do when we're not actively polling.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
joystick_states_.clear();
|
|
||||||
|
|
||||||
if (requested_joysticks_.empty()) {
|
|
||||||
// Connect all joysticks.
|
|
||||||
for (int i = 0; i < SDL_NumJoysticks(); i++) {
|
|
||||||
std::unique_ptr<wxSDLJoyState> joy_state(new wxSDLJoyState(i));
|
|
||||||
if (joy_state->IsValid()) {
|
|
||||||
joystick_states_.emplace(
|
|
||||||
joy_state->joystick_id(), std::move(joy_state));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Only attempt to add the joysticks we care about.
|
|
||||||
for (const wxJoystick& joystick : requested_joysticks_) {
|
|
||||||
std::unique_ptr<wxSDLJoyState> joy_state(
|
|
||||||
new wxSDLJoyState(joystick));
|
|
||||||
if (joy_state->IsValid()) {
|
|
||||||
joystick_states_.emplace(
|
|
||||||
joy_state->joystick_id(), std::move(joy_state));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wxSDLJoyState* wxJoyPoller::FindJoyState(const SDL_JoystickID& joy_id) {
|
|
||||||
const auto iter = joystick_states_.find(joy_id);
|
|
||||||
if (iter == joystick_states_.end())
|
|
||||||
return nullptr;
|
|
||||||
return iter->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxJoyPoller::PollJoysticks(std::set<wxJoystick> joysticks) {
|
|
||||||
// Reset the polling state.
|
|
||||||
StopPolling();
|
|
||||||
|
|
||||||
if (joysticks.empty()) {
|
|
||||||
// Nothing to poll. Return early.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
is_polling_active_ = true;
|
|
||||||
requested_joysticks_ = joysticks;
|
|
||||||
RemapControllers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxJoyPoller::PollAllJoysticks() {
|
|
||||||
// Reset the polling state.
|
|
||||||
StopPolling();
|
|
||||||
is_polling_active_ = true;
|
|
||||||
RemapControllers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxJoyPoller::StopPolling() {
|
|
||||||
joystick_states_.clear();
|
|
||||||
requested_joysticks_.clear();
|
|
||||||
is_polling_active_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wxJoyPoller::SetRumble(bool activate_rumble) {
|
|
||||||
if (joystick_states_.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Do rumble only on the first device.
|
|
||||||
joystick_states_.begin()->second->SetRumble(activate_rumble);
|
|
||||||
}
|
|
|
@ -1,156 +0,0 @@
|
||||||
#ifndef VBAM_WX_WIDGETS_SDLJOY_H_
|
|
||||||
#define VBAM_WX_WIDGETS_SDLJOY_H_
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include <wx/time.h>
|
|
||||||
#include <wx/event.h>
|
|
||||||
|
|
||||||
#include <SDL_joystick.h>
|
|
||||||
#include <SDL_gamecontroller.h>
|
|
||||||
#include <SDL_events.h>
|
|
||||||
|
|
||||||
// Forward declarations.
|
|
||||||
class wxSDLJoyState;
|
|
||||||
class wxJoyPoller;
|
|
||||||
|
|
||||||
// The different types of supported controls.
|
|
||||||
enum wxJoyControl {
|
|
||||||
AxisPlus = 0,
|
|
||||||
AxisMinus,
|
|
||||||
Button,
|
|
||||||
HatNorth,
|
|
||||||
HatSouth,
|
|
||||||
HatWest,
|
|
||||||
HatEast,
|
|
||||||
Last = HatEast
|
|
||||||
};
|
|
||||||
|
|
||||||
// Abstraction for a single joystick. In the current implementation, this
|
|
||||||
// encapsulates an |sdl_index_|. Creation of these objects should only be
|
|
||||||
// handled by wxSDLJoyState, with the exception of the legacy player_index
|
|
||||||
// constructor, which should eventually go away.
|
|
||||||
class wxJoystick {
|
|
||||||
public:
|
|
||||||
static wxJoystick Invalid();
|
|
||||||
|
|
||||||
// TODO: Remove this constructor once the transition to the new UserInput
|
|
||||||
// type is complete.
|
|
||||||
static wxJoystick FromLegacyPlayerIndex(unsigned player_index);
|
|
||||||
|
|
||||||
virtual ~wxJoystick() = default;
|
|
||||||
|
|
||||||
wxString ToString();
|
|
||||||
|
|
||||||
// TODO: Remove this API once the transition to the new UserInput type is
|
|
||||||
// complete.
|
|
||||||
unsigned player_index() { return sdl_index_ + 1; }
|
|
||||||
|
|
||||||
bool operator==(const wxJoystick& other) const;
|
|
||||||
bool operator!=(const wxJoystick& other) const;
|
|
||||||
bool operator<(const wxJoystick& other) const;
|
|
||||||
bool operator<=(const wxJoystick& other) const;
|
|
||||||
bool operator>(const wxJoystick& other) const;
|
|
||||||
bool operator>=(const wxJoystick& other) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static const int kInvalidSdlIndex = -1;
|
|
||||||
friend class wxSDLJoyState;
|
|
||||||
|
|
||||||
wxJoystick() = delete;
|
|
||||||
wxJoystick(int sdl_index);
|
|
||||||
|
|
||||||
int sdl_index_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Represents a Joystick event.
|
|
||||||
class wxJoyEvent : public wxCommandEvent {
|
|
||||||
public:
|
|
||||||
wxJoyEvent(
|
|
||||||
wxJoystick joystick,
|
|
||||||
wxJoyControl control,
|
|
||||||
uint8_t control_index,
|
|
||||||
bool pressed);
|
|
||||||
virtual ~wxJoyEvent() = default;
|
|
||||||
|
|
||||||
wxJoystick joystick() const { return joystick_; }
|
|
||||||
wxJoyControl control() const { return control_; }
|
|
||||||
uint8_t control_index() const { return control_index_; }
|
|
||||||
bool pressed() const { return pressed_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
wxJoystick joystick_;
|
|
||||||
wxJoyControl control_;
|
|
||||||
uint8_t control_index_;
|
|
||||||
bool pressed_;
|
|
||||||
};
|
|
||||||
|
|
||||||
wxDECLARE_EVENT(wxEVT_JOY, wxJoyEvent);
|
|
||||||
|
|
||||||
typedef void (wxEvtHandler::*wxJoyEventFunction)(wxJoyEvent&);
|
|
||||||
#define wxJoyEventHandler(func) wxEVENT_HANDLER_CAST(wxJoyEventFunction, func)
|
|
||||||
#define EVT_SDLJOY(func) \
|
|
||||||
wx__DECLARE_EVT0(wxEVT_JOY, wxJoyEventHandler(func))
|
|
||||||
|
|
||||||
// This is my own SDL-based joystick handler, since wxJoystick is brain-dead.
|
|
||||||
// It's geared towards keyboard emulation.
|
|
||||||
//
|
|
||||||
// After initilization, use PollJoystick() or PollAllJoysticks() for the
|
|
||||||
// joysticks you wish to monitor. The target window will then receive EVT_SDLJOY
|
|
||||||
// events of type wxJoyEvent.
|
|
||||||
// Handling of the wxJoystick value is different depending on the polling mode.
|
|
||||||
// After calls to PollJoysticks(), that value will remain constant for a given
|
|
||||||
// device, even if other joysticks disconnect. This ensures the joystick remains
|
|
||||||
// active during gameplay even if other joysticks disconnect.
|
|
||||||
// However, after calls to PollAllJoysticks(), all joysticks are re-connected
|
|
||||||
// on joystick connect/disconnect. This ensures the right wxJoystick value is
|
|
||||||
// sent to the UI during input event configuration.
|
|
||||||
class wxJoyPoller {
|
|
||||||
public:
|
|
||||||
wxJoyPoller();
|
|
||||||
~wxJoyPoller();
|
|
||||||
|
|
||||||
// Adds a set of joysticks to the list of polled joysticks.
|
|
||||||
// This will disconnect every active joysticks, and reactivates the ones
|
|
||||||
// matching an index in |joysticks|. Missing joysticks will be connected if
|
|
||||||
// they connect later on.
|
|
||||||
void PollJoysticks(std::set<wxJoystick> joysticks);
|
|
||||||
|
|
||||||
// Adds all joysticks to the list of polled joysticks. This will
|
|
||||||
// disconnect every active joysticks, reconnect them and start polling.
|
|
||||||
void PollAllJoysticks();
|
|
||||||
|
|
||||||
// Removes all joysticks from the list of polled joysticks.
|
|
||||||
// This will stop polling.
|
|
||||||
void StopPolling();
|
|
||||||
|
|
||||||
// Activates or deactivates rumble on active joysticks.
|
|
||||||
void SetRumble(bool activate_rumble);
|
|
||||||
|
|
||||||
// Polls active joysticks and empties the SDL event buffer.
|
|
||||||
void Poll();
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Reconnects all controllers.
|
|
||||||
void RemapControllers();
|
|
||||||
|
|
||||||
// Helper method to find a joystick state from a joystick ID.
|
|
||||||
// Returns nullptr if not present.
|
|
||||||
wxSDLJoyState* FindJoyState(const SDL_JoystickID& joy_id);
|
|
||||||
|
|
||||||
// Map of SDL joystick ID to joystick state. Only contains active joysticks.
|
|
||||||
std::unordered_map<SDL_JoystickID, std::unique_ptr<wxSDLJoyState>> joystick_states_;
|
|
||||||
|
|
||||||
// Set of requested SDL joystick indexes.
|
|
||||||
std::set<wxJoystick> requested_joysticks_;
|
|
||||||
|
|
||||||
// Set to true when we are actively polling controllers.
|
|
||||||
bool is_polling_active_ = false;
|
|
||||||
|
|
||||||
// Timestamp when the latest poll was done.
|
|
||||||
wxLongLong last_poll_ = wxGetUTCTimeMillis();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // VBAM_WX_WIDGETS_SDLJOY_H_
|
|
|
@ -1,35 +1,13 @@
|
||||||
#include "wx/widgets/user-input-ctrl.h"
|
#include "wx/widgets/user-input-ctrl.h"
|
||||||
|
|
||||||
|
#include <wx/time.h>
|
||||||
|
|
||||||
#include "wx/config/user-input.h"
|
#include "wx/config/user-input.h"
|
||||||
#include "wx/opts.h"
|
#include "wx/opts.h"
|
||||||
|
#include "wx/widgets/user-input-event.h"
|
||||||
|
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
int FilterKeyCode(const wxKeyEvent& event) {
|
|
||||||
const wxChar keycode = event.GetUnicodeKey();
|
|
||||||
if (keycode == WXK_NONE) {
|
|
||||||
return event.GetKeyCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keycode < 32) {
|
|
||||||
switch (keycode) {
|
|
||||||
case WXK_BACK:
|
|
||||||
case WXK_TAB:
|
|
||||||
case WXK_RETURN:
|
|
||||||
case WXK_ESCAPE:
|
|
||||||
return keycode;
|
|
||||||
default:
|
|
||||||
return WXK_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return keycode;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern const char UserInputCtrlNameStr[] = "userinputctrl";
|
extern const char UserInputCtrlNameStr[] = "userinputctrl";
|
||||||
|
|
||||||
UserInputCtrl::UserInputCtrl() : wxTextCtrl() {}
|
UserInputCtrl::UserInputCtrl() : wxTextCtrl() {}
|
||||||
|
@ -53,6 +31,13 @@ bool UserInputCtrl::Create(wxWindow* parent,
|
||||||
const wxSize& size,
|
const wxSize& size,
|
||||||
long style,
|
long style,
|
||||||
const wxString& name) {
|
const wxString& name) {
|
||||||
|
this->SetClientObject(new UserInputEventSender(this));
|
||||||
|
this->Bind(VBAM_EVT_USER_INPUT_UP, &UserInputCtrl::OnUserInputUp, this);
|
||||||
|
this->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& event) {
|
||||||
|
is_navigating_away_ = false;
|
||||||
|
last_focus_time_ = wxGetUTCTimeMillis();
|
||||||
|
event.Skip();
|
||||||
|
});
|
||||||
return wxTextCtrl::Create(parent, id, value, pos, size, style, wxValidator(), name);
|
return wxTextCtrl::Create(parent, id, value, pos, size, style, wxValidator(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,28 +66,18 @@ void UserInputCtrl::Clear() {
|
||||||
|
|
||||||
wxIMPLEMENT_DYNAMIC_CLASS(UserInputCtrl, wxTextCtrl);
|
wxIMPLEMENT_DYNAMIC_CLASS(UserInputCtrl, wxTextCtrl);
|
||||||
|
|
||||||
// clang-format off
|
void UserInputCtrl::OnUserInputUp(UserInputEvent& event) {
|
||||||
wxBEGIN_EVENT_TABLE(UserInputCtrl, wxTextCtrl)
|
static const wxLongLong kInterval = 100;
|
||||||
EVT_SDLJOY(UserInputCtrl::OnJoy)
|
if (wxGetUTCTimeMillis() - last_focus_time_ < kInterval) {
|
||||||
EVT_KEY_DOWN(UserInputCtrl::OnKeyDown)
|
// Ignore events sent very shortly after focus. This is used to ignore
|
||||||
EVT_KEY_UP(UserInputCtrl::OnKeyUp)
|
// some spurious joystick events like an accidental axis motion.
|
||||||
wxEND_EVENT_TABLE();
|
event.Skip();
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
void UserInputCtrl::OnJoy(wxJoyEvent& event) {
|
|
||||||
static wxLongLong last_event = 0;
|
|
||||||
|
|
||||||
// Filter consecutive axis motions within 300ms, as this adds two bindings
|
|
||||||
// +1/-1 instead of the one intended.
|
|
||||||
if ((event.control() == wxJoyControl::AxisPlus || event.control() == wxJoyControl::AxisMinus) &&
|
|
||||||
wxGetUTCTimeMillis() - last_event < 300) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_event = wxGetUTCTimeMillis();
|
if (is_navigating_away_) {
|
||||||
|
// Ignore events sent after the control has been navigated away from.
|
||||||
// Control was unpressed, ignore.
|
event.Skip();
|
||||||
if (!event.pressed()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,35 +85,10 @@ void UserInputCtrl::OnJoy(wxJoyEvent& event) {
|
||||||
inputs_.clear();
|
inputs_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs_.emplace(event);
|
inputs_.insert(event.input());
|
||||||
UpdateText();
|
|
||||||
Navigate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserInputCtrl::OnKeyDown(wxKeyEvent& event) {
|
|
||||||
last_mod_ = event.GetModifiers();
|
|
||||||
last_key_ = FilterKeyCode(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserInputCtrl::OnKeyUp(wxKeyEvent&) {
|
|
||||||
const int mod = last_mod_;
|
|
||||||
const int key = last_key_;
|
|
||||||
last_mod_ = last_key_ = 0;
|
|
||||||
|
|
||||||
// key is only 0 if we missed the keydown event
|
|
||||||
// or if we are being shipped pseudo keypress events
|
|
||||||
// either way, just ignore.
|
|
||||||
if (!key) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_multikey_) {
|
|
||||||
inputs_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs_.emplace(key, mod);
|
|
||||||
UpdateText();
|
UpdateText();
|
||||||
Navigate();
|
Navigate();
|
||||||
|
is_navigating_away_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserInputCtrl::UpdateText() {
|
void UserInputCtrl::UpdateText() {
|
||||||
|
@ -153,6 +103,7 @@ void UserInputCtrl::UpdateText() {
|
||||||
if (value.empty()) {
|
if (value.empty()) {
|
||||||
SetValue(wxEmptyString);
|
SetValue(wxEmptyString);
|
||||||
} else {
|
} else {
|
||||||
|
// Remove the trailing comma.
|
||||||
SetValue(value.substr(0, value.size() - 1));
|
SetValue(value.substr(0, value.size() - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include <wx/longlong.h>
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
#include <wx/validate.h>
|
#include <wx/validate.h>
|
||||||
|
@ -10,7 +11,7 @@
|
||||||
|
|
||||||
#include "wx/config/game-control.h"
|
#include "wx/config/game-control.h"
|
||||||
#include "wx/config/user-input.h"
|
#include "wx/config/user-input.h"
|
||||||
#include "wx/widgets/sdljoy.h"
|
#include "wx/widgets/user-input-event.h"
|
||||||
|
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
|
@ -59,20 +60,23 @@ public:
|
||||||
void Clear() override;
|
void Clear() override;
|
||||||
|
|
||||||
wxDECLARE_DYNAMIC_CLASS(UserInputCtrl);
|
wxDECLARE_DYNAMIC_CLASS(UserInputCtrl);
|
||||||
wxDECLARE_EVENT_TABLE();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Event handlers.
|
// Event handler.
|
||||||
void OnJoy(wxJoyEvent& event);
|
void OnUserInputUp(widgets::UserInputEvent& event);
|
||||||
void OnKeyDown(wxKeyEvent& event);
|
|
||||||
void OnKeyUp(wxKeyEvent& event);
|
|
||||||
|
|
||||||
// Updates the text in the control to reflect the current inputs.
|
// Updates the text in the control to reflect the current inputs.
|
||||||
void UpdateText();
|
void UpdateText();
|
||||||
|
|
||||||
bool is_multikey_ = false;
|
bool is_multikey_ = false;
|
||||||
int last_mod_ = 0;
|
|
||||||
int last_key_ = 0;
|
// The last time the control was focused. This is used to ignore events sent
|
||||||
|
// very shortly after activation.
|
||||||
|
wxLongLong last_focus_time_ = 0;
|
||||||
|
|
||||||
|
// Set to true after one input has been received. This is used to ignore
|
||||||
|
// subsequent events until the control is focused again.
|
||||||
|
bool is_navigating_away_ = false;
|
||||||
|
|
||||||
std::set<config::UserInput> inputs_;
|
std::set<config::UserInput> inputs_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,283 @@
|
||||||
|
#include "wx/widgets/user-input-event.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <wx/event.h>
|
||||||
|
#include <wx/timer.h>
|
||||||
|
#include <wx/window.h>
|
||||||
|
|
||||||
|
#include "wx/config/user-input.h"
|
||||||
|
|
||||||
|
namespace widgets {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Filters the received key code in the key event for something we can use.
|
||||||
|
wxKeyCode FilterKeyCode(const wxKeyEvent& event) {
|
||||||
|
const wxKeyCode unicode_key = static_cast<wxKeyCode>(event.GetUnicodeKey());
|
||||||
|
if (unicode_key == WXK_NONE) {
|
||||||
|
// We need to filter out modifier keys here so we can differentiate
|
||||||
|
// between a key press and a modifier press.
|
||||||
|
const wxKeyCode keycode = static_cast<wxKeyCode>(event.GetKeyCode());
|
||||||
|
switch (keycode) {
|
||||||
|
case WXK_CONTROL:
|
||||||
|
case WXK_ALT:
|
||||||
|
case WXK_SHIFT:
|
||||||
|
#ifdef __WXMAC__
|
||||||
|
case WXK_RAW_CONTROL:
|
||||||
|
#endif
|
||||||
|
return WXK_NONE;
|
||||||
|
default:
|
||||||
|
return keycode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unicode_key < 32) {
|
||||||
|
switch (unicode_key) {
|
||||||
|
case WXK_BACK:
|
||||||
|
case WXK_TAB:
|
||||||
|
case WXK_RETURN:
|
||||||
|
case WXK_ESCAPE:
|
||||||
|
return unicode_key;
|
||||||
|
default:
|
||||||
|
return WXK_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return unicode_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the set of modifiers for the given key event.
|
||||||
|
std::unordered_set<wxKeyModifier> GetModifiers(const wxKeyEvent& event) {
|
||||||
|
// Standalone modifier are treated as keys and do not set the keyboard modifiers.
|
||||||
|
switch (event.GetKeyCode()) {
|
||||||
|
case WXK_CONTROL:
|
||||||
|
return {wxMOD_CONTROL};
|
||||||
|
case WXK_ALT:
|
||||||
|
return {wxMOD_ALT};
|
||||||
|
case WXK_SHIFT:
|
||||||
|
return {wxMOD_SHIFT};
|
||||||
|
#ifdef __WXMAC__
|
||||||
|
case WXK_RAW_CONTROL:
|
||||||
|
return {wxMOD_RAW_CONTROL};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<wxKeyModifier> mods;
|
||||||
|
if (event.ControlDown()) {
|
||||||
|
mods.insert(wxMOD_CONTROL);
|
||||||
|
}
|
||||||
|
if (event.AltDown()) {
|
||||||
|
mods.insert(wxMOD_ALT);
|
||||||
|
}
|
||||||
|
if (event.ShiftDown()) {
|
||||||
|
mods.insert(wxMOD_SHIFT);
|
||||||
|
}
|
||||||
|
#ifdef __WXMAC__
|
||||||
|
if (event.RawControlDown()) {
|
||||||
|
mods.insert(wxMOD_RAW_CONTROL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds a wxKeyModifier from a set of modifiers.
|
||||||
|
wxKeyModifier GetModifiersFromSet(const std::unordered_set<wxKeyModifier>& mods) {
|
||||||
|
int mod = wxMOD_NONE;
|
||||||
|
for (const wxKeyModifier m : mods) {
|
||||||
|
mod |= m;
|
||||||
|
}
|
||||||
|
return static_cast<wxKeyModifier>(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the key code for a standalone modifier.
|
||||||
|
wxKeyCode KeyFromModifier(const wxKeyModifier mod) {
|
||||||
|
switch (mod) {
|
||||||
|
case wxMOD_CONTROL:
|
||||||
|
return WXK_CONTROL;
|
||||||
|
case wxMOD_ALT:
|
||||||
|
return WXK_ALT;
|
||||||
|
case wxMOD_SHIFT:
|
||||||
|
return WXK_SHIFT;
|
||||||
|
#ifdef __WXMAC__
|
||||||
|
case wxMOD_RAW_CONTROL:
|
||||||
|
return WXK_RAW_CONTROL;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return WXK_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
UserInputEvent::UserInputEvent(const config::UserInput& input, bool pressed)
|
||||||
|
: wxEvent(0, pressed ? VBAM_EVT_USER_INPUT_DOWN : VBAM_EVT_USER_INPUT_UP), input_(input) {}
|
||||||
|
|
||||||
|
wxEvent* UserInputEvent::Clone() const {
|
||||||
|
return new UserInputEvent(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserInputEventSender::UserInputEventSender(wxWindow* const window)
|
||||||
|
: window_(window) {
|
||||||
|
assert(window);
|
||||||
|
|
||||||
|
// Attach the event handlers.
|
||||||
|
window_->Bind(wxEVT_KEY_DOWN, &UserInputEventSender::OnKeyDown, this);
|
||||||
|
window_->Bind(wxEVT_KEY_UP, &UserInputEventSender::OnKeyUp, this);
|
||||||
|
window_->Bind(wxEVT_SET_FOCUS, &UserInputEventSender::Reset, this, window_->GetId());
|
||||||
|
window_->Bind(wxEVT_KILL_FOCUS, &UserInputEventSender::Reset, this, window_->GetId());
|
||||||
|
}
|
||||||
|
|
||||||
|
UserInputEventSender::~UserInputEventSender() = default;
|
||||||
|
|
||||||
|
void UserInputEventSender::OnKeyDown(wxKeyEvent& event) {
|
||||||
|
// Stop propagation of the event.
|
||||||
|
event.Skip(false);
|
||||||
|
|
||||||
|
const wxKeyCode key = FilterKeyCode(event);
|
||||||
|
const std::unordered_set<wxKeyModifier> mods = GetModifiers(event);
|
||||||
|
|
||||||
|
wxKeyCode key_pressed = WXK_NONE;
|
||||||
|
if (key != WXK_NONE) {
|
||||||
|
if (active_keys_.find(key) == active_keys_.end()) {
|
||||||
|
// Key was not pressed before.
|
||||||
|
key_pressed = key;
|
||||||
|
active_keys_.insert(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wxKeyModifier mod_pressed = wxMOD_NONE;
|
||||||
|
for (const wxKeyModifier mod : mods) {
|
||||||
|
if (active_mods_.find(mod) == active_mods_.end()) {
|
||||||
|
// Mod was not pressed before.
|
||||||
|
active_mods_.insert(mod);
|
||||||
|
mod_pressed = mod;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_pressed == WXK_NONE && mod_pressed == wxMOD_NONE) {
|
||||||
|
// No new keys or mods were pressed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxKeyModifier active_mods = GetModifiersFromSet(active_mods_);
|
||||||
|
std::vector<config::UserInput> new_inputs;
|
||||||
|
if (key_pressed == WXK_NONE) {
|
||||||
|
// A new standalone modifier was pressed, send the event.
|
||||||
|
new_inputs.emplace_back(KeyFromModifier(mod_pressed), mod_pressed);
|
||||||
|
} else {
|
||||||
|
// A new key was pressed, send the event with modifiers, first.
|
||||||
|
new_inputs.emplace_back(key, active_mods);
|
||||||
|
|
||||||
|
if (active_mods != wxMOD_NONE) {
|
||||||
|
// Keep track of the key pressed with the active modifiers.
|
||||||
|
active_mod_inputs_.emplace(key, active_mods);
|
||||||
|
|
||||||
|
// Also send the key press event without modifiers.
|
||||||
|
new_inputs.emplace_back(key, wxMOD_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const config::UserInput& input : new_inputs) {
|
||||||
|
wxQueueEvent(window_, new UserInputEvent(input, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
||||||
|
// Stop propagation of the event.
|
||||||
|
event.Skip(false);
|
||||||
|
|
||||||
|
const wxKeyCode key = FilterKeyCode(event);
|
||||||
|
const std::unordered_set<wxKeyModifier> mods = GetModifiers(event);
|
||||||
|
const wxKeyModifier previous_mods = GetModifiersFromSet(active_mods_);
|
||||||
|
|
||||||
|
wxKeyCode key_released = WXK_NONE;
|
||||||
|
if (key != WXK_NONE) {
|
||||||
|
auto iter = active_keys_.find(key);
|
||||||
|
if (iter != active_keys_.end()) {
|
||||||
|
// Key was pressed before.
|
||||||
|
key_released = key;
|
||||||
|
active_keys_.erase(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wxKeyModifier mod_released = wxMOD_NONE;
|
||||||
|
if (key_released == WXK_NONE) {
|
||||||
|
// Only look for a standalone modifier if no key was released.
|
||||||
|
for (const wxKeyModifier mod : mods) {
|
||||||
|
auto iter = active_mods_.find(mod);
|
||||||
|
if (iter != active_mods_.end()) {
|
||||||
|
// Mod was pressed before.
|
||||||
|
mod_released = mod;
|
||||||
|
active_mods_.erase(iter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_released == WXK_NONE && mod_released == wxMOD_NONE) {
|
||||||
|
// No keys or mods were released.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<config::UserInput> released_inputs;
|
||||||
|
if (key_released == WXK_NONE) {
|
||||||
|
// A standalone modifier was released, send it.
|
||||||
|
released_inputs.emplace_back(KeyFromModifier(mod_released), mod_released);
|
||||||
|
} else {
|
||||||
|
// A key was released.
|
||||||
|
if (previous_mods == wxMOD_NONE) {
|
||||||
|
// The key was pressed without modifiers, just send the key release event.
|
||||||
|
released_inputs.emplace_back(key, wxMOD_NONE);
|
||||||
|
} else {
|
||||||
|
// Check if the key was pressed with the active modifiers.
|
||||||
|
const config::UserInput input_with_modifiers(key, previous_mods);
|
||||||
|
auto iter = active_mod_inputs_.find(input_with_modifiers);
|
||||||
|
if (iter == active_mod_inputs_.end()) {
|
||||||
|
// The key press event was never sent, so do it now.
|
||||||
|
wxQueueEvent(window_, new UserInputEvent(input_with_modifiers, true));
|
||||||
|
} else {
|
||||||
|
active_mod_inputs_.erase(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the key release event with the active modifiers.
|
||||||
|
released_inputs.emplace_back(key, previous_mods);
|
||||||
|
|
||||||
|
// Also send the key release event without modifiers.
|
||||||
|
released_inputs.emplace_back(key, wxMOD_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check for any key that were pressed with the previously active
|
||||||
|
// modifiers and release them.
|
||||||
|
for (const wxKeyCode active_key : active_keys_) {
|
||||||
|
const config::UserInput input(active_key, previous_mods);
|
||||||
|
auto iter = active_mod_inputs_.find(input);
|
||||||
|
if (iter != active_mod_inputs_.end()) {
|
||||||
|
active_mod_inputs_.erase(iter);
|
||||||
|
released_inputs.push_back(std::move(input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (const config::UserInput& input : released_inputs) {
|
||||||
|
active_mod_inputs_.erase(input);
|
||||||
|
wxQueueEvent(window_, new UserInputEvent(input, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInputEventSender::Reset(wxFocusEvent& event) {
|
||||||
|
// Reset the internal state.
|
||||||
|
active_keys_.clear();
|
||||||
|
active_mods_.clear();
|
||||||
|
active_mod_inputs_.clear();
|
||||||
|
|
||||||
|
// Let the event propagate.
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace widgets
|
||||||
|
|
||||||
|
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT_DOWN, widgets::UserInputEvent);
|
||||||
|
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT_UP, widgets::UserInputEvent);
|
|
@ -0,0 +1,67 @@
|
||||||
|
#ifndef WX_WIDGETS_USER_INPUT_EVENT_H_
|
||||||
|
#define WX_WIDGETS_USER_INPUT_EVENT_H_
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include <wx/clntdata.h>
|
||||||
|
#include <wx/event.h>
|
||||||
|
|
||||||
|
#include "wx/config/user-input.h"
|
||||||
|
|
||||||
|
namespace widgets {
|
||||||
|
|
||||||
|
// Event fired when a user input is pressed or released. The event contains the
|
||||||
|
// user input that was pressed or released.
|
||||||
|
class UserInputEvent final : public wxEvent {
|
||||||
|
public:
|
||||||
|
UserInputEvent(const config::UserInput& input, bool pressed);
|
||||||
|
virtual ~UserInputEvent() override = default;
|
||||||
|
|
||||||
|
// wxEvent implementation.
|
||||||
|
wxEvent* Clone() const override;
|
||||||
|
|
||||||
|
const config::UserInput& input() const { return input_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const config::UserInput input_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Object that is used to fire user input events when a key is pressed or
|
||||||
|
// released. To use this object, attach it to a wxWindow and listen for
|
||||||
|
// the VBAM_EVT_USER_INPUT_DOWN and VBAM_EVT_USER_INPUT_UP events.
|
||||||
|
class UserInputEventSender final : public wxClientData {
|
||||||
|
public:
|
||||||
|
// `window` must not be nullptr. Will assert otherwise.
|
||||||
|
// `window` must outlive this object.
|
||||||
|
explicit UserInputEventSender(wxWindow* const window);
|
||||||
|
~UserInputEventSender() override;
|
||||||
|
|
||||||
|
// Disable copy and copy assignment.
|
||||||
|
UserInputEventSender(const UserInputEventSender&) = delete;
|
||||||
|
UserInputEventSender& operator=(const UserInputEventSender&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Keyboard event handlers.
|
||||||
|
void OnKeyDown(wxKeyEvent& event);
|
||||||
|
void OnKeyUp(wxKeyEvent& event);
|
||||||
|
|
||||||
|
// Resets the internal state. Called on focus in/out.
|
||||||
|
void Reset(wxFocusEvent& event);
|
||||||
|
|
||||||
|
std::unordered_set<wxKeyCode> active_keys_;
|
||||||
|
std::unordered_set<wxKeyModifier> active_mods_;
|
||||||
|
std::set<config::UserInput> active_mod_inputs_;
|
||||||
|
|
||||||
|
// The wxWindow this object is attached to.
|
||||||
|
// Must outlive this object.
|
||||||
|
wxWindow* const window_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widgets
|
||||||
|
|
||||||
|
// Fired when a user input is pressed.
|
||||||
|
wxDECLARE_EVENT(VBAM_EVT_USER_INPUT_DOWN, widgets::UserInputEvent);
|
||||||
|
// Fired when a user input is released.
|
||||||
|
wxDECLARE_EVENT(VBAM_EVT_USER_INPUT_UP, widgets::UserInputEvent);
|
||||||
|
|
||||||
|
#endif // WX_WIDGETS_USER_INPUT_EVENT_H_
|
|
@ -57,8 +57,6 @@ using std::int32_t;
|
||||||
// GetAccel is inefficent anyway (often I don't want to convert to wxAccEnt)
|
// GetAccel is inefficent anyway (often I don't want to convert to wxAccEnt)
|
||||||
// This is a working replacement for SetAccel, at least.
|
// This is a working replacement for SetAccel, at least.
|
||||||
|
|
||||||
#include "wx/wxutil.h"
|
|
||||||
|
|
||||||
// wxrc helpers (for dynamic strings instead of constant)
|
// wxrc helpers (for dynamic strings instead of constant)
|
||||||
#define XRCID_D(str) wxXmlResource::GetXRCID(str)
|
#define XRCID_D(str) wxXmlResource::GetXRCID(str)
|
||||||
//#define XRCCTRL_D(win, id, type) (wxStaticCast((win).FindWindow(XRCID_D(id)), type))
|
//#define XRCCTRL_D(win, id, type) (wxStaticCast((win).FindWindow(XRCID_D(id)), type))
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
#include "wx/wxutil.h"
|
|
||||||
|
|
||||||
int getKeyboardKeyCode(const wxKeyEvent& event) {
|
|
||||||
const int key_code = event.GetKeyCode();
|
|
||||||
if (key_code > WXK_START) {
|
|
||||||
return key_code;
|
|
||||||
}
|
|
||||||
int uc = event.GetUnicodeKey();
|
|
||||||
if (uc != WXK_NONE) {
|
|
||||||
if (uc < 32) { // not all control chars
|
|
||||||
switch (uc) {
|
|
||||||
case WXK_BACK:
|
|
||||||
case WXK_TAB:
|
|
||||||
case WXK_RETURN:
|
|
||||||
case WXK_ESCAPE:
|
|
||||||
return uc;
|
|
||||||
default:
|
|
||||||
return WXK_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uc;
|
|
||||||
} else {
|
|
||||||
return event.GetKeyCode();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
#ifndef VBAM_WX_UTIL_H_
|
|
||||||
#define VBAM_WX_UTIL_H_
|
|
||||||
|
|
||||||
#include <wx/event.h>
|
|
||||||
|
|
||||||
int getKeyboardKeyCode(const wxKeyEvent& event);
|
|
||||||
|
|
||||||
#endif // VBAM_WX_UTIL_H_
|
|
|
@ -1,14 +1,13 @@
|
||||||
// mainline:
|
|
||||||
// parse cmd line
|
|
||||||
// load xrc file (guiinit.cpp does most of instantiation)
|
|
||||||
// create & display main frame
|
|
||||||
|
|
||||||
#include "wx/wxvbam.h"
|
#include "wx/wxvbam.h"
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __WXGTK__
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <wx/cmdline.h>
|
#include <wx/cmdline.h>
|
||||||
#include <wx/display.h>
|
#include <wx/display.h>
|
||||||
|
@ -31,18 +30,9 @@
|
||||||
#include <wx/zipstrm.h>
|
#include <wx/zipstrm.h>
|
||||||
|
|
||||||
#include "components/user_config/user_config.h"
|
#include "components/user_config/user_config.h"
|
||||||
#include "core/gb/gbGlobals.h"
|
|
||||||
#include "core/gba/gbaSound.h"
|
#include "core/gba/gbaSound.h"
|
||||||
|
|
||||||
#if defined(VBAM_ENABLE_DEBUGGER)
|
|
||||||
#include "core/gba/gbaRemote.h"
|
|
||||||
#endif // defined(VBAM_ENABLE_DEBUGGER)
|
|
||||||
|
|
||||||
// The built-in xrc file
|
|
||||||
#include "wx/builtin-xrc.h"
|
|
||||||
|
|
||||||
// The built-in vba-over.ini
|
|
||||||
#include "wx/builtin-over.h"
|
#include "wx/builtin-over.h"
|
||||||
|
#include "wx/builtin-xrc.h"
|
||||||
#include "wx/config/game-control.h"
|
#include "wx/config/game-control.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
#include "wx/config/option.h"
|
#include "wx/config/option.h"
|
||||||
|
@ -53,9 +43,9 @@
|
||||||
#include "wx/widgets/user-input-ctrl.h"
|
#include "wx/widgets/user-input-ctrl.h"
|
||||||
#include "wx/widgets/utils.h"
|
#include "wx/widgets/utils.h"
|
||||||
|
|
||||||
#ifdef __WXGTK__
|
#if defined(VBAM_ENABLE_DEBUGGER)
|
||||||
#include <gdk/gdk.h>
|
#include "core/gba/gbaRemote.h"
|
||||||
#endif
|
#endif // defined(VBAM_ENABLE_DEBUGGER)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -261,6 +251,13 @@ static void tack_full_path(wxString& s, const wxString& app = wxEmptyString)
|
||||||
s += wxT("\n\t") + full_config_path[i] + app;
|
s += wxT("\n\t") + full_config_path[i] + app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxvbamApp::wxvbamApp()
|
||||||
|
: wxApp(),
|
||||||
|
pending_fullscreen(false),
|
||||||
|
frame(nullptr),
|
||||||
|
using_wayland(false),
|
||||||
|
sdl_poller_(widgets::SdlPoller(std::bind(&wxvbamApp::GetJoyEventHandler, this))) {}
|
||||||
|
|
||||||
const wxString wxvbamApp::GetPluginsDir()
|
const wxString wxvbamApp::GetPluginsDir()
|
||||||
{
|
{
|
||||||
return wxStandardPaths::Get().GetPluginsDir();
|
return wxStandardPaths::Get().GetPluginsDir();
|
||||||
|
@ -836,6 +833,30 @@ wxvbamApp::~wxvbamApp() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxEvtHandler* wxvbamApp::GetJoyEventHandler() {
|
||||||
|
// Use the active window, if any.
|
||||||
|
wxWindow* focused_window = wxWindow::FindFocus();
|
||||||
|
if (focused_window) {
|
||||||
|
return focused_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frame) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto panel = frame->GetPanel();
|
||||||
|
if (!panel || !panel->panel) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OPTION(kUIAllowJoystickBackgroundInput)) {
|
||||||
|
// Use the game panel, if the background polling option is enabled.
|
||||||
|
return panel->panel->GetWindow()->GetEventHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
MainFrame::MainFrame()
|
MainFrame::MainFrame()
|
||||||
: wxFrame(),
|
: wxFrame(),
|
||||||
paused(false),
|
paused(false),
|
||||||
|
@ -849,8 +870,6 @@ MainFrame::MainFrame()
|
||||||
keep_on_top_styler_(this),
|
keep_on_top_styler_(this),
|
||||||
status_bar_observer_(config::OptionID::kGenStatusBar,
|
status_bar_observer_(config::OptionID::kGenStatusBar,
|
||||||
std::bind(&MainFrame::OnStatusBarChanged, this)) {
|
std::bind(&MainFrame::OnStatusBarChanged, this)) {
|
||||||
jpoll = new JoystickPoller();
|
|
||||||
this->Connect(wxID_ANY, wxEVT_SHOW, wxShowEventHandler(JoystickPoller::ShowDialog), jpoll, jpoll);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MainFrame::~MainFrame() {
|
MainFrame::~MainFrame() {
|
||||||
|
@ -983,19 +1002,15 @@ int MainFrame::FilterEvent(wxEvent& event) {
|
||||||
return wxEventFilter::Event_Skip;
|
return wxEventFilter::Event_Skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
int command = 0;
|
if (event.GetEventType() != VBAM_EVT_USER_INPUT_DOWN) {
|
||||||
if (event.GetEventType() == wxEVT_KEY_DOWN) {
|
// We only treat "VBAM_EVT_USER_INPUT_DOWN" events here.
|
||||||
const wxKeyEvent& key_event = static_cast<wxKeyEvent&>(event);
|
return wxEventFilter::Event_Skip;
|
||||||
command = gopts.shortcuts.CommandForInput(config::UserInput(key_event));
|
|
||||||
} else if (event.GetEventType() == wxEVT_JOY) {
|
|
||||||
const wxJoyEvent& joy_event = static_cast<wxJoyEvent&>(event);
|
|
||||||
if (joy_event.pressed()) {
|
|
||||||
// We ignore "button up" events here.
|
|
||||||
command = gopts.shortcuts.CommandForInput(config::UserInput(joy_event));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const widgets::UserInputEvent& user_input_event = static_cast<widgets::UserInputEvent&>(event);
|
||||||
|
const int command = gopts.shortcuts.CommandForInput(user_input_event.input());
|
||||||
if (command == 0) {
|
if (command == 0) {
|
||||||
|
// No associated command found.
|
||||||
return wxEventFilter::Event_Skip;
|
return wxEventFilter::Event_Skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1028,58 +1043,6 @@ wxString MainFrame::GetGamePath(wxString path)
|
||||||
return game_path;
|
return game_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainFrame::SetJoystick()
|
|
||||||
{
|
|
||||||
/* Remove all attached joysticks to avoid errors while
|
|
||||||
* destroying and creating the GameArea `panel`. */
|
|
||||||
joy.StopPolling();
|
|
||||||
|
|
||||||
if (!emulating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::set<wxJoystick> needed_joysticks = gopts.shortcuts.Joysticks();
|
|
||||||
for (const auto& iter : gopts.game_control_bindings) {
|
|
||||||
for (const auto& input_iter : iter.second) {
|
|
||||||
needed_joysticks.emplace(input_iter.joystick());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
joy.PollJoysticks(std::move(needed_joysticks));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainFrame::StopJoyPollTimer()
|
|
||||||
{
|
|
||||||
if (jpoll && jpoll->IsRunning())
|
|
||||||
jpoll->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainFrame::StartJoyPollTimer()
|
|
||||||
{
|
|
||||||
if (jpoll && !jpoll->IsRunning())
|
|
||||||
jpoll->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainFrame::IsJoyPollTimerRunning()
|
|
||||||
{
|
|
||||||
return jpoll->IsRunning();
|
|
||||||
}
|
|
||||||
|
|
||||||
wxEvtHandler* MainFrame::GetJoyEventHandler()
|
|
||||||
{
|
|
||||||
auto focused_window = wxWindow::FindFocus();
|
|
||||||
|
|
||||||
if (focused_window)
|
|
||||||
return focused_window;
|
|
||||||
|
|
||||||
auto panel = GetPanel();
|
|
||||||
if (!panel)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (OPTION(kUIAllowJoystickBackgroundInput))
|
|
||||||
return panel->GetEventHandler();
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainFrame::enable_menus()
|
void MainFrame::enable_menus()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ncmds; i++)
|
for (int i = 0; i < ncmds; i++)
|
||||||
|
@ -1403,7 +1366,8 @@ void MainFrame::IdentifyRom()
|
||||||
// grabbing those keys, but I can't track it down.
|
// grabbing those keys, but I can't track it down.
|
||||||
int wxvbamApp::FilterEvent(wxEvent& event)
|
int wxvbamApp::FilterEvent(wxEvent& event)
|
||||||
{
|
{
|
||||||
if (frame)
|
if (frame) {
|
||||||
return frame->FilterEvent(event);
|
return frame->FilterEvent(event);
|
||||||
|
}
|
||||||
return wxApp::FilterEvent(event);
|
return wxApp::FilterEvent(event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
#include "wx/dialogs/base-dialog.h"
|
#include "wx/dialogs/base-dialog.h"
|
||||||
#include "wx/widgets/dpi-support.h"
|
#include "wx/widgets/dpi-support.h"
|
||||||
#include "wx/widgets/keep-on-top-styler.h"
|
#include "wx/widgets/keep-on-top-styler.h"
|
||||||
#include "wx/widgets/sdljoy.h"
|
#include "wx/widgets/sdl-poller.h"
|
||||||
|
#include "wx/widgets/user-input-event.h"
|
||||||
#include "wx/widgets/wxmisc.h"
|
#include "wx/widgets/wxmisc.h"
|
||||||
#include "wx/wxhead.h"
|
#include "wx/wxhead.h"
|
||||||
|
|
||||||
|
@ -30,7 +31,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "wx/wxlogdebug.h"
|
#include "wx/wxlogdebug.h"
|
||||||
#include "wx/wxutil.h"
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void CheckPointer(T pointer)
|
void CheckPointer(T pointer)
|
||||||
|
@ -59,13 +59,7 @@ class MainFrame;
|
||||||
|
|
||||||
class wxvbamApp : public wxApp {
|
class wxvbamApp : public wxApp {
|
||||||
public:
|
public:
|
||||||
wxvbamApp()
|
wxvbamApp();
|
||||||
: wxApp()
|
|
||||||
, pending_fullscreen(false)
|
|
||||||
, frame(NULL)
|
|
||||||
, using_wayland(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
virtual bool OnInit();
|
virtual bool OnInit();
|
||||||
virtual int OnRun();
|
virtual int OnRun();
|
||||||
virtual bool OnCmdLineHelp(wxCmdLineParser&);
|
virtual bool OnCmdLineHelp(wxCmdLineParser&);
|
||||||
|
@ -94,6 +88,8 @@ public:
|
||||||
// without this, global accels don't always work
|
// without this, global accels don't always work
|
||||||
int FilterEvent(wxEvent&);
|
int FilterEvent(wxEvent&);
|
||||||
|
|
||||||
|
widgets::SdlPoller* sdl_poller() { return &sdl_poller_; }
|
||||||
|
|
||||||
// vba-over.ini
|
// vba-over.ini
|
||||||
wxFileConfig* overrides = nullptr;
|
wxFileConfig* overrides = nullptr;
|
||||||
|
|
||||||
|
@ -130,9 +126,14 @@ protected:
|
||||||
int console_status = 0;
|
int console_status = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Returns the currently active event handler to use for user input events.
|
||||||
|
wxEvtHandler* GetJoyEventHandler();
|
||||||
|
|
||||||
wxPathList config_path;
|
wxPathList config_path;
|
||||||
char* home = nullptr;
|
char* home = nullptr;
|
||||||
|
|
||||||
|
widgets::SdlPoller sdl_poller_;
|
||||||
|
|
||||||
// Main configuration file.
|
// Main configuration file.
|
||||||
wxFileName config_file_;
|
wxFileName config_file_;
|
||||||
};
|
};
|
||||||
|
@ -216,8 +217,6 @@ public:
|
||||||
void GetMenuOptionBool(const wxString& menuName, bool* field);
|
void GetMenuOptionBool(const wxString& menuName, bool* field);
|
||||||
void SetMenuOption(const wxString& menuName, bool value);
|
void SetMenuOption(const wxString& menuName, bool value);
|
||||||
|
|
||||||
void SetJoystick();
|
|
||||||
|
|
||||||
int FilterEvent(wxEvent& event);
|
int FilterEvent(wxEvent& event);
|
||||||
|
|
||||||
GameArea* GetPanel()
|
GameArea* GetPanel()
|
||||||
|
@ -308,24 +307,11 @@ public:
|
||||||
|
|
||||||
virtual bool DialogOpened() { return dialog_opened != 0; }
|
virtual bool DialogOpened() { return dialog_opened != 0; }
|
||||||
|
|
||||||
virtual void SetJoystickRumble(bool b) { joy.SetRumble(b); }
|
|
||||||
|
|
||||||
bool IsPaused(bool incendental = false)
|
bool IsPaused(bool incendental = false)
|
||||||
{
|
{
|
||||||
return (paused && !pause_next && !incendental) || dialog_opened;
|
return (paused && !pause_next && !incendental) || dialog_opened;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PollJoysticks() { joy.Poll(); }
|
|
||||||
|
|
||||||
void PollAllJoysticks() { joy.PollAllJoysticks(); }
|
|
||||||
|
|
||||||
// Poll joysticks with timer.
|
|
||||||
void StartJoyPollTimer();
|
|
||||||
void StopJoyPollTimer();
|
|
||||||
bool IsJoyPollTimerRunning();
|
|
||||||
|
|
||||||
wxEvtHandler* GetJoyEventHandler();
|
|
||||||
|
|
||||||
// required for building from xrc
|
// required for building from xrc
|
||||||
DECLARE_DYNAMIC_CLASS(MainFrame);
|
DECLARE_DYNAMIC_CLASS(MainFrame);
|
||||||
// required for event handling
|
// required for event handling
|
||||||
|
@ -351,9 +337,6 @@ private:
|
||||||
checkable_mi_array_t checkable_mi;
|
checkable_mi_array_t checkable_mi;
|
||||||
// recent menu item accels
|
// recent menu item accels
|
||||||
wxMenu* recent;
|
wxMenu* recent;
|
||||||
// joystick reader
|
|
||||||
wxJoyPoller joy;
|
|
||||||
JoystickPoller* jpoll = nullptr;
|
|
||||||
// quicker & more accurate than FindFocus() != NULL
|
// quicker & more accurate than FindFocus() != NULL
|
||||||
bool focused;
|
bool focused;
|
||||||
// One-time toggle to indicate that this object is fully initialized. This
|
// One-time toggle to indicate that this object is fully initialized. This
|
||||||
|
@ -390,20 +373,6 @@ private:
|
||||||
#include "wx/cmdhandlers.h"
|
#include "wx/cmdhandlers.h"
|
||||||
};
|
};
|
||||||
|
|
||||||
// a class for polling joystick keys
|
|
||||||
class JoystickPoller : public wxTimer {
|
|
||||||
public:
|
|
||||||
void Notify() {
|
|
||||||
wxGetApp().frame->PollJoysticks();
|
|
||||||
}
|
|
||||||
void ShowDialog(wxShowEvent& ev) {
|
|
||||||
if (ev.IsShown())
|
|
||||||
Start(50);
|
|
||||||
else
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// a helper class to avoid forgetting StopModal()
|
// a helper class to avoid forgetting StopModal()
|
||||||
class ModalPause {
|
class ModalPause {
|
||||||
public:
|
public:
|
||||||
|
@ -581,9 +550,8 @@ protected:
|
||||||
|
|
||||||
bool paused;
|
bool paused;
|
||||||
void OnIdle(wxIdleEvent&);
|
void OnIdle(wxIdleEvent&);
|
||||||
void OnKeyDown(wxKeyEvent& ev);
|
void OnUserInputDown(widgets::UserInputEvent& event);
|
||||||
void OnKeyUp(wxKeyEvent& ev);
|
void OnUserInputUp(widgets::UserInputEvent& event);
|
||||||
void OnSDLJoy(wxJoyEvent& ev);
|
|
||||||
void PaintEv(wxPaintEvent& ev);
|
void PaintEv(wxPaintEvent& ev);
|
||||||
void EraseBackground(wxEraseEvent& ev);
|
void EraseBackground(wxEraseEvent& ev);
|
||||||
void OnSize(wxSizeEvent& ev);
|
void OnSize(wxSizeEvent& ev);
|
||||||
|
|
Loading…
Reference in New Issue