mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-02-09 21:13:08 +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"
|
#include "gamepadmanager.h"
|
||||||
|
|
||||||
#if defined(ALAKARTE_HAVE_QT_GAMEPAD)
|
#include <QCoreApplication>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#include <QGamepad>
|
#include <memory>
|
||||||
#include <QGamepadManager>
|
|
||||||
|
#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::s_instance = nullptr;
|
||||||
|
|
||||||
GamepadManager::GamepadManager(QObject *parent)
|
GamepadManager::GamepadManager(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
auto *manager = QGamepadManager::instance();
|
SDL_SetMainReady();
|
||||||
|
|
||||||
connect(manager, &QGamepadManager::gamepadConnected, this, &GamepadManager::onGamepadConnected);
|
m_sdlInitialized = SDL_Init(SDL_INIT_GAMEPAD);
|
||||||
connect(manager, &QGamepadManager::gamepadDisconnected, this, &GamepadManager::onGamepadDisconnected);
|
|
||||||
|
|
||||||
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);
|
connect(&m_repeatTimer, &QTimer::timeout, this, &GamepadManager::processInput);
|
||||||
|
|
||||||
const auto gamepads = manager->connectedGamepads();
|
if (m_sdlInitialized) {
|
||||||
if (!gamepads.isEmpty()) {
|
SDL_SetGamepadEventsEnabled(true);
|
||||||
setupGamepad(gamepads.first());
|
openFirstAvailableGamepad();
|
||||||
|
m_pollTimer.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,6 +136,31 @@ bool GamepadManager::active() const
|
||||||
return m_active;
|
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)
|
void GamepadManager::setActive(bool active)
|
||||||
{
|
{
|
||||||
if (m_active != 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) {
|
if (!m_sdlInitialized || m_gamepad) {
|
||||||
setupGamepad(deviceId);
|
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) {
|
if (!m_sdlInitialized || m_gamepad) {
|
||||||
m_gamepad->deleteLater();
|
return;
|
||||||
m_gamepad = nullptr;
|
|
||||||
m_connected = false;
|
|
||||||
m_repeatTimer.stop();
|
|
||||||
Q_EMIT connectedChanged();
|
|
||||||
|
|
||||||
const auto gamepads = QGamepadManager::instance()->connectedGamepads();
|
|
||||||
if (!gamepads.isEmpty()) {
|
|
||||||
setupGamepad(gamepads.first());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GamepadManager::setupGamepad(int deviceId)
|
SDL_Gamepad *pad = SDL_OpenGamepad(static_cast<SDL_JoystickID>(deviceId));
|
||||||
{
|
if (!pad) {
|
||||||
m_gamepad = new QGamepad(deviceId, this);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_gamepad = pad;
|
||||||
|
m_gamepadId = deviceId;
|
||||||
m_connected = true;
|
m_connected = true;
|
||||||
Q_EMIT connectedChanged();
|
Q_EMIT connectedChanged();
|
||||||
|
updateControllerInfo();
|
||||||
|
}
|
||||||
|
|
||||||
connect(m_gamepad, &QGamepad::axisLeftYChanged, this, [this](double value) {
|
void GamepadManager::closeGamepad()
|
||||||
if (qAbs(value) > 0.5) {
|
{
|
||||||
setActive(true);
|
if (!m_gamepad) {
|
||||||
if (value < -0.5) {
|
return;
|
||||||
Q_EMIT navigateUp();
|
}
|
||||||
|
|
||||||
|
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 {
|
} 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();
|
Q_EMIT navigateDown();
|
||||||
}
|
}
|
||||||
m_repeatTimer.start();
|
|
||||||
} else {
|
|
||||||
m_repeatTimer.stop();
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_gamepad, &QGamepad::axisLeftXChanged, this, [this](double value) {
|
if (dirX != m_leftDirX) {
|
||||||
if (qAbs(value) > 0.5) {
|
m_leftDirX = dirX;
|
||||||
setActive(true);
|
if (dirX < 0) {
|
||||||
if (value < -0.5) {
|
|
||||||
Q_EMIT navigateLeft();
|
Q_EMIT navigateLeft();
|
||||||
} else {
|
} else if (dirX > 0) {
|
||||||
Q_EMIT navigateRight();
|
Q_EMIT navigateRight();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_leftDirX != 0 || m_leftDirY != 0) {
|
||||||
|
if (!m_repeatTimer.isActive()) {
|
||||||
m_repeatTimer.start();
|
m_repeatTimer.start();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m_repeatTimer.stop();
|
m_repeatTimer.stop();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_gamepad, &QGamepad::buttonAChanged, this, [this](bool pressed) {
|
break;
|
||||||
if (pressed) {
|
}
|
||||||
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: {
|
||||||
|
if (!m_gamepad || static_cast<int>(e.gbutton.which) != m_gamepadId) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
setActive(true);
|
setActive(true);
|
||||||
|
|
||||||
|
switch (static_cast<SDL_GamepadButton>(e.gbutton.button)) {
|
||||||
|
case SDL_GAMEPAD_BUTTON_SOUTH:
|
||||||
Q_EMIT selectPressed();
|
Q_EMIT selectPressed();
|
||||||
}
|
break;
|
||||||
});
|
case SDL_GAMEPAD_BUTTON_EAST:
|
||||||
|
case SDL_GAMEPAD_BUTTON_BACK:
|
||||||
connect(m_gamepad, &QGamepad::buttonBChanged, this, [this](bool pressed) {
|
|
||||||
if (pressed) {
|
|
||||||
setActive(true);
|
|
||||||
Q_EMIT backPressed();
|
Q_EMIT backPressed();
|
||||||
}
|
break;
|
||||||
});
|
case SDL_GAMEPAD_BUTTON_WEST:
|
||||||
|
Q_EMIT detailsPressed();
|
||||||
connect(m_gamepad, &QGamepad::buttonStartChanged, this, [this](bool pressed) {
|
break;
|
||||||
if (pressed) {
|
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||||
setActive(true);
|
Q_EMIT leftBumperPressed();
|
||||||
|
break;
|
||||||
|
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||||
|
Q_EMIT rightBumperPressed();
|
||||||
|
break;
|
||||||
|
case SDL_GAMEPAD_BUTTON_START:
|
||||||
Q_EMIT menuPressed();
|
Q_EMIT menuPressed();
|
||||||
}
|
break;
|
||||||
});
|
case SDL_GAMEPAD_BUTTON_NORTH:
|
||||||
|
|
||||||
connect(m_gamepad, &QGamepad::buttonYChanged, this, [this](bool pressed) {
|
|
||||||
if (pressed) {
|
|
||||||
setActive(true);
|
|
||||||
Q_EMIT searchPressed();
|
Q_EMIT searchPressed();
|
||||||
}
|
break;
|
||||||
});
|
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||||
|
|
||||||
connect(m_gamepad, &QGamepad::buttonUpChanged, this, [this](bool pressed) {
|
|
||||||
if (pressed) {
|
|
||||||
setActive(true);
|
|
||||||
Q_EMIT navigateUp();
|
Q_EMIT navigateUp();
|
||||||
}
|
break;
|
||||||
});
|
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||||
|
|
||||||
connect(m_gamepad, &QGamepad::buttonDownChanged, this, [this](bool pressed) {
|
|
||||||
if (pressed) {
|
|
||||||
setActive(true);
|
|
||||||
Q_EMIT navigateDown();
|
Q_EMIT navigateDown();
|
||||||
}
|
break;
|
||||||
});
|
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||||
|
|
||||||
connect(m_gamepad, &QGamepad::buttonLeftChanged, this, [this](bool pressed) {
|
|
||||||
if (pressed) {
|
|
||||||
setActive(true);
|
|
||||||
Q_EMIT navigateLeft();
|
Q_EMIT navigateLeft();
|
||||||
}
|
break;
|
||||||
});
|
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||||
|
|
||||||
connect(m_gamepad, &QGamepad::buttonRightChanged, this, [this](bool pressed) {
|
|
||||||
if (pressed) {
|
|
||||||
setActive(true);
|
|
||||||
Q_EMIT navigateRight();
|
Q_EMIT navigateRight();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GamepadManager::processInput()
|
void GamepadManager::processInput()
|
||||||
{
|
{
|
||||||
if (!m_gamepad)
|
if (!m_gamepad) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
double y = m_gamepad->axisLeftY();
|
if (m_leftY < -AxisThreshold) {
|
||||||
double x = m_gamepad->axisLeftX();
|
|
||||||
|
|
||||||
if (y < -0.5) {
|
|
||||||
Q_EMIT navigateUp();
|
Q_EMIT navigateUp();
|
||||||
} else if (y > 0.5) {
|
} else if (m_leftY > AxisThreshold) {
|
||||||
Q_EMIT navigateDown();
|
Q_EMIT navigateDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x < -0.5) {
|
if (m_leftX < -AxisThreshold) {
|
||||||
Q_EMIT navigateLeft();
|
Q_EMIT navigateLeft();
|
||||||
} else if (x > 0.5) {
|
} else if (m_leftX > AxisThreshold) {
|
||||||
Q_EMIT navigateRight();
|
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 <QObject>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
#include <QString>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
class QGamepad;
|
struct SDL_Gamepad;
|
||||||
class QGamepadManager;
|
|
||||||
|
|
||||||
class GamepadManager : public QObject
|
class GamepadManager : public QObject
|
||||||
{
|
{
|
||||||
|
|
@ -18,17 +18,38 @@ class GamepadManager : public QObject
|
||||||
|
|
||||||
Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
|
Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
|
||||||
Q_PROPERTY(bool active READ active NOTIFY activeChanged)
|
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:
|
public:
|
||||||
|
enum ControllerStyle {
|
||||||
|
UnknownController = 0,
|
||||||
|
XboxController,
|
||||||
|
PlayStationController,
|
||||||
|
NintendoController,
|
||||||
|
GenericController,
|
||||||
|
};
|
||||||
|
Q_ENUM(ControllerStyle)
|
||||||
|
|
||||||
static GamepadManager *instance();
|
static GamepadManager *instance();
|
||||||
static GamepadManager *create(QQmlEngine *engine, QJSEngine *scriptEngine);
|
static GamepadManager *create(QQmlEngine *engine, QJSEngine *scriptEngine);
|
||||||
|
|
||||||
bool connected() const;
|
bool connected() const;
|
||||||
bool active() const;
|
bool active() const;
|
||||||
|
|
||||||
|
ControllerStyle controllerStyle() const;
|
||||||
|
QString confirmButtonLabel() const;
|
||||||
|
QString backButtonLabel() const;
|
||||||
|
QString detailsButtonLabel() const;
|
||||||
|
QString searchButtonLabel() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void connectedChanged();
|
void connectedChanged();
|
||||||
void activeChanged();
|
void activeChanged();
|
||||||
|
void controllerInfoChanged();
|
||||||
|
|
||||||
void navigateUp();
|
void navigateUp();
|
||||||
void navigateDown();
|
void navigateDown();
|
||||||
|
|
@ -36,22 +57,40 @@ Q_SIGNALS:
|
||||||
void navigateRight();
|
void navigateRight();
|
||||||
void selectPressed();
|
void selectPressed();
|
||||||
void backPressed();
|
void backPressed();
|
||||||
|
void detailsPressed();
|
||||||
void menuPressed();
|
void menuPressed();
|
||||||
void searchPressed();
|
void searchPressed();
|
||||||
|
|
||||||
|
void leftBumperPressed();
|
||||||
|
void rightBumperPressed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit GamepadManager(QObject *parent = nullptr);
|
explicit GamepadManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
static GamepadManager *s_instance;
|
static GamepadManager *s_instance;
|
||||||
|
|
||||||
QGamepad *m_gamepad = nullptr;
|
SDL_Gamepad *m_gamepad = nullptr;
|
||||||
|
int m_gamepadId = 0;
|
||||||
bool m_connected = false;
|
bool m_connected = false;
|
||||||
bool m_active = 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;
|
QTimer m_repeatTimer;
|
||||||
|
qint16 m_leftX = 0;
|
||||||
|
qint16 m_leftY = 0;
|
||||||
|
qint8 m_leftDirX = 0;
|
||||||
|
qint8 m_leftDirY = 0;
|
||||||
|
|
||||||
void onGamepadConnected(int deviceId);
|
void openFirstAvailableGamepad();
|
||||||
void onGamepadDisconnected(int deviceId);
|
void openGamepad(int deviceId);
|
||||||
void setupGamepad(int deviceId);
|
void closeGamepad();
|
||||||
|
void updateControllerInfo();
|
||||||
|
void pollEvents();
|
||||||
void processInput();
|
void processInput();
|
||||||
void setActive(bool active);
|
void setActive(bool active);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue