mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-02-09 13:03:09 +00:00
Input: Use SDL3 for gamepad navigation
Implement SDL3-based gamepad handling and expose controller-specific button labels to QML.
This commit is contained in:
parent
40f1c5ff38
commit
24c0137fff
2 changed files with 408 additions and 154 deletions
|
|
@ -3,27 +3,111 @@
|
|||
|
||||
#include "gamepadmanager.h"
|
||||
|
||||
#if defined(ALAKARTE_HAVE_QT_GAMEPAD)
|
||||
#include <QCoreApplication>
|
||||
#include <QString>
|
||||
|
||||
#include <QGamepad>
|
||||
#include <QGamepadManager>
|
||||
#include <memory>
|
||||
|
||||
#define SDL_MAIN_HANDLED 1
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int PollIntervalMs = 16;
|
||||
constexpr int RepeatIntervalMs = 150;
|
||||
constexpr qint16 AxisThreshold = 16000;
|
||||
|
||||
QString buttonLabelToString(SDL_GamepadButtonLabel label)
|
||||
{
|
||||
switch (label) {
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_A:
|
||||
return QStringLiteral("A");
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_B:
|
||||
return QStringLiteral("B");
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_X:
|
||||
return QStringLiteral("X");
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_Y:
|
||||
return QStringLiteral("Y");
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
|
||||
return QStringLiteral("Cross");
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
|
||||
return QStringLiteral("Circle");
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
|
||||
return QStringLiteral("Square");
|
||||
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
|
||||
return QStringLiteral("Triangle");
|
||||
default:
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
GamepadManager::ControllerStyle controllerStyleFromTypeVendor(SDL_GamepadType type, Uint16 vendor)
|
||||
{
|
||||
switch (type) {
|
||||
case SDL_GAMEPAD_TYPE_XBOX360:
|
||||
case SDL_GAMEPAD_TYPE_XBOXONE:
|
||||
return GamepadManager::XboxController;
|
||||
case SDL_GAMEPAD_TYPE_PS3:
|
||||
case SDL_GAMEPAD_TYPE_PS4:
|
||||
case SDL_GAMEPAD_TYPE_PS5:
|
||||
return GamepadManager::PlayStationController;
|
||||
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
|
||||
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
|
||||
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
|
||||
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
|
||||
return GamepadManager::NintendoController;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (vendor == 0x045E) {
|
||||
return GamepadManager::XboxController;
|
||||
}
|
||||
if (vendor == 0x054C) {
|
||||
return GamepadManager::PlayStationController;
|
||||
}
|
||||
if (vendor == 0x057E) {
|
||||
return GamepadManager::NintendoController;
|
||||
}
|
||||
|
||||
if (type == SDL_GAMEPAD_TYPE_STANDARD) {
|
||||
return GamepadManager::GenericController;
|
||||
}
|
||||
|
||||
return GamepadManager::UnknownController;
|
||||
}
|
||||
}
|
||||
|
||||
GamepadManager *GamepadManager::s_instance = nullptr;
|
||||
|
||||
GamepadManager::GamepadManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
auto *manager = QGamepadManager::instance();
|
||||
SDL_SetMainReady();
|
||||
|
||||
connect(manager, &QGamepadManager::gamepadConnected, this, &GamepadManager::onGamepadConnected);
|
||||
connect(manager, &QGamepadManager::gamepadDisconnected, this, &GamepadManager::onGamepadDisconnected);
|
||||
m_sdlInitialized = SDL_Init(SDL_INIT_GAMEPAD);
|
||||
|
||||
m_repeatTimer.setInterval(150);
|
||||
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, [this]() {
|
||||
m_pollTimer.stop();
|
||||
m_repeatTimer.stop();
|
||||
closeGamepad();
|
||||
if (m_sdlInitialized) {
|
||||
SDL_Quit();
|
||||
m_sdlInitialized = false;
|
||||
}
|
||||
});
|
||||
|
||||
m_pollTimer.setInterval(PollIntervalMs);
|
||||
connect(&m_pollTimer, &QTimer::timeout, this, &GamepadManager::pollEvents);
|
||||
|
||||
m_repeatTimer.setInterval(RepeatIntervalMs);
|
||||
connect(&m_repeatTimer, &QTimer::timeout, this, &GamepadManager::processInput);
|
||||
|
||||
const auto gamepads = manager->connectedGamepads();
|
||||
if (!gamepads.isEmpty()) {
|
||||
setupGamepad(gamepads.first());
|
||||
if (m_sdlInitialized) {
|
||||
SDL_SetGamepadEventsEnabled(true);
|
||||
openFirstAvailableGamepad();
|
||||
m_pollTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,6 +136,31 @@ bool GamepadManager::active() const
|
|||
return m_active;
|
||||
}
|
||||
|
||||
GamepadManager::ControllerStyle GamepadManager::controllerStyle() const
|
||||
{
|
||||
return m_controllerStyle;
|
||||
}
|
||||
|
||||
QString GamepadManager::confirmButtonLabel() const
|
||||
{
|
||||
return m_confirmButtonLabel;
|
||||
}
|
||||
|
||||
QString GamepadManager::backButtonLabel() const
|
||||
{
|
||||
return m_backButtonLabel;
|
||||
}
|
||||
|
||||
QString GamepadManager::detailsButtonLabel() const
|
||||
{
|
||||
return m_detailsButtonLabel;
|
||||
}
|
||||
|
||||
QString GamepadManager::searchButtonLabel() const
|
||||
{
|
||||
return m_searchButtonLabel;
|
||||
}
|
||||
|
||||
void GamepadManager::setActive(bool active)
|
||||
{
|
||||
if (m_active != active) {
|
||||
|
|
@ -60,173 +169,279 @@ void GamepadManager::setActive(bool active)
|
|||
}
|
||||
}
|
||||
|
||||
void GamepadManager::onGamepadConnected(int deviceId)
|
||||
void GamepadManager::openFirstAvailableGamepad()
|
||||
{
|
||||
if (!m_gamepad) {
|
||||
setupGamepad(deviceId);
|
||||
if (!m_sdlInitialized || m_gamepad) {
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
std::unique_ptr<SDL_JoystickID[], decltype(&SDL_free)> ids(SDL_GetGamepads(&count), SDL_free);
|
||||
if (!ids || count <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SDL_JoystickID instanceId = ids[0];
|
||||
|
||||
if (SDL_IsGamepad(instanceId)) {
|
||||
openGamepad(static_cast<int>(instanceId));
|
||||
}
|
||||
}
|
||||
|
||||
void GamepadManager::onGamepadDisconnected(int deviceId)
|
||||
void GamepadManager::openGamepad(int deviceId)
|
||||
{
|
||||
if (m_gamepad && m_gamepad->deviceId() == deviceId) {
|
||||
m_gamepad->deleteLater();
|
||||
m_gamepad = nullptr;
|
||||
m_connected = false;
|
||||
m_repeatTimer.stop();
|
||||
Q_EMIT connectedChanged();
|
||||
|
||||
const auto gamepads = QGamepadManager::instance()->connectedGamepads();
|
||||
if (!gamepads.isEmpty()) {
|
||||
setupGamepad(gamepads.first());
|
||||
}
|
||||
if (!m_sdlInitialized || m_gamepad) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GamepadManager::setupGamepad(int deviceId)
|
||||
{
|
||||
m_gamepad = new QGamepad(deviceId, this);
|
||||
SDL_Gamepad *pad = SDL_OpenGamepad(static_cast<SDL_JoystickID>(deviceId));
|
||||
if (!pad) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_gamepad = pad;
|
||||
m_gamepadId = deviceId;
|
||||
m_connected = true;
|
||||
Q_EMIT connectedChanged();
|
||||
updateControllerInfo();
|
||||
}
|
||||
|
||||
void GamepadManager::closeGamepad()
|
||||
{
|
||||
if (!m_gamepad) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_CloseGamepad(m_gamepad);
|
||||
m_gamepad = nullptr;
|
||||
m_gamepadId = 0;
|
||||
m_connected = false;
|
||||
setActive(false);
|
||||
m_leftX = 0;
|
||||
m_leftY = 0;
|
||||
m_leftDirX = 0;
|
||||
m_leftDirY = 0;
|
||||
m_repeatTimer.stop();
|
||||
Q_EMIT connectedChanged();
|
||||
updateControllerInfo();
|
||||
}
|
||||
|
||||
void GamepadManager::updateControllerInfo()
|
||||
{
|
||||
ControllerStyle style = UnknownController;
|
||||
QString confirmLabel;
|
||||
QString backLabel;
|
||||
QString detailsLabel;
|
||||
QString searchLabel;
|
||||
|
||||
if (m_gamepad) {
|
||||
const SDL_GamepadType type = SDL_GetGamepadType(m_gamepad);
|
||||
const Uint16 vendor = SDL_GetGamepadVendor(m_gamepad);
|
||||
style = controllerStyleFromTypeVendor(type, vendor);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(3, 2, 0)
|
||||
confirmLabel = buttonLabelToString(SDL_GetGamepadButtonLabel(m_gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
||||
backLabel = buttonLabelToString(SDL_GetGamepadButtonLabel(m_gamepad, SDL_GAMEPAD_BUTTON_EAST));
|
||||
detailsLabel = buttonLabelToString(SDL_GetGamepadButtonLabel(m_gamepad, SDL_GAMEPAD_BUTTON_WEST));
|
||||
searchLabel = buttonLabelToString(SDL_GetGamepadButtonLabel(m_gamepad, SDL_GAMEPAD_BUTTON_NORTH));
|
||||
#endif
|
||||
|
||||
if (confirmLabel.isEmpty() || backLabel.isEmpty() || detailsLabel.isEmpty() || searchLabel.isEmpty()) {
|
||||
if (style == PlayStationController) {
|
||||
if (confirmLabel.isEmpty()) {
|
||||
confirmLabel = QStringLiteral("Cross");
|
||||
}
|
||||
if (backLabel.isEmpty()) {
|
||||
backLabel = QStringLiteral("Circle");
|
||||
}
|
||||
if (detailsLabel.isEmpty()) {
|
||||
detailsLabel = QStringLiteral("Square");
|
||||
}
|
||||
if (searchLabel.isEmpty()) {
|
||||
searchLabel = QStringLiteral("Triangle");
|
||||
}
|
||||
} else if (style == NintendoController) {
|
||||
if (confirmLabel.isEmpty()) {
|
||||
confirmLabel = QStringLiteral("B");
|
||||
}
|
||||
if (backLabel.isEmpty()) {
|
||||
backLabel = QStringLiteral("A");
|
||||
}
|
||||
if (detailsLabel.isEmpty()) {
|
||||
detailsLabel = QStringLiteral("Y");
|
||||
}
|
||||
if (searchLabel.isEmpty()) {
|
||||
searchLabel = QStringLiteral("X");
|
||||
}
|
||||
} else {
|
||||
if (confirmLabel.isEmpty()) {
|
||||
confirmLabel = QStringLiteral("A");
|
||||
}
|
||||
if (backLabel.isEmpty()) {
|
||||
backLabel = QStringLiteral("B");
|
||||
}
|
||||
if (detailsLabel.isEmpty()) {
|
||||
detailsLabel = QStringLiteral("X");
|
||||
}
|
||||
if (searchLabel.isEmpty()) {
|
||||
searchLabel = QStringLiteral("Y");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool changed = (style != m_controllerStyle) || (confirmLabel != m_confirmButtonLabel) || (backLabel != m_backButtonLabel)
|
||||
|| (detailsLabel != m_detailsButtonLabel) || (searchLabel != m_searchButtonLabel);
|
||||
|
||||
m_controllerStyle = style;
|
||||
m_confirmButtonLabel = confirmLabel;
|
||||
m_backButtonLabel = backLabel;
|
||||
m_detailsButtonLabel = detailsLabel;
|
||||
m_searchButtonLabel = searchLabel;
|
||||
|
||||
if (changed) {
|
||||
Q_EMIT controllerInfoChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void GamepadManager::pollEvents()
|
||||
{
|
||||
if (!m_sdlInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
switch (e.type) {
|
||||
case SDL_EVENT_GAMEPAD_ADDED: {
|
||||
if (!m_gamepad) {
|
||||
openGamepad(static_cast<int>(e.gdevice.which));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_REMOVED: {
|
||||
if (m_gamepad && static_cast<int>(e.gdevice.which) == m_gamepadId) {
|
||||
closeGamepad();
|
||||
openFirstAvailableGamepad();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
|
||||
if (!m_gamepad || static_cast<int>(e.gaxis.which) != m_gamepadId) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (e.gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX) {
|
||||
m_leftX = e.gaxis.value;
|
||||
} else if (e.gaxis.axis == SDL_GAMEPAD_AXIS_LEFTY) {
|
||||
m_leftY = e.gaxis.value;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
const qint8 dirX = (m_leftX < -AxisThreshold) ? -1 : (m_leftX > AxisThreshold ? 1 : 0);
|
||||
const qint8 dirY = (m_leftY < -AxisThreshold) ? -1 : (m_leftY > AxisThreshold ? 1 : 0);
|
||||
|
||||
if (dirX != 0 || dirY != 0) {
|
||||
setActive(true);
|
||||
}
|
||||
|
||||
if (dirY != m_leftDirY) {
|
||||
m_leftDirY = dirY;
|
||||
if (dirY < 0) {
|
||||
Q_EMIT navigateUp();
|
||||
} else if (dirY > 0) {
|
||||
Q_EMIT navigateDown();
|
||||
}
|
||||
}
|
||||
|
||||
if (dirX != m_leftDirX) {
|
||||
m_leftDirX = dirX;
|
||||
if (dirX < 0) {
|
||||
Q_EMIT navigateLeft();
|
||||
} else if (dirX > 0) {
|
||||
Q_EMIT navigateRight();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_leftDirX != 0 || m_leftDirY != 0) {
|
||||
if (!m_repeatTimer.isActive()) {
|
||||
m_repeatTimer.start();
|
||||
}
|
||||
} else {
|
||||
m_repeatTimer.stop();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: {
|
||||
if (!m_gamepad || static_cast<int>(e.gbutton.which) != m_gamepadId) {
|
||||
break;
|
||||
}
|
||||
|
||||
connect(m_gamepad, &QGamepad::axisLeftYChanged, this, [this](double value) {
|
||||
if (qAbs(value) > 0.5) {
|
||||
setActive(true);
|
||||
if (value < -0.5) {
|
||||
|
||||
switch (static_cast<SDL_GamepadButton>(e.gbutton.button)) {
|
||||
case SDL_GAMEPAD_BUTTON_SOUTH:
|
||||
Q_EMIT selectPressed();
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_EAST:
|
||||
case SDL_GAMEPAD_BUTTON_BACK:
|
||||
Q_EMIT backPressed();
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_WEST:
|
||||
Q_EMIT detailsPressed();
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||
Q_EMIT leftBumperPressed();
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
Q_EMIT rightBumperPressed();
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_START:
|
||||
Q_EMIT menuPressed();
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_NORTH:
|
||||
Q_EMIT searchPressed();
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
Q_EMIT navigateUp();
|
||||
} else {
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
Q_EMIT navigateDown();
|
||||
}
|
||||
m_repeatTimer.start();
|
||||
} else {
|
||||
m_repeatTimer.stop();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_gamepad, &QGamepad::axisLeftXChanged, this, [this](double value) {
|
||||
if (qAbs(value) > 0.5) {
|
||||
setActive(true);
|
||||
if (value < -0.5) {
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
Q_EMIT navigateLeft();
|
||||
} else {
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
Q_EMIT navigateRight();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_repeatTimer.start();
|
||||
} else {
|
||||
m_repeatTimer.stop();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_gamepad, &QGamepad::buttonAChanged, this, [this](bool pressed) {
|
||||
if (pressed) {
|
||||
setActive(true);
|
||||
Q_EMIT selectPressed();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_gamepad, &QGamepad::buttonBChanged, this, [this](bool pressed) {
|
||||
if (pressed) {
|
||||
setActive(true);
|
||||
Q_EMIT backPressed();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_gamepad, &QGamepad::buttonStartChanged, this, [this](bool pressed) {
|
||||
if (pressed) {
|
||||
setActive(true);
|
||||
Q_EMIT menuPressed();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_gamepad, &QGamepad::buttonYChanged, this, [this](bool pressed) {
|
||||
if (pressed) {
|
||||
setActive(true);
|
||||
Q_EMIT searchPressed();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_gamepad, &QGamepad::buttonUpChanged, this, [this](bool pressed) {
|
||||
if (pressed) {
|
||||
setActive(true);
|
||||
Q_EMIT navigateUp();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_gamepad, &QGamepad::buttonDownChanged, this, [this](bool pressed) {
|
||||
if (pressed) {
|
||||
setActive(true);
|
||||
Q_EMIT navigateDown();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_gamepad, &QGamepad::buttonLeftChanged, this, [this](bool pressed) {
|
||||
if (pressed) {
|
||||
setActive(true);
|
||||
Q_EMIT navigateLeft();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_gamepad, &QGamepad::buttonRightChanged, this, [this](bool pressed) {
|
||||
if (pressed) {
|
||||
setActive(true);
|
||||
Q_EMIT navigateRight();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void GamepadManager::processInput()
|
||||
{
|
||||
if (!m_gamepad)
|
||||
if (!m_gamepad) {
|
||||
return;
|
||||
}
|
||||
|
||||
double y = m_gamepad->axisLeftY();
|
||||
double x = m_gamepad->axisLeftX();
|
||||
|
||||
if (y < -0.5) {
|
||||
if (m_leftY < -AxisThreshold) {
|
||||
Q_EMIT navigateUp();
|
||||
} else if (y > 0.5) {
|
||||
} else if (m_leftY > AxisThreshold) {
|
||||
Q_EMIT navigateDown();
|
||||
}
|
||||
|
||||
if (x < -0.5) {
|
||||
if (m_leftX < -AxisThreshold) {
|
||||
Q_EMIT navigateLeft();
|
||||
} else if (x > 0.5) {
|
||||
} else if (m_leftX > AxisThreshold) {
|
||||
Q_EMIT navigateRight();
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
GamepadManager *GamepadManager::s_instance = nullptr;
|
||||
|
||||
GamepadManager::GamepadManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
GamepadManager *GamepadManager::instance()
|
||||
{
|
||||
if (!s_instance) {
|
||||
s_instance = new GamepadManager();
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
GamepadManager *GamepadManager::create(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
{
|
||||
Q_UNUSED(engine)
|
||||
Q_UNUSED(scriptEngine)
|
||||
return instance();
|
||||
}
|
||||
|
||||
bool GamepadManager::connected() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GamepadManager::active() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
class QGamepad;
|
||||
class QGamepadManager;
|
||||
struct SDL_Gamepad;
|
||||
|
||||
class GamepadManager : public QObject
|
||||
{
|
||||
|
|
@ -18,17 +18,38 @@ class GamepadManager : public QObject
|
|||
|
||||
Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
|
||||
Q_PROPERTY(bool active READ active NOTIFY activeChanged)
|
||||
Q_PROPERTY(ControllerStyle controllerStyle READ controllerStyle NOTIFY controllerInfoChanged)
|
||||
Q_PROPERTY(QString confirmButtonLabel READ confirmButtonLabel NOTIFY controllerInfoChanged)
|
||||
Q_PROPERTY(QString backButtonLabel READ backButtonLabel NOTIFY controllerInfoChanged)
|
||||
Q_PROPERTY(QString detailsButtonLabel READ detailsButtonLabel NOTIFY controllerInfoChanged)
|
||||
Q_PROPERTY(QString searchButtonLabel READ searchButtonLabel NOTIFY controllerInfoChanged)
|
||||
|
||||
public:
|
||||
enum ControllerStyle {
|
||||
UnknownController = 0,
|
||||
XboxController,
|
||||
PlayStationController,
|
||||
NintendoController,
|
||||
GenericController,
|
||||
};
|
||||
Q_ENUM(ControllerStyle)
|
||||
|
||||
static GamepadManager *instance();
|
||||
static GamepadManager *create(QQmlEngine *engine, QJSEngine *scriptEngine);
|
||||
|
||||
bool connected() const;
|
||||
bool active() const;
|
||||
|
||||
ControllerStyle controllerStyle() const;
|
||||
QString confirmButtonLabel() const;
|
||||
QString backButtonLabel() const;
|
||||
QString detailsButtonLabel() const;
|
||||
QString searchButtonLabel() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectedChanged();
|
||||
void activeChanged();
|
||||
void controllerInfoChanged();
|
||||
|
||||
void navigateUp();
|
||||
void navigateDown();
|
||||
|
|
@ -36,22 +57,40 @@ Q_SIGNALS:
|
|||
void navigateRight();
|
||||
void selectPressed();
|
||||
void backPressed();
|
||||
void detailsPressed();
|
||||
void menuPressed();
|
||||
void searchPressed();
|
||||
|
||||
void leftBumperPressed();
|
||||
void rightBumperPressed();
|
||||
|
||||
private:
|
||||
explicit GamepadManager(QObject *parent = nullptr);
|
||||
|
||||
static GamepadManager *s_instance;
|
||||
|
||||
QGamepad *m_gamepad = nullptr;
|
||||
SDL_Gamepad *m_gamepad = nullptr;
|
||||
int m_gamepadId = 0;
|
||||
bool m_connected = false;
|
||||
bool m_active = false;
|
||||
bool m_sdlInitialized = false;
|
||||
ControllerStyle m_controllerStyle = UnknownController;
|
||||
QString m_confirmButtonLabel;
|
||||
QString m_backButtonLabel;
|
||||
QString m_detailsButtonLabel;
|
||||
QString m_searchButtonLabel;
|
||||
QTimer m_pollTimer;
|
||||
QTimer m_repeatTimer;
|
||||
qint16 m_leftX = 0;
|
||||
qint16 m_leftY = 0;
|
||||
qint8 m_leftDirX = 0;
|
||||
qint8 m_leftDirY = 0;
|
||||
|
||||
void onGamepadConnected(int deviceId);
|
||||
void onGamepadDisconnected(int deviceId);
|
||||
void setupGamepad(int deviceId);
|
||||
void openFirstAvailableGamepad();
|
||||
void openGamepad(int deviceId);
|
||||
void closeGamepad();
|
||||
void updateControllerInfo();
|
||||
void pollEvents();
|
||||
void processInput();
|
||||
void setActive(bool active);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue