1
0
mirror of https://git.sr.ht/~thestr4ng3r/chiaki synced 2025-03-12 05:25:23 -07:00

Support SDL GameController

This commit is contained in:
Florian Märkl 2019-08-18 17:15:13 +02:00
parent 8fd2610941
commit 84756ba8ef
No known key found for this signature in database
GPG Key ID: 125BC8A5A6A1E857
8 changed files with 391 additions and 9 deletions

@ -5,7 +5,8 @@ project(chiaki)
option(CHIAKI_ENABLE_TESTS "Enable tests for Chiaki" ON)
option(CHIAKI_ENABLE_CLI "Enable CLI for Chiaki" OFF)
option(CHIAKI_GUI_ENABLE_QT_GAMEPAD "Use QtGamepad for Input" ON)
option(CHIAKI_GUI_ENABLE_QT_GAMEPAD "Use QtGamepad for Input" OFF)
option(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER "Use SDL Gamecontroller for Input" ON)
set(CHIAKI_VERSION_MAJOR 1)
set(CHIAKI_VERSION_MINOR 0)

@ -3,11 +3,17 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Multimedia OpenGL Svg)
if(CHIAKI_GUI_ENABLE_QT_GAMEPAD AND CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
message(FATAL_ERROR "Only one input method may be enabled. Disable either CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER or CHIAKI_GUI_ENABLE_QT_GAMEPAD.")
endif()
if(CHIAKI_GUI_ENABLE_QT_GAMEPAD)
find_package(Qt5 REQUIRED COMPONENTS Gamepad)
endif()
if(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
find_package(SDL2 REQUIRED)
endif()
find_package(FFMPEG REQUIRED COMPONENTS avcodec)
find_package(FFMPEG REQUIRED COMPONENTS avcodec avutil)
set(RESOURCE_FILES "")
@ -16,6 +22,7 @@ if(APPLE)
endif()
add_executable(chiaki
${RESOURCE_FILES}
include/exception.h
src/main.cpp
include/streamwindow.h
@ -51,7 +58,8 @@ add_executable(chiaki
include/manualhostdialog.h
src/manualhostdialog.cpp
res/resources.qrc
${RESOURCE_FILES})
include/controllermanager.h
src/controllermanager.cpp)
target_include_directories(chiaki PRIVATE include)
target_link_libraries(chiaki chiaki-lib)
@ -60,12 +68,16 @@ if(CHIAKI_ENABLE_CLI)
target_link_libraries(chiaki chiaki-cli-lib)
endif()
target_link_libraries(chiaki FFMPEG::avcodec)
target_link_libraries(chiaki FFMPEG::avcodec FFMPEG::avutil)
target_link_libraries(chiaki Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Multimedia Qt5::OpenGL Qt5::Svg)
if(CHIAKI_GUI_ENABLE_QT_GAMEPAD)
target_link_libraries(chiaki Qt5::Gamepad)
target_compile_definitions(chiaki PRIVATE CHIAKI_GUI_ENABLE_QT_GAMEPAD)
endif()
if(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
target_link_libraries(chiaki SDL2::SDL2)
target_compile_definitions(chiaki PRIVATE CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
endif()
set_target_properties(chiaki PROPERTIES
MACOSX_BUNDLE TRUE

@ -0,0 +1,96 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_CONTROLLERMANAGER_H
#define CHIAKI_CONTROLLERMANAGER_H
#include <chiaki/controller.h>
#include <QObject>
#include <QSet>
#include <QMap>
#include <QString>
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
#include <SDL.h>
#endif
class Controller;
class ControllerManager : public QObject
{
Q_OBJECT
friend class Controller;
private:
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
QSet<SDL_JoystickID> available_controllers;
#endif
QMap<int, Controller *> open_controllers;
void ControllerClosed(Controller *controller);
private slots:
void UpdateAvailableControllers();
void HandleEvents();
void ControllerEvent(int device_id);
public:
static ControllerManager *GetInstance();
ControllerManager(QObject *parent = nullptr);
~ControllerManager();
QList<int> GetAvailableControllers();
Controller *OpenController(int device_id);
signals:
void AvailableControllersUpdated();
};
class Controller : public QObject
{
Q_OBJECT
friend class ControllerManager;
private:
Controller(int device_id, ControllerManager *manager);
void UpdateState();
ControllerManager *manager;
int id;
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
SDL_GameController *controller;
#endif
public:
~Controller();
bool IsConnected();
int GetDeviceID();
QString GetName();
ChiakiControllerState GetState();
signals:
void StateChanged();
};
#endif // CHIAKI_CONTROLLERMANAGER_H

@ -21,6 +21,7 @@
#include "videodecoder.h"
#include "exception.h"
#include "sessionlog.h"
#include "controllermanager.h"
#include <QObject>
#include <QImage>
@ -68,6 +69,7 @@ class StreamSession : public QObject
#if CHIAKI_GUI_ENABLE_QT_GAMEPAD
QGamepad *gamepad;
#endif
Controller *controller;
ChiakiControllerState keyboard_state;
@ -93,6 +95,7 @@ class StreamSession : public QObject
#if CHIAKI_GUI_ENABLE_QT_GAMEPAD
QGamepad *GetGamepad() { return gamepad; }
#endif
Controller *GetController() { return controller; }
VideoDecoder *GetVideoDecoder() { return &video_decoder; }
void HandleKeyboardEvent(QKeyEvent *event);
@ -102,9 +105,7 @@ class StreamSession : public QObject
void SessionQuit(ChiakiQuitReason reason, const QString &reason_str);
private slots:
#if CHIAKI_GUI_ENABLE_QT_GAMEPAD
void UpdateGamepads();
#endif
void SendFeedbackState();
};

@ -0,0 +1,231 @@
/*
* This file is part of Chiaki.
*
* Chiaki is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chiaki is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
*/
#include <controllermanager.h>
#include <QCoreApplication>
#include <QMessageBox>
#include <QByteArray>
#include <QTimer>
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
#include <SDL.h>
#endif
static ControllerManager *instance = nullptr;
#define UPDATE_INTERVAL_MS 4
ControllerManager *ControllerManager::GetInstance()
{
if(!instance)
instance = new ControllerManager(qApp);
return instance;
}
ControllerManager::ControllerManager(QObject *parent)
: QObject(parent)
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
SDL_SetMainReady();
if(SDL_Init(SDL_INIT_GAMECONTROLLER) < 0)
{
const char *err = SDL_GetError();
QMessageBox::critical(nullptr, "SDL Init", tr("Failed to initialized SDL Gamecontroller: %1").arg(err ? err : ""));
}
auto timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &ControllerManager::HandleEvents);
timer->start(UPDATE_INTERVAL_MS);
#endif
UpdateAvailableControllers();
}
ControllerManager::~ControllerManager()
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
SDL_Quit();
#endif
}
void ControllerManager::UpdateAvailableControllers()
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
QSet<SDL_JoystickID> current_controllers;
for(int i=0; i<SDL_NumJoysticks(); i++)
{
if(!SDL_IsGameController(i))
continue;
current_controllers.insert(SDL_JoystickGetDeviceInstanceID(i));
}
if(current_controllers != available_controllers)
{
available_controllers = current_controllers;
emit AvailableControllersUpdated();
}
#endif
}
void ControllerManager::HandleEvents()
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
SDL_Event event;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_JOYDEVICEADDED:
case SDL_JOYDEVICEREMOVED:
UpdateAvailableControllers();
break;
case SDL_CONTROLLERBUTTONUP:
case SDL_CONTROLLERBUTTONDOWN:
ControllerEvent(event.cbutton.which);
break;
case SDL_CONTROLLERAXISMOTION:
ControllerEvent(event.caxis.which);
break;
}
}
#endif
}
void ControllerManager::ControllerEvent(int device_id)
{
if(!open_controllers.contains(device_id))
return;
open_controllers[device_id]->UpdateState();
}
QList<int> ControllerManager::GetAvailableControllers()
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
return available_controllers.toList();
#else
return {};
#endif
}
Controller *ControllerManager::OpenController(int device_id)
{
if(open_controllers.contains(device_id))
return nullptr;
auto controller = new Controller(device_id, this);
open_controllers[device_id] = controller;
return controller;
}
void ControllerManager::ControllerClosed(Controller *controller)
{
open_controllers.remove(controller->GetDeviceID());
}
Controller::Controller(int device_id, ControllerManager *manager) : QObject(manager)
{
this->id = device_id;
this->manager = manager;
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
controller = nullptr;
for(int i=0; i<SDL_NumJoysticks(); i++)
{
if(SDL_JoystickGetDeviceInstanceID(i) == device_id)
{
controller = SDL_GameControllerOpen(i);
break;
}
}
#endif
}
Controller::~Controller()
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
if(controller)
SDL_GameControllerClose(controller);
#endif
manager->ControllerClosed(this);
}
void Controller::UpdateState()
{
emit StateChanged();
}
bool Controller::IsConnected()
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
return controller && SDL_GameControllerGetAttached(controller);
#else
return false;
#endif
}
int Controller::GetDeviceID()
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
return id;
#else
return -1;
#endif
}
QString Controller::GetName()
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
if(!controller)
return QString();
return SDL_GameControllerName(controller);
#else
return QString();
#endif
}
ChiakiControllerState Controller::GetState()
{
ChiakiControllerState state = {};
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
if(!controller)
return state;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_A) ? CHIAKI_CONTROLLER_BUTTON_CROSS : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_B) ? CHIAKI_CONTROLLER_BUTTON_MOON : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_X) ? CHIAKI_CONTROLLER_BUTTON_BOX : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_Y) ? CHIAKI_CONTROLLER_BUTTON_PYRAMID : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT) ? CHIAKI_CONTROLLER_BUTTON_DPAD_LEFT : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT) ? CHIAKI_CONTROLLER_BUTTON_DPAD_RIGHT : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP) ? CHIAKI_CONTROLLER_BUTTON_DPAD_UP : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN) ? CHIAKI_CONTROLLER_BUTTON_DPAD_DOWN : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER) ? CHIAKI_CONTROLLER_BUTTON_L1 : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) ? CHIAKI_CONTROLLER_BUTTON_R1 : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSTICK) ? CHIAKI_CONTROLLER_BUTTON_L3 : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSTICK) ? CHIAKI_CONTROLLER_BUTTON_R3 : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_START) ? CHIAKI_CONTROLLER_BUTTON_OPTIONS : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_BACK) ? CHIAKI_CONTROLLER_BUTTON_SHARE : 0;
state.buttons |= SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_GUIDE) ? CHIAKI_CONTROLLER_BUTTON_PS : 0;
state.l2_state = (uint8_t)(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) >> 4);
state.r2_state = (uint8_t)(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) >> 4);
state.left_x = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
state.left_y = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
state.right_x = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
state.right_y = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
#endif
return state;
}

