mirror of https://github.com/PCSX2/pcsx2.git
Qt: Fix firing multiple bindings with chords
This commit is contained in:
parent
8e23d8d557
commit
a524410b0a
|
@ -42,6 +42,7 @@ DisplayWidget::DisplayWidget(QWidget* parent)
|
|||
setAttribute(Qt::WA_NativeWindow, true);
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
setAttribute(Qt::WA_PaintOnScreen, true);
|
||||
setAttribute(Qt::WA_KeyCompression, false);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
@ -142,12 +143,33 @@ bool DisplayWidget::event(QEvent* event)
|
|||
case QEvent::KeyRelease:
|
||||
{
|
||||
const QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
|
||||
if (!key_event->isAutoRepeat())
|
||||
if (key_event->isAutoRepeat())
|
||||
return true;
|
||||
|
||||
// For some reason, Windows sends "fake" key events.
|
||||
// Scenario: Press shift, press F1, release shift, release F1.
|
||||
// Events: Shift=Pressed, F1=Pressed, Shift=Released, **F1=Pressed**, F1=Released.
|
||||
// To work around this, we keep track of keys pressed with modifiers in a list, and
|
||||
// discard the press event when it's been previously activated. It's pretty gross,
|
||||
// but I can't think of a better way of handling it, and there doesn't appear to be
|
||||
// any window flag which changes this behavior that I can see.
|
||||
|
||||
const int key = key_event->key();
|
||||
const bool pressed = (key_event->type() == QEvent::KeyPress);
|
||||
const auto it = std::find(m_keys_pressed_with_modifiers.begin(), m_keys_pressed_with_modifiers.end(), key);
|
||||
if (it != m_keys_pressed_with_modifiers.end())
|
||||
{
|
||||
emit windowKeyEvent(key_event->key(), static_cast<int>(key_event->modifiers()),
|
||||
event->type() == QEvent::KeyPress);
|
||||
if (pressed)
|
||||
return true;
|
||||
else
|
||||
m_keys_pressed_with_modifiers.erase(it);
|
||||
}
|
||||
else if (key_event->modifiers() != Qt::NoModifier && pressed)
|
||||
{
|
||||
m_keys_pressed_with_modifiers.push_back(key);
|
||||
}
|
||||
|
||||
emit windowKeyEvent(key, pressed);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtWidgets/QStackedWidget>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
class DisplayWidget final : public QWidget
|
||||
{
|
||||
|
@ -42,7 +43,7 @@ Q_SIGNALS:
|
|||
void windowResizedEvent(int width, int height, float scale);
|
||||
void windowRestoredEvent();
|
||||
void windowClosedEvent();
|
||||
void windowKeyEvent(int key_code, int mods, bool pressed);
|
||||
void windowKeyEvent(int key_code, bool pressed);
|
||||
void windowMouseMoveEvent(int x, int y);
|
||||
void windowMouseButtonEvent(int button, bool pressed);
|
||||
void windowMouseWheelEvent(const QPoint& angle_delta);
|
||||
|
@ -54,6 +55,7 @@ private:
|
|||
QPoint m_relative_mouse_start_position{};
|
||||
QPoint m_relative_mouse_last_position{};
|
||||
bool m_relative_mouse_enabled = false;
|
||||
std::vector<int> m_keys_pressed_with_modifiers;
|
||||
};
|
||||
|
||||
class DisplayContainer final : public QStackedWidget
|
||||
|
|
|
@ -568,7 +568,7 @@ void EmuThread::onDisplayWindowMouseButtonEvent(int button, bool pressed)
|
|||
|
||||
void EmuThread::onDisplayWindowMouseWheelEvent(const QPoint& delta_angle) {}
|
||||
|
||||
void EmuThread::onDisplayWindowKeyEvent(int key, int mods, bool pressed)
|
||||
void EmuThread::onDisplayWindowKeyEvent(int key, bool pressed)
|
||||
{
|
||||
InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), pressed ? 1.0f : 0.0f);
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ private Q_SLOTS:
|
|||
void onDisplayWindowMouseWheelEvent(const QPoint& delta_angle);
|
||||
void onDisplayWindowResized(int width, int height, float scale);
|
||||
void onDisplayWindowFocused();
|
||||
void onDisplayWindowKeyEvent(int key, int mods, bool pressed);
|
||||
void onDisplayWindowKeyEvent(int key, bool pressed);
|
||||
|
||||
private:
|
||||
QThread* m_ui_thread;
|
||||
|
|
|
@ -650,6 +650,42 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value)
|
|||
if (range.first == s_binding_map.end())
|
||||
return false;
|
||||
|
||||
// Workaround for modifier keys. Basically, if we bind say, F1 and Shift+F1, and press shift
|
||||
// and then F1, we'll fire bindings for both F1 and Shift+F1, when we really only want to fire
|
||||
// the binding for Shift+F1. So, let's search through the binding list, and see if there's a
|
||||
// "longer" binding (more keys), and if so, only activate that and not the shorter binding(s).
|
||||
const InputBinding* longest_hotkey_binding = nullptr;
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
{
|
||||
InputBinding* binding = it->second.get();
|
||||
if (binding->handler.IsAxis())
|
||||
continue;
|
||||
|
||||
// find the key which matches us
|
||||
for (u32 i = 0; i < binding->num_keys; i++)
|
||||
{
|
||||
if (binding->keys[i].MaskDirection() != masked_key)
|
||||
continue;
|
||||
|
||||
const u8 bit = static_cast<u8>(1) << i;
|
||||
const bool negative = binding->keys[i].negative;
|
||||
const bool new_state = (negative ? (value < 0.0f) : (value > 0.0f));
|
||||
const u8 new_mask = (new_state ? (binding->current_mask | bit) : (binding->current_mask & ~bit));
|
||||
const bool prev_full_state = (binding->current_mask == binding->full_mask);
|
||||
const bool new_full_state = (new_mask == binding->full_mask);
|
||||
|
||||
// If we're activating this chord, block activation of other bindings with fewer keys.
|
||||
if (prev_full_state || new_full_state)
|
||||
{
|
||||
if (!longest_hotkey_binding || longest_hotkey_binding->num_keys < binding->num_keys)
|
||||
longest_hotkey_binding = binding;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we can actually fire/activate bindings.
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
{
|
||||
InputBinding* binding = it->second.get();
|
||||
|
@ -664,6 +700,14 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value)
|
|||
const bool negative = binding->keys[i].negative;
|
||||
const bool new_state = (negative ? (value < 0.0f) : (value > 0.0f));
|
||||
|
||||
// Don't register the key press when we're part of a longer chord. That way,
|
||||
// the state won't change, and it won't get the released event either.
|
||||
if (longest_hotkey_binding && new_state && !binding->handler.IsAxis() &&
|
||||
binding->num_keys != longest_hotkey_binding->num_keys)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// update state based on whether the whole chord was activated
|
||||
const u8 new_mask = (new_state ? (binding->current_mask | bit) : (binding->current_mask & ~bit));
|
||||
const bool prev_full_state = (binding->current_mask == binding->full_mask);
|
||||
|
|
Loading…
Reference in New Issue