shift-shell/components/gamingshellplugin/gamepaddevice.cpp
Marco Allegretti bcb985c652 Add quick settings panel and game sources
Slide-out panel with brightness, volume, Wi-Fi, Bluetooth,
and airplane mode. Fully gamepad-navigable; D-pad and stick
input routed to the panel while open. System status bar
(clock, battery, connectivity) added to game center header.

Lutris games discovered from ~/.local/share/lutris/pga.db,
Heroic from ~/.config/heroic/store_cache/ JSON files.
Deduplication extended to cover all launcher sources.

Desktop entries launched via KIO::ApplicationLauncherJob to
expand Exec field codes. Generic commands parsed with
KShell::splitArgs. SQLite uses RAII cleanup guard.

Clamped SDL axis, rumble, and LED values. Renamed rumble
params from frequency to intensity. GamepadManager uses
singleton factory. setPlayerIndex checks SDL return.

FavouritesBar popup uses screen virtualX/Y for multi-monitor.
2026-04-20 14:09:33 +02:00

144 lines
3.3 KiB
C++

// SPDX-FileCopyrightText: 2026 Marco Allegretti
// SPDX-License-Identifier: EUPL-1.2
#include "gamepaddevice.h"
#include <SDL3/SDL_gamepad.h>
#include <SDL3/SDL_power.h>
#include <SDL3/SDL_properties.h>
#include <algorithm>
GamepadDevice::GamepadDevice(SDL_Gamepad *pad, int id, QObject *parent)
: QObject(parent)
, m_pad(pad)
, m_id(id)
{
refreshBattery();
}
GamepadDevice::~GamepadDevice()
{
if (m_pad) {
SDL_CloseGamepad(m_pad);
m_pad = nullptr;
}
}
int GamepadDevice::deviceId() const
{
return m_id;
}
QString GamepadDevice::name() const
{
if (!m_pad) {
return {};
}
const char *n = SDL_GetGamepadName(m_pad);
return n ? QString::fromUtf8(n) : QString();
}
QString GamepadDevice::type() const
{
if (!m_pad) {
return QStringLiteral("unknown");
}
switch (SDL_GetGamepadType(m_pad)) {
case SDL_GAMEPAD_TYPE_XBOX360:
case SDL_GAMEPAD_TYPE_XBOXONE:
return QStringLiteral("xbox");
case SDL_GAMEPAD_TYPE_PS3:
case SDL_GAMEPAD_TYPE_PS4:
case SDL_GAMEPAD_TYPE_PS5:
return QStringLiteral("playstation");
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 QStringLiteral("nintendo");
default:
return QStringLiteral("generic");
}
}
int GamepadDevice::batteryPercent() const
{
return m_batteryPercent;
}
bool GamepadDevice::hasRumble() const
{
if (!m_pad) {
return false;
}
SDL_PropertiesID props = SDL_GetGamepadProperties(m_pad);
return SDL_GetBooleanProperty(props, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, false);
}
bool GamepadDevice::hasLED() const
{
if (!m_pad) {
return false;
}
SDL_PropertiesID props = SDL_GetGamepadProperties(m_pad);
return SDL_GetBooleanProperty(props, SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN, false);
}
int GamepadDevice::playerIndex() const
{
if (!m_pad) {
return -1;
}
return SDL_GetGamepadPlayerIndex(m_pad);
}
void GamepadDevice::setPlayerIndex(int index)
{
if (!m_pad) {
return;
}
if (SDL_SetGamepadPlayerIndex(m_pad, index)) {
Q_EMIT playerIndexChanged();
}
}
bool GamepadDevice::rumble(int lowIntensity, int highIntensity, int durationMs)
{
if (!m_pad) {
return false;
}
auto lo = static_cast<uint16_t>(std::clamp(lowIntensity, 0, 65535));
auto hi = static_cast<uint16_t>(std::clamp(highIntensity, 0, 65535));
auto dur = static_cast<uint32_t>(std::clamp(durationMs, 0, durationMs));
return SDL_RumbleGamepad(m_pad, lo, hi, dur);
}
bool GamepadDevice::setLED(int r, int g, int b)
{
if (!m_pad) {
return false;
}
auto cr = static_cast<uint8_t>(std::clamp(r, 0, 255));
auto cg = static_cast<uint8_t>(std::clamp(g, 0, 255));
auto cb = static_cast<uint8_t>(std::clamp(b, 0, 255));
return SDL_SetGamepadLED(m_pad, cr, cg, cb);
}
SDL_Gamepad *GamepadDevice::sdlGamepad() const
{
return m_pad;
}
void GamepadDevice::refreshBattery()
{
if (!m_pad) {
return;
}
int pct = -1;
SDL_GetGamepadPowerInfo(m_pad, &pct);
if (pct != m_batteryPercent) {
m_batteryPercent = pct;
Q_EMIT batteryPercentChanged();
}
}