Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Input: Make macro chords work properly #12285

Merged
merged 1 commit into from
Mar 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions pcsx2/Input/InputManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ namespace InputManager
static std::optional<InputBindingKey> ParseHostKeyboardKey(const std::string_view source, const std::string_view sub_binding);
static std::optional<InputBindingKey> ParsePointerKey(const std::string_view source, const std::string_view sub_binding);

static std::vector<std::string_view> SplitChord(const std::string_view binding);
static bool SplitBinding(const std::string_view binding, std::string_view* source, std::string_view* sub_binding);
static void PrettifyInputBindingPart(const std::string_view binding, SmallString& ret, bool& changed);
static void AddBinding(const std::string_view binding, const InputEventHandler& handler);
Expand Down Expand Up @@ -752,7 +751,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index)
const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f);
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f);
AddBindings(
bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, deadzone](float value) {
bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, deadzone](InputBindingKey key, float value) {
Pad::SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}});
}
Expand All @@ -772,9 +771,9 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index)
if (!bindings.empty())
{
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("Macro{}Deadzone", macro_button_index + 1).c_str(), 0.0f);
AddBindings(bindings, InputAxisEventHandler{[pad_index, macro_button_index, deadzone](float value) {
AddBindings(bindings, InputAxisEventHandler{[pad_index, macro_button_index, deadzone](InputBindingKey key, float value) {
const bool state = (value > deadzone);
Pad::SetMacroButtonState(pad_index, macro_button_index, state);
Pad::SetMacroButtonState(key, pad_index, macro_button_index, state);
}});
}
}
Expand Down Expand Up @@ -836,7 +835,7 @@ void InputManager::AddUSBBindings(SettingsInterface& si, u32 port)
{
const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f);
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f);
AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index, sensitivity, deadzone](float value) {
AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index, sensitivity, deadzone](InputBindingKey key, float value) {
USB::SetDeviceBindValue(port, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}});
}
Expand Down Expand Up @@ -977,7 +976,7 @@ bool InputManager::ProcessEvent(InputBindingKey key, float value, bool skip_butt
if (IsAxisHandler(binding->handler))
{
if (value_to_pass >= 0.0f && (!skip_button_handlers || value_to_pass == 0.0f))
std::get<InputAxisEventHandler>(binding->handler)(value_to_pass);
std::get<InputAxisEventHandler>(binding->handler)(key, value_to_pass);
}
else if (binding->num_keys >= min_num_keys)
{
Expand Down Expand Up @@ -1056,7 +1055,7 @@ void InputManager::ClearBindStateFromSource(InputBindingKey key)
if (binding->keys[i].MaskDirection() != match_key)
continue;

std::get<InputAxisEventHandler>(binding->handler)(0.0f);
std::get<InputAxisEventHandler>(binding->handler)(key, 0.0f);
break;
}
}
Expand Down
5 changes: 4 additions & 1 deletion pcsx2/Input/InputManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ struct InputBindingKeyHash
using InputButtonEventHandler = std::function<void(s32 value)>;

/// Callback types for a normalized event. Usually used for pads.
using InputAxisEventHandler = std::function<void(float value)>;
using InputAxisEventHandler = std::function<void(InputBindingKey key, float value)>;

/// Input monitoring for external access.
struct InputInterceptHook
Expand Down Expand Up @@ -211,6 +211,9 @@ namespace InputManager
/// Represents a binding with icon fonts, if available.
bool PrettifyInputBinding(SmallStringBase& binding);

/// Splits a chord into individual bindings.
std::vector<std::string_view> SplitChord(const std::string_view binding);

/// Returns a list of all hotkeys.
std::vector<const HotkeyInfo*> GetHotkeyList();

Expand Down
34 changes: 33 additions & 1 deletion pcsx2/SIO/Pad/Pad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@

#include <vector>

//Map of actively pressed keys so that chords work
using KeyMap = std::unordered_multimap<u64, bool>;

namespace Pad
{
struct MacroButton
{
std::vector<u32> buttons; ///< Buttons to activate.
KeyMap active_buttons; ///< Currently active buttons.
float pressure; ///< Pressure to apply when macro is active.
u16 toggle_frequency; ///< Interval at which the buttons will be toggled, if not 0.
u16 toggle_counter; ///< When this counter reaches zero, buttons will be toggled.
Expand Down Expand Up @@ -670,15 +674,43 @@ void Pad::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const Cont
}
}

void Pad::SetMacroButtonState(u32 pad, u32 index, bool state)
void Pad::SetMacroButtonState(InputBindingKey& key, u32 pad, u32 index, bool state)
{
//0 appears for some reason and breaks mb.active_buttons.size() != binding_count
if (key.bits == 0)
return;

if (pad >= Pad::NUM_CONTROLLER_PORTS || index >= NUM_MACRO_BUTTONS_PER_CONTROLLER)
return;

MacroButton& mb = s_macro_buttons[pad][index];
if (mb.buttons.empty())
return;

SettingsInterface& sif = *Host::GetSettingsInterface();
std::vector<std::string> data = sif.GetStringList(fmt::format("Pad{}", pad+1).c_str(), fmt::format("Macro{}", index+1).c_str());
size_t binding_count = 0;
//just in case there's more than one index
for (std::string bind : data)
{
binding_count += InputManager::SplitChord(bind).size();
}
if (mb.active_buttons.find(key.bits) != mb.active_buttons.end())
mb.active_buttons.erase(key.bits);

mb.active_buttons.emplace(key.bits, state);

if (mb.active_buttons.size() != binding_count)
return;

if (mb.active_buttons.size() > 1 && state)
{
for (auto it = mb.active_buttons.begin(); it != mb.active_buttons.end(); ++it)
{
if (!it->second)
return;
}
}
const bool trigger_state = (mb.trigger_toggle ? (state ? !mb.trigger_state : mb.trigger_state) : state);
if (mb.trigger_state == trigger_state)
return;
Expand Down
3 changes: 2 additions & 1 deletion pcsx2/SIO/Pad/Pad.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include "Config.h"
#include "Input/InputManager.h"
#include "SIO/Pad/PadTypes.h"

#include <memory>
Expand Down Expand Up @@ -68,6 +69,6 @@ namespace Pad
bool Freeze(StateWrapper& sw);

// Sets the state of the specified macro button.
void SetMacroButtonState(u32 pad, u32 index, bool state);
void SetMacroButtonState(InputBindingKey& key, u32 pad, u32 index, bool state);
void UpdateMacroButtons();
}; // namespace Pad