@ -7,6 +7,7 @@
#include <registdialog.h>
#include <host.h>
#include <avopenglwidget.h>
#include <controllermanager.h>
#ifdef CHIAKI_ENABLE_CLI
#include <chiaki-cli.h>

@ -17,6 +17,7 @@
#include <streamsession.h>
#include <settings.h>
#include <controllermanager.h>
#include <chiaki/base64.h>
@ -57,6 +58,7 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje
#if CHIAKI_GUI_ENABLE_QT_GAMEPAD
gamepad(nullptr),
#endif
controller(nullptr),
video_decoder(log.GetChiakiLog()),
audio_output(nullptr),
audio_io(nullptr)
@ -87,8 +89,12 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje
#if CHIAKI_GUI_ENABLE_QT_GAMEPAD
connect(QGamepadManager::instance(), &QGamepadManager::connectedGamepadsChanged, this, &StreamSession::UpdateGamepads);
UpdateGamepads();
#endif
#if CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
connect(ControllerManager::GetInstance(), &ControllerManager::AvailableControllersUpdated, this, &StreamSession::UpdateGamepads);
#endif
UpdateGamepads();
}
StreamSession::~StreamSession()
@ -98,6 +104,9 @@ StreamSession::~StreamSession()
#if CHIAKI_GUI_ENABLE_QT_GAMEPAD
delete gamepad;
#endif
#if CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
delete controller;
#endif
}
void StreamSession::Start()
@ -157,9 +166,9 @@ void StreamSession::HandleKeyboardEvent(QKeyEvent *event)
SendFeedbackState();
}
#if CHIAKI_GUI_ENABLE_QT_GAMEPAD
void StreamSession::UpdateGamepads()
{
#if CHIAKI_GUI_ENABLE_QT_GAMEPAD
if(!gamepad || !gamepad->isConnected())
{
if(gamepad)
@ -198,8 +207,33 @@ void StreamSession::UpdateGamepads()
}
SendFeedbackState();
}
#endif
#if CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
if(!controller || !controller->IsConnected())
{
if(controller)
{
CHIAKI_LOGI(log.GetChiakiLog(), "Controller %d disconnected", controller->GetDeviceID());
delete controller;
controller = nullptr;
}
const auto available_controllers = ControllerManager::GetInstance()->GetAvailableControllers();
if(!available_controllers.isEmpty())
{
controller = ControllerManager::GetInstance()->OpenController(available_controllers[0]);
if(!controller)
{
CHIAKI_LOGE(log.GetChiakiLog(), "Failed to open controller %d", available_controllers[0]);
return;
}
CHIAKI_LOGI(log.GetChiakiLog(), "Controller %d opened: \"%s\"", available_controllers[0], controller->GetName().toLocal8Bit().constData());
connect(controller, &Controller::StateChanged, this, &StreamSession::SendFeedbackState);
}
}
SendFeedbackState();
#endif
}
void StreamSession::SendFeedbackState()
{
@ -232,6 +266,11 @@ void StreamSession::SendFeedbackState()
}
#endif
#if CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
if(controller)
state = controller->GetState();
#endif
chiaki_controller_state_or(&state, &state, &keyboard_state);
chiaki_session_set_controller_state(&session, &state);
}

@ -25,6 +25,7 @@
StreamWindow::StreamWindow(const StreamSessionConnectInfo &connect_info, QWidget *parent)
: QMainWindow(parent)
{
setAttribute(Qt::WA_DeleteOnClose);
try
{
session = new StreamSession(connect_info, this);