More work for remappable controllers
This commit is contained in:
@@ -61,25 +61,35 @@ InputSettings::InputSettings(nlohmann::json &settings) : QWidget(nullptr), setti
|
|||||||
kbButtons[17] = std::make_unique<QPushButton>(str.c_str());
|
kbButtons[17] = std::make_unique<QPushButton>(str.c_str());
|
||||||
|
|
||||||
for (int i = 0; i < 18; i++) {
|
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);
|
devices->setEnabled(false);
|
||||||
for (const auto &kbButton : kbButtons) {
|
for (const auto &kbButton : kbButtons) {
|
||||||
kbButton->setEnabled(false);
|
kbButton->setEnabled(false);
|
||||||
}
|
}
|
||||||
grabKeyboard();
|
|
||||||
grabbing = true;
|
grabbing = true;
|
||||||
whichGrabbing = i;
|
whichGrabbing = i;
|
||||||
|
|
||||||
|
if (devices->currentText() == "Keyboard/Mouse") {
|
||||||
|
grabKeyboard();
|
||||||
|
} else {
|
||||||
|
selectedDeviceIsNotKeyboard = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connect(devices.get(), &QComboBox::currentTextChanged, this, [&](const QString &text) {
|
||||||
|
JSONSetField<std::string>(settings, "input", "Device", text.toStdString());
|
||||||
|
emit modified();
|
||||||
|
});
|
||||||
|
|
||||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
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);
|
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");
|
devices->addItem("Keyboard/Mouse");
|
||||||
deviceComboBoxLayout->addWidget(devicesLabel.get());
|
deviceComboBoxLayout->addWidget(devicesLabel.get());
|
||||||
@@ -138,17 +148,21 @@ InputSettings::InputSettings(nlohmann::json &settings) : QWidget(nullptr), setti
|
|||||||
|
|
||||||
void InputSettings::keyPressEvent(QKeyEvent *e) {
|
void InputSettings::keyPressEvent(QKeyEvent *e) {
|
||||||
if (grabbing) {
|
if (grabbing) {
|
||||||
const auto k = QKeySequence(e->key()).toString();
|
const auto k = QKeySequence(e->key()).toString().toStdString();
|
||||||
JSONSetField<std::string>(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k.toStdString());
|
JSONSetField<std::string>(settings, "input", buttonLabels[whichGrabbing]->text().toStdString(), k);
|
||||||
kbButtons[whichGrabbing]->setText(k);
|
kbButtons[whichGrabbing]->setText(k.c_str());
|
||||||
grabbing = false;
|
|
||||||
whichGrabbing = -1;
|
|
||||||
devices->setEnabled(true);
|
devices->setEnabled(true);
|
||||||
for (const auto &kbButton : kbButtons) {
|
for (const auto &kbButton : kbButtons) {
|
||||||
kbButton->setEnabled(true);
|
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<SDL_GamepadButton>(e.gbutton.button));
|
||||||
|
JSONSetField<std::string>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class InputSettings : public QWidget {
|
|||||||
int whichGrabbing = -1;
|
int whichGrabbing = -1;
|
||||||
|
|
||||||
void QueryDevices() noexcept;
|
void QueryDevices() noexcept;
|
||||||
|
void PollGamepad() noexcept;
|
||||||
|
|
||||||
std::unordered_map<u32, std::string> gamepadIndexes{};
|
std::unordered_map<u32, std::string> gamepadIndexes{};
|
||||||
|
|
||||||
@@ -29,12 +30,13 @@ class InputSettings : public QWidget {
|
|||||||
std::array<std::unique_ptr<QPushButton>, 18> kbButtons;
|
std::array<std::unique_ptr<QPushButton>, 18> kbButtons;
|
||||||
std::array<std::unique_ptr<QLabel>, 18> buttonLabels;
|
std::array<std::unique_ptr<QLabel>, 18> buttonLabels;
|
||||||
std::unique_ptr<QHBoxLayout> deviceComboBoxLayout = std::make_unique<QHBoxLayout>();
|
std::unique_ptr<QHBoxLayout> deviceComboBoxLayout = std::make_unique<QHBoxLayout>();
|
||||||
QTimer refresh;
|
QTimer refresh, pollGamepad;
|
||||||
std::unique_ptr<QLabel> devicesLabel = std::make_unique<QLabel>("Device:");
|
std::unique_ptr<QLabel> devicesLabel = std::make_unique<QLabel>("Device:");
|
||||||
std::unique_ptr<QComboBox> devices = std::make_unique<QComboBox>();
|
std::unique_ptr<QComboBox> devices = std::make_unique<QComboBox>();
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
InputSettings(nlohmann::json &);
|
bool selectedDeviceIsNotKeyboard = false;
|
||||||
|
explicit InputSettings(nlohmann::json &);
|
||||||
nlohmann::json &settings;
|
nlohmann::json &settings;
|
||||||
void keyPressEvent(QKeyEvent *) override;
|
void keyPressEvent(QKeyEvent *) override;
|
||||||
std::array<Qt::Key, 18> GetMappedKeys() const;
|
std::array<Qt::Key, 18> GetMappedKeys() const;
|
||||||
|
|||||||
@@ -24,10 +24,17 @@ KaizenQt::KaizenQt() noexcept : QWidget(nullptr) {
|
|||||||
mainWindow->show();
|
mainWindow->show();
|
||||||
debugger->hide();
|
debugger->hide();
|
||||||
settingsWindow->hide();
|
settingsWindow->hide();
|
||||||
connect(settingsWindow.get(), &SettingsWindow::regrabKeyboard, this, [&] { grabKeyboard(); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenQt::ConnectMainWindowSignalsToSlots() noexcept {
|
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::OpenSettings, this, [this] { settingsWindow->show(); });
|
||||||
connect(mainWindow.get(), &MainWindow::OpenDebugger, this, [this] { debugger->show(); });
|
connect(mainWindow.get(), &MainWindow::OpenDebugger, this, [this] { debugger->show(); });
|
||||||
connect(mainWindow.get(), &MainWindow::OpenROM, this, &KaizenQt::LoadROM);
|
connect(mainWindow.get(), &MainWindow::OpenROM, this, &KaizenQt::LoadROM);
|
||||||
@@ -44,22 +51,22 @@ void KaizenQt::dragEnterEvent(QDragEnterEvent *event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KaizenQt::dropEvent(QDropEvent *event) {
|
void KaizenQt::dropEvent(QDropEvent *event) {
|
||||||
auto path = event->mimeData()->urls()[0].toLocalFile();
|
const auto path = event->mimeData()->urls()[0].toLocalFile();
|
||||||
LoadROM(path);
|
LoadROM(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenQt::LoadROM(const QString &fileName) noexcept {
|
void KaizenQt::LoadROM(const QString &path) const noexcept {
|
||||||
mainWindow->actionPause->setEnabled(true);
|
mainWindow->actionPause->setEnabled(true);
|
||||||
mainWindow->actionReset->setEnabled(true);
|
mainWindow->actionReset->setEnabled(true);
|
||||||
mainWindow->actionStop->setEnabled(true);
|
mainWindow->actionStop->setEnabled(true);
|
||||||
emuThread->start();
|
emuThread->start();
|
||||||
emuThread->core->LoadROM(fileName.toStdString());
|
emuThread->core->LoadROM(path.toStdString());
|
||||||
auto gameNameDB = emuThread->core->cpu->GetMem().rom.gameNameDB;
|
const auto gameNameDB = emuThread->core->cpu->GetMem().rom.gameNameDB;
|
||||||
mainWindow->setWindowTitle(emuThread->core->cpu->GetMem().rom.gameNameDB.c_str());
|
mainWindow->setWindowTitle(emuThread->core->cpu->GetMem().rom.gameNameDB.c_str());
|
||||||
Util::RPC::GetInstance().Update(Util::RPC::Playing, gameNameDB);
|
Util::RPC::GetInstance().Update(Util::RPC::Playing, gameNameDB);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenQt::Quit() noexcept {
|
void KaizenQt::Quit() const noexcept {
|
||||||
if (emuThread) {
|
if (emuThread) {
|
||||||
emuThread->requestInterruption();
|
emuThread->requestInterruption();
|
||||||
while (emuThread->isRunning())
|
while (emuThread->isRunning())
|
||||||
@@ -80,11 +87,14 @@ void KaizenQt::LoadTAS(const QString &fileName) const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KaizenQt::keyPressEvent(QKeyEvent *e) {
|
void KaizenQt::keyPressEvent(QKeyEvent *e) {
|
||||||
|
if (settingsWindow->inputSettings->selectedDeviceIsNotKeyboard)
|
||||||
|
return;
|
||||||
|
|
||||||
emuThread->core->pause = true;
|
emuThread->core->pause = true;
|
||||||
n64::Mem &mem = emuThread->core->cpu->GetMem();
|
n64::Mem &mem = emuThread->core->cpu->GetMem();
|
||||||
n64::PIF &pif = mem.mmio.si.pif;
|
n64::PIF &pif = mem.mmio.si.pif;
|
||||||
|
|
||||||
auto k = static_cast<Qt::Key>(e->key());
|
const auto k = static_cast<Qt::Key>(e->key());
|
||||||
for (int i = 0; i < 14; i++) {
|
for (int i = 0; i < 14; i++) {
|
||||||
if (k == settingsWindow->keyMap[i])
|
if (k == settingsWindow->keyMap[i])
|
||||||
pif.UpdateButton(0, static_cast<n64::Controller::Key>(i), true);
|
pif.UpdateButton(0, static_cast<n64::Controller::Key>(i), true);
|
||||||
@@ -104,11 +114,14 @@ void KaizenQt::keyPressEvent(QKeyEvent *e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KaizenQt::keyReleaseEvent(QKeyEvent *e) {
|
void KaizenQt::keyReleaseEvent(QKeyEvent *e) {
|
||||||
|
if (settingsWindow->inputSettings->selectedDeviceIsNotKeyboard)
|
||||||
|
return;
|
||||||
|
|
||||||
emuThread->core->pause = true;
|
emuThread->core->pause = true;
|
||||||
n64::Mem &mem = emuThread->core->cpu->GetMem();
|
n64::Mem &mem = emuThread->core->cpu->GetMem();
|
||||||
n64::PIF &pif = mem.mmio.si.pif;
|
n64::PIF &pif = mem.mmio.si.pif;
|
||||||
|
|
||||||
auto k = static_cast<Qt::Key>(e->key());
|
const auto k = static_cast<Qt::Key>(e->key());
|
||||||
for (int i = 0; i < 14; i++) {
|
for (int i = 0; i < 14; i++) {
|
||||||
if (k == settingsWindow->keyMap[i])
|
if (k == settingsWindow->keyMap[i])
|
||||||
pif.UpdateButton(0, static_cast<n64::Controller::Key>(i), false);
|
pif.UpdateButton(0, static_cast<n64::Controller::Key>(i), false);
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ class KaizenQt : public QWidget {
|
|||||||
public:
|
public:
|
||||||
KaizenQt() noexcept;
|
KaizenQt() noexcept;
|
||||||
void LoadTAS(const QString &path) const 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 dropEvent(QDropEvent *) override;
|
||||||
void dragEnterEvent(QDragEnterEvent *) override;
|
void dragEnterEvent(QDragEnterEvent *) override;
|
||||||
void keyPressEvent(QKeyEvent *) override;
|
void keyPressEvent(QKeyEvent *) override;
|
||||||
void keyReleaseEvent(QKeyEvent *) override;
|
void keyReleaseEvent(QKeyEvent *) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Quit() noexcept;
|
void Quit() const noexcept;
|
||||||
void ConnectMainWindowSignalsToSlots() noexcept;
|
void ConnectMainWindowSignalsToSlots() noexcept;
|
||||||
std::unique_ptr<MainWindow> mainWindow;
|
std::unique_ptr<MainWindow> mainWindow;
|
||||||
std::unique_ptr<SettingsWindow> settingsWindow;
|
std::unique_ptr<SettingsWindow> settingsWindow;
|
||||||
|
|||||||
@@ -29,6 +29,39 @@ RenderWidget::RenderWidget(const std::shared_ptr<n64::Core> &core) : QWidget(nul
|
|||||||
}
|
}
|
||||||
|
|
||||||
void QtWSIPlatform::poll_input() {
|
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) {
|
if (gamepadConnected) {
|
||||||
n64::PIF &pif = core->cpu->GetMem().mmio.si.pif;
|
n64::PIF &pif = core->cpu->GetMem().mmio.si.pif;
|
||||||
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ public:
|
|||||||
|
|
||||||
void event_frame_tick(double frame, double elapsed) override {}
|
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; }
|
const VkApplicationInfo *get_application_info() override { return &appInfo; }
|
||||||
|
|
||||||
VkApplicationInfo appInfo{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .apiVersion = VK_API_VERSION_1_3};
|
VkApplicationInfo appInfo{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .apiVersion = VK_API_VERSION_1_3};
|
||||||
@@ -90,6 +93,7 @@ private:
|
|||||||
std::shared_ptr<n64::Core> core;
|
std::shared_ptr<n64::Core> core;
|
||||||
SDL_Gamepad *gamepad{};
|
SDL_Gamepad *gamepad{};
|
||||||
bool gamepadConnected = false;
|
bool gamepadConnected = false;
|
||||||
|
bool canPollEvents = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RenderWidget : public QWidget {
|
class RenderWidget : public QWidget {
|
||||||
@@ -99,7 +103,7 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] QPaintEngine *paintEngine() const override { return nullptr; }
|
[[nodiscard]] QPaintEngine *paintEngine() const override { return nullptr; }
|
||||||
std::shared_ptr<ParallelRDP::WindowInfo> windowInfo;
|
std::shared_ptr<ParallelRDP::WindowInfo> windowInfo;
|
||||||
std::shared_ptr<Vulkan::WSIPlatform> wsiPlatform;
|
std::shared_ptr<QtWSIPlatform> wsiPlatform;
|
||||||
std::shared_ptr<QtInstanceFactory> qtVkInstanceFactory;
|
std::shared_ptr<QtInstanceFactory> qtVkInstanceFactory;
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void Show() { show(); }
|
void Show() { show(); }
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ SettingsWindow::SettingsWindow() : QWidget(nullptr) {
|
|||||||
|
|
||||||
connect(apply.get(), &QPushButton::pressed, this, [&]() {
|
connect(apply.get(), &QPushButton::pressed, this, [&]() {
|
||||||
auto newMap = inputSettings->GetMappedKeys();
|
auto newMap = inputSettings->GetMappedKeys();
|
||||||
if (!std::equal(keyMap.begin(), keyMap.end(), newMap.begin(), newMap.end())) {
|
if (!std::ranges::equal(keyMap, newMap)) {
|
||||||
keyMap = newMap;
|
keyMap = newMap;
|
||||||
emit regrabKeyboard();
|
emit regrabKeyboard();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,15 +25,20 @@ class SettingsWindow : public QWidget {
|
|||||||
std::unique_ptr<QHBoxLayout> buttonsLayout = std::make_unique<QHBoxLayout>();
|
std::unique_ptr<QHBoxLayout> buttonsLayout = std::make_unique<QHBoxLayout>();
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
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 getVolumeL() { return float(audioSettings->volumeL->value()) / 100.f; }
|
||||||
float getVolumeR() { return float(audioSettings->volumeR->value()) / 100.f; }
|
float getVolumeR() { return float(audioSettings->volumeR->value()) / 100.f; }
|
||||||
|
|
||||||
std::array<Qt::Key, 18> keyMap{};
|
std::array<Qt::Key, 18> keyMap{};
|
||||||
SettingsWindow();
|
|
||||||
nlohmann::json settings;
|
nlohmann::json settings;
|
||||||
std::unique_ptr<CPUSettings> cpuSettings{};
|
std::unique_ptr<CPUSettings> cpuSettings{};
|
||||||
std::unique_ptr<AudioSettings> audioSettings{};
|
std::unique_ptr<AudioSettings> audioSettings{};
|
||||||
std::unique_ptr<InputSettings> inputSettings{};
|
std::unique_ptr<InputSettings> inputSettings{};
|
||||||
std::unique_ptr<QWidget> generalSettings{};
|
std::unique_ptr<QWidget> generalSettings{};
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
void gotOpened();
|
||||||
|
void gotClosed();
|
||||||
void regrabKeyboard();
|
void regrabKeyboard();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user