mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-17 19:25:46 -04:00
Allow mapping half axes to buttons
This allows to bind pressure sensitive NeGcon buttons to keyboard, mouse and controller buttons
This commit is contained in:
@ -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"
|
||||
@ -1055,8 +1054,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()));
|
||||
@ -1066,7 +1066,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;
|
||||
|
||||
@ -1198,8 +1198,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)
|
||||
@ -1227,6 +1263,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;
|
||||
@ -1533,7 +1581,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");
|
||||
@ -1577,8 +1625,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());
|
||||
@ -1636,7 +1684,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());
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "common/string.h"
|
||||
#include "core/controller.h"
|
||||
#include "core/host_interface.h"
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
@ -195,7 +196,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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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>;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>;
|
||||
|
Reference in New Issue
Block a user