From b6f795a4df4ea5675ec0995c27265122b74b1791 Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Sat, 28 Sep 2024 14:54:24 +0200 Subject: [PATCH] More work for remappable controllers --- src/frontend/InputSettings.cpp | 71 ++++++++++++++++++++++++++------- src/frontend/InputSettings.hpp | 6 ++- src/frontend/KaizenQt.cpp | 29 ++++++++++---- src/frontend/KaizenQt.hpp | 4 +- src/frontend/RenderWidget.cpp | 33 +++++++++++++++ src/frontend/RenderWidget.hpp | 6 ++- src/frontend/SettingsWindow.cpp | 2 +- src/frontend/SettingsWindow.hpp | 7 +++- 8 files changed, 129 insertions(+), 29 deletions(-) diff --git a/src/frontend/InputSettings.cpp b/src/frontend/InputSettings.cpp index 04a0828e..948b1331 100644 --- a/src/frontend/InputSettings.cpp +++ b/src/frontend/InputSettings.cpp @@ -61,25 +61,35 @@ InputSettings::InputSettings(nlohmann::json &settings) : QWidget(nullptr), setti kbButtons[17] = std::make_unique(str.c_str()); for (int i = 0; i < 18; i++) { - connect(kbButtons[i].get(), &QPushButton::pressed, this, [&, i]() { + connect(kbButtons[i].get(), &QPushButton::pressed, this, [&, i] { devices->setEnabled(false); for (const auto &kbButton : kbButtons) { kbButton->setEnabled(false); } - grabKeyboard(); + grabbing = true; whichGrabbing = i; + + if (devices->currentText() == "Keyboard/Mouse") { + grabKeyboard(); + } else { + selectedDeviceIsNotKeyboard = true; + } }); } + connect(devices.get(), &QComboBox::currentTextChanged, this, [&](const QString &text) { + JSONSetField(settings, "input", "Device", text.toStdString()); + emit modified(); + }); + SDL_InitSubSystem(SDL_INIT_GAMEPAD); - if (SDL_AddGamepadMappingsFromFile("resources/gamecontrollerdb.txt") < 0) { - Util::warn("[SDL] Could not load game controller DB"); - } - connect(&refresh, &QTimer::timeout, this, &InputSettings::QueryDevices); - refresh.start(1000); + refresh.start(16); + + connect(&pollGamepad, &QTimer::timeout, this, &InputSettings::PollGamepad); + pollGamepad.start(16); devices->addItem("Keyboard/Mouse"); deviceComboBoxLayout->addWidget(devicesLabel.get()); @@ -138,17 +148,21 @@ InputSettings::InputSettings(nlohmann::json &settings) : QWidget(nullptr), setti void InputSettings::keyPressEvent(QKeyEvent *e) { if (grabbing) { - const auto k = QKeySequence(e->key()).toString(); - JSONSetField(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k.toStdString()); - kbButtons[whichGrabbing]->setText(k); - grabbing = false; - whichGrabbing = -1; + const auto k = QKeySequence(e->key()).toString().toStdString(); + JSONSetField(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k); + kbButtons[whichGrabbing]->setText(k.c_str()); devices->setEnabled(true); for (const auto &kbButton : kbButtons) { kbButton->setEnabled(true); } - releaseKeyboard(); - emit modified(); + + grabbing = false; + whichGrabbing = -1; + + if (devices->currentText() == "Keyboard/Mouse") { + releaseKeyboard(); + emit modified(); + } } } @@ -210,3 +224,32 @@ void InputSettings::QueryDevices() noexcept { } } } + +void InputSettings::PollGamepad() noexcept { + if (!selectedDeviceIsNotKeyboard) + return; + + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch (e.type) { + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + { + const auto k = SDL_GetGamepadStringForButton(static_cast(e.gbutton.button)); + JSONSetField(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k); + kbButtons[whichGrabbing]->setText(k); + devices->setEnabled(true); + for (const auto &kbButton : kbButtons) { + kbButton->setEnabled(true); + } + + grabbing = false; + whichGrabbing = -1; + + emit modified(); + } + break; + default: + break; + } + } +} diff --git a/src/frontend/InputSettings.hpp b/src/frontend/InputSettings.hpp index aaca1ec4..0ca408df 100644 --- a/src/frontend/InputSettings.hpp +++ b/src/frontend/InputSettings.hpp @@ -13,6 +13,7 @@ class InputSettings : public QWidget { int whichGrabbing = -1; void QueryDevices() noexcept; + void PollGamepad() noexcept; std::unordered_map gamepadIndexes{}; @@ -29,12 +30,13 @@ class InputSettings : public QWidget { std::array, 18> kbButtons; std::array, 18> buttonLabels; std::unique_ptr deviceComboBoxLayout = std::make_unique(); - QTimer refresh; + QTimer refresh, pollGamepad; std::unique_ptr devicesLabel = std::make_unique("Device:"); std::unique_ptr devices = std::make_unique(); Q_OBJECT public: - InputSettings(nlohmann::json &); + bool selectedDeviceIsNotKeyboard = false; + explicit InputSettings(nlohmann::json &); nlohmann::json &settings; void keyPressEvent(QKeyEvent *) override; std::array GetMappedKeys() const; diff --git a/src/frontend/KaizenQt.cpp b/src/frontend/KaizenQt.cpp index eb2004a8..bf8649a2 100644 --- a/src/frontend/KaizenQt.cpp +++ b/src/frontend/KaizenQt.cpp @@ -24,10 +24,17 @@ KaizenQt::KaizenQt() noexcept : QWidget(nullptr) { mainWindow->show(); debugger->hide(); settingsWindow->hide(); - connect(settingsWindow.get(), &SettingsWindow::regrabKeyboard, this, [&] { grabKeyboard(); }); } void KaizenQt::ConnectMainWindowSignalsToSlots() noexcept { + connect(settingsWindow.get(), &SettingsWindow::regrabKeyboard, this, [&] { grabKeyboard(); }); + + connect(settingsWindow.get(), &SettingsWindow::gotClosed, this, + [&] { mainWindow->vulkanWidget->wsiPlatform->EnableEventPolling(); }); + + connect(settingsWindow.get(), &SettingsWindow::gotOpened, this, + [&] { mainWindow->vulkanWidget->wsiPlatform->DisableEventPolling(); }); + connect(mainWindow.get(), &MainWindow::OpenSettings, this, [this] { settingsWindow->show(); }); connect(mainWindow.get(), &MainWindow::OpenDebugger, this, [this] { debugger->show(); }); connect(mainWindow.get(), &MainWindow::OpenROM, this, &KaizenQt::LoadROM); @@ -44,22 +51,22 @@ void KaizenQt::dragEnterEvent(QDragEnterEvent *event) { } void KaizenQt::dropEvent(QDropEvent *event) { - auto path = event->mimeData()->urls()[0].toLocalFile(); + const auto path = event->mimeData()->urls()[0].toLocalFile(); LoadROM(path); } -void KaizenQt::LoadROM(const QString &fileName) noexcept { +void KaizenQt::LoadROM(const QString &path) const noexcept { mainWindow->actionPause->setEnabled(true); mainWindow->actionReset->setEnabled(true); mainWindow->actionStop->setEnabled(true); emuThread->start(); - emuThread->core->LoadROM(fileName.toStdString()); - auto gameNameDB = emuThread->core->cpu->GetMem().rom.gameNameDB; + emuThread->core->LoadROM(path.toStdString()); + const auto gameNameDB = emuThread->core->cpu->GetMem().rom.gameNameDB; mainWindow->setWindowTitle(emuThread->core->cpu->GetMem().rom.gameNameDB.c_str()); Util::RPC::GetInstance().Update(Util::RPC::Playing, gameNameDB); } -void KaizenQt::Quit() noexcept { +void KaizenQt::Quit() const noexcept { if (emuThread) { emuThread->requestInterruption(); while (emuThread->isRunning()) @@ -80,11 +87,14 @@ void KaizenQt::LoadTAS(const QString &fileName) const noexcept { } void KaizenQt::keyPressEvent(QKeyEvent *e) { + if (settingsWindow->inputSettings->selectedDeviceIsNotKeyboard) + return; + emuThread->core->pause = true; n64::Mem &mem = emuThread->core->cpu->GetMem(); n64::PIF &pif = mem.mmio.si.pif; - auto k = static_cast(e->key()); + const auto k = static_cast(e->key()); for (int i = 0; i < 14; i++) { if (k == settingsWindow->keyMap[i]) pif.UpdateButton(0, static_cast(i), true); @@ -104,11 +114,14 @@ void KaizenQt::keyPressEvent(QKeyEvent *e) { } void KaizenQt::keyReleaseEvent(QKeyEvent *e) { + if (settingsWindow->inputSettings->selectedDeviceIsNotKeyboard) + return; + emuThread->core->pause = true; n64::Mem &mem = emuThread->core->cpu->GetMem(); n64::PIF &pif = mem.mmio.si.pif; - auto k = static_cast(e->key()); + const auto k = static_cast(e->key()); for (int i = 0; i < 14; i++) { if (k == settingsWindow->keyMap[i]) pif.UpdateButton(0, static_cast(i), false); diff --git a/src/frontend/KaizenQt.hpp b/src/frontend/KaizenQt.hpp index cc8fef49..86cd25b9 100644 --- a/src/frontend/KaizenQt.hpp +++ b/src/frontend/KaizenQt.hpp @@ -27,14 +27,14 @@ class KaizenQt : public QWidget { public: KaizenQt() noexcept; void LoadTAS(const QString &path) const noexcept; - void LoadROM(const QString &path) noexcept; + void LoadROM(const QString &path) const noexcept; void dropEvent(QDropEvent *) override; void dragEnterEvent(QDragEnterEvent *) override; void keyPressEvent(QKeyEvent *) override; void keyReleaseEvent(QKeyEvent *) override; private: - void Quit() noexcept; + void Quit() const noexcept; void ConnectMainWindowSignalsToSlots() noexcept; std::unique_ptr mainWindow; std::unique_ptr settingsWindow; diff --git a/src/frontend/RenderWidget.cpp b/src/frontend/RenderWidget.cpp index c1debbb7..b73e2f81 100644 --- a/src/frontend/RenderWidget.cpp +++ b/src/frontend/RenderWidget.cpp @@ -29,6 +29,39 @@ RenderWidget::RenderWidget(const std::shared_ptr &core) : QWidget(nul } void QtWSIPlatform::poll_input() { + if (!canPollEvents) + return; + + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch (e.type) { + case SDL_EVENT_GAMEPAD_ADDED: + { + const auto index = e.gdevice.which; + + gamepad = SDL_OpenGamepad(index); + Util::info("Controller found!"); + + const auto serial = SDL_GetGamepadSerial(gamepad); + const auto name = SDL_GetGamepadName(gamepad); + const auto path = SDL_GetGamepadPath(gamepad); + + Util::info("\tName: {}", name ? name : "Not available"); + Util::info("\tSerial: {}", serial ? serial : "Not available"); + Util::info("\tPath: {}", path ? path : "Not available"); + + gamepadConnected = true; + } + break; + case SDL_EVENT_GAMEPAD_REMOVED: + { + gamepadConnected = false; + SDL_CloseGamepad(gamepad); + } + break; + } + } + if (gamepadConnected) { n64::PIF &pif = core->cpu->GetMem().mmio.si.pif; pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); diff --git a/src/frontend/RenderWidget.hpp b/src/frontend/RenderWidget.hpp index 1083099e..713f6993 100644 --- a/src/frontend/RenderWidget.hpp +++ b/src/frontend/RenderWidget.hpp @@ -80,6 +80,9 @@ public: void event_frame_tick(double frame, double elapsed) override {} + void EnableEventPolling() { canPollEvents = true; } + void DisableEventPolling() { canPollEvents = false; } + const VkApplicationInfo *get_application_info() override { return &appInfo; } VkApplicationInfo appInfo{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .apiVersion = VK_API_VERSION_1_3}; @@ -90,6 +93,7 @@ private: std::shared_ptr core; SDL_Gamepad *gamepad{}; bool gamepadConnected = false; + bool canPollEvents = true; }; class RenderWidget : public QWidget { @@ -99,7 +103,7 @@ public: [[nodiscard]] QPaintEngine *paintEngine() const override { return nullptr; } std::shared_ptr windowInfo; - std::shared_ptr wsiPlatform; + std::shared_ptr wsiPlatform; std::shared_ptr qtVkInstanceFactory; Q_SIGNALS: void Show() { show(); } diff --git a/src/frontend/SettingsWindow.cpp b/src/frontend/SettingsWindow.cpp index f2094a9a..26e093ad 100644 --- a/src/frontend/SettingsWindow.cpp +++ b/src/frontend/SettingsWindow.cpp @@ -52,7 +52,7 @@ SettingsWindow::SettingsWindow() : QWidget(nullptr) { connect(apply.get(), &QPushButton::pressed, this, [&]() { auto newMap = inputSettings->GetMappedKeys(); - if (!std::equal(keyMap.begin(), keyMap.end(), newMap.begin(), newMap.end())) { + if (!std::ranges::equal(keyMap, newMap)) { keyMap = newMap; emit regrabKeyboard(); } diff --git a/src/frontend/SettingsWindow.hpp b/src/frontend/SettingsWindow.hpp index 3969ca35..178c5b67 100644 --- a/src/frontend/SettingsWindow.hpp +++ b/src/frontend/SettingsWindow.hpp @@ -25,15 +25,20 @@ class SettingsWindow : public QWidget { std::unique_ptr buttonsLayout = std::make_unique(); Q_OBJECT public: + SettingsWindow(); + void hideEvent(QHideEvent *event) override { emit gotClosed(); } + void showEvent(QShowEvent *event) override { emit gotOpened(); } float getVolumeL() { return float(audioSettings->volumeL->value()) / 100.f; } float getVolumeR() { return float(audioSettings->volumeR->value()) / 100.f; } + std::array keyMap{}; - SettingsWindow(); nlohmann::json settings; std::unique_ptr cpuSettings{}; std::unique_ptr audioSettings{}; std::unique_ptr inputSettings{}; std::unique_ptr generalSettings{}; Q_SIGNALS: + void gotOpened(); + void gotClosed(); void regrabKeyboard(); };