Introduce ControllerInterface class, port SDL interface over
This breaks controller support in the SDL frontend until it's ported over to the common interface.
This commit is contained in:
parent
6ce97e0e13
commit
29a09a3685
|
@ -1,6 +1,6 @@
|
||||||
#include "inputbindingwidgets.h"
|
#include "inputbindingwidgets.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "frontend-common/sdl_controller_interface.h"
|
#include "frontend-common/controller_interface.h"
|
||||||
#include "qthostinterface.h"
|
#include "qthostinterface.h"
|
||||||
#include "qtutils.h"
|
#include "qtutils.h"
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
@ -130,33 +130,41 @@ bool InputButtonBindingWidget::eventFilter(QObject* watched, QEvent* event)
|
||||||
|
|
||||||
void InputButtonBindingWidget::hookControllerInput()
|
void InputButtonBindingWidget::hookControllerInput()
|
||||||
{
|
{
|
||||||
|
ControllerInterface* controller_interface = m_host_interface->getControllerInterface();
|
||||||
|
if (!controller_interface)
|
||||||
|
return;
|
||||||
|
|
||||||
m_host_interface->enableBackgroundControllerPolling();
|
m_host_interface->enableBackgroundControllerPolling();
|
||||||
g_sdl_controller_interface.SetHook([this](const SDLControllerInterface::Hook& ei) {
|
controller_interface->SetHook([this](const ControllerInterface::Hook& ei) {
|
||||||
if (ei.type == SDLControllerInterface::Hook::Type::Axis)
|
if (ei.type == ControllerInterface::Hook::Type::Axis)
|
||||||
{
|
{
|
||||||
// wait until it's at least half pushed so we don't get confused between axises with small movement
|
// wait until it's at least half pushed so we don't get confused between axises with small movement
|
||||||
if (std::abs(ei.value) < 0.5f)
|
if (std::abs(ei.value) < 0.5f)
|
||||||
return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
return ControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
||||||
|
|
||||||
// TODO: this probably should consider the "last value"
|
// TODO: this probably should consider the "last value"
|
||||||
QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index),
|
QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index),
|
||||||
Q_ARG(int, ei.button_or_axis_number), Q_ARG(bool, ei.value > 0));
|
Q_ARG(int, ei.button_or_axis_number), Q_ARG(bool, ei.value > 0));
|
||||||
return SDLControllerInterface::Hook::CallbackResult::StopMonitoring;
|
return ControllerInterface::Hook::CallbackResult::StopMonitoring;
|
||||||
}
|
}
|
||||||
else if (ei.type == SDLControllerInterface::Hook::Type::Button && ei.value > 0.0f)
|
else if (ei.type == ControllerInterface::Hook::Type::Button && ei.value > 0.0f)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(this, "bindToControllerButton", Q_ARG(int, ei.controller_index),
|
QMetaObject::invokeMethod(this, "bindToControllerButton", Q_ARG(int, ei.controller_index),
|
||||||
Q_ARG(int, ei.button_or_axis_number));
|
Q_ARG(int, ei.button_or_axis_number));
|
||||||
return SDLControllerInterface::Hook::CallbackResult::StopMonitoring;
|
return ControllerInterface::Hook::CallbackResult::StopMonitoring;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
return ControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputButtonBindingWidget::unhookControllerInput()
|
void InputButtonBindingWidget::unhookControllerInput()
|
||||||
{
|
{
|
||||||
g_sdl_controller_interface.ClearHook();
|
ControllerInterface* controller_interface = m_host_interface->getControllerInterface();
|
||||||
|
if (!controller_interface)
|
||||||
|
return;
|
||||||
|
|
||||||
|
controller_interface->ClearHook();
|
||||||
m_host_interface->disableBackgroundControllerPolling();
|
m_host_interface->disableBackgroundControllerPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,26 +208,34 @@ InputAxisBindingWidget::~InputAxisBindingWidget()
|
||||||
|
|
||||||
void InputAxisBindingWidget::hookControllerInput()
|
void InputAxisBindingWidget::hookControllerInput()
|
||||||
{
|
{
|
||||||
|
ControllerInterface* controller_interface = m_host_interface->getControllerInterface();
|
||||||
|
if (!controller_interface)
|
||||||
|
return;
|
||||||
|
|
||||||
m_host_interface->enableBackgroundControllerPolling();
|
m_host_interface->enableBackgroundControllerPolling();
|
||||||
g_sdl_controller_interface.SetHook([this](const SDLControllerInterface::Hook& ei) {
|
controller_interface->SetHook([this](const ControllerInterface::Hook& ei) {
|
||||||
if (ei.type == SDLControllerInterface::Hook::Type::Axis)
|
if (ei.type == ControllerInterface::Hook::Type::Axis)
|
||||||
{
|
{
|
||||||
// wait until it's at least half pushed so we don't get confused between axises with small movement
|
// wait until it's at least half pushed so we don't get confused between axises with small movement
|
||||||
if (std::abs(ei.value) < 0.5f)
|
if (std::abs(ei.value) < 0.5f)
|
||||||
return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
return ControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
||||||
|
|
||||||
QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index),
|
QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index),
|
||||||
Q_ARG(int, ei.button_or_axis_number));
|
Q_ARG(int, ei.button_or_axis_number));
|
||||||
return SDLControllerInterface::Hook::CallbackResult::StopMonitoring;
|
return ControllerInterface::Hook::CallbackResult::StopMonitoring;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
return ControllerInterface::Hook::CallbackResult::ContinueMonitoring;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputAxisBindingWidget::unhookControllerInput()
|
void InputAxisBindingWidget::unhookControllerInput()
|
||||||
{
|
{
|
||||||
g_sdl_controller_interface.ClearHook();
|
ControllerInterface* controller_interface = m_host_interface->getControllerInterface();
|
||||||
|
if (!controller_interface)
|
||||||
|
return;
|
||||||
|
|
||||||
|
controller_interface->ClearHook();
|
||||||
m_host_interface->disableBackgroundControllerPolling();
|
m_host_interface->disableBackgroundControllerPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ QtHostInterface::QtHostInterface(QObject* parent)
|
||||||
{
|
{
|
||||||
qRegisterMetaType<SystemBootParameters>();
|
qRegisterMetaType<SystemBootParameters>();
|
||||||
|
|
||||||
|
// TODO: This probably should wait until the thread finishes initializing.
|
||||||
loadSettings();
|
loadSettings();
|
||||||
createThread();
|
createThread();
|
||||||
}
|
}
|
||||||
|
@ -45,6 +46,23 @@ QtHostInterface::~QtHostInterface()
|
||||||
stopThread();
|
stopThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QtHostInterface::Initialize()
|
||||||
|
{
|
||||||
|
if (!CommonHostInterface::Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_controller_interface)
|
||||||
|
m_controller_interface->PollEvents();
|
||||||
|
|
||||||
|
updateInputMap();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::Shutdown()
|
||||||
|
{
|
||||||
|
CommonHostInterface::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
void QtHostInterface::ReportError(const char* message)
|
void QtHostInterface::ReportError(const char* message)
|
||||||
{
|
{
|
||||||
HostInterface::ReportError(message);
|
HostInterface::ReportError(message);
|
||||||
|
@ -77,19 +95,19 @@ bool QtHostInterface::ConfirmMessage(const char* message)
|
||||||
|
|
||||||
QVariant QtHostInterface::getSettingValue(const QString& name, const QVariant& default_value)
|
QVariant QtHostInterface::getSettingValue(const QString& name, const QVariant& default_value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
|
std::lock_guard<std::recursive_mutex> guard(m_qsettings_mutex);
|
||||||
return m_qsettings.value(name, default_value);
|
return m_qsettings.value(name, default_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::putSettingValue(const QString& name, const QVariant& value)
|
void QtHostInterface::putSettingValue(const QString& name, const QVariant& value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
|
std::lock_guard<std::recursive_mutex> guard(m_qsettings_mutex);
|
||||||
m_qsettings.setValue(name, value);
|
m_qsettings.setValue(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::removeSettingValue(const QString& name)
|
void QtHostInterface::removeSettingValue(const QString& name)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
|
std::lock_guard<std::recursive_mutex> guard(m_qsettings_mutex);
|
||||||
m_qsettings.remove(name);
|
m_qsettings.remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,10 +119,10 @@ void QtHostInterface::setDefaultSettings()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
|
std::lock_guard<std::recursive_mutex> guard(m_qsettings_mutex);
|
||||||
QtSettingsInterface si(m_qsettings);
|
QtSettingsInterface si(m_qsettings);
|
||||||
UpdateSettings([this, &si]() { m_settings.Load(si); });
|
UpdateSettings([this, &si]() { m_settings.Load(si); });
|
||||||
UpdateInputMap(si);
|
CommonHostInterface::UpdateInputMap(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::applySettings()
|
void QtHostInterface::applySettings()
|
||||||
|
@ -115,10 +133,10 @@ void QtHostInterface::applySettings()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
|
std::lock_guard<std::recursive_mutex> guard(m_qsettings_mutex);
|
||||||
QtSettingsInterface si(m_qsettings);
|
QtSettingsInterface si(m_qsettings);
|
||||||
UpdateSettings([this, &si]() { m_settings.Load(si); });
|
UpdateSettings([this, &si]() { m_settings.Load(si); });
|
||||||
UpdateInputMap(si);
|
CommonHostInterface::UpdateInputMap(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::loadSettings()
|
void QtHostInterface::loadSettings()
|
||||||
|
@ -143,7 +161,7 @@ void QtHostInterface::refreshGameList(bool invalidate_cache /* = false */, bool
|
||||||
{
|
{
|
||||||
Assert(!isOnWorkerThread());
|
Assert(!isOnWorkerThread());
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(m_qsettings_mutex);
|
std::lock_guard<std::recursive_mutex> lock(m_qsettings_mutex);
|
||||||
QtSettingsInterface si(m_qsettings);
|
QtSettingsInterface si(m_qsettings);
|
||||||
m_game_list->SetSearchDirectoriesFromSettings(si);
|
m_game_list->SetSearchDirectoriesFromSettings(si);
|
||||||
|
|
||||||
|
@ -348,13 +366,9 @@ void QtHostInterface::OnSystemStateSaved(bool global, s32 slot)
|
||||||
emit stateSaved(QString::fromStdString(m_system->GetRunningCode()), global, slot);
|
emit stateSaved(QString::fromStdString(m_system->GetRunningCode()), global, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::OnControllerTypeChanged(u32 slot)
|
void QtHostInterface::UpdateInputMap()
|
||||||
{
|
{
|
||||||
HostInterface::OnControllerTypeChanged(slot);
|
updateInputMap();
|
||||||
|
|
||||||
// this assumes the settings mutex is already locked - as it comes from updateSettings().
|
|
||||||
QtSettingsInterface si(m_qsettings);
|
|
||||||
UpdateInputMap(si);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::updateInputMap()
|
void QtHostInterface::updateInputMap()
|
||||||
|
@ -365,9 +379,9 @@ void QtHostInterface::updateInputMap()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(m_qsettings_mutex);
|
std::lock_guard<std::recursive_mutex> lock(m_qsettings_mutex);
|
||||||
QtSettingsInterface si(m_qsettings);
|
QtSettingsInterface si(m_qsettings);
|
||||||
UpdateInputMap(si);
|
CommonHostInterface::UpdateInputMap(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::powerOffSystem()
|
void QtHostInterface::powerOffSystem()
|
||||||
|
@ -613,7 +627,7 @@ void QtHostInterface::enableBackgroundControllerPolling()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_background_controller_polling_enable_count++ > 0)
|
if (!m_controller_interface || m_background_controller_polling_enable_count++ > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!m_system || m_paused)
|
if (!m_system || m_paused)
|
||||||
|
@ -621,7 +635,7 @@ void QtHostInterface::enableBackgroundControllerPolling()
|
||||||
createBackgroundControllerPollTimer();
|
createBackgroundControllerPollTimer();
|
||||||
|
|
||||||
// drain the event queue so we don't get events late
|
// drain the event queue so we don't get events late
|
||||||
g_sdl_controller_interface.PumpSDLEvents();
|
m_controller_interface->PollEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,7 +648,7 @@ void QtHostInterface::disableBackgroundControllerPolling()
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(m_background_controller_polling_enable_count > 0);
|
Assert(m_background_controller_polling_enable_count > 0);
|
||||||
if (--m_background_controller_polling_enable_count > 0)
|
if (!m_controller_interface || --m_background_controller_polling_enable_count > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!m_system || m_paused)
|
if (!m_system || m_paused)
|
||||||
|
@ -643,7 +657,7 @@ void QtHostInterface::disableBackgroundControllerPolling()
|
||||||
|
|
||||||
void QtHostInterface::doBackgroundControllerPoll()
|
void QtHostInterface::doBackgroundControllerPoll()
|
||||||
{
|
{
|
||||||
g_sdl_controller_interface.PumpSDLEvents();
|
m_controller_interface->PollEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::createBackgroundControllerPollTimer()
|
void QtHostInterface::createBackgroundControllerPollTimer()
|
||||||
|
@ -689,9 +703,8 @@ void QtHostInterface::threadEntryPoint()
|
||||||
m_worker_thread_event_loop = new QEventLoop();
|
m_worker_thread_event_loop = new QEventLoop();
|
||||||
|
|
||||||
// set up controller interface and immediate poll to pick up the controller attached events
|
// set up controller interface and immediate poll to pick up the controller attached events
|
||||||
g_sdl_controller_interface.Initialize(this);
|
if (!Initialize())
|
||||||
g_sdl_controller_interface.PumpSDLEvents();
|
Panic("Failed to initialize host interface");
|
||||||
updateInputMap();
|
|
||||||
|
|
||||||
// TODO: Event which flags the thread as ready
|
// TODO: Event which flags the thread as ready
|
||||||
while (!m_shutdown_flag.load())
|
while (!m_shutdown_flag.load())
|
||||||
|
@ -711,14 +724,12 @@ void QtHostInterface::threadEntryPoint()
|
||||||
m_system->Throttle();
|
m_system->Throttle();
|
||||||
|
|
||||||
m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents);
|
m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents);
|
||||||
g_sdl_controller_interface.PumpSDLEvents();
|
if (m_controller_interface)
|
||||||
|
m_controller_interface->PollEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_system.reset();
|
Shutdown();
|
||||||
m_audio_stream.reset();
|
|
||||||
|
|
||||||
g_sdl_controller_interface.Shutdown();
|
|
||||||
|
|
||||||
delete m_worker_thread_event_loop;
|
delete m_worker_thread_event_loop;
|
||||||
m_worker_thread_event_loop = nullptr;
|
m_worker_thread_event_loop = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class QtDisplayWidget;
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(SystemBootParameters);
|
Q_DECLARE_METATYPE(SystemBootParameters);
|
||||||
|
|
||||||
class QtHostInterface : public QObject, private CommonHostInterface
|
class QtHostInterface final : public QObject, private CommonHostInterface
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -36,6 +36,9 @@ public:
|
||||||
explicit QtHostInterface(QObject* parent = nullptr);
|
explicit QtHostInterface(QObject* parent = nullptr);
|
||||||
~QtHostInterface();
|
~QtHostInterface();
|
||||||
|
|
||||||
|
bool Initialize() override;
|
||||||
|
void Shutdown() override;
|
||||||
|
|
||||||
void ReportError(const char* message) override;
|
void ReportError(const char* message) override;
|
||||||
void ReportMessage(const char* message) override;
|
void ReportMessage(const char* message) override;
|
||||||
bool ConfirmMessage(const char* message) override;
|
bool ConfirmMessage(const char* message) override;
|
||||||
|
@ -50,6 +53,7 @@ public:
|
||||||
void refreshGameList(bool invalidate_cache = false, bool invalidate_database = false);
|
void refreshGameList(bool invalidate_cache = false, bool invalidate_database = false);
|
||||||
|
|
||||||
ALWAYS_INLINE const HotkeyInfoList& getHotkeyInfoList() const { return GetHotkeyInfoList(); }
|
ALWAYS_INLINE const HotkeyInfoList& getHotkeyInfoList() const { return GetHotkeyInfoList(); }
|
||||||
|
ALWAYS_INLINE ControllerInterface* getControllerInterface() const { return GetControllerInterface(); }
|
||||||
|
|
||||||
ALWAYS_INLINE bool isOnWorkerThread() const { return QThread::currentThread() == m_worker_thread; }
|
ALWAYS_INLINE bool isOnWorkerThread() const { return QThread::currentThread() == m_worker_thread; }
|
||||||
|
|
||||||
|
@ -125,7 +129,8 @@ protected:
|
||||||
void OnSystemPerformanceCountersUpdated() override;
|
void OnSystemPerformanceCountersUpdated() override;
|
||||||
void OnRunningGameChanged() override;
|
void OnRunningGameChanged() override;
|
||||||
void OnSystemStateSaved(bool global, s32 slot) override;
|
void OnSystemStateSaved(bool global, s32 slot) override;
|
||||||
void OnControllerTypeChanged(u32 slot) override;
|
|
||||||
|
void UpdateInputMap() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum : u32
|
enum : u32
|
||||||
|
@ -161,7 +166,7 @@ private:
|
||||||
void wakeThread();
|
void wakeThread();
|
||||||
|
|
||||||
QSettings m_qsettings;
|
QSettings m_qsettings;
|
||||||
std::mutex m_qsettings_mutex;
|
std::recursive_mutex m_qsettings_mutex;
|
||||||
|
|
||||||
MainWindow* m_main_window = nullptr;
|
MainWindow* m_main_window = nullptr;
|
||||||
QtDisplayWidget* m_display_widget = nullptr;
|
QtDisplayWidget* m_display_widget = nullptr;
|
||||||
|
|
|
@ -32,7 +32,6 @@ SDLHostInterface::SDLHostInterface()
|
||||||
|
|
||||||
SDLHostInterface::~SDLHostInterface()
|
SDLHostInterface::~SDLHostInterface()
|
||||||
{
|
{
|
||||||
g_sdl_controller_interface.Shutdown();
|
|
||||||
if (m_display)
|
if (m_display)
|
||||||
{
|
{
|
||||||
DestroyDisplay();
|
DestroyDisplay();
|
||||||
|
@ -232,7 +231,6 @@ void SDLHostInterface::OnSystemCreated()
|
||||||
HostInterface::OnSystemCreated();
|
HostInterface::OnSystemCreated();
|
||||||
|
|
||||||
UpdateKeyboardControllerMapping();
|
UpdateKeyboardControllerMapping();
|
||||||
g_sdl_controller_interface.SetDefaultBindings();
|
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +252,6 @@ void SDLHostInterface::OnControllerTypeChanged(u32 slot)
|
||||||
HostInterface::OnControllerTypeChanged(slot);
|
HostInterface::OnControllerTypeChanged(slot);
|
||||||
|
|
||||||
UpdateKeyboardControllerMapping();
|
UpdateKeyboardControllerMapping();
|
||||||
g_sdl_controller_interface.SetDefaultBindings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLHostInterface::RunLater(std::function<void()> callback)
|
void SDLHostInterface::RunLater(std::function<void()> callback)
|
||||||
|
@ -309,12 +306,6 @@ std::unique_ptr<SDLHostInterface> SDLHostInterface::Create()
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_sdl_controller_interface.Initialize(intf.get()))
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to initialize controller interface.");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
intf->CreateImGuiContext();
|
intf->CreateImGuiContext();
|
||||||
if (!intf->CreateDisplay())
|
if (!intf->CreateDisplay())
|
||||||
{
|
{
|
||||||
|
@ -365,7 +356,7 @@ bool SDLHostInterface::ConfirmMessage(const char* message)
|
||||||
void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
||||||
{
|
{
|
||||||
ImGui_ImplSDL2_ProcessEvent(event);
|
ImGui_ImplSDL2_ProcessEvent(event);
|
||||||
g_sdl_controller_interface.ProcessSDLEvent(event);
|
//g_sdl_controller_interface.ProcessSDLEvent(event);
|
||||||
|
|
||||||
switch (event->type)
|
switch (event->type)
|
||||||
{
|
{
|
||||||
|
@ -397,7 +388,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
||||||
|
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
case SDL_CONTROLLERDEVICEADDED:
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
case SDL_CONTROLLERDEVICEREMOVED:
|
||||||
g_sdl_controller_interface.SetDefaultBindings();
|
//g_sdl_controller_interface.SetDefaultBindings();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
|
@ -1435,7 +1426,7 @@ void SDLHostInterface::Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_sdl_controller_interface.UpdateControllerRumble();
|
//g_sdl_controller_interface.UpdateControllerRumble();
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "common/gl/texture.h"
|
#include "common/gl/texture.h"
|
||||||
#include "core/host_display.h"
|
#include "core/host_display.h"
|
||||||
#include "core/host_interface.h"
|
#include "core/host_interface.h"
|
||||||
|
#include "frontend-common/sdl_controller_interface.h"
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
@ -121,7 +122,7 @@ private:
|
||||||
std::unique_ptr<HostDisplayTexture> m_app_icon_texture;
|
std::unique_ptr<HostDisplayTexture> m_app_icon_texture;
|
||||||
|
|
||||||
KeyboardControllerActionMap m_keyboard_button_mapping;
|
KeyboardControllerActionMap m_keyboard_button_mapping;
|
||||||
|
|
||||||
u32 m_run_later_event_id = 0;
|
u32 m_run_later_event_id = 0;
|
||||||
|
|
||||||
bool m_fullscreen = false;
|
bool m_fullscreen = false;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
add_library(frontend-common
|
add_library(frontend-common
|
||||||
common_host_interface.cpp
|
common_host_interface.cpp
|
||||||
common_host_interface.h
|
common_host_interface.h
|
||||||
|
controller_interface.cpp
|
||||||
|
controller_interface.h
|
||||||
icon.cpp
|
icon.cpp
|
||||||
icon.h
|
icon.h
|
||||||
imgui_styles.cpp
|
imgui_styles.cpp
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "common/audio_stream.h"
|
#include "common/audio_stream.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
#include "controller_interface.h"
|
||||||
#include "core/controller.h"
|
#include "core/controller.h"
|
||||||
#include "core/game_list.h"
|
#include "core/game_list.h"
|
||||||
#include "core/gpu.h"
|
#include "core/gpu.h"
|
||||||
|
@ -14,14 +15,41 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
Log_SetChannel(CommonHostInterface);
|
Log_SetChannel(CommonHostInterface);
|
||||||
|
|
||||||
CommonHostInterface::CommonHostInterface() : HostInterface()
|
CommonHostInterface::CommonHostInterface() = default;
|
||||||
|
|
||||||
|
CommonHostInterface::~CommonHostInterface() = default;
|
||||||
|
|
||||||
|
bool CommonHostInterface::Initialize()
|
||||||
{
|
{
|
||||||
RegisterGeneralHotkeys();
|
RegisterGeneralHotkeys();
|
||||||
RegisterGraphicsHotkeys();
|
RegisterGraphicsHotkeys();
|
||||||
RegisterSaveStateHotkeys();
|
RegisterSaveStateHotkeys();
|
||||||
|
|
||||||
|
m_controller_interface = CreateControllerInterface();
|
||||||
|
if (m_controller_interface && !m_controller_interface->Initialize(this))
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Failed to initialize controller bindings are not possible.");
|
||||||
|
m_controller_interface.reset();
|
||||||
|
}
|
||||||
|
else if (!m_controller_interface)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("No controller interface created, controller bindings are not possible.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommonHostInterface::~CommonHostInterface() = default;
|
void CommonHostInterface::Shutdown()
|
||||||
|
{
|
||||||
|
m_system.reset();
|
||||||
|
m_audio_stream.reset();
|
||||||
|
|
||||||
|
if (m_controller_interface)
|
||||||
|
{
|
||||||
|
m_controller_interface->Shutdown();
|
||||||
|
m_controller_interface.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CommonHostInterface::SetFullscreen(bool enabled) {}
|
void CommonHostInterface::SetFullscreen(bool enabled) {}
|
||||||
|
|
||||||
|
@ -47,6 +75,16 @@ std::unique_ptr<AudioStream> CommonHostInterface::CreateAudioStream(AudioBackend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ControllerInterface> CommonHostInterface::CreateControllerInterface()
|
||||||
|
{
|
||||||
|
// In the future we might want to use different controller interfaces.
|
||||||
|
#ifdef WITH_SDL2
|
||||||
|
return std::make_unique<SDLControllerInterface>();
|
||||||
|
#else
|
||||||
|
return nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void CommonHostInterface::OnSystemCreated()
|
void CommonHostInterface::OnSystemCreated()
|
||||||
{
|
{
|
||||||
HostInterface::OnSystemCreated();
|
HostInterface::OnSystemCreated();
|
||||||
|
@ -65,6 +103,13 @@ void CommonHostInterface::OnSystemPaused(bool paused)
|
||||||
SetFullscreen(true);
|
SetFullscreen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommonHostInterface::OnControllerTypeChanged(u32 slot)
|
||||||
|
{
|
||||||
|
HostInterface::OnControllerTypeChanged(slot);
|
||||||
|
|
||||||
|
UpdateInputMap();
|
||||||
|
}
|
||||||
|
|
||||||
void CommonHostInterface::SetDefaultSettings(SettingsInterface& si)
|
void CommonHostInterface::SetDefaultSettings(SettingsInterface& si)
|
||||||
{
|
{
|
||||||
HostInterface::SetDefaultSettings(si);
|
HostInterface::SetDefaultSettings(si);
|
||||||
|
@ -117,9 +162,8 @@ bool CommonHostInterface::HandleHostKeyEvent(HostKeyCode key, bool pressed)
|
||||||
void CommonHostInterface::UpdateInputMap(SettingsInterface& si)
|
void CommonHostInterface::UpdateInputMap(SettingsInterface& si)
|
||||||
{
|
{
|
||||||
m_keyboard_input_handlers.clear();
|
m_keyboard_input_handlers.clear();
|
||||||
#ifdef WITH_SDL2
|
if (m_controller_interface)
|
||||||
g_sdl_controller_interface.ClearControllerBindings();
|
m_controller_interface->ClearBindings();
|
||||||
#endif
|
|
||||||
|
|
||||||
UpdateControllerInputMap(si);
|
UpdateControllerInputMap(si);
|
||||||
UpdateHotkeyInputMap(si);
|
UpdateHotkeyInputMap(si);
|
||||||
|
@ -232,9 +276,14 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_SDL2
|
|
||||||
if (StringUtil::StartsWith(device, "Controller"))
|
if (StringUtil::StartsWith(device, "Controller"))
|
||||||
{
|
{
|
||||||
|
if (!m_controller_interface)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("No controller interface set, cannot bind '%s'", binding.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const std::optional<int> controller_index = StringUtil::FromChars<int>(device.substr(10));
|
const std::optional<int> controller_index = StringUtil::FromChars<int>(device.substr(10));
|
||||||
if (!controller_index || *controller_index < 0)
|
if (!controller_index || *controller_index < 0)
|
||||||
{
|
{
|
||||||
|
@ -246,7 +295,7 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const
|
||||||
{
|
{
|
||||||
const std::optional<int> button_index = StringUtil::FromChars<int>(button.substr(6));
|
const std::optional<int> button_index = StringUtil::FromChars<int>(button.substr(6));
|
||||||
if (!button_index ||
|
if (!button_index ||
|
||||||
!g_sdl_controller_interface.BindControllerButton(*controller_index, *button_index, std::move(handler)))
|
!m_controller_interface->BindControllerButton(*controller_index, *button_index, std::move(handler)))
|
||||||
{
|
{
|
||||||
Log_WarningPrintf("Failed to bind controller button '%s' to button", binding.c_str());
|
Log_WarningPrintf("Failed to bind controller button '%s' to button", binding.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
@ -258,8 +307,8 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const
|
||||||
{
|
{
|
||||||
const std::optional<int> axis_index = StringUtil::FromChars<int>(button.substr(5));
|
const std::optional<int> axis_index = StringUtil::FromChars<int>(button.substr(5));
|
||||||
const bool positive = (button[0] == '+');
|
const bool positive = (button[0] == '+');
|
||||||
if (!axis_index || !g_sdl_controller_interface.BindControllerAxisToButton(*controller_index, *axis_index,
|
if (!axis_index || !m_controller_interface->BindControllerAxisToButton(*controller_index, *axis_index, positive,
|
||||||
positive, std::move(handler)))
|
std::move(handler)))
|
||||||
{
|
{
|
||||||
Log_WarningPrintf("Failed to bind controller axis '%s' to button", binding.c_str());
|
Log_WarningPrintf("Failed to bind controller axis '%s' to button", binding.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
@ -271,7 +320,6 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const
|
||||||
Log_WarningPrintf("Malformed controller binding '%s' in button", binding.c_str());
|
Log_WarningPrintf("Malformed controller binding '%s' in button", binding.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
Log_WarningPrintf("Unknown input device in button binding '%s'", binding.c_str());
|
Log_WarningPrintf("Unknown input device in button binding '%s'", binding.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
@ -280,9 +328,14 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const
|
||||||
bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const std::string_view& device,
|
bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const std::string_view& device,
|
||||||
const std::string_view& axis, InputAxisHandler handler)
|
const std::string_view& axis, InputAxisHandler handler)
|
||||||
{
|
{
|
||||||
#ifdef WITH_SDL2
|
|
||||||
if (StringUtil::StartsWith(device, "Controller"))
|
if (StringUtil::StartsWith(device, "Controller"))
|
||||||
{
|
{
|
||||||
|
if (!m_controller_interface)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("No controller interface set, cannot bind '%s'", binding.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const std::optional<int> controller_index = StringUtil::FromChars<int>(device.substr(10));
|
const std::optional<int> controller_index = StringUtil::FromChars<int>(device.substr(10));
|
||||||
if (!controller_index || *controller_index < 0)
|
if (!controller_index || *controller_index < 0)
|
||||||
{
|
{
|
||||||
|
@ -294,7 +347,7 @@ bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const st
|
||||||
{
|
{
|
||||||
const std::optional<int> axis_index = StringUtil::FromChars<int>(axis.substr(4));
|
const std::optional<int> axis_index = StringUtil::FromChars<int>(axis.substr(4));
|
||||||
if (!axis_index ||
|
if (!axis_index ||
|
||||||
!g_sdl_controller_interface.BindControllerAxis(*controller_index, *axis_index, std::move(handler)))
|
!m_controller_interface->BindControllerAxis(*controller_index, *axis_index, std::move(handler)))
|
||||||
{
|
{
|
||||||
Log_WarningPrintf("Failed to bind controller axis '%s' to axi", binding.c_str());
|
Log_WarningPrintf("Failed to bind controller axis '%s' to axi", binding.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
@ -306,7 +359,6 @@ bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const st
|
||||||
Log_WarningPrintf("Malformed controller binding '%s' in button", binding.c_str());
|
Log_WarningPrintf("Malformed controller binding '%s' in button", binding.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
Log_WarningPrintf("Unknown input device in axis binding '%s'", binding.c_str());
|
Log_WarningPrintf("Unknown input device in axis binding '%s'", binding.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -11,9 +11,13 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class ControllerInterface;
|
||||||
|
|
||||||
class CommonHostInterface : public HostInterface
|
class CommonHostInterface : public HostInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
friend ControllerInterface;
|
||||||
|
|
||||||
using HostKeyCode = s32;
|
using HostKeyCode = s32;
|
||||||
|
|
||||||
using InputButtonHandler = std::function<void(bool)>;
|
using InputButtonHandler = std::function<void(bool)>;
|
||||||
|
@ -29,8 +33,14 @@ public:
|
||||||
|
|
||||||
using HotkeyInfoList = std::vector<HotkeyInfo>;
|
using HotkeyInfoList = std::vector<HotkeyInfo>;
|
||||||
|
|
||||||
|
virtual bool Initialize();
|
||||||
|
virtual void Shutdown();
|
||||||
|
|
||||||
/// Returns a list of all available hotkeys.
|
/// Returns a list of all available hotkeys.
|
||||||
const HotkeyInfoList& GetHotkeyInfoList() const { return m_hotkeys; }
|
ALWAYS_INLINE const HotkeyInfoList& GetHotkeyInfoList() const { return m_hotkeys; }
|
||||||
|
|
||||||
|
/// Access to current controller interface.
|
||||||
|
ALWAYS_INLINE ControllerInterface* GetControllerInterface() const { return m_controller_interface.get(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CommonHostInterface();
|
CommonHostInterface();
|
||||||
|
@ -40,28 +50,36 @@ protected:
|
||||||
virtual void ToggleFullscreen();
|
virtual void ToggleFullscreen();
|
||||||
|
|
||||||
virtual std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
|
virtual std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
|
||||||
|
virtual std::unique_ptr<ControllerInterface> CreateControllerInterface();
|
||||||
|
|
||||||
virtual void OnSystemCreated() override;
|
virtual void OnSystemCreated() override;
|
||||||
virtual void OnSystemPaused(bool paused) override;
|
virtual void OnSystemPaused(bool paused) override;
|
||||||
|
virtual void OnControllerTypeChanged(u32 slot) override;
|
||||||
|
|
||||||
virtual void SetDefaultSettings(SettingsInterface& si) override;
|
virtual void SetDefaultSettings(SettingsInterface& si) override;
|
||||||
|
|
||||||
virtual std::optional<HostKeyCode> GetHostKeyCode(const std::string_view key_code) const;
|
virtual std::optional<HostKeyCode> GetHostKeyCode(const std::string_view key_code) const;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/// Reloads the input map from config. Callable from controller interface.
|
||||||
|
virtual void UpdateInputMap() = 0;
|
||||||
|
|
||||||
void RegisterHotkey(String category, String name, String display_name, InputButtonHandler handler);
|
void RegisterHotkey(String category, String name, String display_name, InputButtonHandler handler);
|
||||||
bool HandleHostKeyEvent(HostKeyCode code, bool pressed);
|
bool HandleHostKeyEvent(HostKeyCode code, bool pressed);
|
||||||
void UpdateInputMap(SettingsInterface& si);
|
void UpdateInputMap(SettingsInterface& si);
|
||||||
|
|
||||||
|
std::unique_ptr<ControllerInterface> m_controller_interface;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RegisterGeneralHotkeys();
|
void RegisterGeneralHotkeys();
|
||||||
void RegisterGraphicsHotkeys();
|
void RegisterGraphicsHotkeys();
|
||||||
void RegisterSaveStateHotkeys();
|
void RegisterSaveStateHotkeys();
|
||||||
void UpdateControllerInputMap(SettingsInterface& si);
|
void UpdateControllerInputMap(SettingsInterface& si);
|
||||||
void UpdateHotkeyInputMap(SettingsInterface& si);
|
void UpdateHotkeyInputMap(SettingsInterface& si);
|
||||||
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);
|
|
||||||
|
|
||||||
HotkeyInfoList m_hotkeys;
|
HotkeyInfoList m_hotkeys;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
#include "controller_interface.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "core/controller.h"
|
||||||
|
#include "core/system.h"
|
||||||
|
#include <cmath>
|
||||||
|
Log_SetChannel(ControllerInterface);
|
||||||
|
|
||||||
|
ControllerInterface::ControllerInterface() = default;
|
||||||
|
|
||||||
|
ControllerInterface::~ControllerInterface() = default;
|
||||||
|
|
||||||
|
bool ControllerInterface::Initialize(CommonHostInterface* host_interface)
|
||||||
|
{
|
||||||
|
m_host_interface = host_interface;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerInterface::Shutdown()
|
||||||
|
{
|
||||||
|
m_host_interface = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
System* ControllerInterface::GetSystem() const
|
||||||
|
{
|
||||||
|
return m_host_interface->GetSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller* ControllerInterface::GetController(u32 slot) const
|
||||||
|
{
|
||||||
|
System* system = GetSystem();
|
||||||
|
return system ? system->GetController(slot) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerInterface::SetHook(Hook::Callback callback)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_event_intercept_mutex);
|
||||||
|
Assert(!m_event_intercept_callback);
|
||||||
|
m_event_intercept_callback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerInterface::ClearHook()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_event_intercept_mutex);
|
||||||
|
if (m_event_intercept_callback)
|
||||||
|
m_event_intercept_callback = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControllerInterface::DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_event_intercept_mutex);
|
||||||
|
if (!m_event_intercept_callback)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Hook ei{type, controller_index, button_or_axis_number, value};
|
||||||
|
const Hook::CallbackResult action = m_event_intercept_callback(ei);
|
||||||
|
if (action == Hook::CallbackResult::StopMonitoring)
|
||||||
|
m_event_intercept_callback = {};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerInterface::OnControllerConnected(int host_id)
|
||||||
|
{
|
||||||
|
Log_InfoPrintf("Host controller %d connected, updating input map", host_id);
|
||||||
|
m_host_interface->UpdateInputMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerInterface::OnControllerDisconnected(int host_id)
|
||||||
|
{
|
||||||
|
Log_InfoPrintf("Host controller %d disconnected, updating input map", host_id);
|
||||||
|
m_host_interface->UpdateInputMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerInterface::ClearBindings() {}
|
||||||
|
|
||||||
|
bool ControllerInterface::BindControllerAxis(int controller_index, int axis_number, AxisCallback callback)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControllerInterface::BindControllerButton(int controller_index, int button_number, ButtonCallback callback)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControllerInterface::BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
|
||||||
|
ButtonCallback callback)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerInterface::UpdateControllerRumble() {}
|
|
@ -0,0 +1,80 @@
|
||||||
|
#pragma once
|
||||||
|
#include "common_host_interface.h"
|
||||||
|
#include "core/types.h"
|
||||||
|
#include <array>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
class HostInterface;
|
||||||
|
class System;
|
||||||
|
class Controller;
|
||||||
|
|
||||||
|
class ControllerInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum : int
|
||||||
|
{
|
||||||
|
MAX_NUM_AXISES = 7,
|
||||||
|
MAX_NUM_BUTTONS = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
using AxisCallback = CommonHostInterface::InputAxisHandler;
|
||||||
|
using ButtonCallback = CommonHostInterface::InputButtonHandler;
|
||||||
|
|
||||||
|
ControllerInterface();
|
||||||
|
virtual ~ControllerInterface();
|
||||||
|
|
||||||
|
virtual bool Initialize(CommonHostInterface* host_interface);
|
||||||
|
virtual void Shutdown();
|
||||||
|
|
||||||
|
// Removes all bindings. Call before setting new bindings.
|
||||||
|
virtual void ClearBindings() = 0;
|
||||||
|
|
||||||
|
// Binding to events. If a binding for this axis/button already exists, returns false.
|
||||||
|
virtual bool BindControllerAxis(int controller_index, int axis_number, AxisCallback callback) = 0;
|
||||||
|
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 void PollEvents() = 0;
|
||||||
|
virtual void UpdateControllerRumble() = 0;
|
||||||
|
|
||||||
|
// Input monitoring for external access.
|
||||||
|
struct Hook
|
||||||
|
{
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
Axis,
|
||||||
|
Button
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CallbackResult
|
||||||
|
{
|
||||||
|
StopMonitoring,
|
||||||
|
ContinueMonitoring
|
||||||
|
};
|
||||||
|
|
||||||
|
using Callback = std::function<CallbackResult(const Hook& ei)>;
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
int controller_index;
|
||||||
|
int button_or_axis_number;
|
||||||
|
float value; // 0/1 for buttons, -1..1 for axises
|
||||||
|
};
|
||||||
|
void SetHook(Hook::Callback callback);
|
||||||
|
void ClearHook();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
System* GetSystem() const;
|
||||||
|
Controller* GetController(u32 slot) const;
|
||||||
|
bool DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value);
|
||||||
|
|
||||||
|
void OnControllerConnected(int host_id);
|
||||||
|
void OnControllerDisconnected(int host_id);
|
||||||
|
|
||||||
|
CommonHostInterface* m_host_interface = nullptr;
|
||||||
|
|
||||||
|
std::mutex m_event_intercept_mutex;
|
||||||
|
Hook::Callback m_event_intercept_callback;
|
||||||
|
};
|
|
@ -56,6 +56,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="common_host_interface.cpp" />
|
<ClCompile Include="common_host_interface.cpp" />
|
||||||
|
<ClCompile Include="controller_interface.cpp" />
|
||||||
<ClCompile Include="icon.cpp" />
|
<ClCompile Include="icon.cpp" />
|
||||||
<ClCompile Include="imgui_styles.cpp" />
|
<ClCompile Include="imgui_styles.cpp" />
|
||||||
<ClCompile Include="ini_settings_interface.cpp" />
|
<ClCompile Include="ini_settings_interface.cpp" />
|
||||||
|
@ -65,6 +66,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="common_host_interface.h" />
|
<ClInclude Include="common_host_interface.h" />
|
||||||
|
<ClInclude Include="controller_interface.h" />
|
||||||
<ClInclude Include="icon.h" />
|
<ClInclude Include="icon.h" />
|
||||||
<ClInclude Include="imgui_styles.h" />
|
<ClInclude Include="imgui_styles.h" />
|
||||||
<ClInclude Include="ini_settings_interface.h" />
|
<ClInclude Include="ini_settings_interface.h" />
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<ClCompile Include="sdl_initializer.cpp" />
|
<ClCompile Include="sdl_initializer.cpp" />
|
||||||
<ClCompile Include="common_host_interface.cpp" />
|
<ClCompile Include="common_host_interface.cpp" />
|
||||||
<ClCompile Include="ini_settings_interface.cpp" />
|
<ClCompile Include="ini_settings_interface.cpp" />
|
||||||
|
<ClCompile Include="controller_interface.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="icon.h" />
|
<ClInclude Include="icon.h" />
|
||||||
|
@ -17,6 +18,7 @@
|
||||||
<ClInclude Include="sdl_initializer.h" />
|
<ClInclude Include="sdl_initializer.h" />
|
||||||
<ClInclude Include="common_host_interface.h" />
|
<ClInclude Include="common_host_interface.h" />
|
||||||
<ClInclude Include="ini_settings_interface.h" />
|
<ClInclude Include="ini_settings_interface.h" />
|
||||||
|
<ClInclude Include="controller_interface.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="font_roboto_regular.inl" />
|
<None Include="font_roboto_regular.inl" />
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
Log_SetChannel(SDLControllerInterface);
|
Log_SetChannel(SDLControllerInterface);
|
||||||
|
|
||||||
SDLControllerInterface g_sdl_controller_interface;
|
|
||||||
|
|
||||||
SDLControllerInterface::SDLControllerInterface() = default;
|
SDLControllerInterface::SDLControllerInterface() = default;
|
||||||
|
|
||||||
SDLControllerInterface::~SDLControllerInterface()
|
SDLControllerInterface::~SDLControllerInterface()
|
||||||
|
@ -18,8 +16,11 @@ SDLControllerInterface::~SDLControllerInterface()
|
||||||
Assert(m_controllers.empty());
|
Assert(m_controllers.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLControllerInterface::Initialize(HostInterface* host_interface)
|
bool SDLControllerInterface::Initialize(CommonHostInterface* host_interface)
|
||||||
{
|
{
|
||||||
|
if (!ControllerInterface::Initialize(host_interface))
|
||||||
|
return false;
|
||||||
|
|
||||||
FrontendCommon::EnsureSDLInitialized();
|
FrontendCommon::EnsureSDLInitialized();
|
||||||
|
|
||||||
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) < 0)
|
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) < 0)
|
||||||
|
@ -29,26 +30,25 @@ bool SDLControllerInterface::Initialize(HostInterface* host_interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we should open the controllers as the connected events come in, so no need to do any more here
|
// we should open the controllers as the connected events come in, so no need to do any more here
|
||||||
m_host_interface = host_interface;
|
m_sdl_subsystem_initialized = true;
|
||||||
m_initialized = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLControllerInterface::Shutdown()
|
void SDLControllerInterface::Shutdown()
|
||||||
{
|
{
|
||||||
while (!m_controllers.empty())
|
ControllerInterface::Shutdown();
|
||||||
CloseGameController(m_controllers.begin()->first);
|
|
||||||
|
|
||||||
if (m_initialized)
|
while (!m_controllers.empty())
|
||||||
|
CloseGameController(m_controllers.begin()->joystick_id);
|
||||||
|
|
||||||
|
if (m_sdl_subsystem_initialized)
|
||||||
{
|
{
|
||||||
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC);
|
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC);
|
||||||
m_initialized = false;
|
m_sdl_subsystem_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_host_interface = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLControllerInterface::PumpSDLEvents()
|
void SDLControllerInterface::PollEvents()
|
||||||
{
|
{
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
@ -90,244 +90,140 @@ bool SDLControllerInterface::ProcessSDLEvent(const SDL_Event* event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System* SDLControllerInterface::GetSystem() const
|
SDLControllerInterface::ControllerDataVector::iterator
|
||||||
|
SDLControllerInterface::GetControllerDataForController(void* controller)
|
||||||
{
|
{
|
||||||
return m_host_interface->GetSystem();
|
return std::find_if(m_controllers.begin(), m_controllers.end(),
|
||||||
|
[controller](const ControllerData& cd) { return cd.controller == controller; });
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller* SDLControllerInterface::GetController(u32 slot) const
|
SDLControllerInterface::ControllerDataVector::iterator SDLControllerInterface::GetControllerDataForJoystickId(int id)
|
||||||
{
|
{
|
||||||
System* system = GetSystem();
|
return std::find_if(m_controllers.begin(), m_controllers.end(),
|
||||||
return system ? system->GetController(slot) : nullptr;
|
[id](const ControllerData& cd) { return cd.joystick_id == id; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLControllerInterface::SetHook(Hook::Callback callback)
|
SDLControllerInterface::ControllerDataVector::iterator SDLControllerInterface::GetControllerDataForPlayerId(int id)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(m_event_intercept_mutex);
|
return std::find_if(m_controllers.begin(), m_controllers.end(),
|
||||||
Assert(!m_event_intercept_callback);
|
[id](const ControllerData& cd) { return cd.player_id == id; });
|
||||||
m_event_intercept_callback = std::move(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLControllerInterface::ClearHook()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(m_event_intercept_mutex);
|
|
||||||
if (m_event_intercept_callback)
|
|
||||||
m_event_intercept_callback = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SDLControllerInterface::DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(m_event_intercept_mutex);
|
|
||||||
if (!m_event_intercept_callback)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const Hook ei{type, controller_index, button_or_axis_number, value};
|
|
||||||
const Hook::CallbackResult action = m_event_intercept_callback(ei);
|
|
||||||
if (action == Hook::CallbackResult::StopMonitoring)
|
|
||||||
m_event_intercept_callback = {};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLControllerInterface::OpenGameController(int index)
|
bool SDLControllerInterface::OpenGameController(int index)
|
||||||
{
|
{
|
||||||
if (m_controllers.find(index) != m_controllers.end())
|
|
||||||
CloseGameController(index);
|
|
||||||
|
|
||||||
SDL_GameController* gcontroller = SDL_GameControllerOpen(index);
|
SDL_GameController* gcontroller = SDL_GameControllerOpen(index);
|
||||||
if (!gcontroller)
|
SDL_Joystick* joystick = gcontroller ? SDL_GameControllerGetJoystick(gcontroller) : nullptr;
|
||||||
|
if (!gcontroller || !joystick)
|
||||||
{
|
{
|
||||||
Log_WarningPrintf("Failed to open controller %d", index);
|
Log_WarningPrintf("Failed to open controller %d", index);
|
||||||
|
if (gcontroller)
|
||||||
|
SDL_GameControllerClose(gcontroller);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log_InfoPrintf("Opened controller %d: %s", index, SDL_GameControllerName(gcontroller));
|
int player_index = SDL_GameControllerGetPlayerIndex(gcontroller);
|
||||||
|
int joystick_id = SDL_JoystickInstanceID(joystick);
|
||||||
|
|
||||||
|
Log_InfoPrintf("Opened controller %d (instance id %d, player id %d): %s", index, joystick_id, player_index,
|
||||||
|
SDL_GameControllerName(gcontroller));
|
||||||
|
|
||||||
ControllerData cd = {};
|
ControllerData cd = {};
|
||||||
cd.controller = gcontroller;
|
cd.controller = gcontroller;
|
||||||
|
cd.player_id = player_index;
|
||||||
|
cd.joystick_id = joystick_id;
|
||||||
|
|
||||||
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(gcontroller);
|
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick);
|
||||||
if (joystick)
|
if (SDL_HapticRumbleSupported(haptic) && SDL_HapticRumbleInit(haptic) == 0)
|
||||||
{
|
cd.haptic = haptic;
|
||||||
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick);
|
else if (haptic)
|
||||||
if (SDL_HapticRumbleSupported(haptic) && SDL_HapticRumbleInit(haptic) == 0)
|
SDL_HapticClose(haptic);
|
||||||
cd.haptic = haptic;
|
|
||||||
else
|
|
||||||
SDL_HapticClose(haptic);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cd.haptic)
|
if (cd.haptic)
|
||||||
Log_InfoPrintf("Rumble is supported on '%s'", SDL_GameControllerName(gcontroller));
|
Log_InfoPrintf("Rumble is supported on '%s'", SDL_GameControllerName(gcontroller));
|
||||||
else
|
else
|
||||||
Log_WarningPrintf("Rumble is not supported on '%s'", SDL_GameControllerName(gcontroller));
|
Log_WarningPrintf("Rumble is not supported on '%s'", SDL_GameControllerName(gcontroller));
|
||||||
|
|
||||||
m_controllers.emplace(index, cd);
|
m_controllers.push_back(std::move(cd));
|
||||||
|
OnControllerConnected(player_index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLControllerInterface::CloseGameControllers()
|
void SDLControllerInterface::CloseGameControllers()
|
||||||
{
|
{
|
||||||
while (!m_controllers.empty())
|
while (!m_controllers.empty())
|
||||||
CloseGameController(m_controllers.begin()->first);
|
CloseGameController(m_controllers.begin()->player_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLControllerInterface::CloseGameController(int index)
|
bool SDLControllerInterface::CloseGameController(int joystick_index)
|
||||||
{
|
{
|
||||||
auto it = m_controllers.find(index);
|
auto it = GetControllerDataForJoystickId(joystick_index);
|
||||||
if (it == m_controllers.end())
|
if (it == m_controllers.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (it->second.haptic)
|
const int player_index = it->player_id;
|
||||||
SDL_HapticClose(static_cast<SDL_Haptic*>(it->second.haptic));
|
|
||||||
|
|
||||||
SDL_GameControllerClose(static_cast<SDL_GameController*>(it->second.controller));
|
if (it->haptic)
|
||||||
|
SDL_HapticClose(static_cast<SDL_Haptic*>(it->haptic));
|
||||||
|
|
||||||
|
SDL_GameControllerClose(static_cast<SDL_GameController*>(it->controller));
|
||||||
m_controllers.erase(it);
|
m_controllers.erase(it);
|
||||||
|
|
||||||
|
OnControllerDisconnected(player_index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLControllerInterface::ClearControllerBindings()
|
void SDLControllerInterface::ClearBindings()
|
||||||
{
|
{
|
||||||
for (auto& it : m_controllers)
|
for (auto& it : m_controllers)
|
||||||
{
|
{
|
||||||
for (AxisCallback& ac : it.second.axis_mapping)
|
for (AxisCallback& ac : it.axis_mapping)
|
||||||
ac = {};
|
ac = {};
|
||||||
for (ButtonCallback& bc : it.second.button_mapping)
|
for (ButtonCallback& bc : it.button_mapping)
|
||||||
bc = {};
|
bc = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLControllerInterface::BindControllerAxis(int controller_index, int axis_number, AxisCallback callback)
|
bool SDLControllerInterface::BindControllerAxis(int controller_index, int axis_number, AxisCallback callback)
|
||||||
{
|
{
|
||||||
auto it = m_controllers.find(controller_index);
|
auto it = GetControllerDataForPlayerId(controller_index);
|
||||||
if (it == m_controllers.end())
|
if (it == m_controllers.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (axis_number < 0 || axis_number >= MAX_NUM_AXISES)
|
if (axis_number < 0 || axis_number >= MAX_NUM_AXISES)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
it->second.axis_mapping[axis_number] = std::move(callback);
|
it->axis_mapping[axis_number] = std::move(callback);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLControllerInterface::BindControllerButton(int controller_index, int button_number, ButtonCallback callback)
|
bool SDLControllerInterface::BindControllerButton(int controller_index, int button_number, ButtonCallback callback)
|
||||||
{
|
{
|
||||||
auto it = m_controllers.find(controller_index);
|
auto it = GetControllerDataForPlayerId(controller_index);
|
||||||
if (it == m_controllers.end())
|
if (it == m_controllers.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (button_number < 0 || button_number >= MAX_NUM_BUTTONS)
|
if (button_number < 0 || button_number >= MAX_NUM_BUTTONS)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
it->second.button_mapping[button_number] = std::move(callback);
|
it->button_mapping[button_number] = std::move(callback);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLControllerInterface::BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
|
bool SDLControllerInterface::BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
|
||||||
ButtonCallback callback)
|
ButtonCallback callback)
|
||||||
{
|
{
|
||||||
auto it = m_controllers.find(controller_index);
|
auto it = GetControllerDataForPlayerId(controller_index);
|
||||||
if (it == m_controllers.end())
|
if (it == m_controllers.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (axis_number < 0 || axis_number >= MAX_NUM_AXISES)
|
if (axis_number < 0 || axis_number >= MAX_NUM_AXISES)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
it->second.axis_button_mapping[axis_number][BoolToUInt8(direction)] = std::move(callback);
|
it->axis_button_mapping[axis_number][BoolToUInt8(direction)] = std::move(callback);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLControllerInterface::SetDefaultBindings()
|
|
||||||
{
|
|
||||||
ClearControllerBindings();
|
|
||||||
|
|
||||||
const ControllerType type = m_host_interface->GetSettings().controller_types[0];
|
|
||||||
if (type == ControllerType::None || m_controllers.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const int first_controller_index = m_controllers.begin()->first;
|
|
||||||
|
|
||||||
#define SET_AXIS_MAP(axis, name) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
std::optional<s32> code = Controller::GetAxisCodeByName(type, name); \
|
|
||||||
if (code) \
|
|
||||||
{ \
|
|
||||||
const s32 code_value = code.value(); \
|
|
||||||
BindControllerAxis(first_controller_index, axis, [this, code_value](float value) { \
|
|
||||||
Controller* controller = GetController(0); \
|
|
||||||
if (controller) \
|
|
||||||
controller->SetAxisState(code_value, value); \
|
|
||||||
}); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SET_BUTTON_MAP(button, name) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
std::optional<s32> code = Controller::GetButtonCodeByName(type, name); \
|
|
||||||
if (code) \
|
|
||||||
{ \
|
|
||||||
const s32 code_value = code.value(); \
|
|
||||||
BindControllerButton(first_controller_index, button, [this, code_value](bool pressed) { \
|
|
||||||
Controller* controller = GetController(0); \
|
|
||||||
if (controller) \
|
|
||||||
controller->SetButtonState(code_value, pressed); \
|
|
||||||
}); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SET_AXIS_BUTTON_MAP(axis, direction, name) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
std::optional<s32> code = Controller::GetButtonCodeByName(type, name); \
|
|
||||||
if (code) \
|
|
||||||
{ \
|
|
||||||
const s32 code_value = code.value(); \
|
|
||||||
BindControllerAxisToButton(first_controller_index, axis, direction, [this, code_value](bool pressed) { \
|
|
||||||
Controller* controller = GetController(0); \
|
|
||||||
if (controller) \
|
|
||||||
controller->SetButtonState(code_value, pressed); \
|
|
||||||
}); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
SET_AXIS_MAP(SDL_CONTROLLER_AXIS_LEFTX, "LeftX");
|
|
||||||
SET_AXIS_MAP(SDL_CONTROLLER_AXIS_LEFTY, "LeftY");
|
|
||||||
SET_AXIS_MAP(SDL_CONTROLLER_AXIS_RIGHTX, "RightX");
|
|
||||||
SET_AXIS_MAP(SDL_CONTROLLER_AXIS_RIGHTY, "RightY");
|
|
||||||
SET_AXIS_MAP(SDL_CONTROLLER_AXIS_TRIGGERLEFT, "LeftTrigger");
|
|
||||||
SET_AXIS_MAP(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, "RightTrigger");
|
|
||||||
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_UP, "Up");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_DOWN, "Down");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_LEFT, "Left");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, "Right");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_Y, "Triangle");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_A, "Cross");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_X, "Square");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_B, "Circle");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, "L1");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, "R1");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_LEFTSTICK, "L3");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_RIGHTSTICK, "R3");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_START, "Start");
|
|
||||||
SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_BACK, "Select");
|
|
||||||
|
|
||||||
// fallback axis -> button mappings
|
|
||||||
SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_LEFTX, false, "Left");
|
|
||||||
SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_LEFTX, true, "Right");
|
|
||||||
SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_LEFTY, false, "Up");
|
|
||||||
SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_LEFTY, true, "Down");
|
|
||||||
SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_TRIGGERLEFT, true, "L2");
|
|
||||||
SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, true, "R2");
|
|
||||||
|
|
||||||
#undef SET_AXIS_MAP
|
|
||||||
#undef SET_BUTTON_MAP
|
|
||||||
#undef SET_AXIS_BUTTON_MAP
|
|
||||||
|
|
||||||
// TODO: L2/R2 -> buttons
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev)
|
bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev)
|
||||||
{
|
{
|
||||||
// TODO: Make deadzone customizable.
|
// TODO: Make deadzone customizable.
|
||||||
|
@ -339,12 +235,11 @@ bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev)
|
||||||
if (DoEventHook(Hook::Type::Axis, ev->caxis.which, ev->caxis.axis, value))
|
if (DoEventHook(Hook::Type::Axis, ev->caxis.which, ev->caxis.axis, value))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto it = m_controllers.find(ev->caxis.which);
|
auto it = GetControllerDataForJoystickId(ev->caxis.which);
|
||||||
if (it == m_controllers.end())
|
if (it == m_controllers.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const ControllerData& cd = it->second;
|
const AxisCallback& cb = it->axis_mapping[ev->caxis.axis];
|
||||||
const AxisCallback& cb = cd.axis_mapping[ev->caxis.axis];
|
|
||||||
if (cb)
|
if (cb)
|
||||||
{
|
{
|
||||||
cb(value);
|
cb(value);
|
||||||
|
@ -354,8 +249,8 @@ bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev)
|
||||||
// set the other direction to false so large movements don't leave the opposite on
|
// set the other direction to false so large movements don't leave the opposite on
|
||||||
const bool outside_deadzone = (std::abs(value) >= deadzone);
|
const bool outside_deadzone = (std::abs(value) >= deadzone);
|
||||||
const bool positive = (value >= 0.0f);
|
const bool positive = (value >= 0.0f);
|
||||||
const ButtonCallback& other_button_cb = cd.axis_button_mapping[ev->caxis.axis][BoolToUInt8(!positive)];
|
const ButtonCallback& other_button_cb = it->axis_button_mapping[ev->caxis.axis][BoolToUInt8(!positive)];
|
||||||
const ButtonCallback& button_cb = cd.axis_button_mapping[ev->caxis.axis][BoolToUInt8(positive)];
|
const ButtonCallback& button_cb = it->axis_button_mapping[ev->caxis.axis][BoolToUInt8(positive)];
|
||||||
if (button_cb)
|
if (button_cb)
|
||||||
{
|
{
|
||||||
button_cb(outside_deadzone);
|
button_cb(outside_deadzone);
|
||||||
|
@ -383,11 +278,11 @@ bool SDLControllerInterface::HandleControllerButtonEvent(const SDL_Event* ev)
|
||||||
if (DoEventHook(Hook::Type::Button, ev->cbutton.which, ev->cbutton.button, pressed ? 1.0f : 0.0f))
|
if (DoEventHook(Hook::Type::Button, ev->cbutton.which, ev->cbutton.button, pressed ? 1.0f : 0.0f))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto it = m_controllers.find(ev->caxis.which);
|
auto it = GetControllerDataForJoystickId(ev->caxis.which);
|
||||||
if (it == m_controllers.end())
|
if (it == m_controllers.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const ButtonCallback& cb = it->second.button_mapping[ev->cbutton.button];
|
const ButtonCallback& cb = it->button_mapping[ev->cbutton.button];
|
||||||
if (!cb)
|
if (!cb)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -397,14 +292,14 @@ bool SDLControllerInterface::HandleControllerButtonEvent(const SDL_Event* ev)
|
||||||
|
|
||||||
void SDLControllerInterface::UpdateControllerRumble()
|
void SDLControllerInterface::UpdateControllerRumble()
|
||||||
{
|
{
|
||||||
for (auto& it : m_controllers)
|
for (auto& cd : m_controllers)
|
||||||
{
|
{
|
||||||
ControllerData& cd = it.second;
|
// TODO: FIXME proper binding
|
||||||
if (!cd.haptic)
|
if (!cd.haptic || cd.player_id < 0 || cd.player_id >= 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float new_strength = 0.0f;
|
float new_strength = 0.0f;
|
||||||
Controller* controller = GetController(cd.controller_index);
|
Controller* controller = GetController(cd.player_id);
|
||||||
if (controller)
|
if (controller)
|
||||||
{
|
{
|
||||||
const u32 motor_count = controller->GetVibrationMotorCount();
|
const u32 motor_count = controller->GetVibrationMotorCount();
|
||||||
|
|
|
@ -1,92 +1,43 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "core/types.h"
|
#include "core/types.h"
|
||||||
|
#include "controller_interface.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
class HostInterface;
|
|
||||||
class System;
|
|
||||||
class Controller;
|
|
||||||
|
|
||||||
union SDL_Event;
|
union SDL_Event;
|
||||||
|
|
||||||
class SDLControllerInterface
|
class SDLControllerInterface final : public ControllerInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum : int
|
|
||||||
{
|
|
||||||
MAX_NUM_AXISES = 7,
|
|
||||||
MAX_NUM_BUTTONS = 15
|
|
||||||
};
|
|
||||||
|
|
||||||
using AxisCallback = std::function<void(float value)>;
|
|
||||||
using ButtonCallback = std::function<void(bool pressed)>;
|
|
||||||
|
|
||||||
SDLControllerInterface();
|
SDLControllerInterface();
|
||||||
~SDLControllerInterface();
|
~SDLControllerInterface();
|
||||||
|
|
||||||
bool Initialize(HostInterface* host_interface);
|
bool Initialize(CommonHostInterface* host_interface) override;
|
||||||
void Shutdown();
|
void Shutdown() override;
|
||||||
|
|
||||||
// Removes all bindings. Call before setting new bindings.
|
// Removes all bindings. Call before setting new bindings.
|
||||||
void ClearControllerBindings();
|
void ClearBindings() override;
|
||||||
|
|
||||||
// Binding to events. If a binding for this axis/button already exists, returns false.
|
// Binding to events. If a binding for this axis/button already exists, returns false.
|
||||||
bool BindControllerAxis(int controller_index, int axis_number, AxisCallback callback);
|
bool BindControllerAxis(int controller_index, int axis_number, AxisCallback callback) override;
|
||||||
bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback);
|
bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) override;
|
||||||
bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction, ButtonCallback callback);
|
bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction, ButtonCallback callback) override;
|
||||||
|
|
||||||
// Default bindings, used by SDL frontend.
|
void PollEvents() override;
|
||||||
void SetDefaultBindings();
|
|
||||||
|
|
||||||
void PumpSDLEvents();
|
|
||||||
|
|
||||||
bool ProcessSDLEvent(const SDL_Event* event);
|
bool ProcessSDLEvent(const SDL_Event* event);
|
||||||
|
|
||||||
void UpdateControllerRumble();
|
void UpdateControllerRumble() override;
|
||||||
|
|
||||||
// Input monitoring for external access.
|
|
||||||
struct Hook
|
|
||||||
{
|
|
||||||
enum class Type
|
|
||||||
{
|
|
||||||
Axis,
|
|
||||||
Button
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class CallbackResult
|
|
||||||
{
|
|
||||||
StopMonitoring,
|
|
||||||
ContinueMonitoring
|
|
||||||
};
|
|
||||||
|
|
||||||
using Callback = std::function<CallbackResult(const Hook& ei)>;
|
|
||||||
|
|
||||||
Type type;
|
|
||||||
int controller_index;
|
|
||||||
int button_or_axis_number;
|
|
||||||
float value; // 0/1 for buttons, -1..1 for axises
|
|
||||||
};
|
|
||||||
void SetHook(Hook::Callback callback);
|
|
||||||
void ClearHook();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
System* GetSystem() const;
|
|
||||||
Controller* GetController(u32 slot) const;
|
|
||||||
bool DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value);
|
|
||||||
|
|
||||||
bool OpenGameController(int index);
|
|
||||||
bool CloseGameController(int index);
|
|
||||||
void CloseGameControllers();
|
|
||||||
bool HandleControllerAxisEvent(const SDL_Event* event);
|
|
||||||
bool HandleControllerButtonEvent(const SDL_Event* event);
|
|
||||||
|
|
||||||
struct ControllerData
|
struct ControllerData
|
||||||
{
|
{
|
||||||
void* controller;
|
void* controller;
|
||||||
void* haptic;
|
void* haptic;
|
||||||
u32 controller_index;
|
int joystick_id;
|
||||||
|
int player_id;
|
||||||
float last_rumble_strength;
|
float last_rumble_strength;
|
||||||
|
|
||||||
std::array<AxisCallback, MAX_NUM_AXISES> axis_mapping;
|
std::array<AxisCallback, MAX_NUM_AXISES> axis_mapping;
|
||||||
|
@ -94,14 +45,22 @@ private:
|
||||||
std::array<std::array<ButtonCallback, 2>, MAX_NUM_AXISES> axis_button_mapping;
|
std::array<std::array<ButtonCallback, 2>, MAX_NUM_AXISES> axis_button_mapping;
|
||||||
};
|
};
|
||||||
|
|
||||||
HostInterface* m_host_interface = nullptr;
|
using ControllerDataVector = std::vector<ControllerData>;
|
||||||
|
|
||||||
std::map<int, ControllerData> m_controllers;
|
ControllerDataVector::iterator GetControllerDataForController(void* controller);
|
||||||
|
ControllerDataVector::iterator GetControllerDataForJoystickId(int id);
|
||||||
|
ControllerDataVector::iterator GetControllerDataForPlayerId(int id);
|
||||||
|
|
||||||
|
bool OpenGameController(int index);
|
||||||
|
bool CloseGameController(int joystick_index);
|
||||||
|
void CloseGameControllers();
|
||||||
|
bool HandleControllerAxisEvent(const SDL_Event* event);
|
||||||
|
bool HandleControllerButtonEvent(const SDL_Event* event);
|
||||||
|
|
||||||
|
ControllerDataVector m_controllers;
|
||||||
|
|
||||||
std::mutex m_event_intercept_mutex;
|
std::mutex m_event_intercept_mutex;
|
||||||
Hook::Callback m_event_intercept_callback;
|
Hook::Callback m_event_intercept_callback;
|
||||||
|
|
||||||
bool m_initialized = false;
|
bool m_sdl_subsystem_initialized = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern SDLControllerInterface g_sdl_controller_interface;
|
|
||||||
|
|
Loading…
Reference in New Issue