diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0843434..6178c0d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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)
diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt
index 53a40e4..3ee763f 100644
--- a/gui/CMakeLists.txt
+++ b/gui/CMakeLists.txt
@@ -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
diff --git a/gui/include/controllermanager.h b/gui/include/controllermanager.h
new file mode 100644
index 0000000..952275e
--- /dev/null
+++ b/gui/include/controllermanager.h
@@ -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 .
+ */
+
+#ifndef CHIAKI_CONTROLLERMANAGER_H
+#define CHIAKI_CONTROLLERMANAGER_H
+
+#include
+
+#include
+#include
+#include
+#include
+
+#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
+#include
+#endif
+
+class Controller;
+
+class ControllerManager : public QObject
+{
+ Q_OBJECT
+
+ friend class Controller;
+
+ private:
+#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
+ QSet available_controllers;
+#endif
+ QMap 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 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
diff --git a/gui/include/streamsession.h b/gui/include/streamsession.h
index 3dab317..6a783de 100644
--- a/gui/include/streamsession.h
+++ b/gui/include/streamsession.h
@@ -21,6 +21,7 @@
#include "videodecoder.h"
#include "exception.h"
#include "sessionlog.h"
+#include "controllermanager.h"
#include
#include
@@ -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();
};
diff --git a/gui/src/controllermanager.cpp b/gui/src/controllermanager.cpp
new file mode 100644
index 0000000..77941de
--- /dev/null
+++ b/gui/src/controllermanager.cpp
@@ -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 .
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+
+#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
+#include
+#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 current_controllers;
+ for(int i=0; iUpdateState();
+}
+
+QList 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; iControllerClosed(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;
+}
\ No newline at end of file
diff --git a/gui/src/main.cpp b/gui/src/main.cpp
index eb95488..716fbca 100644
--- a/gui/src/main.cpp
+++ b/gui/src/main.cpp
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#ifdef CHIAKI_ENABLE_CLI
#include
diff --git a/gui/src/streamsession.cpp b/gui/src/streamsession.cpp
index cb146a6..0f186a9 100644
--- a/gui/src/streamsession.cpp
+++ b/gui/src/streamsession.cpp
@@ -17,6 +17,7 @@
#include
#include
+#include
#include
@@ -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);
}
diff --git a/gui/src/streamwindow.cpp b/gui/src/streamwindow.cpp
index ee99a92..bc91823 100644
--- a/gui/src/streamwindow.cpp
+++ b/gui/src/streamwindow.cpp
@@ -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);