ControllerInterface: Add XInput controller backend
This commit is contained in:
parent
62d0ec5584
commit
3c46f7b44c
|
@ -11,6 +11,7 @@ A "BIOS" ROM image is required to to start the emulator and to play games. You c
|
||||||
|
|
||||||
## Latest News
|
## Latest News
|
||||||
|
|
||||||
|
- 2020/08/22: XInput controller backend added.
|
||||||
- 2020/08/20: Per-game setting overrides added. Mostly for compatibility, but some options are customizable.
|
- 2020/08/20: Per-game setting overrides added. Mostly for compatibility, but some options are customizable.
|
||||||
- 2020/08/19: CPU PGXP mode added. It is very slow and incompatible with the recompiler, only use for games which need it.
|
- 2020/08/19: CPU PGXP mode added. It is very slow and incompatible with the recompiler, only use for games which need it.
|
||||||
- 2020/08/15: Playlist support/single memcard for multi-disc games in Qt frontend added.
|
- 2020/08/15: Playlist support/single memcard for multi-disc games in Qt frontend added.
|
||||||
|
|
|
@ -364,7 +364,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
@ -386,7 +386,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
@ -408,7 +408,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
@ -432,7 +432,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
@ -457,7 +457,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
@ -481,7 +481,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
@ -506,7 +506,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
@ -530,7 +530,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "generalsettingswidget.h"
|
#include "generalsettingswidget.h"
|
||||||
#include "autoupdaterdialog.h"
|
#include "autoupdaterdialog.h"
|
||||||
|
#include "frontend-common/controller_interface.h"
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
#include "settingwidgetbinder.h"
|
#include "settingwidgetbinder.h"
|
||||||
|
|
||||||
|
@ -8,6 +9,12 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
|
||||||
{
|
{
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < static_cast<u32>(ControllerInterface::Backend::Count); i++)
|
||||||
|
{
|
||||||
|
m_ui.controllerBackend->addItem(qApp->translate(
|
||||||
|
"ControllerInterface", ControllerInterface::GetBackendName(static_cast<ControllerInterface::Backend>(i))));
|
||||||
|
}
|
||||||
|
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pauseOnStart, "Main", "StartPaused", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pauseOnStart, "Main", "StartPaused", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.startFullscreen, "Main", "StartFullscreen",
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.startFullscreen, "Main", "StartFullscreen",
|
||||||
false);
|
false);
|
||||||
|
@ -32,6 +39,9 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
|
||||||
"IncreaseTimerResolution", true);
|
"IncreaseTimerResolution", true);
|
||||||
SettingWidgetBinder::BindWidgetToNormalizedSetting(m_host_interface, m_ui.emulationSpeed, "Main", "EmulationSpeed",
|
SettingWidgetBinder::BindWidgetToNormalizedSetting(m_host_interface, m_ui.emulationSpeed, "Main", "EmulationSpeed",
|
||||||
100.0f, 1.0f);
|
100.0f, 1.0f);
|
||||||
|
SettingWidgetBinder::BindWidgetToEnumSetting(
|
||||||
|
m_host_interface, m_ui.controllerBackend, "Main", "ControllerBackend", &ControllerInterface::ParseBackendName,
|
||||||
|
&ControllerInterface::GetBackendName, ControllerInterface::GetDefaultBackend());
|
||||||
|
|
||||||
connect(m_ui.enableSpeedLimiter, &QCheckBox::stateChanged, this,
|
connect(m_ui.enableSpeedLimiter, &QCheckBox::stateChanged, this,
|
||||||
&GeneralSettingsWidget::onEnableSpeedLimiterStateChanged);
|
&GeneralSettingsWidget::onEnableSpeedLimiterStateChanged);
|
||||||
|
@ -87,6 +97,11 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.showSpeed, tr("Show Speed"), tr("Unchecked"),
|
m_ui.showSpeed, tr("Show Speed"), tr("Unchecked"),
|
||||||
tr("Shows the current emulation speed of the system in the top-right corner of the display as a percentage."));
|
tr("Shows the current emulation speed of the system in the top-right corner of the display as a percentage."));
|
||||||
|
dialog->registerWidgetHelp(m_ui.controllerBackend, tr("Controller Backend"),
|
||||||
|
qApp->translate("ControllerInterface", ControllerInterface::GetBackendName(
|
||||||
|
ControllerInterface::GetDefaultBackend())),
|
||||||
|
tr("Determines the backend which is used for controller input. Windows users may prefer "
|
||||||
|
"to use XInput over SDL2 for compatibility."));
|
||||||
|
|
||||||
// Since this one is compile-time selected, we don't put it in the .ui file.
|
// Since this one is compile-time selected, we don't put it in the .ui file.
|
||||||
int current_col = 1;
|
int current_col = 1;
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>502</width>
|
<width>652</width>
|
||||||
<height>358</height>
|
<height>483</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -190,6 +190,29 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Miscellaneous</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Controller Backend:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="controllerBackend"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
|
@ -222,7 +222,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
@ -244,7 +244,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
@ -266,7 +266,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
@ -291,7 +291,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
@ -317,7 +317,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
@ -341,7 +341,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
@ -366,7 +366,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
@ -390,7 +390,7 @@
|
||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WITH_DISCORD_PRESENCE=1;WITH_SDL2=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
|
|
@ -277,11 +277,6 @@ std::unique_ptr<AudioStream> SDLHostInterface::CreateAudioStream(AudioBackend ba
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ControllerInterface> SDLHostInterface::CreateControllerInterface()
|
|
||||||
{
|
|
||||||
return std::make_unique<SDLControllerInterface>();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<CommonHostInterface::HostKeyCode> SDLHostInterface::GetHostKeyCode(const std::string_view key_code) const
|
std::optional<CommonHostInterface::HostKeyCode> SDLHostInterface::GetHostKeyCode(const std::string_view key_code) const
|
||||||
{
|
{
|
||||||
const std::optional<u32> code = SDLKeyNames::ParseKeyString(key_code);
|
const std::optional<u32> code = SDLKeyNames::ParseKeyString(key_code);
|
||||||
|
|
|
@ -46,7 +46,6 @@ protected:
|
||||||
bool AcquireHostDisplay() override;
|
bool AcquireHostDisplay() override;
|
||||||
void ReleaseHostDisplay() override;
|
void ReleaseHostDisplay() override;
|
||||||
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
|
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
|
||||||
std::unique_ptr<ControllerInterface> CreateControllerInterface() override;
|
|
||||||
|
|
||||||
void OnSystemCreated() override;
|
void OnSystemCreated() override;
|
||||||
void OnSystemPaused(bool paused) override;
|
void OnSystemPaused(bool paused) override;
|
||||||
|
|
|
@ -23,6 +23,8 @@ if(WIN32)
|
||||||
target_sources(frontend-common PRIVATE
|
target_sources(frontend-common PRIVATE
|
||||||
d3d11_host_display.cpp
|
d3d11_host_display.cpp
|
||||||
d3d11_host_display.h
|
d3d11_host_display.h
|
||||||
|
xinput_controller_interface.cpp
|
||||||
|
xinput_controller_interface.h
|
||||||
)
|
)
|
||||||
target_link_libraries(frontend-common PRIVATE d3d11.lib dxgi.lib)
|
target_link_libraries(frontend-common PRIVATE d3d11.lib dxgi.lib)
|
||||||
endif()
|
endif()
|
||||||
|
@ -36,7 +38,7 @@ if(SDL2_FOUND AND NOT BUILD_LIBRETRO_CORE)
|
||||||
sdl_initializer.cpp
|
sdl_initializer.cpp
|
||||||
sdl_initializer.h
|
sdl_initializer.h
|
||||||
)
|
)
|
||||||
target_compile_definitions(frontend-common PRIVATE "WITH_SDL2=1")
|
target_compile_definitions(frontend-common PUBLIC "WITH_SDL2=1")
|
||||||
target_include_directories(frontend-common PRIVATE ${SDL2_INCLUDE_DIRS})
|
target_include_directories(frontend-common PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||||
target_link_libraries(frontend-common PRIVATE ${SDL2_LIBRARIES})
|
target_link_libraries(frontend-common PRIVATE ${SDL2_LIBRARIES})
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
#ifdef WITH_SDL2
|
#ifdef WITH_SDL2
|
||||||
#include "sdl_audio_stream.h"
|
#include "sdl_audio_stream.h"
|
||||||
#include "sdl_controller_interface.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_DISCORD_PRESENCE
|
#ifdef WITH_DISCORD_PRESENCE
|
||||||
|
@ -81,17 +80,7 @@ bool CommonHostInterface::Initialize()
|
||||||
RegisterSaveStateHotkeys();
|
RegisterSaveStateHotkeys();
|
||||||
RegisterAudioHotkeys();
|
RegisterAudioHotkeys();
|
||||||
|
|
||||||
m_controller_interface = CreateControllerInterface();
|
UpdateControllerInterface();
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,14 +448,45 @@ std::unique_ptr<AudioStream> CommonHostInterface::CreateAudioStream(AudioBackend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ControllerInterface> CommonHostInterface::CreateControllerInterface()
|
void CommonHostInterface::UpdateControllerInterface()
|
||||||
{
|
{
|
||||||
// In the future we might want to use different controller interfaces.
|
const std::string backend_str = GetStringSettingValue(
|
||||||
#ifdef WITH_SDL2
|
"Main", "ControllerBackend", ControllerInterface::GetBackendName(ControllerInterface::GetDefaultBackend()));
|
||||||
return std::make_unique<SDLControllerInterface>();
|
const std::optional<ControllerInterface::Backend> new_backend =
|
||||||
#else
|
ControllerInterface::ParseBackendName(backend_str.c_str());
|
||||||
return nullptr;
|
const ControllerInterface::Backend current_backend =
|
||||||
#endif
|
(m_controller_interface ? m_controller_interface->GetBackend() : ControllerInterface::Backend::None);
|
||||||
|
if (new_backend == current_backend)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_controller_interface)
|
||||||
|
{
|
||||||
|
m_controller_interface->Shutdown();
|
||||||
|
m_controller_interface.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new_backend.has_value())
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Invalid controller interface type: '%s'", backend_str.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_backend == ControllerInterface::Backend::None)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("No controller interface created, controller bindings are not possible.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_controller_interface = ControllerInterface::Create(new_backend.value());
|
||||||
|
if (!m_controller_interface || !m_controller_interface->Initialize(this))
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Failed to initialize controller interface, bindings are not possible.");
|
||||||
|
if (m_controller_interface)
|
||||||
|
{
|
||||||
|
m_controller_interface->Shutdown();
|
||||||
|
m_controller_interface.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommonHostInterface::LoadState(bool global, s32 slot)
|
bool CommonHostInterface::LoadState(bool global, s32 slot)
|
||||||
|
@ -1831,6 +1851,9 @@ void CommonHostInterface::SetDefaultSettings(SettingsInterface& si)
|
||||||
si.SetStringValue("Hotkeys", "DecreaseResolutionScale", "Keyboard/PageDown");
|
si.SetStringValue("Hotkeys", "DecreaseResolutionScale", "Keyboard/PageDown");
|
||||||
si.SetStringValue("Hotkeys", "ToggleSoftwareRendering", "Keyboard/End");
|
si.SetStringValue("Hotkeys", "ToggleSoftwareRendering", "Keyboard/End");
|
||||||
|
|
||||||
|
si.SetStringValue("Main", "ControllerBackend",
|
||||||
|
ControllerInterface::GetBackendName(ControllerInterface::GetDefaultBackend()));
|
||||||
|
|
||||||
#ifdef WITH_DISCORD_PRESENCE
|
#ifdef WITH_DISCORD_PRESENCE
|
||||||
si.SetBoolValue("Main", "EnableDiscordPresence", false);
|
si.SetBoolValue("Main", "EnableDiscordPresence", false);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1854,6 +1877,8 @@ void CommonHostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
{
|
{
|
||||||
HostInterface::CheckForSettingsChanges(old_settings);
|
HostInterface::CheckForSettingsChanges(old_settings);
|
||||||
|
|
||||||
|
UpdateControllerInterface();
|
||||||
|
|
||||||
if (System::IsValid())
|
if (System::IsValid())
|
||||||
{
|
{
|
||||||
if (g_settings.audio_backend != old_settings.audio_backend ||
|
if (g_settings.audio_backend != old_settings.audio_backend ||
|
||||||
|
|
|
@ -182,7 +182,7 @@ protected:
|
||||||
virtual bool SetFullscreen(bool enabled);
|
virtual bool SetFullscreen(bool enabled);
|
||||||
|
|
||||||
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 UpdateControllerInterface();
|
||||||
|
|
||||||
virtual void OnSystemCreated() override;
|
virtual void OnSystemCreated() override;
|
||||||
virtual void OnSystemPaused(bool paused);
|
virtual void OnSystemPaused(bool paused);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "controller_interface.h"
|
#include "controller_interface.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
#include "core/controller.h"
|
#include "core/controller.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -79,3 +80,61 @@ bool ControllerInterface::BindControllerAxisToButton(int controller_index, int a
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<const char*, static_cast<u32>(ControllerInterface::Backend::Count)> s_backend_names = {{
|
||||||
|
TRANSLATABLE("ControllerInterface", "None"),
|
||||||
|
#ifdef WITH_SDL2
|
||||||
|
TRANSLATABLE("ControllerInterface", "SDL"),
|
||||||
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
TRANSLATABLE("ControllerInterface", "XInput"),
|
||||||
|
#endif
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::optional<ControllerInterface::Backend> ControllerInterface::ParseBackendName(const char* name)
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < static_cast<u32>(s_backend_names.size()); i++)
|
||||||
|
{
|
||||||
|
if (StringUtil::Strcasecmp(name, s_backend_names[i]) == 0)
|
||||||
|
return static_cast<Backend>(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ControllerInterface::GetBackendName(Backend type)
|
||||||
|
{
|
||||||
|
return s_backend_names[static_cast<u32>(type)];
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerInterface::Backend ControllerInterface::GetDefaultBackend()
|
||||||
|
{
|
||||||
|
#ifdef WITH_SDL2
|
||||||
|
return Backend::SDL;
|
||||||
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
return Backend::XInput;
|
||||||
|
#else
|
||||||
|
return Backend::None;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_SDL2
|
||||||
|
#include "sdl_controller_interface.h"
|
||||||
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "xinput_controller_interface.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::unique_ptr<ControllerInterface> ControllerInterface::Create(Backend type)
|
||||||
|
{
|
||||||
|
#ifdef WITH_SDL2
|
||||||
|
if (type == Backend::SDL)
|
||||||
|
return std::make_unique<SDLControllerInterface>();
|
||||||
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
if (type == Backend::XInput)
|
||||||
|
return std::make_unique<XInputControllerInterface>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "core/types.h"
|
#include "core/types.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
@ -12,6 +13,18 @@ class Controller;
|
||||||
class ControllerInterface
|
class ControllerInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class Backend
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
#ifdef WITH_SDL2
|
||||||
|
SDL,
|
||||||
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
XInput,
|
||||||
|
#endif
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
enum : int
|
enum : int
|
||||||
{
|
{
|
||||||
MAX_NUM_AXISES = 7,
|
MAX_NUM_AXISES = 7,
|
||||||
|
@ -24,6 +37,12 @@ public:
|
||||||
ControllerInterface();
|
ControllerInterface();
|
||||||
virtual ~ControllerInterface();
|
virtual ~ControllerInterface();
|
||||||
|
|
||||||
|
static std::optional<Backend> ParseBackendName(const char* name);
|
||||||
|
static const char* GetBackendName(Backend type);
|
||||||
|
static Backend GetDefaultBackend();
|
||||||
|
static std::unique_ptr<ControllerInterface> Create(Backend type);
|
||||||
|
|
||||||
|
virtual Backend GetBackend() const = 0;
|
||||||
virtual bool Initialize(CommonHostInterface* host_interface);
|
virtual bool Initialize(CommonHostInterface* host_interface);
|
||||||
virtual void Shutdown();
|
virtual void Shutdown();
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
<ClCompile Include="sdl_controller_interface.cpp" />
|
<ClCompile Include="sdl_controller_interface.cpp" />
|
||||||
<ClCompile Include="sdl_initializer.cpp" />
|
<ClCompile Include="sdl_initializer.cpp" />
|
||||||
<ClCompile Include="vulkan_host_display.cpp" />
|
<ClCompile Include="vulkan_host_display.cpp" />
|
||||||
|
<ClCompile Include="xinput_controller_interface.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="common_host_interface.h" />
|
<ClInclude Include="common_host_interface.h" />
|
||||||
|
@ -90,6 +91,7 @@
|
||||||
<ClInclude Include="sdl_controller_interface.h" />
|
<ClInclude Include="sdl_controller_interface.h" />
|
||||||
<ClInclude Include="sdl_initializer.h" />
|
<ClInclude Include="sdl_initializer.h" />
|
||||||
<ClInclude Include="vulkan_host_display.h" />
|
<ClInclude Include="vulkan_host_display.h" />
|
||||||
|
<ClInclude Include="xinput_controller_interface.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="font_roboto_regular.inl" />
|
<None Include="font_roboto_regular.inl" />
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<ClCompile Include="vulkan_host_display.cpp" />
|
<ClCompile Include="vulkan_host_display.cpp" />
|
||||||
<ClCompile Include="d3d11_host_display.cpp" />
|
<ClCompile Include="d3d11_host_display.cpp" />
|
||||||
<ClCompile Include="opengl_host_display.cpp" />
|
<ClCompile Include="opengl_host_display.cpp" />
|
||||||
|
<ClCompile Include="xinput_controller_interface.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="icon.h" />
|
<ClInclude Include="icon.h" />
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
<ClInclude Include="vulkan_host_display.h" />
|
<ClInclude Include="vulkan_host_display.h" />
|
||||||
<ClInclude Include="d3d11_host_display.h" />
|
<ClInclude Include="d3d11_host_display.h" />
|
||||||
<ClInclude Include="opengl_host_display.h" />
|
<ClInclude Include="opengl_host_display.h" />
|
||||||
|
<ClInclude Include="xinput_controller_interface.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="font_roboto_regular.inl" />
|
<None Include="font_roboto_regular.inl" />
|
||||||
|
|
|
@ -17,6 +17,11 @@ SDLControllerInterface::~SDLControllerInterface()
|
||||||
Assert(m_controllers.empty());
|
Assert(m_controllers.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ControllerInterface::Backend SDLControllerInterface::GetBackend() const
|
||||||
|
{
|
||||||
|
return ControllerInterface::Backend::SDL;
|
||||||
|
}
|
||||||
|
|
||||||
bool SDLControllerInterface::Initialize(CommonHostInterface* host_interface)
|
bool SDLControllerInterface::Initialize(CommonHostInterface* host_interface)
|
||||||
{
|
{
|
||||||
if (!ControllerInterface::Initialize(host_interface))
|
if (!ControllerInterface::Initialize(host_interface))
|
||||||
|
|
|
@ -14,6 +14,7 @@ public:
|
||||||
SDLControllerInterface();
|
SDLControllerInterface();
|
||||||
~SDLControllerInterface();
|
~SDLControllerInterface();
|
||||||
|
|
||||||
|
Backend GetBackend() const override;
|
||||||
bool Initialize(CommonHostInterface* host_interface) override;
|
bool Initialize(CommonHostInterface* host_interface) override;
|
||||||
void Shutdown() override;
|
void Shutdown() override;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,318 @@
|
||||||
|
#include "xinput_controller_interface.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/file_system.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "core/controller.h"
|
||||||
|
#include "core/host_interface.h"
|
||||||
|
#include "core/system.h"
|
||||||
|
#include <cmath>
|
||||||
|
Log_SetChannel(XInputControllerInterface);
|
||||||
|
|
||||||
|
XInputControllerInterface::XInputControllerInterface() = default;
|
||||||
|
|
||||||
|
XInputControllerInterface::~XInputControllerInterface()
|
||||||
|
{
|
||||||
|
if (m_xinput_module)
|
||||||
|
FreeLibrary(m_xinput_module);
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerInterface::Backend XInputControllerInterface::GetBackend() const
|
||||||
|
{
|
||||||
|
return ControllerInterface::Backend::XInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XInputControllerInterface::Initialize(CommonHostInterface* host_interface)
|
||||||
|
{
|
||||||
|
m_xinput_module = LoadLibraryA("xinput1_4.dll");
|
||||||
|
if (!m_xinput_module)
|
||||||
|
{
|
||||||
|
m_xinput_module = LoadLibraryA("xinput9_1_0.dll");
|
||||||
|
if (!m_xinput_module)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to load XInput module.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the hidden version of XInputGetState(), which lets us query the guide button.
|
||||||
|
m_xinput_get_state =
|
||||||
|
reinterpret_cast<decltype(m_xinput_get_state)>(GetProcAddress(m_xinput_module, reinterpret_cast<LPCSTR>(100)));
|
||||||
|
if (!m_xinput_get_state)
|
||||||
|
reinterpret_cast<decltype(m_xinput_get_state)>(GetProcAddress(m_xinput_module, "XInputGetState"));
|
||||||
|
m_xinput_get_capabilities =
|
||||||
|
reinterpret_cast<decltype(m_xinput_get_capabilities)>(GetProcAddress(m_xinput_module, "XInputGetCapabilities"));
|
||||||
|
m_xinput_set_state =
|
||||||
|
reinterpret_cast<decltype(m_xinput_set_state)>(GetProcAddress(m_xinput_module, "XInputSetState"));
|
||||||
|
if (!m_xinput_get_state || !m_xinput_get_capabilities || !m_xinput_set_state)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to get XInput function pointers.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ControllerInterface::Initialize(host_interface))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XInputControllerInterface::Shutdown()
|
||||||
|
{
|
||||||
|
ControllerInterface::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XInputControllerInterface::PollEvents()
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < XUSER_MAX_COUNT; i++)
|
||||||
|
{
|
||||||
|
XINPUT_STATE new_state;
|
||||||
|
const DWORD result = m_xinput_get_state(i, &new_state);
|
||||||
|
ControllerData& cd = m_controllers[i];
|
||||||
|
if (result == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (!cd.connected)
|
||||||
|
{
|
||||||
|
cd.connected = true;
|
||||||
|
UpdateCapabilities(i);
|
||||||
|
OnControllerConnected(static_cast<int>(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckForStateChanges(i, new_state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (result != ERROR_DEVICE_NOT_CONNECTED)
|
||||||
|
Log_WarningPrintf("XInputGetState(%u) failed: 0x%08X / 0x%08X", i, result, GetLastError());
|
||||||
|
|
||||||
|
if (cd.connected)
|
||||||
|
{
|
||||||
|
cd.connected = false;
|
||||||
|
cd.last_state = {};
|
||||||
|
OnControllerDisconnected(static_cast<int>(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XInputControllerInterface::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state)
|
||||||
|
{
|
||||||
|
ControllerData& cd = m_controllers[index];
|
||||||
|
if (new_state.dwPacketNumber == cd.last_state.dwPacketNumber)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cd.last_state.dwPacketNumber = new_state.dwPacketNumber;
|
||||||
|
|
||||||
|
XINPUT_GAMEPAD& ogp = cd.last_state.Gamepad;
|
||||||
|
const XINPUT_GAMEPAD& ngp = new_state.Gamepad;
|
||||||
|
if (ogp.sThumbLX != ngp.sThumbLX)
|
||||||
|
{
|
||||||
|
HandleAxisEvent(index, Axis::LeftX, ngp.sThumbLX);
|
||||||
|
ogp.sThumbLX = ngp.sThumbLX;
|
||||||
|
}
|
||||||
|
if (ogp.sThumbLY != ngp.sThumbLY)
|
||||||
|
{
|
||||||
|
HandleAxisEvent(index, Axis::LeftY, -ngp.sThumbLY);
|
||||||
|
ogp.sThumbLY = ngp.sThumbLY;
|
||||||
|
}
|
||||||
|
if (ogp.sThumbRX != ngp.sThumbRX)
|
||||||
|
{
|
||||||
|
HandleAxisEvent(index, Axis::RightX, ngp.sThumbRX);
|
||||||
|
ogp.sThumbRX = ngp.sThumbRX;
|
||||||
|
}
|
||||||
|
if (ogp.sThumbRY != ngp.sThumbRY)
|
||||||
|
{
|
||||||
|
HandleAxisEvent(index, Axis::RightY, -ngp.sThumbRY);
|
||||||
|
ogp.sThumbRY = ngp.sThumbRY;
|
||||||
|
}
|
||||||
|
if (ogp.bLeftTrigger != ngp.bLeftTrigger)
|
||||||
|
{
|
||||||
|
HandleAxisEvent(index, Axis::LeftTrigger, static_cast<s32>(ZeroExtend32(ngp.bLeftTrigger) << 8));
|
||||||
|
ogp.bLeftTrigger = ngp.bLeftTrigger;
|
||||||
|
}
|
||||||
|
if (ogp.bRightTrigger != ngp.bRightTrigger)
|
||||||
|
{
|
||||||
|
HandleAxisEvent(index, Axis::RightTrigger, static_cast<s32>(ZeroExtend32(ngp.bRightTrigger) << 8));
|
||||||
|
ogp.bRightTrigger = ngp.bRightTrigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<u16, NUM_BUTTONS> button_masks = {
|
||||||
|
{XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, XINPUT_GAMEPAD_BACK,
|
||||||
|
0x400 /* XINPUT_GAMEPAD_GUIDE */, XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
|
||||||
|
XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN,
|
||||||
|
XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT}};
|
||||||
|
|
||||||
|
const u16 old_button_bits = ogp.wButtons;
|
||||||
|
const u16 new_button_bits = ngp.wButtons;
|
||||||
|
if (old_button_bits != new_button_bits)
|
||||||
|
{
|
||||||
|
for (u32 button = 0; button < static_cast<u32>(button_masks.size()); button++)
|
||||||
|
{
|
||||||
|
const u16 button_mask = button_masks[button];
|
||||||
|
if ((old_button_bits & button_mask) != (new_button_bits & button_mask))
|
||||||
|
HandleButtonEvent(index, button, (new_button_bits & button_mask) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ogp.wButtons = ngp.wButtons;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XInputControllerInterface::UpdateCapabilities(u32 index)
|
||||||
|
{
|
||||||
|
ControllerData& cd = m_controllers[index];
|
||||||
|
|
||||||
|
XINPUT_CAPABILITIES caps = {};
|
||||||
|
m_xinput_get_capabilities(index, 0, &caps);
|
||||||
|
cd.supports_rumble = (caps.Flags & 0x0001 /* XINPUT_CAPS_FFB_SUPPORTED */);
|
||||||
|
|
||||||
|
Log_InfoPrintf("Controller %u: Rumble is %s", index, cd.supports_rumble ? "supported" : "not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
void XInputControllerInterface::ClearBindings()
|
||||||
|
{
|
||||||
|
for (auto& it : m_controllers)
|
||||||
|
{
|
||||||
|
for (AxisCallback& ac : it.axis_mapping)
|
||||||
|
ac = {};
|
||||||
|
for (ButtonCallback& bc : it.button_mapping)
|
||||||
|
bc = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XInputControllerInterface::BindControllerAxis(int controller_index, int axis_number, AxisCallback callback)
|
||||||
|
{
|
||||||
|
if (static_cast<u32>(controller_index) >= m_controllers.size() || !m_controllers[controller_index].connected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (axis_number < 0 || axis_number >= NUM_AXISES)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_controllers[controller_index].axis_mapping[axis_number] = std::move(callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XInputControllerInterface::BindControllerButton(int controller_index, int button_number, ButtonCallback callback)
|
||||||
|
{
|
||||||
|
if (static_cast<u32>(controller_index) >= m_controllers.size() || !m_controllers[controller_index].connected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (button_number < 0 || button_number >= NUM_BUTTONS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_controllers[controller_index].button_mapping[button_number] = std::move(callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XInputControllerInterface::BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
|
||||||
|
ButtonCallback callback)
|
||||||
|
{
|
||||||
|
if (static_cast<u32>(controller_index) >= m_controllers.size() || !m_controllers[controller_index].connected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (axis_number < 0 || axis_number >= MAX_NUM_AXISES)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_controllers[controller_index].axis_button_mapping[axis_number][BoolToUInt8(direction)] = 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);
|
||||||
|
Log_DevPrintf("controller %u axis %u %d %f", index, static_cast<u32>(axis), value, f_value);
|
||||||
|
DebugAssert(index < XUSER_MAX_COUNT);
|
||||||
|
|
||||||
|
if (DoEventHook(Hook::Type::Axis, index, static_cast<u32>(axis), f_value))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const AxisCallback& cb = m_controllers[index].axis_mapping[static_cast<u32>(axis)];
|
||||||
|
if (cb)
|
||||||
|
{
|
||||||
|
// Apply axis scaling only when controller axis is mapped to an axis
|
||||||
|
cb(std::clamp(m_controllers[index].axis_scale * f_value, -1.0f, 1.0f));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the other direction to false so large movements don't leave the opposite on
|
||||||
|
const bool outside_deadzone = (std::abs(f_value) >= m_controllers[index].deadzone);
|
||||||
|
const bool positive = (f_value >= 0.0f);
|
||||||
|
const ButtonCallback& other_button_cb =
|
||||||
|
m_controllers[index].axis_button_mapping[static_cast<u32>(axis)][BoolToUInt8(!positive)];
|
||||||
|
const ButtonCallback& button_cb =
|
||||||
|
m_controllers[index].axis_button_mapping[static_cast<u32>(axis)][BoolToUInt8(positive)];
|
||||||
|
if (button_cb)
|
||||||
|
{
|
||||||
|
button_cb(outside_deadzone);
|
||||||
|
if (other_button_cb)
|
||||||
|
other_button_cb(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (other_button_cb)
|
||||||
|
{
|
||||||
|
other_button_cb(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XInputControllerInterface::HandleButtonEvent(u32 index, u32 button, bool pressed)
|
||||||
|
{
|
||||||
|
Log_DevPrintf("controller %u button %u %s", index, button, pressed ? "pressed" : "released");
|
||||||
|
DebugAssert(index < XUSER_MAX_COUNT);
|
||||||
|
|
||||||
|
if (DoEventHook(Hook::Type::Button, index, button, pressed ? 1.0f : 0.0f))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const ButtonCallback& cb = m_controllers[index].button_mapping[button];
|
||||||
|
if (!cb)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cb(pressed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 XInputControllerInterface::GetControllerRumbleMotorCount(int controller_index)
|
||||||
|
{
|
||||||
|
if (static_cast<u32>(controller_index) >= XUSER_MAX_COUNT || !m_controllers[controller_index].connected)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_controllers[controller_index].supports_rumble ? NUM_RUMBLE_MOTORS : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XInputControllerInterface::SetControllerRumbleStrength(int controller_index, const float* strengths,
|
||||||
|
u32 num_motors)
|
||||||
|
{
|
||||||
|
DebugAssert(static_cast<u32>(controller_index) < XUSER_MAX_COUNT);
|
||||||
|
|
||||||
|
// we'll update before this duration is elapsed
|
||||||
|
static constexpr float MIN_STRENGTH = 0.01f;
|
||||||
|
static constexpr u32 DURATION = 100000;
|
||||||
|
|
||||||
|
XINPUT_VIBRATION vib;
|
||||||
|
vib.wLeftMotorSpeed = (strengths[0] >= MIN_STRENGTH) ? static_cast<u16>(strengths[0] * 65535.0f) : 0;
|
||||||
|
vib.wRightMotorSpeed = (strengths[1] >= MIN_STRENGTH) ? static_cast<u16>(strengths[1] * 65535.0f) : 0;
|
||||||
|
m_xinput_set_state(static_cast<u32>(controller_index), &vib);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XInputControllerInterface::SetControllerAxisScale(int controller_index, float scale /* = 1.00f */)
|
||||||
|
{
|
||||||
|
if (static_cast<u32>(controller_index) >= XUSER_MAX_COUNT || !m_controllers[controller_index].connected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_controllers[static_cast<u32>(controller_index)].axis_scale = std::clamp(std::abs(scale), 0.01f, 1.50f);
|
||||||
|
Log_InfoPrintf("Controller %d axis scale set to %f", controller_index,
|
||||||
|
m_controllers[static_cast<u32>(controller_index)].axis_scale);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XInputControllerInterface::SetControllerDeadzone(int controller_index, float size /* = 0.25f */)
|
||||||
|
{
|
||||||
|
if (static_cast<u32>(controller_index) >= XUSER_MAX_COUNT || !m_controllers[controller_index].connected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_controllers[static_cast<u32>(controller_index)].deadzone = std::clamp(std::abs(size), 0.01f, 0.99f);
|
||||||
|
Log_InfoPrintf("Controller %d deadzone size set to %f", controller_index,
|
||||||
|
m_controllers[static_cast<u32>(controller_index)].deadzone);
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
#include "common/windows_headers.h"
|
||||||
|
#include "controller_interface.h"
|
||||||
|
#include "core/types.h"
|
||||||
|
#include <Xinput.h>
|
||||||
|
#include <array>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class XInputControllerInterface final : public ControllerInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
XInputControllerInterface();
|
||||||
|
~XInputControllerInterface() override;
|
||||||
|
|
||||||
|
Backend GetBackend() const override;
|
||||||
|
bool Initialize(CommonHostInterface* host_interface) override;
|
||||||
|
void Shutdown() override;
|
||||||
|
|
||||||
|
// Removes all bindings. Call before setting new bindings.
|
||||||
|
void ClearBindings() override;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Changing rumble strength.
|
||||||
|
u32 GetControllerRumbleMotorCount(int controller_index) override;
|
||||||
|
void SetControllerRumbleStrength(int controller_index, const float* strengths, u32 num_motors) override;
|
||||||
|
|
||||||
|
// Set scaling that will be applied on axis-to-axis mappings
|
||||||
|
bool SetControllerAxisScale(int controller_index, float scale = 1.00f) override;
|
||||||
|
|
||||||
|
// Set deadzone that will be applied on axis-to-button mappings
|
||||||
|
bool SetControllerDeadzone(int controller_index, float size = 0.25f) override;
|
||||||
|
|
||||||
|
void PollEvents() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum : u32
|
||||||
|
{
|
||||||
|
NUM_AXISES = 6,
|
||||||
|
NUM_BUTTONS = 15,
|
||||||
|
NUM_RUMBLE_MOTORS = 2
|
||||||
|
};
|
||||||
|
enum class Axis : u32
|
||||||
|
{
|
||||||
|
LeftX,
|
||||||
|
LeftY,
|
||||||
|
RightX,
|
||||||
|
RightY,
|
||||||
|
LeftTrigger,
|
||||||
|
RightTrigger
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ControllerData
|
||||||
|
{
|
||||||
|
XINPUT_STATE last_state = {};
|
||||||
|
bool connected = false;
|
||||||
|
bool supports_rumble = false;
|
||||||
|
|
||||||
|
// Scaling value of 1.30f to 1.40f recommended when using recent controllers
|
||||||
|
float axis_scale = 1.00f;
|
||||||
|
float deadzone = 0.25f;
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ControllerDataArray = std::array<ControllerData, XUSER_MAX_COUNT>;
|
||||||
|
|
||||||
|
void CheckForStateChanges(u32 index, const XINPUT_STATE& new_state);
|
||||||
|
void UpdateCapabilities(u32 index);
|
||||||
|
bool HandleAxisEvent(u32 index, Axis axis, s32 value);
|
||||||
|
bool HandleButtonEvent(u32 index, u32 button, bool pressed);
|
||||||
|
|
||||||
|
ControllerDataArray m_controllers;
|
||||||
|
|
||||||
|
HMODULE m_xinput_module{};
|
||||||
|
DWORD(WINAPI* m_xinput_get_state)(DWORD, XINPUT_STATE*);
|
||||||
|
DWORD(WINAPI* m_xinput_get_capabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*);
|
||||||
|
DWORD(WINAPI* m_xinput_set_state)(DWORD, XINPUT_VIBRATION*);
|
||||||
|
std::mutex m_event_intercept_mutex;
|
||||||
|
Hook::Callback m_event_intercept_callback;
|
||||||
|
};
|
Loading…
Reference in New Issue