InputManager: Fix chord bindings when activating in reverse

This commit is contained in:
Connor McLaughlin 2022-06-21 19:31:34 +10:00 committed by refractionpcsx2
parent b20e5a1e01
commit 14181ec70d
5 changed files with 97 additions and 95 deletions

View File

@ -952,7 +952,7 @@ SysMtgsThread& GetMTGS()
// ------------------------------------------------------------------------
BEGIN_HOTKEY_LIST(g_host_hotkeys)
DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](bool pressed) {
DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](s32 pressed) {
if (!pressed)
{
// run it on the host thread, that way we get the confirm prompt (if enabled)
@ -960,16 +960,16 @@ DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](bool press
Q_ARG(bool, true), Q_ARG(bool, true), Q_ARG(bool, true));
}
})
DEFINE_HOTKEY("TogglePause", "System", "Toggle Pause", [](bool pressed) {
DEFINE_HOTKEY("TogglePause", "System", "Toggle Pause", [](s32 pressed) {
if (!pressed)
g_emu_thread->setVMPaused(VMManager::GetState() != VMState::Paused);
})
DEFINE_HOTKEY("ToggleFullscreen", "General", "Toggle Fullscreen", [](bool pressed) {
DEFINE_HOTKEY("ToggleFullscreen", "General", "Toggle Fullscreen", [](s32 pressed) {
if (!pressed)
g_emu_thread->toggleFullscreen();
})
// Input Recording Hot Keys
DEFINE_HOTKEY("InputRecToggleMode", "Input Recording", "Toggle Recording Mode", [](bool pressed) {
DEFINE_HOTKEY("InputRecToggleMode", "Input Recording", "Toggle Recording Mode", [](s32 pressed) {
if (!pressed) // ?? - not pressed so it is on key up?
{
g_InputRecordingControls.RecordModeToggle();

View File

@ -622,51 +622,20 @@ bool InputManager::IsAxisHandler(const InputEventHandler& handler)
bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key)
{
if (PreprocessEvent(key, value, generic_key))
if (DoEventHook(key, value))
return true;
// If imgui ate the event, don't fire our handlers.
const bool skip_button_handlers = PreprocessEvent(key, value, generic_key);
// find all the bindings associated with this key
const InputBindingKey masked_key = key.MaskDirection();
const auto range = s_binding_map.equal_range(masked_key);
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 (IsAxisHandler(binding->handler))
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.
u32 min_num_keys = 0;
for (auto it = range.first; it != range.second; ++it)
{
InputBinding* binding = it->second.get();
@ -681,20 +650,6 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBi
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 && !IsAxisHandler(binding->handler) &&
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);
const bool new_full_state = (new_mask == binding->full_mask);
binding->current_mask = new_mask;
// invert if we're negative, since the handler expects 0..1
const float value_to_pass = (negative ? ((value < 0.0f) ? -value : 0.0f) : (value > 0.0f) ? value : 0.0f);
@ -704,13 +659,60 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBi
// and 0 on release (when the full state changes).
if (IsAxisHandler(binding->handler))
{
if (prev_full_state != new_full_state || value_to_pass >= 0.0f)
if (value_to_pass >= 0.0f)
std::get<InputAxisEventHandler>(binding->handler)(value_to_pass);
}
else
else if (binding->num_keys >= min_num_keys)
{
if (prev_full_state != new_full_state)
std::get<InputButtonEventHandler>(binding->handler)(value_to_pass > 0.0f);
// 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);
const bool new_full_state = (new_mask == binding->full_mask);
binding->current_mask = new_mask;
// Workaround for multi-key bindings that share the same keys.
if (binding->num_keys > 1 && new_full_state && prev_full_state != new_full_state &&
range.first != range.second)
{
// Because the binding map isn't ordered, we could iterate in the order of Shift+F1 and then
// F1, which would mean that F1 wouldn't get cancelled and still activate. So, to handle this
// case, we skip activating any future bindings with a fewer number of keys.
min_num_keys = std::max<u32>(min_num_keys, binding->num_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,
// when we activate a multi-key chord (key press), we go through the binding map for all the
// other keys in the chord, and cancel them if they have a shorter chord. If they're longer,
// they could still activate and take precedence over us, so we leave them alone.
for (u32 i = 0; i < binding->num_keys; i++)
{
const auto range = s_binding_map.equal_range(binding->keys[i].MaskDirection());
for (auto it = range.first; it != range.second; ++it)
{
InputBinding* other_binding = it->second.get();
if (other_binding == binding || IsAxisHandler(other_binding->handler) ||
other_binding->num_keys >= binding->num_keys)
{
continue;
}
// We only need to cancel the binding if it was fully active before. Which in the above
// case of Shift+F1 / F1, it will be.
if (other_binding->current_mask == other_binding->full_mask)
std::get<InputButtonEventHandler>(other_binding->handler)(-1);
// Zero out the current bits so that we don't release this binding, if the other part
// of the chord releases first.
other_binding->current_mask = 0;
}
}
}
if (prev_full_state != new_full_state && binding->num_keys >= min_num_keys)
{
const s32 pressed = skip_button_handlers ? -1 : static_cast<s32>(value_to_pass > 0.0f);
std::get<InputButtonEventHandler>(binding->handler)(pressed);
}
}
// bail out, since we shouldn't have the same key twice in the chord
@ -723,9 +725,6 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBi
bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key)
{
if (DoEventHook(key, value))
return true;
// does imgui want the event?
if (key.source_type == InputSourceType::Keyboard)
{
@ -739,7 +738,7 @@ bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInpu
}
else if (generic_key != GenericInputBinding::Unknown)
{
if (ImGuiManager::ProcessGenericInputEvent(generic_key, value))
if (ImGuiManager::ProcessGenericInputEvent(generic_key, value) && value != 0.0f)
return true;
}

View File

@ -90,7 +90,7 @@ struct InputBindingKeyHash
};
/// Callback type for a binary event. Usually used for hotkeys.
using InputButtonEventHandler = std::function<void(bool value)>;
using InputButtonEventHandler = std::function<void(s32 value)>;
/// Callback types for a normalized event. Usually used for pads.
using InputAxisEventHandler = std::function<void(float value)>;
@ -110,12 +110,15 @@ struct InputInterceptHook
};
/// Hotkeys are actions (e.g. toggle frame limit) which can be bound to keys or chords.
/// The handler is called with an integer representing the key state, where 0 means that
/// one or more keys were released, 1 means all the keys were pressed, and -1 means that
/// the hotkey was cancelled due to a chord with more keys being activated.
struct HotkeyInfo
{
const char* name;
const char* category;
const char* display_name;
void (*handler)(bool pressed);
void (*handler)(s32 pressed);
};
#define DECLARE_HOTKEY_LIST(name) extern const HotkeyInfo name[]
#define BEGIN_HOTKEY_LIST(name) const HotkeyInfo name[] = {

View File

@ -1623,7 +1623,7 @@ static void HotkeyAdjustZoom(double delta)
}
BEGIN_HOTKEY_LIST(g_gs_hotkeys)
{"Screenshot", "Graphics", "Save Screenshot", [](bool pressed) {
{"Screenshot", "Graphics", "Save Screenshot", [](s32 pressed) {
if (!pressed)
{
GetMTGS().RunOnGSThread([]() {
@ -1631,7 +1631,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys)
});
}
}},
{"GSDumpSingleFrame", "Graphics", "Save Single Frame GS Dump", [](bool pressed) {
{"GSDumpSingleFrame", "Graphics", "Save Single Frame GS Dump", [](s32 pressed) {
if (!pressed)
{
GetMTGS().RunOnGSThread([]() {
@ -1639,27 +1639,27 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys)
});
}
}},
{"GSDumpMultiFrame", "Graphics", "Save Multi Frame GS Dump", [](bool pressed) {
{"GSDumpMultiFrame", "Graphics", "Save Multi Frame GS Dump", [](s32 pressed) {
GetMTGS().RunOnGSThread([pressed]() {
if (pressed)
if (pressed > 0)
GSQueueSnapshot(std::string(), std::numeric_limits<u32>::max());
else
GSStopGSDump();
});
}},
{"ToggleSoftwareRendering", "Graphics", "Toggle Software Rendering", [](bool pressed) {
{"ToggleSoftwareRendering", "Graphics", "Toggle Software Rendering", [](s32 pressed) {
if (!pressed)
GetMTGS().ToggleSoftwareRendering();
}},
{"IncreaseUpscaleMultiplier", "Graphics", "Increase Upscale Multiplier", [](bool pressed) {
{"IncreaseUpscaleMultiplier", "Graphics", "Increase Upscale Multiplier", [](s32 pressed) {
if (!pressed)
HotkeyAdjustUpscaleMultiplier(1);
}},
{"DecreaseUpscaleMultiplier", "Graphics", "Decrease Upscale Multiplier", [](bool pressed) {
{"DecreaseUpscaleMultiplier", "Graphics", "Decrease Upscale Multiplier", [](s32 pressed) {
if (!pressed)
HotkeyAdjustUpscaleMultiplier(-1);
}},
{"CycleAspectRatio", "Graphics", "Cycle Aspect Ratio", [](bool pressed) {
{"CycleAspectRatio", "Graphics", "Cycle Aspect Ratio", [](s32 pressed) {
if (pressed)
return;
@ -1667,7 +1667,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys)
EmuConfig.CurrentAspectRatio = static_cast<AspectRatioType>((static_cast<int>(EmuConfig.CurrentAspectRatio) + 1) % static_cast<int>(AspectRatioType::MaxCount));
Host::AddKeyedFormattedOSDMessage("CycleAspectRatio", 10.0f, "Aspect ratio set to '%s'.", Pcsx2Config::GSOptions::AspectRatioNames[static_cast<int>(EmuConfig.CurrentAspectRatio)]);
}},
{"CycleMipmapMode", "Graphics", "Cycle Hardware Mipmapping", [](bool pressed) {
{"CycleMipmapMode", "Graphics", "Cycle Hardware Mipmapping", [](s32 pressed) {
if (pressed)
return;
@ -1684,7 +1684,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys)
g_gs_renderer->PurgePool();
});
}},
{"CycleInterlaceMode", "Graphics", "Cycle Deinterlace Mode", [](bool pressed) {
{"CycleInterlaceMode", "Graphics", "Cycle Deinterlace Mode", [](s32 pressed) {
if (pressed)
return;
@ -1705,15 +1705,15 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys)
GetMTGS().RunOnGSThread([new_mode]() { GSConfig.InterlaceMode = new_mode; });
}},
{"ZoomIn", "Graphics", "Zoom In", [](bool pressed) {
{"ZoomIn", "Graphics", "Zoom In", [](s32 pressed) {
if (!pressed)
HotkeyAdjustZoom(1.0);
}},
{"ZoomOut", "Graphics", "Zoom Out", [](bool pressed) {
{"ZoomOut", "Graphics", "Zoom Out", [](s32 pressed) {
if (!pressed)
HotkeyAdjustZoom(-1.0);
}},
{"ToggleTextureDumping", "Graphics", "Toggle Texture Dumping", [](bool pressed) {
{"ToggleTextureDumping", "Graphics", "Toggle Texture Dumping", [](s32 pressed) {
if (!pressed)
{
EmuConfig.GS.DumpReplaceableTextures = !EmuConfig.GS.DumpReplaceableTextures;
@ -1721,7 +1721,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys)
GetMTGS().ApplySettings();
}
}},
{"ToggleTextureReplacements", "Graphics", "Toggle Texture Replacements", [](bool pressed) {
{"ToggleTextureReplacements", "Graphics", "Toggle Texture Replacements", [](s32 pressed) {
if (!pressed)
{
EmuConfig.GS.LoadTextureReplacements = !EmuConfig.GS.LoadTextureReplacements;
@ -1729,7 +1729,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys)
GetMTGS().ApplySettings();
}
}},
{"ReloadTextureReplacements", "Graphics", "Reload Texture Replacements", [](bool pressed) {
{"ReloadTextureReplacements", "Graphics", "Reload Texture Replacements", [](s32 pressed) {
if (!pressed)
{
if (!EmuConfig.GS.LoadTextureReplacements)

View File

@ -1713,7 +1713,7 @@ static void HotkeySaveStateSlot(s32 slot)
}
BEGIN_HOTKEY_LIST(g_vm_manager_hotkeys)
DEFINE_HOTKEY("ToggleFrameLimit", "System", "Toggle Frame Limit", [](bool pressed) {
DEFINE_HOTKEY("ToggleFrameLimit", "System", "Toggle Frame Limit", [](s32 pressed) {
if (!pressed)
{
VMManager::SetLimiterMode((EmuConfig.LimiterMode != LimiterModeType::Unlimited) ?
@ -1721,7 +1721,7 @@ DEFINE_HOTKEY("ToggleFrameLimit", "System", "Toggle Frame Limit", [](bool presse
LimiterModeType::Nominal);
}
})
DEFINE_HOTKEY("ToggleTurbo", "System", "Toggle Turbo", [](bool pressed) {
DEFINE_HOTKEY("ToggleTurbo", "System", "Toggle Turbo", [](s32 pressed) {
if (!pressed)
{
VMManager::SetLimiterMode((EmuConfig.LimiterMode != LimiterModeType::Turbo) ?
@ -1729,21 +1729,21 @@ DEFINE_HOTKEY("ToggleTurbo", "System", "Toggle Turbo", [](bool pressed) {
LimiterModeType::Nominal);
}
})
DEFINE_HOTKEY("HoldTurbo", "System", "Turbo (Hold)", [](bool pressed) {
if (pressed && !s_limiter_mode_prior_to_hold_interaction.has_value())
DEFINE_HOTKEY("HoldTurbo", "System", "Turbo (Hold)", [](s32 pressed) {
if (pressed > 0 && !s_limiter_mode_prior_to_hold_interaction.has_value())
{
s_limiter_mode_prior_to_hold_interaction = VMManager::GetLimiterMode();
VMManager::SetLimiterMode((s_limiter_mode_prior_to_hold_interaction.value() != LimiterModeType::Turbo) ?
LimiterModeType::Turbo :
LimiterModeType::Nominal);
}
else if (!pressed && s_limiter_mode_prior_to_hold_interaction.has_value())
else if (pressed >= 0 && s_limiter_mode_prior_to_hold_interaction.has_value())
{
VMManager::SetLimiterMode(s_limiter_mode_prior_to_hold_interaction.value());
s_limiter_mode_prior_to_hold_interaction.reset();
}
})
DEFINE_HOTKEY("ToggleSlowMotion", "System", "Toggle Slow Motion", [](bool pressed) {
DEFINE_HOTKEY("ToggleSlowMotion", "System", "Toggle Slow Motion", [](s32 pressed) {
if (!pressed)
{
VMManager::SetLimiterMode((EmuConfig.LimiterMode != LimiterModeType::Slomo) ?
@ -1751,42 +1751,42 @@ DEFINE_HOTKEY("ToggleSlowMotion", "System", "Toggle Slow Motion", [](bool presse
LimiterModeType::Nominal);
}
})
DEFINE_HOTKEY("IncreaseSpeed", "System", "Increase Target Speed", [](bool pressed) {
DEFINE_HOTKEY("IncreaseSpeed", "System", "Increase Target Speed", [](s32 pressed) {
if (!pressed)
HotkeyAdjustTargetSpeed(0.1);
})
DEFINE_HOTKEY("DecreaseSpeed", "System", "Decrease Target Speed", [](bool pressed) {
DEFINE_HOTKEY("DecreaseSpeed", "System", "Decrease Target Speed", [](s32 pressed) {
if (!pressed)
HotkeyAdjustTargetSpeed(-0.1);
})
DEFINE_HOTKEY("ResetVM", "System", "Reset Virtual Machine", [](bool pressed) {
DEFINE_HOTKEY("ResetVM", "System", "Reset Virtual Machine", [](s32 pressed) {
if (!pressed && VMManager::HasValidVM())
VMManager::Reset();
})
DEFINE_HOTKEY("FrameAdvance", "System", "Frame Advance", [](bool pressed) {
DEFINE_HOTKEY("FrameAdvance", "System", "Frame Advance", [](s32 pressed) {
if (!pressed)
VMManager::FrameAdvance(1);
})
DEFINE_HOTKEY("PreviousSaveStateSlot", "Save States", "Select Previous Save Slot", [](bool pressed) {
DEFINE_HOTKEY("PreviousSaveStateSlot", "Save States", "Select Previous Save Slot", [](s32 pressed) {
if (!pressed)
HotkeyCycleSaveSlot(-1);
})
DEFINE_HOTKEY("NextSaveStateSlot", "Save States", "Select Next Save Slot", [](bool pressed) {
DEFINE_HOTKEY("NextSaveStateSlot", "Save States", "Select Next Save Slot", [](s32 pressed) {
if (!pressed)
HotkeyCycleSaveSlot(1);
})
DEFINE_HOTKEY("SaveStateToSlot", "Save States", "Save State To Selected Slot", [](bool pressed) {
DEFINE_HOTKEY("SaveStateToSlot", "Save States", "Save State To Selected Slot", [](s32 pressed) {
if (!pressed)
VMManager::SaveStateToSlot(s_current_save_slot);
})
DEFINE_HOTKEY("LoadStateFromSlot", "Save States", "Load State From Selected Slot", [](bool pressed) {
DEFINE_HOTKEY("LoadStateFromSlot", "Save States", "Load State From Selected Slot", [](s32 pressed) {
if (!pressed)
HotkeyLoadStateSlot(s_current_save_slot);
})
#define DEFINE_HOTKEY_SAVESTATE_X(slotnum, slotnumstr) DEFINE_HOTKEY("SaveStateToSlot" #slotnum, \
"Save States", "Save State To Slot " #slotnumstr, [](bool pressed) { if (!pressed) HotkeySaveStateSlot(slotnum); })
"Save States", "Save State To Slot " #slotnumstr, [](s32 pressed) { if (!pressed) HotkeySaveStateSlot(slotnum); })
DEFINE_HOTKEY_SAVESTATE_X(1, 01)
DEFINE_HOTKEY_SAVESTATE_X(2, 02)
DEFINE_HOTKEY_SAVESTATE_X(3, 03)
@ -1798,7 +1798,7 @@ DEFINE_HOTKEY_SAVESTATE_X(8, 08)
DEFINE_HOTKEY_SAVESTATE_X(9, 09)
DEFINE_HOTKEY_SAVESTATE_X(10, 10)
#define DEFINE_HOTKEY_LOADSTATE_X(slotnum, slotnumstr) DEFINE_HOTKEY("LoadStateFromSlot" #slotnum, \
"Save States", "Load State From Slot " #slotnumstr, [](bool pressed) { \
"Save States", "Load State From Slot " #slotnumstr, [](s32 pressed) { \
if (!pressed) \
HotkeyLoadStateSlot(slotnum); \
})