[UserInput] Only process shortcut commands once
This modifies the UserInputEvent class to fire a vector of events at once, rather than individual down/up events for each UserInput. This simplifies handling of the global event filter and prevents the firing of spurious events. This also fixes a bug when pressing "Ctrl+1" would trigger the command for both the command assigned to "Ctrl+1" and to "1". Now, only the "Ctrl+1" command will fire.
This commit is contained in:
parent
d32be9ddbe
commit
902c6c8e4b
|
@ -1177,8 +1177,7 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
||||||
wxWindow* w = panel->GetWindow();
|
wxWindow* w = panel->GetWindow();
|
||||||
|
|
||||||
// set up event handlers
|
// set up event handlers
|
||||||
w->Bind(VBAM_EVT_USER_INPUT_DOWN, &GameArea::OnUserInputDown, this);
|
w->Bind(VBAM_EVT_USER_INPUT, &GameArea::OnUserInput, this);
|
||||||
w->Bind(VBAM_EVT_USER_INPUT_UP, &GameArea::OnUserInputUp, this);
|
|
||||||
w->Bind(wxEVT_PAINT, &GameArea::PaintEv, this);
|
w->Bind(wxEVT_PAINT, &GameArea::PaintEv, this);
|
||||||
w->Bind(wxEVT_ERASE_BACKGROUND, &GameArea::EraseBackground, this);
|
w->Bind(wxEVT_ERASE_BACKGROUND, &GameArea::EraseBackground, this);
|
||||||
|
|
||||||
|
@ -1330,26 +1329,33 @@ static Display* GetX11Display() {
|
||||||
}
|
}
|
||||||
#endif // __WXGTK__
|
#endif // __WXGTK__
|
||||||
|
|
||||||
void GameArea::OnUserInputDown(widgets::UserInputEvent& event) {
|
void GameArea::OnUserInput(widgets::UserInputEvent& event) {
|
||||||
if (wxGetApp().emulated_gamepad()->OnInputPressed(event.input())) {
|
bool emulated_key_pressed = false;
|
||||||
wxWakeUpIdle();
|
for (const auto& event_data : event.data()) {
|
||||||
}
|
if (event_data.pressed) {
|
||||||
}
|
if (wxGetApp().emulated_gamepad()->OnInputPressed(event_data.input)) {
|
||||||
|
emulated_key_pressed = true;
|
||||||
void GameArea::OnUserInputUp(widgets::UserInputEvent& event) {
|
}
|
||||||
if (wxGetApp().emulated_gamepad()->OnInputReleased(event.input())) {
|
} else {
|
||||||
wxWakeUpIdle();
|
if (wxGetApp().emulated_gamepad()->OnInputReleased(event_data.input)) {
|
||||||
|
emulated_key_pressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tell Linux to turn off the screensaver/screen-blank if joystick button was pressed
|
if (emulated_key_pressed) {
|
||||||
// this shouldn't be necessary of course
|
wxWakeUpIdle();
|
||||||
|
|
||||||
#if defined(__WXGTK__) && defined(HAVE_X11) && !defined(HAVE_XSS)
|
#if defined(__WXGTK__) && defined(HAVE_X11) && !defined(HAVE_XSS)
|
||||||
if (event.input().is_joystick() && !wxGetApp().UsingWayland()) {
|
// Tell X11 to turn off the screensaver/screen-blank if a button was
|
||||||
auto display = GetX11Display();
|
// was pressed. This shouldn't be necessary.
|
||||||
XResetScreenSaver(display);
|
if (!wxGetApp().UsingWayland()) {
|
||||||
XFlush(display);
|
auto display = GetX11Display();
|
||||||
}
|
XResetScreenSaver(display);
|
||||||
|
XFlush(display);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// these three are forwarded to the DrawingPanel instance
|
// these three are forwarded to the DrawingPanel instance
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <SDL_events.h>
|
|
||||||
#include <wx/timer.h>
|
#include <wx/timer.h>
|
||||||
#include <wx/toplevel.h>
|
#include <wx/toplevel.h>
|
||||||
|
|
||||||
|
@ -86,9 +85,10 @@ public:
|
||||||
bool is_game_controller() const { return !!game_controller_; }
|
bool is_game_controller() const { return !!game_controller_; }
|
||||||
|
|
||||||
// Processes the corresponding events.
|
// Processes the corresponding events.
|
||||||
std::vector<UserInputEvent> ProcessAxisEvent(const uint8_t index, const JoyAxisStatus status);
|
std::vector<UserInputEvent::Data> ProcessAxisEvent(const uint8_t index,
|
||||||
std::vector<UserInputEvent> ProcessButtonEvent(const uint8_t index, const bool pressed);
|
const JoyAxisStatus status);
|
||||||
std::vector<UserInputEvent> ProcessHatEvent(const uint8_t index, const uint8_t status);
|
std::vector<UserInputEvent::Data> ProcessButtonEvent(const uint8_t index, const bool pressed);
|
||||||
|
std::vector<UserInputEvent::Data> ProcessHatEvent(const uint8_t index, const uint8_t status);
|
||||||
|
|
||||||
// Activates or deactivates rumble.
|
// Activates or deactivates rumble.
|
||||||
void SetRumble(bool activate_rumble);
|
void SetRumble(bool activate_rumble);
|
||||||
|
@ -192,14 +192,14 @@ bool JoyState::IsValid() const {
|
||||||
return sdl_joystick_;
|
return sdl_joystick_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<UserInputEvent> JoyState::ProcessAxisEvent(const uint8_t index,
|
std::vector<UserInputEvent::Data> JoyState::ProcessAxisEvent(const uint8_t index,
|
||||||
const JoyAxisStatus status) {
|
const JoyAxisStatus status) {
|
||||||
const JoyAxisStatus previous_status = axis_[index];
|
const JoyAxisStatus previous_status = axis_[index];
|
||||||
std::vector<UserInputEvent> events;
|
std::vector<UserInputEvent::Data> event_data;
|
||||||
|
|
||||||
// Nothing to do if no-op.
|
// Nothing to do if no-op.
|
||||||
if (status == previous_status) {
|
if (status == previous_status) {
|
||||||
return events;
|
return event_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the value.
|
// Update the value.
|
||||||
|
@ -207,48 +207,50 @@ std::vector<UserInputEvent> JoyState::ProcessAxisEvent(const uint8_t index,
|
||||||
|
|
||||||
if (previous_status != JoyAxisStatus::Neutral) {
|
if (previous_status != JoyAxisStatus::Neutral) {
|
||||||
// Send the "unpressed" event.
|
// Send the "unpressed" event.
|
||||||
events.push_back(UserInputEvent(
|
event_data.emplace_back(
|
||||||
config::JoyInput(wx_joystick_, AxisStatusToJoyControl(previous_status), index), false));
|
config::JoyInput(wx_joystick_, AxisStatusToJoyControl(previous_status), index), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We already sent the "unpressed" event so nothing more to do.
|
// We already sent the "unpressed" event so nothing more to do.
|
||||||
if (status == JoyAxisStatus::Neutral) {
|
if (status == JoyAxisStatus::Neutral) {
|
||||||
return events;
|
return event_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the "pressed" event.
|
// Send the "pressed" event.
|
||||||
events.push_back(UserInputEvent(
|
event_data.emplace_back(config::JoyInput(wx_joystick_, AxisStatusToJoyControl(status), index),
|
||||||
config::JoyInput(wx_joystick_, AxisStatusToJoyControl(status), index), true));
|
true);
|
||||||
|
|
||||||
return events;
|
return event_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<UserInputEvent> JoyState::ProcessButtonEvent(const uint8_t index, const bool status) {
|
std::vector<UserInputEvent::Data> JoyState::ProcessButtonEvent(const uint8_t index,
|
||||||
|
const bool status) {
|
||||||
const bool previous_status = buttons_[index];
|
const bool previous_status = buttons_[index];
|
||||||
std::vector<UserInputEvent> events;
|
std::vector<UserInputEvent::Data> event_data;
|
||||||
|
|
||||||
// Nothing to do if no-op.
|
// Nothing to do if no-op.
|
||||||
if (status == previous_status) {
|
if (status == previous_status) {
|
||||||
return events;
|
return event_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the value.
|
// Update the value.
|
||||||
buttons_[index] = status;
|
buttons_[index] = status;
|
||||||
|
|
||||||
// Send the event.
|
// Send the event.
|
||||||
events.push_back(
|
event_data.emplace_back(config::JoyInput(wx_joystick_, config::JoyControl::Button, index),
|
||||||
UserInputEvent(config::JoyInput(wx_joystick_, config::JoyControl::Button, index), status));
|
status);
|
||||||
|
|
||||||
return events;
|
return event_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<UserInputEvent> JoyState::ProcessHatEvent(const uint8_t index, const uint8_t status) {
|
std::vector<UserInputEvent::Data> JoyState::ProcessHatEvent(const uint8_t index,
|
||||||
|
const uint8_t status) {
|
||||||
const uint16_t previous_status = hats_[index];
|
const uint16_t previous_status = hats_[index];
|
||||||
std::vector<UserInputEvent> events;
|
std::vector<UserInputEvent::Data> event_data;
|
||||||
|
|
||||||
// Nothing to do if no-op.
|
// Nothing to do if no-op.
|
||||||
if (status == previous_status) {
|
if (status == previous_status) {
|
||||||
return events;
|
return event_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the value.
|
// Update the value.
|
||||||
|
@ -262,17 +264,17 @@ std::vector<UserInputEvent> JoyState::ProcessHatEvent(const uint8_t index, const
|
||||||
const bool new_control_pressed = (status & bit) != 0;
|
const bool new_control_pressed = (status & bit) != 0;
|
||||||
if (old_control_pressed && !new_control_pressed) {
|
if (old_control_pressed && !new_control_pressed) {
|
||||||
// Send the "unpressed" event.
|
// Send the "unpressed" event.
|
||||||
events.push_back(UserInputEvent(
|
event_data.emplace_back(
|
||||||
config::JoyInput(wx_joystick_, HatStatusToJoyControl(bit), index), false));
|
config::JoyInput(wx_joystick_, HatStatusToJoyControl(bit), index), false);
|
||||||
}
|
}
|
||||||
if (!old_control_pressed && new_control_pressed) {
|
if (!old_control_pressed && new_control_pressed) {
|
||||||
// Send the "pressed" event.
|
// Send the "pressed" event.
|
||||||
events.push_back(UserInputEvent(
|
event_data.emplace_back(
|
||||||
config::JoyInput(wx_joystick_, HatStatusToJoyControl(bit), index), true));
|
config::JoyInput(wx_joystick_, HatStatusToJoyControl(bit), index), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return events;
|
return event_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoyState::SetRumble(bool activate_rumble) {
|
void JoyState::SetRumble(bool activate_rumble) {
|
||||||
|
@ -341,23 +343,23 @@ void SdlPoller::Notify() {
|
||||||
SDL_Event sdl_event;
|
SDL_Event sdl_event;
|
||||||
|
|
||||||
while (SDL_PollEvent(&sdl_event)) {
|
while (SDL_PollEvent(&sdl_event)) {
|
||||||
std::vector<UserInputEvent> events;
|
std::vector<UserInputEvent::Data> event_data;
|
||||||
JoyState* joy_state = nullptr;
|
JoyState* joy_state = nullptr;
|
||||||
switch (sdl_event.type) {
|
switch (sdl_event.type) {
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
case SDL_CONTROLLERBUTTONUP:
|
case SDL_CONTROLLERBUTTONUP:
|
||||||
joy_state = FindJoyState(sdl_event.cbutton.which);
|
joy_state = FindJoyState(sdl_event.cbutton.which);
|
||||||
if (joy_state) {
|
if (joy_state) {
|
||||||
events = joy_state->ProcessButtonEvent(sdl_event.cbutton.button,
|
event_data = joy_state->ProcessButtonEvent(sdl_event.cbutton.button,
|
||||||
sdl_event.cbutton.state);
|
sdl_event.cbutton.state);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_CONTROLLERAXISMOTION:
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
joy_state = FindJoyState(sdl_event.caxis.which);
|
joy_state = FindJoyState(sdl_event.caxis.which);
|
||||||
if (joy_state) {
|
if (joy_state) {
|
||||||
events = joy_state->ProcessAxisEvent(sdl_event.caxis.axis,
|
event_data = joy_state->ProcessAxisEvent(
|
||||||
AxisValueToStatus(sdl_event.caxis.value));
|
sdl_event.caxis.axis, AxisValueToStatus(sdl_event.caxis.value));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -372,23 +374,24 @@ void SdlPoller::Notify() {
|
||||||
case SDL_JOYBUTTONUP:
|
case SDL_JOYBUTTONUP:
|
||||||
joy_state = FindJoyState(sdl_event.jbutton.which);
|
joy_state = FindJoyState(sdl_event.jbutton.which);
|
||||||
if (joy_state && !joy_state->is_game_controller()) {
|
if (joy_state && !joy_state->is_game_controller()) {
|
||||||
events = joy_state->ProcessButtonEvent(sdl_event.jbutton.button,
|
event_data = joy_state->ProcessButtonEvent(sdl_event.jbutton.button,
|
||||||
sdl_event.jbutton.state);
|
sdl_event.jbutton.state);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_JOYAXISMOTION:
|
case SDL_JOYAXISMOTION:
|
||||||
joy_state = FindJoyState(sdl_event.jaxis.which);
|
joy_state = FindJoyState(sdl_event.jaxis.which);
|
||||||
if (joy_state && !joy_state->is_game_controller()) {
|
if (joy_state && !joy_state->is_game_controller()) {
|
||||||
events = joy_state->ProcessAxisEvent(sdl_event.jaxis.axis,
|
event_data = joy_state->ProcessAxisEvent(
|
||||||
AxisValueToStatus(sdl_event.jaxis.value));
|
sdl_event.jaxis.axis, AxisValueToStatus(sdl_event.jaxis.value));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_JOYHATMOTION:
|
case SDL_JOYHATMOTION:
|
||||||
joy_state = FindJoyState(sdl_event.jhat.which);
|
joy_state = FindJoyState(sdl_event.jhat.which);
|
||||||
if (joy_state && !joy_state->is_game_controller()) {
|
if (joy_state && !joy_state->is_game_controller()) {
|
||||||
events = joy_state->ProcessHatEvent(sdl_event.jhat.hat, sdl_event.jhat.value);
|
event_data =
|
||||||
|
joy_state->ProcessHatEvent(sdl_event.jhat.hat, sdl_event.jhat.value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -402,12 +405,10 @@ void SdlPoller::Notify() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!events.empty()) {
|
if (!event_data.empty()) {
|
||||||
wxEvtHandler* handler = handler_provider_();
|
wxEvtHandler* handler = handler_provider_();
|
||||||
if (handler) {
|
if (handler) {
|
||||||
for (const auto& user_input_event : events) {
|
handler->QueueEvent(new UserInputEvent(std::move(event_data)));
|
||||||
handler->QueueEvent(user_input_event.Clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,8 @@ bool UserInputCtrl::Create(wxWindow* parent,
|
||||||
long style,
|
long style,
|
||||||
const wxString& name) {
|
const wxString& name) {
|
||||||
this->SetClientObject(new UserInputEventSender(this));
|
this->SetClientObject(new UserInputEventSender(this));
|
||||||
this->Bind(VBAM_EVT_USER_INPUT_UP, &UserInputCtrl::OnUserInputUp, this);
|
this->Bind(VBAM_EVT_USER_INPUT, &UserInputCtrl::OnUserInput, this);
|
||||||
this->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& event) {
|
this->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& event) {
|
||||||
is_navigating_away_ = false;
|
|
||||||
last_focus_time_ = wxGetUTCTimeMillis();
|
last_focus_time_ = wxGetUTCTimeMillis();
|
||||||
event.Skip();
|
event.Skip();
|
||||||
});
|
});
|
||||||
|
@ -66,7 +65,15 @@ void UserInputCtrl::Clear() {
|
||||||
|
|
||||||
wxIMPLEMENT_DYNAMIC_CLASS(UserInputCtrl, wxTextCtrl);
|
wxIMPLEMENT_DYNAMIC_CLASS(UserInputCtrl, wxTextCtrl);
|
||||||
|
|
||||||
void UserInputCtrl::OnUserInputUp(UserInputEvent& event) {
|
void UserInputCtrl::OnUserInput(UserInputEvent& event) {
|
||||||
|
// Find the first pressed input.
|
||||||
|
nonstd::optional<config::UserInput> input = event.FirstReleasedInput();
|
||||||
|
|
||||||
|
if (input == nonstd::nullopt) {
|
||||||
|
// No pressed inputs.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static const wxLongLong kInterval = 100;
|
static const wxLongLong kInterval = 100;
|
||||||
if (wxGetUTCTimeMillis() - last_focus_time_ < kInterval) {
|
if (wxGetUTCTimeMillis() - last_focus_time_ < kInterval) {
|
||||||
// Ignore events sent very shortly after focus. This is used to ignore
|
// Ignore events sent very shortly after focus. This is used to ignore
|
||||||
|
@ -75,20 +82,13 @@ void UserInputCtrl::OnUserInputUp(UserInputEvent& event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_navigating_away_) {
|
|
||||||
// Ignore events sent after the control has been navigated away from.
|
|
||||||
event.Skip();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_multikey_) {
|
if (!is_multikey_) {
|
||||||
inputs_.clear();
|
inputs_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs_.insert(event.input());
|
inputs_.insert(std::move(input.value()));
|
||||||
UpdateText();
|
UpdateText();
|
||||||
Navigate();
|
Navigate();
|
||||||
is_navigating_away_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserInputCtrl::UpdateText() {
|
void UserInputCtrl::UpdateText() {
|
||||||
|
|
|
@ -61,7 +61,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Event handler.
|
// Event handler.
|
||||||
void OnUserInputUp(widgets::UserInputEvent& event);
|
void OnUserInput(widgets::UserInputEvent& 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();
|
||||||
|
@ -72,10 +72,6 @@ private:
|
||||||
// very shortly after activation.
|
// very shortly after activation.
|
||||||
wxLongLong last_focus_time_ = 0;
|
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::unordered_set<config::UserInput> inputs_;
|
std::unordered_set<config::UserInput> inputs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#include "wx/widgets/user-input-event.h"
|
#include "wx/widgets/user-input-event.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
|
#include <wx/eventfilter.h>
|
||||||
#include <wx/window.h>
|
#include <wx/window.h>
|
||||||
|
|
||||||
#include "wx/config/user-input.h"
|
#include "wx/config/user-input.h"
|
||||||
|
@ -109,11 +111,44 @@ wxKeyCode KeyFromModifier(const wxKeyModifier mod) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
UserInputEvent::UserInputEvent(const config::UserInput& input, bool pressed)
|
UserInputEvent::UserInputEvent(std::vector<Data> event_data)
|
||||||
: wxEvent(0, pressed ? VBAM_EVT_USER_INPUT_DOWN : VBAM_EVT_USER_INPUT_UP), input_(input) {}
|
: wxEvent(0, VBAM_EVT_USER_INPUT), data_(std::move(event_data)) {}
|
||||||
|
|
||||||
|
nonstd::optional<config::UserInput> UserInputEvent::FirstReleasedInput() const {
|
||||||
|
const auto iter =
|
||||||
|
std::find_if(data_.begin(), data_.end(), [](const auto& data) { return !data.pressed; });
|
||||||
|
|
||||||
|
if (iter == data_.end()) {
|
||||||
|
// No pressed inputs.
|
||||||
|
return nonstd::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iter->input;
|
||||||
|
}
|
||||||
|
|
||||||
|
int UserInputEvent::FilterProcessedInput(const config::UserInput& user_input) {
|
||||||
|
// Keep all data not using `user_input`.
|
||||||
|
std::vector<Data> new_data;
|
||||||
|
for (const auto& data : data_) {
|
||||||
|
if (data.input != user_input) {
|
||||||
|
new_data.push_back(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the internal data.
|
||||||
|
data_ = std::move(new_data);
|
||||||
|
|
||||||
|
if (data_.empty()) {
|
||||||
|
// All data was removed, the event was fully processed.
|
||||||
|
return wxEventFilter::Event_Processed;
|
||||||
|
} else {
|
||||||
|
// Some data remains, let the event propagate.
|
||||||
|
return wxEventFilter::Event_Skip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wxEvent* UserInputEvent::Clone() const {
|
wxEvent* UserInputEvent::Clone() const {
|
||||||
return new UserInputEvent(*this);
|
return new UserInputEvent(this->data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInputEventSender::UserInputEventSender(wxWindow* const window)
|
UserInputEventSender::UserInputEventSender(wxWindow* const window)
|
||||||
|
@ -161,26 +196,25 @@ void UserInputEventSender::OnKeyDown(wxKeyEvent& event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const wxKeyModifier active_mods = GetModifiersFromSet(active_mods_);
|
const wxKeyModifier active_mods = GetModifiersFromSet(active_mods_);
|
||||||
std::vector<config::KeyboardInput> new_inputs;
|
std::vector<UserInputEvent::Data> event_data;
|
||||||
if (key_pressed == WXK_NONE) {
|
if (key_pressed == WXK_NONE) {
|
||||||
// A new standalone modifier was pressed, send the event.
|
// A new standalone modifier was pressed, send the event.
|
||||||
new_inputs.emplace_back(KeyFromModifier(mod_pressed), mod_pressed);
|
event_data.emplace_back(config::KeyboardInput(KeyFromModifier(mod_pressed), mod_pressed),
|
||||||
|
true);
|
||||||
} else {
|
} else {
|
||||||
// A new key was pressed, send the event with modifiers, first.
|
// A new key was pressed, send the event with modifiers, first.
|
||||||
new_inputs.emplace_back(key, active_mods);
|
event_data.emplace_back(config::KeyboardInput(key, active_mods), true);
|
||||||
|
|
||||||
if (active_mods != wxMOD_NONE) {
|
if (active_mods != wxMOD_NONE) {
|
||||||
// Keep track of the key pressed with the active modifiers.
|
// Keep track of the key pressed with the active modifiers.
|
||||||
active_mod_inputs_.emplace(key, active_mods);
|
active_mod_inputs_.emplace(key, active_mods);
|
||||||
|
|
||||||
// Also send the key press event without modifiers.
|
// Also send the key press event without modifiers.
|
||||||
new_inputs.emplace_back(key, wxMOD_NONE);
|
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const config::KeyboardInput& input : new_inputs) {
|
wxQueueEvent(window_, new UserInputEvent(std::move(event_data)));
|
||||||
wxQueueEvent(window_, new UserInputEvent(input, true));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
||||||
|
@ -220,31 +254,32 @@ void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<config::KeyboardInput> released_inputs;
|
std::vector<UserInputEvent::Data> event_data;
|
||||||
if (key_released == WXK_NONE) {
|
if (key_released == WXK_NONE) {
|
||||||
// A standalone modifier was released, send it.
|
// A standalone modifier was released, send it.
|
||||||
released_inputs.emplace_back(KeyFromModifier(mod_released), mod_released);
|
event_data.emplace_back(config::KeyboardInput(KeyFromModifier(mod_released), mod_released),
|
||||||
|
false);
|
||||||
} else {
|
} else {
|
||||||
// A key was released.
|
// A key was released.
|
||||||
if (previous_mods == wxMOD_NONE) {
|
if (previous_mods == wxMOD_NONE) {
|
||||||
// The key was pressed without modifiers, just send the key release event.
|
// The key was pressed without modifiers, just send the key release event.
|
||||||
released_inputs.emplace_back(key, wxMOD_NONE);
|
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), false);
|
||||||
} else {
|
} else {
|
||||||
// Check if the key was pressed with the active modifiers.
|
// Check if the key was pressed with the active modifiers.
|
||||||
const config::KeyboardInput input_with_modifiers(key, previous_mods);
|
const config::KeyboardInput input_with_modifiers(key, previous_mods);
|
||||||
auto iter = active_mod_inputs_.find(input_with_modifiers);
|
auto iter = active_mod_inputs_.find(input_with_modifiers);
|
||||||
if (iter == active_mod_inputs_.end()) {
|
if (iter == active_mod_inputs_.end()) {
|
||||||
// The key press event was never sent, so do it now.
|
// The key press event was never sent, so do it now.
|
||||||
wxQueueEvent(window_, new UserInputEvent(input_with_modifiers, true));
|
event_data.emplace_back(input_with_modifiers, true);
|
||||||
} else {
|
} else {
|
||||||
active_mod_inputs_.erase(iter);
|
active_mod_inputs_.erase(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the key release event with the active modifiers.
|
// Send the key release event with the active modifiers.
|
||||||
released_inputs.emplace_back(key, previous_mods);
|
event_data.emplace_back(config::KeyboardInput(key, previous_mods), false);
|
||||||
|
|
||||||
// Also send the key release event without modifiers.
|
// Also send the key release event without modifiers.
|
||||||
released_inputs.emplace_back(key, wxMOD_NONE);
|
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,15 +290,14 @@ void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
||||||
auto iter = active_mod_inputs_.find(input);
|
auto iter = active_mod_inputs_.find(input);
|
||||||
if (iter != active_mod_inputs_.end()) {
|
if (iter != active_mod_inputs_.end()) {
|
||||||
active_mod_inputs_.erase(iter);
|
active_mod_inputs_.erase(iter);
|
||||||
released_inputs.push_back(std::move(input));
|
event_data.emplace_back(std::move(input), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& data : event_data) {
|
||||||
for (const config::KeyboardInput& input : released_inputs) {
|
active_mod_inputs_.erase(data.input.keyboard_input());
|
||||||
active_mod_inputs_.erase(input);
|
|
||||||
wxQueueEvent(window_, new UserInputEvent(input, false));
|
|
||||||
}
|
}
|
||||||
|
wxQueueEvent(window_, new UserInputEvent(std::move(event_data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserInputEventSender::Reset(wxFocusEvent& event) {
|
void UserInputEventSender::Reset(wxFocusEvent& event) {
|
||||||
|
@ -278,5 +312,4 @@ void UserInputEventSender::Reset(wxFocusEvent& event) {
|
||||||
|
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
|
|
||||||
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT_DOWN, widgets::UserInputEvent);
|
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT, widgets::UserInputEvent);
|
||||||
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT_UP, widgets::UserInputEvent);
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#define WX_WIDGETS_USER_INPUT_EVENT_H_
|
#define WX_WIDGETS_USER_INPUT_EVENT_H_
|
||||||
|
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <optional.hpp>
|
||||||
|
|
||||||
#include <wx/clntdata.h>
|
#include <wx/clntdata.h>
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
|
@ -10,20 +13,43 @@
|
||||||
|
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
// Event fired when a user input is pressed or released. The event contains the
|
// Event fired when a set of user input are pressed or released. The event
|
||||||
// user input that was pressed or released.
|
// contains the set of user input that were pressed or released. The order
|
||||||
|
// in the vector matters, this is the order in which the inputs were pressed or
|
||||||
|
// released.
|
||||||
class UserInputEvent final : public wxEvent {
|
class UserInputEvent final : public wxEvent {
|
||||||
public:
|
public:
|
||||||
UserInputEvent(const config::UserInput& input, bool pressed);
|
// Data for the event. Contains the user input and whether it was pressed or
|
||||||
|
// released.
|
||||||
|
struct Data {
|
||||||
|
const config::UserInput input;
|
||||||
|
const bool pressed;
|
||||||
|
|
||||||
|
Data(config::UserInput input, bool pressed) : input(input), pressed(pressed){};
|
||||||
|
};
|
||||||
|
|
||||||
|
UserInputEvent(std::vector<Data> event_data);
|
||||||
virtual ~UserInputEvent() override = default;
|
virtual ~UserInputEvent() override = default;
|
||||||
|
|
||||||
|
// Disable copy and copy assignment.
|
||||||
|
UserInputEvent(const UserInputEvent&) = delete;
|
||||||
|
UserInputEvent& operator=(const UserInputEvent&) = delete;
|
||||||
|
|
||||||
|
// Returns the first pressed input, if any.
|
||||||
|
nonstd::optional<config::UserInput> FirstReleasedInput() const;
|
||||||
|
|
||||||
|
// Mark `event_data` as processed and returns the new event filter. This is
|
||||||
|
// meant to be used with FilterEvent() to process global shortcuts before
|
||||||
|
// sending the event to the next handler.
|
||||||
|
int FilterProcessedInput(const config::UserInput& user_input);
|
||||||
|
|
||||||
// wxEvent implementation.
|
// wxEvent implementation.
|
||||||
wxEvent* Clone() const override;
|
wxEvent* Clone() const override;
|
||||||
|
|
||||||
const config::UserInput& input() const { return input_; }
|
const std::vector<Data>& data() const { return data_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const config::UserInput input_;
|
std::vector<Data> data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Object that is used to fire user input events when a key is pressed or
|
// Object that is used to fire user input events when a key is pressed or
|
||||||
|
@ -59,9 +85,7 @@ private:
|
||||||
|
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
|
|
||||||
// Fired when a user input is pressed.
|
// Fired when a set of user inputs are pressed or released.
|
||||||
wxDECLARE_EVENT(VBAM_EVT_USER_INPUT_DOWN, widgets::UserInputEvent);
|
wxDECLARE_EVENT(VBAM_EVT_USER_INPUT, 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_
|
#endif // WX_WIDGETS_USER_INPUT_EVENT_H_
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "wx/wxvbam.h"
|
#include "wx/wxvbam.h"
|
||||||
#include "wx/config/command.h"
|
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -35,6 +34,7 @@
|
||||||
#include "wx/builtin-over.h"
|
#include "wx/builtin-over.h"
|
||||||
#include "wx/builtin-xrc.h"
|
#include "wx/builtin-xrc.h"
|
||||||
#include "wx/config/cmdtab.h"
|
#include "wx/config/cmdtab.h"
|
||||||
|
#include "wx/config/command.h"
|
||||||
#include "wx/config/emulated-gamepad.h"
|
#include "wx/config/emulated-gamepad.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
#include "wx/config/option.h"
|
#include "wx/config/option.h"
|
||||||
|
@ -995,27 +995,46 @@ int MainFrame::FilterEvent(wxEvent& event) {
|
||||||
return wxEventFilter::Event_Skip;
|
return wxEventFilter::Event_Skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.GetEventType() != VBAM_EVT_USER_INPUT_DOWN) {
|
if (event.GetEventType() != VBAM_EVT_USER_INPUT) {
|
||||||
// We only treat "VBAM_EVT_USER_INPUT_DOWN" events here.
|
// We only treat "VBAM_EVT_USER_INPUT" events here.
|
||||||
return wxEventFilter::Event_Skip;
|
return wxEventFilter::Event_Skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
const widgets::UserInputEvent& user_input_event = static_cast<widgets::UserInputEvent&>(event);
|
widgets::UserInputEvent& user_input_event = static_cast<widgets::UserInputEvent&>(event);
|
||||||
nonstd::optional<config::Command> command =
|
const config::Bindings* bindings = wxGetApp().bindings();
|
||||||
wxGetApp().bindings()->CommandForInput(user_input_event.input());
|
int command_id = wxID_NONE;
|
||||||
if (command == nonstd::nullopt) {
|
nonstd::optional<config::UserInput> user_input;
|
||||||
|
|
||||||
|
for (const auto& event_data : user_input_event.data()) {
|
||||||
|
if (!event_data.pressed) {
|
||||||
|
// We only treat key press events here.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonstd::optional<config::Command> command =
|
||||||
|
bindings->CommandForInput(event_data.input);
|
||||||
|
if (command != nonstd::nullopt && command->is_shortcut()) {
|
||||||
|
// Associated shortcut command found.
|
||||||
|
command_id = command->shortcut().id();
|
||||||
|
user_input.emplace(event_data.input);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command_id == wxID_NONE) {
|
||||||
// No associated command found.
|
// No associated command found.
|
||||||
return wxEventFilter::Event_Skip;
|
return wxEventFilter::Event_Skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!command->is_shortcut()) {
|
// Execute the associated shortcut command.
|
||||||
return wxEventFilter::Event_Skip;
|
wxCommandEvent command_event(wxEVT_COMMAND_MENU_SELECTED, command_id);
|
||||||
}
|
|
||||||
|
|
||||||
wxCommandEvent command_event(wxEVT_COMMAND_MENU_SELECTED, command->shortcut().id());
|
|
||||||
command_event.SetEventObject(this);
|
command_event.SetEventObject(this);
|
||||||
this->GetEventHandler()->ProcessEvent(command_event);
|
this->GetEventHandler()->ProcessEvent(command_event);
|
||||||
return wxEventFilter::Event_Processed;
|
|
||||||
|
// Filter out the processed input so it is not processed again. This also
|
||||||
|
// prevents us from firing 2 commands if the user presses "Ctrl+1" and has
|
||||||
|
// a shortcut for both "Ctrl+1" and "1". Only one will be handled here.
|
||||||
|
return user_input_event.FilterProcessedInput(user_input.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString MainFrame::GetGamePath(wxString path)
|
wxString MainFrame::GetGamePath(wxString path)
|
||||||
|
|
|
@ -530,8 +530,7 @@ protected:
|
||||||
|
|
||||||
bool paused;
|
bool paused;
|
||||||
void OnIdle(wxIdleEvent&);
|
void OnIdle(wxIdleEvent&);
|
||||||
void OnUserInputDown(widgets::UserInputEvent& event);
|
void OnUserInput(widgets::UserInputEvent& event);
|
||||||
void OnUserInputUp(widgets::UserInputEvent& event);
|
|
||||||
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