Merge pull request #806 from CookiePLMonster/axis-to-button-map

Allow mapping axes to buttons
This commit is contained in:
Connor McLaughlin
2020-08-29 23:35:18 +10:00
committed by GitHub
17 changed files with 306 additions and 171 deletions

View File

@ -7,7 +7,6 @@
#include "common/string_util.h"
#include "controller_interface.h"
#include "core/cdrom.h"
#include "core/controller.h"
#include "core/cpu_code_cache.h"
#include "core/dma.h"
#include "core/game_list.h"
@ -1061,8 +1060,9 @@ void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
const auto axis_names = Controller::GetAxisNames(ctype);
for (const auto& it : axis_names)
{
const std::string& axis_name = it.first;
const s32 axis_code = it.second;
const std::string& axis_name = std::get<std::string>(it);
const s32 axis_code = std::get<s32>(it);
const auto axis_type = std::get<Controller::AxisType>(it);
const std::vector<std::string> bindings =
si.GetStringList(category, TinyString::FromFormat("Axis%s", axis_name.c_str()));
@ -1072,7 +1072,7 @@ void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
if (!SplitBinding(binding, &device, &axis))
continue;
AddAxisToInputMap(binding, device, axis, [this, controller_index, axis_code](float value) {
AddAxisToInputMap(binding, device, axis, axis_type, [this, controller_index, axis_code](float value) {
if (System::IsShutdown())
return;
@ -1204,8 +1204,44 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const
}
bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const std::string_view& device,
const std::string_view& axis, InputAxisHandler handler)
const std::string_view& axis, Controller::AxisType axis_type,
InputAxisHandler handler)
{
if (axis_type == Controller::AxisType::Half)
{
if (device == "Keyboard")
{
std::optional<int> key_id = GetHostKeyCode(axis);
if (!key_id.has_value())
{
Log_WarningPrintf("Unknown keyboard key in binding '%s'", binding.c_str());
return false;
}
m_keyboard_input_handlers.emplace(key_id.value(), std::move(handler));
return true;
}
if (device == "Mouse")
{
if (StringUtil::StartsWith(axis, "Button"))
{
const std::optional<s32> button_index = StringUtil::FromChars<s32>(axis.substr(6));
if (!button_index.has_value())
{
Log_WarningPrintf("Invalid button in mouse binding '%s'", binding.c_str());
return false;
}
m_mouse_input_handlers.emplace(static_cast<HostMouseButton>(button_index.value()), std::move(handler));
return true;
}
Log_WarningPrintf("Malformed mouse binding '%s'", binding.c_str());
return false;
}
}
if (StringUtil::StartsWith(device, "Controller"))
{
if (!m_controller_interface)
@ -1233,6 +1269,18 @@ bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const st
return true;
}
else if (StringUtil::StartsWith(axis, "Button") && axis_type == Controller::AxisType::Half)
{
const std::optional<int> button_index = StringUtil::FromChars<int>(axis.substr(6));
if (!button_index ||
!m_controller_interface->BindControllerButtonToAxis(*controller_index, *button_index, std::move(handler)))
{
Log_WarningPrintf("Failed to bind controller button '%s' to axis", binding.c_str());
return false;
}
return true;
}
Log_WarningPrintf("Malformed controller binding '%s' in button", binding.c_str());
return false;
@ -1539,7 +1587,7 @@ void CommonHostInterface::ClearAllControllerBindings(SettingsInterface& si)
si.DeleteValue(section_name, button.first.c_str());
for (const auto& axis : Controller::GetAxisNames(ctype))
si.DeleteValue(section_name, axis.first.c_str());
si.DeleteValue(section_name, std::get<std::string>(axis).c_str());
if (Controller::GetVibrationMotorCount(ctype) > 0)
si.DeleteValue(section_name, "Rumble");
@ -1583,8 +1631,8 @@ void CommonHostInterface::ApplyInputProfile(const char* profile_path, SettingsIn
for (const auto& axis : Controller::GetAxisNames(*ctype))
{
const auto key_name = TinyString::FromFormat("Axis%s", axis.first.c_str());
si.DeleteValue(section_name, axis.first.c_str());
const auto key_name = TinyString::FromFormat("Axis%s", std::get<std::string>(axis).c_str());
si.DeleteValue(section_name, std::get<std::string>(axis).c_str());
const std::vector<std::string> bindings = profile.GetStringList(section_name, key_name);
for (const std::string& binding : bindings)
si.AddToStringList(section_name, key_name, binding.c_str());
@ -1642,7 +1690,7 @@ bool CommonHostInterface::SaveInputProfile(const char* profile_path, SettingsInt
for (const auto& axis : Controller::GetAxisNames(ctype))
{
const auto key_name = TinyString::FromFormat("Axis%s", axis.first.c_str());
const auto key_name = TinyString::FromFormat("Axis%s", std::get<std::string>(axis).c_str());
const std::vector<std::string> bindings = si.GetStringList(section_name, key_name);
for (const std::string& binding : bindings)
profile.AddToStringList(section_name, key_name, binding.c_str());

View File

@ -1,5 +1,6 @@
#pragma once
#include "common/string.h"
#include "core/controller.h"
#include "core/host_interface.h"
#include <atomic>
#include <functional>
@ -196,7 +197,8 @@ protected:
virtual bool AddButtonToInputMap(const std::string& binding, const std::string_view& device,
const std::string_view& button, InputButtonHandler handler);
virtual bool AddAxisToInputMap(const std::string& binding, const std::string_view& device,
const std::string_view& axis, InputAxisHandler handler);
const std::string_view& axis, Controller::AxisType axis_type,
InputAxisHandler handler);
virtual bool AddRumbleToInputMap(const std::string& binding, u32 controller_index, u32 num_motors);
/// Reloads the input map from config. Callable from controller interface.

View File

@ -3,9 +3,9 @@
#include "core/types.h"
#include <array>
#include <functional>
#include <optional>
#include <map>
#include <mutex>
#include <optional>
class HostInterface;
class Controller;
@ -54,6 +54,7 @@ public:
virtual bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) = 0;
virtual bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
ButtonCallback callback) = 0;
virtual bool BindControllerButtonToAxis(int controller_index, int button_number, AxisCallback callback) = 0;
virtual void PollEvents() = 0;

View File

@ -35,8 +35,7 @@ bool SDLControllerInterface::Initialize(CommonHostInterface* host_interface)
Log_InfoPrintf("Loading game controller mappings from '%s'", gcdb_file_name.c_str());
if (SDL_GameControllerAddMappingsFromFile(gcdb_file_name.c_str()) < 0)
{
Log_ErrorPrintf("SDL_GameControllerAddMappingsFromFile(%s) failed: %s",
gcdb_file_name.c_str(), SDL_GetError());
Log_ErrorPrintf("SDL_GameControllerAddMappingsFromFile(%s) failed: %s", gcdb_file_name.c_str(), SDL_GetError());
}
}
@ -293,6 +292,19 @@ bool SDLControllerInterface::BindControllerAxisToButton(int controller_index, in
return true;
}
bool SDLControllerInterface::BindControllerButtonToAxis(int controller_index, int button_number, AxisCallback callback)
{
auto it = GetControllerDataForPlayerId(controller_index);
if (it == m_controllers.end())
return false;
if (button_number < 0 || button_number >= MAX_NUM_BUTTONS)
return false;
it->button_axis_mapping[button_number] = std::move(callback);
return true;
}
bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev)
{
const float value = static_cast<float>(ev->caxis.value) / (ev->caxis.value < 0 ? 32768.0f : 32767.0f);
@ -350,11 +362,20 @@ bool SDLControllerInterface::HandleControllerButtonEvent(const SDL_Event* ev)
return true;
const ButtonCallback& cb = it->button_mapping[ev->cbutton.button];
if (!cb)
return false;
if (cb)
{
cb(pressed);
return true;
}
cb(pressed);
return true;
// Assume a half-axis, i.e. in 0..1 range
const AxisCallback& axis_cb = it->button_axis_mapping[ev->cbutton.button];
if (axis_cb)
{
axis_cb(pressed ? 1.0f : 0.0f);
}
return false;
}
u32 SDLControllerInterface::GetControllerRumbleMotorCount(int controller_index)

View File

@ -1,10 +1,10 @@
#pragma once
#include "core/types.h"
#include "controller_interface.h"
#include "core/types.h"
#include <array>
#include <functional>
#include <vector>
#include <mutex>
#include <vector>
union SDL_Event;
@ -27,7 +27,9 @@ public:
// Binding to events. If a binding for this axis/button already exists, returns false.
bool BindControllerAxis(int controller_index, int axis_number, AxisCallback callback) override;
bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) override;
bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction, ButtonCallback callback) override;
bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
ButtonCallback callback) override;
bool BindControllerButtonToAxis(int controller_index, int button_number, AxisCallback callback) override;
// Changing rumble strength.
u32 GetControllerRumbleMotorCount(int controller_index) override;
@ -59,6 +61,7 @@ private:
std::array<AxisCallback, MAX_NUM_AXISES> axis_mapping;
std::array<ButtonCallback, MAX_NUM_BUTTONS> button_mapping;
std::array<std::array<ButtonCallback, 2>, MAX_NUM_AXISES> axis_button_mapping;
std::array<AxisCallback, MAX_NUM_BUTTONS> button_axis_mapping;
};
using ControllerDataVector = std::vector<ControllerData>;

