Add support for SDL controller accelerometer/gyro events
This commit is contained in:
parent
4817b00843
commit
853909996b
6 changed files with 187 additions and 4 deletions
|
@ -144,7 +144,7 @@ if (ENABLE_SDL2)
|
||||||
if (CITRA_USE_BUNDLED_SDL2)
|
if (CITRA_USE_BUNDLED_SDL2)
|
||||||
# Detect toolchain and platform
|
# Detect toolchain and platform
|
||||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
|
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
|
||||||
set(SDL2_VER "SDL2-2.0.12")
|
set(SDL2_VER "SDL2-2.0.16")
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable CITRA_USE_BUNDLED_SDL2 and provide your own.")
|
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable CITRA_USE_BUNDLED_SDL2 and provide your own.")
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QTimer>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include "citra_qt/configuration/configure_motion_touch.h"
|
#include "citra_qt/configuration/configure_motion_touch.h"
|
||||||
#include "citra_qt/configuration/configure_touch_from_button.h"
|
#include "citra_qt/configuration/configure_touch_from_button.h"
|
||||||
|
@ -70,16 +71,18 @@ void CalibrationConfigurationDialog::UpdateButtonText(QString text) {
|
||||||
cancel_button->setText(text);
|
cancel_button->setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::array<std::pair<const char*, const char*>, 2> MotionProviders = {
|
const std::array<std::pair<const char*, const char*>, 3> MotionProviders = {
|
||||||
{{"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Mouse (Right Click)")},
|
{{"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Mouse (Right Click)")},
|
||||||
{"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}}};
|
{"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")},
|
||||||
|
{"sdl", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "SDL")}}};
|
||||||
|
|
||||||
const std::array<std::pair<const char*, const char*>, 2> TouchProviders = {
|
const std::array<std::pair<const char*, const char*>, 2> TouchProviders = {
|
||||||
{{"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")},
|
{{"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")},
|
||||||
{"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}}};
|
{"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}}};
|
||||||
|
|
||||||
ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent)
|
ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent)
|
||||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureMotionTouch>()) {
|
: QDialog(parent), ui(std::make_unique<Ui::ConfigureMotionTouch>()),
|
||||||
|
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
for (auto [provider, name] : MotionProviders) {
|
for (auto [provider, name] : MotionProviders) {
|
||||||
ui->motion_provider->addItem(tr(name), QString::fromUtf8(provider));
|
ui->motion_provider->addItem(tr(name), QString::fromUtf8(provider));
|
||||||
|
@ -95,6 +98,22 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent)
|
||||||
"using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
|
"using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
|
||||||
"style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
|
"style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
|
||||||
|
|
||||||
|
timeout_timer->setSingleShot(true);
|
||||||
|
connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
|
||||||
|
|
||||||
|
connect(poll_timer.get(), &QTimer::timeout, [this]() {
|
||||||
|
Common::ParamPackage params;
|
||||||
|
for (auto& poller : device_pollers) {
|
||||||
|
params = poller->GetNextInput();
|
||||||
|
// We want all the input systems to be in a "polling" state, but we only care about the
|
||||||
|
// input from SDL.
|
||||||
|
if (params.Has("engine") && params.Get("engine", "") == "sdl") {
|
||||||
|
SetPollingResult(params, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
SetConfiguration();
|
SetConfiguration();
|
||||||
UpdateUiDisplay();
|
UpdateUiDisplay();
|
||||||
ConnectEvents();
|
ConnectEvents();
|
||||||
|
@ -122,6 +141,9 @@ void ConfigureMotionTouch::SetConfiguration() {
|
||||||
Settings::values.current_input_profile.touch_from_button_map_index);
|
Settings::values.current_input_profile.touch_from_button_map_index);
|
||||||
ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
|
ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
|
||||||
|
|
||||||
|
guid = motion_param.Get("guid", "0");
|
||||||
|
port = motion_param.Get("port", 0);
|
||||||
|
|
||||||
min_x = touch_param.Get("min_x", 100);
|
min_x = touch_param.Get("min_x", 100);
|
||||||
min_y = touch_param.Get("min_y", 50);
|
min_y = touch_param.Get("min_y", 50);
|
||||||
max_x = touch_param.Get("max_x", 1800);
|
max_x = touch_param.Get("max_x", 1800);
|
||||||
|
@ -145,6 +167,14 @@ void ConfigureMotionTouch::UpdateUiDisplay() {
|
||||||
ui->motion_sensitivity->setVisible(false);
|
ui->motion_sensitivity->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (motion_engine == "sdl") {
|
||||||
|
ui->motion_controller_label->setVisible(true);
|
||||||
|
ui->motion_controller_button->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->motion_controller_label->setVisible(false);
|
||||||
|
ui->motion_controller_button->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (touch_engine == "cemuhookudp") {
|
if (touch_engine == "cemuhookudp") {
|
||||||
ui->touch_calibration->setVisible(true);
|
ui->touch_calibration->setVisible(true);
|
||||||
ui->touch_calibration_config->setVisible(true);
|
ui->touch_calibration_config->setVisible(true);
|
||||||
|
@ -172,6 +202,30 @@ void ConfigureMotionTouch::ConnectEvents() {
|
||||||
connect(ui->touch_provider,
|
connect(ui->touch_provider,
|
||||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||||
[this]([[maybe_unused]] int index) { UpdateUiDisplay(); });
|
[this]([[maybe_unused]] int index) { UpdateUiDisplay(); });
|
||||||
|
connect(ui->motion_controller_button, &QPushButton::clicked, [=]() {
|
||||||
|
if (QMessageBox::information(this, tr("Information"),
|
||||||
|
tr("After pressing OK, press a button on the controller whose "
|
||||||
|
"motion you want to track."),
|
||||||
|
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
|
||||||
|
ui->motion_controller_button->setText(tr("[press button]"));
|
||||||
|
ui->motion_controller_button->setFocus();
|
||||||
|
|
||||||
|
input_setter = [=](const Common::ParamPackage& params) {
|
||||||
|
guid = params.Get("guid", "0");
|
||||||
|
port = params.Get("port", 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
device_pollers =
|
||||||
|
InputCommon::Polling::GetPollers(InputCommon::Polling::DeviceType::Button);
|
||||||
|
|
||||||
|
for (auto& poller : device_pollers) {
|
||||||
|
poller->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout_timer->start(5000); // Cancel after 5 seconds
|
||||||
|
poll_timer->start(200); // Check for new inputs every 200ms
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest);
|
connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest);
|
||||||
connect(ui->touch_calibration_config, &QPushButton::clicked, this,
|
connect(ui->touch_calibration_config, &QPushButton::clicked, this,
|
||||||
&ConfigureMotionTouch::OnConfigureTouchCalibration);
|
&ConfigureMotionTouch::OnConfigureTouchCalibration);
|
||||||
|
@ -183,6 +237,21 @@ void ConfigureMotionTouch::ConnectEvents() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigureMotionTouch::SetPollingResult(const Common::ParamPackage& params, bool abort) {
|
||||||
|
timeout_timer->stop();
|
||||||
|
poll_timer->stop();
|
||||||
|
for (auto& poller : device_pollers) {
|
||||||
|
poller->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!abort && input_setter) {
|
||||||
|
(*input_setter)(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->motion_controller_button->setText(tr("Configure"));
|
||||||
|
input_setter.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigureMotionTouch::OnCemuhookUDPTest() {
|
void ConfigureMotionTouch::OnCemuhookUDPTest() {
|
||||||
ui->udp_test->setEnabled(false);
|
ui->udp_test->setEnabled(false);
|
||||||
ui->udp_test->setText(tr("Testing"));
|
ui->udp_test->setText(tr("Testing"));
|
||||||
|
@ -285,6 +354,9 @@ void ConfigureMotionTouch::ApplyConfiguration() {
|
||||||
|
|
||||||
if (motion_engine == "motion_emu") {
|
if (motion_engine == "motion_emu") {
|
||||||
motion_param.Set("sensitivity", static_cast<float>(ui->motion_sensitivity->value()));
|
motion_param.Set("sensitivity", static_cast<float>(ui->motion_sensitivity->value()));
|
||||||
|
} else if (motion_engine == "sdl") {
|
||||||
|
motion_param.Set("guid", guid);
|
||||||
|
motion_param.Set("port", port);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (touch_engine == "cemuhookudp") {
|
if (touch_engine == "cemuhookudp") {
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
#include "input_common/main.h"
|
||||||
#include "input_common/udp/udp.h"
|
#include "input_common/udp/udp.h"
|
||||||
|
|
||||||
class QVBoxLayout;
|
class QVBoxLayout;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ConfigureMotionTouch;
|
class ConfigureMotionTouch;
|
||||||
|
@ -63,10 +65,21 @@ private:
|
||||||
void SetConfiguration();
|
void SetConfiguration();
|
||||||
void UpdateUiDisplay();
|
void UpdateUiDisplay();
|
||||||
void ConnectEvents();
|
void ConnectEvents();
|
||||||
|
void SetPollingResult(const Common::ParamPackage& params, bool abort);
|
||||||
bool CanCloseDialog();
|
bool CanCloseDialog();
|
||||||
|
|
||||||
std::unique_ptr<Ui::ConfigureMotionTouch> ui;
|
std::unique_ptr<Ui::ConfigureMotionTouch> ui;
|
||||||
|
|
||||||
|
// Used for SDL input polling
|
||||||
|
std::string guid;
|
||||||
|
int port;
|
||||||
|
std::unique_ptr<QTimer> timeout_timer;
|
||||||
|
std::unique_ptr<QTimer> poll_timer;
|
||||||
|
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
|
||||||
|
|
||||||
|
/// This will be the the setting function when an input is awaiting configuration.
|
||||||
|
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
|
||||||
|
|
||||||
// Coordinate system of the CemuhookUDP touch provider
|
// Coordinate system of the CemuhookUDP touch provider
|
||||||
int min_x, min_y, max_x, max_y;
|
int min_x, min_y, max_x, max_y;
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,24 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="motion_controller_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Controller:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="motion_controller_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Configure</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -173,6 +173,24 @@ public:
|
||||||
std::lock_guard lock{mutex};
|
std::lock_guard lock{mutex};
|
||||||
return (state.hats.at(hat) & direction) != 0;
|
return (state.hats.at(hat) & direction) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetAccel(const float x, const float y, const float z) {
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
state.accel.x = x;
|
||||||
|
state.accel.y = y;
|
||||||
|
state.accel.z = z;
|
||||||
|
}
|
||||||
|
void SetGyro(const float pitch, const float yaw, const float roll) {
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
state.gyro.x = pitch;
|
||||||
|
state.gyro.y = yaw;
|
||||||
|
state.gyro.z = roll;
|
||||||
|
}
|
||||||
|
std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetMotion() const {
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
return std::make_tuple(state.accel, state.gyro);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The guid of the joystick
|
* The guid of the joystick
|
||||||
*/
|
*/
|
||||||
|
@ -204,6 +222,8 @@ private:
|
||||||
std::unordered_map<int, bool> buttons;
|
std::unordered_map<int, bool> buttons;
|
||||||
std::unordered_map<int, Sint16> axes;
|
std::unordered_map<int, Sint16> axes;
|
||||||
std::unordered_map<int, Uint8> hats;
|
std::unordered_map<int, Uint8> hats;
|
||||||
|
Common::Vec3<float> accel;
|
||||||
|
Common::Vec3<float> gyro;
|
||||||
} state;
|
} state;
|
||||||
std::string guid;
|
std::string guid;
|
||||||
int port;
|
int port;
|
||||||
|
@ -473,6 +493,14 @@ void SDLState::InitGameController(int controller_index) {
|
||||||
LOG_WARNING(Input, "failed to open joystick {} as controller", controller_index);
|
LOG_WARNING(Input, "failed to open joystick {} as controller", controller_index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 14)
|
||||||
|
if (SDL_GameControllerHasSensor(sdl_controller, SDL_SENSOR_ACCEL)) {
|
||||||
|
SDL_GameControllerSetSensorEnabled(sdl_controller, SDL_SENSOR_ACCEL, SDL_TRUE);
|
||||||
|
}
|
||||||
|
if (SDL_GameControllerHasSensor(sdl_controller, SDL_SENSOR_GYRO)) {
|
||||||
|
SDL_GameControllerSetSensorEnabled(sdl_controller, SDL_SENSOR_GYRO, SDL_TRUE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
const std::string guid = GetGUID(SDL_GameControllerGetJoystick(sdl_controller));
|
const std::string guid = GetGUID(SDL_GameControllerGetJoystick(sdl_controller));
|
||||||
|
|
||||||
LOG_INFO(Input, "opened joystick {} as controller", controller_index);
|
LOG_INFO(Input, "opened joystick {} as controller", controller_index);
|
||||||
|
@ -557,6 +585,25 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 14)
|
||||||
|
case SDL_CONTROLLERSENSORUPDATE: {
|
||||||
|
if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) {
|
||||||
|
switch (event.csensor.sensor) {
|
||||||
|
case SDL_SENSOR_ACCEL:
|
||||||
|
joystick->SetAccel(event.csensor.data[0] / SDL_STANDARD_GRAVITY,
|
||||||
|
-event.csensor.data[1] / SDL_STANDARD_GRAVITY,
|
||||||
|
event.csensor.data[2] / SDL_STANDARD_GRAVITY);
|
||||||
|
break;
|
||||||
|
case SDL_SENSOR_GYRO:
|
||||||
|
joystick->SetGyro(-event.csensor.data[0] * (180.0f / Common::PI),
|
||||||
|
event.csensor.data[1] * (180.0f / Common::PI),
|
||||||
|
-event.csensor.data[2] * (180.0f / Common::PI));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
case SDL_JOYDEVICEREMOVED:
|
case SDL_JOYDEVICEREMOVED:
|
||||||
LOG_DEBUG(Input, "Joystick removed with Instance_ID {}", event.jdevice.which);
|
LOG_DEBUG(Input, "Joystick removed with Instance_ID {}", event.jdevice.which);
|
||||||
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
|
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
|
||||||
|
@ -658,6 +705,18 @@ private:
|
||||||
const float deadzone;
|
const float deadzone;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SDLMotion final : public Input::MotionDevice {
|
||||||
|
public:
|
||||||
|
explicit SDLMotion(std::shared_ptr<SDLJoystick> joystick_) : joystick(std::move(joystick_)) {}
|
||||||
|
|
||||||
|
std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override {
|
||||||
|
return joystick->GetMotion();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<SDLJoystick> joystick;
|
||||||
|
};
|
||||||
|
|
||||||
/// A button device factory that creates button devices from SDL joystick
|
/// A button device factory that creates button devices from SDL joystick
|
||||||
class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
|
class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
|
||||||
public:
|
public:
|
||||||
|
@ -764,10 +823,28 @@ private:
|
||||||
SDLState& state;
|
SDLState& state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
|
||||||
|
public:
|
||||||
|
explicit SDLMotionFactory(SDLState& state_) : state(state_) {}
|
||||||
|
|
||||||
|
std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
|
||||||
|
const std::string guid = params.Get("guid", "0");
|
||||||
|
const int port = params.Get("port", 0);
|
||||||
|
|
||||||
|
auto joystick = state.GetSDLJoystickByGUID(guid, port);
|
||||||
|
|
||||||
|
return std::make_unique<SDLMotion>(joystick);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDLState& state;
|
||||||
|
};
|
||||||
|
|
||||||
SDLState::SDLState() {
|
SDLState::SDLState() {
|
||||||
using namespace Input;
|
using namespace Input;
|
||||||
RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
|
RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
|
||||||
RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
|
RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
|
||||||
|
RegisterFactory<MotionDevice>("sdl", std::make_shared<SDLMotionFactory>(*this));
|
||||||
|
|
||||||
// If the frontend is going to manage the event loop, then we dont start one here
|
// If the frontend is going to manage the event loop, then we dont start one here
|
||||||
start_thread = !SDL_WasInit(SDL_INIT_GAMECONTROLLER);
|
start_thread = !SDL_WasInit(SDL_INIT_GAMECONTROLLER);
|
||||||
|
@ -812,6 +889,7 @@ SDLState::~SDLState() {
|
||||||
using namespace Input;
|
using namespace Input;
|
||||||
UnregisterFactory<ButtonDevice>("sdl");
|
UnregisterFactory<ButtonDevice>("sdl");
|
||||||
UnregisterFactory<AnalogDevice>("sdl");
|
UnregisterFactory<AnalogDevice>("sdl");
|
||||||
|
UnregisterFactory<MotionDevice>("sdl");
|
||||||
|
|
||||||
CloseJoysticks();
|
CloseJoysticks();
|
||||||
CloseGameControllers();
|
CloseGameControllers();
|
||||||
|
|
|
@ -22,6 +22,7 @@ class SDLJoystick;
|
||||||
class SDLGameController;
|
class SDLGameController;
|
||||||
class SDLButtonFactory;
|
class SDLButtonFactory;
|
||||||
class SDLAnalogFactory;
|
class SDLAnalogFactory;
|
||||||
|
class SDLMotionFactory;
|
||||||
|
|
||||||
class SDLState : public State {
|
class SDLState : public State {
|
||||||
public:
|
public:
|
||||||
|
@ -73,6 +74,7 @@ private:
|
||||||
|
|
||||||
std::shared_ptr<SDLButtonFactory> button_factory;
|
std::shared_ptr<SDLButtonFactory> button_factory;
|
||||||
std::shared_ptr<SDLAnalogFactory> analog_factory;
|
std::shared_ptr<SDLAnalogFactory> analog_factory;
|
||||||
|
std::shared_ptr<SDLMotionFactory> motion_factory;
|
||||||
|
|
||||||
bool start_thread = false;
|
bool start_thread = false;
|
||||||
std::atomic<bool> initialized = false;
|
std::atomic<bool> initialized = false;
|
||||||
|
|
Loading…
Reference in a new issue