View File

@ -204,6 +204,19 @@ bool XInputControllerInterface::BindControllerAxisToButton(int controller_index,
return true;
}
bool XInputControllerInterface::BindControllerButtonToAxis(int controller_index, int button_number,
AxisCallback callback)
{
if (static_cast<u32>(controller_index) >= m_controllers.size() || !m_controllers[controller_index].connected)
return false;
if (button_number < 0 || button_number >= MAX_NUM_BUTTONS)
return false;
m_controllers[controller_index].button_axis_mapping[button_number] = std::move(callback);
return true;
}
bool XInputControllerInterface::HandleAxisEvent(u32 index, Axis axis, s32 value)
{
const float f_value = static_cast<float>(value) / (value < 0 ? 32768.0f : 32767.0f);
@ -255,10 +268,18 @@ bool XInputControllerInterface::HandleButtonEvent(u32 index, u32 button, bool pr
return true;
const ButtonCallback& cb = m_controllers[index].button_mapping[button];
if (!cb)
return false;
if (cb)
{
cb(pressed);
return true;
}
cb(pressed);
// Assume a half-axis, i.e. in 0..1 range
const AxisCallback& axis_cb = m_controllers[index].button_axis_mapping[button];
if (axis_cb)
{
axis_cb(pressed ? 1.0f : 0.0f);
}
return true;
}

View File

@ -26,6 +26,7 @@ public:
bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) override;
bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
ButtonCallback callback) override;
bool BindControllerButtonToAxis(int controller_index, int button_number, AxisCallback callback) override;
// Changing rumble strength.
u32 GetControllerRumbleMotorCount(int controller_index) override;
@ -68,6 +69,7 @@ private:
std::array<AxisCallback, MAX_NUM_AXISES> axis_mapping;
std::array<ButtonCallback, MAX_NUM_BUTTONS> button_mapping;
std::array<std::array<ButtonCallback, 2>, MAX_NUM_AXISES> axis_button_mapping;
std::array<AxisCallback, MAX_NUM_BUTTONS> button_axis_mapping;
};
using ControllerDataArray = std::array<ControllerData, XUSER_MAX_COUNT>;