From a605ba083d1cfde8154c6372446440bee5d5d0eb Mon Sep 17 00:00:00 2001 From: Felipe Lavratti Date: Sat, 29 Feb 2020 11:38:03 -0300 Subject: [PATCH] GUI: Add settings for keyboard mapping (#174) --- gui/CMakeLists.txt | 2 + gui/include/settings.h | 18 +++++ gui/include/settingskeycapturedialog.h | 37 ++++++++++ gui/include/streamsession.h | 5 +- gui/src/settings.cpp | 97 ++++++++++++++++++++++++++ gui/src/settingsdialog.cpp | 61 +++++++++++++--- gui/src/settingskeycapturedialog.cpp | 45 ++++++++++++ gui/src/streamsession.cpp | 79 ++++++++++----------- 8 files changed, 292 insertions(+), 52 deletions(-) create mode 100644 gui/include/settingskeycapturedialog.h create mode 100644 gui/src/settingskeycapturedialog.cpp diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 76aec59..072320f 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -60,6 +60,8 @@ add_executable(chiaki WIN32 src/registdialog.cpp include/host.h src/host.cpp + include/settingskeycapturedialog.h + src/settingskeycapturedialog.cpp include/settingsdialog.h src/settingsdialog.cpp include/manualhostdialog.h diff --git a/gui/include/settings.h b/gui/include/settings.h index 71cff6c..e15a56a 100644 --- a/gui/include/settings.h +++ b/gui/include/settings.h @@ -24,6 +24,19 @@ #include +enum class ControllerButtonExt +{ + // must not overlap with ChiakiControllerButton and ChiakiControllerAnalogButton + ANALOG_STICK_LEFT_X_UP = (1 << 18), + ANALOG_STICK_LEFT_X_DOWN = (1 << 19), + ANALOG_STICK_LEFT_Y_UP = (1 << 20), + ANALOG_STICK_LEFT_Y_DOWN = (1 << 21), + ANALOG_STICK_RIGHT_X_UP = (1 << 22), + ANALOG_STICK_RIGHT_X_DOWN = (1 << 23), + ANALOG_STICK_RIGHT_Y_UP = (1 << 24), + ANALOG_STICK_RIGHT_Y_DOWN = (1 << 25), +}; + class Settings : public QObject { Q_OBJECT @@ -90,6 +103,11 @@ class Settings : public QObject bool GetManualHostExists(int id) { return manual_hosts.contains(id); } ManualHost GetManualHost(int id) const { return manual_hosts[id]; } + static QString GetChiakiControllerButtonName(int); + void SetControllerButtonMapping(int, Qt::Key); + QMap GetControllerMapping(); + QMap GetControllerMappingForDecoding(); + signals: void RegisteredHostsUpdated(); void ManualHostsUpdated(); diff --git a/gui/include/settingskeycapturedialog.h b/gui/include/settingskeycapturedialog.h new file mode 100644 index 0000000..a4d7b36 --- /dev/null +++ b/gui/include/settingskeycapturedialog.h @@ -0,0 +1,37 @@ +/* + * 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_SETTINGSKEYCAPTUREDIALOG_H +#define CHIAKI_SETTINGSKEYCAPTUREDIALOG_H + +#include + +class SettingsKeyCaptureDialog : public QDialog +{ + Q_OBJECT + + signals: + void KeyCaptured(Qt::Key); + + protected: + void keyReleaseEvent(QKeyEvent *event) override; + + public: + explicit SettingsKeyCaptureDialog(QWidget *parent = nullptr); +}; + +#endif // CHIAKI_SETTINGSKEYCAPTUREDIALOG_H diff --git a/gui/include/streamsession.h b/gui/include/streamsession.h index 721270b..fabb634 100644 --- a/gui/include/streamsession.h +++ b/gui/include/streamsession.h @@ -29,6 +29,7 @@ #include #include #include +#include #if CHIAKI_GUI_ENABLE_QT_GAMEPAD class QGamepad; @@ -47,6 +48,7 @@ class ChiakiException: public Exception struct StreamSessionConnectInfo { + QMap key_map; uint32_t log_level_mask; QString log_file; QString host; @@ -55,7 +57,6 @@ struct StreamSessionConnectInfo ChiakiConnectVideoProfile video_profile; unsigned int audio_buffer_size; - StreamSessionConnectInfo(); StreamSessionConnectInfo(Settings *settings, QString host, QByteArray regist_key, QByteArray morning); }; @@ -83,6 +84,8 @@ class StreamSession : public QObject QAudioOutput *audio_output; QIODevice *audio_io; + QMap key_map; + void PushAudioFrame(int16_t *buf, size_t samples_count); void PushVideoSample(uint8_t *buf, size_t buf_size); void Event(ChiakiEvent *event); diff --git a/gui/src/settings.cpp b/gui/src/settings.cpp index b74552b..559c4e4 100644 --- a/gui/src/settings.cpp +++ b/gui/src/settings.cpp @@ -16,6 +16,7 @@ */ #include +#include #define SETTINGS_VERSION 1 @@ -207,3 +208,99 @@ void Settings::RemoveManualHost(int id) SaveManualHosts(); emit ManualHostsUpdated(); } + +QString Settings::GetChiakiControllerButtonName(int button) +{ + switch(button) + { + case CHIAKI_CONTROLLER_BUTTON_CROSS : return tr("Cross"); + case CHIAKI_CONTROLLER_BUTTON_MOON : return tr("Moon"); + case CHIAKI_CONTROLLER_BUTTON_BOX : return tr("Box"); + case CHIAKI_CONTROLLER_BUTTON_PYRAMID : return tr("Pyramid"); + case CHIAKI_CONTROLLER_BUTTON_DPAD_LEFT : return tr("D-Pad Left"); + case CHIAKI_CONTROLLER_BUTTON_DPAD_RIGHT : return tr("D-Pad Right"); + case CHIAKI_CONTROLLER_BUTTON_DPAD_UP : return tr("D-Pad Up"); + case CHIAKI_CONTROLLER_BUTTON_DPAD_DOWN : return tr("D-Pad Down"); + case CHIAKI_CONTROLLER_BUTTON_L1 : return tr("L1"); + case CHIAKI_CONTROLLER_BUTTON_R1 : return tr("R1"); + case CHIAKI_CONTROLLER_BUTTON_L3 : return tr("L3"); + case CHIAKI_CONTROLLER_BUTTON_R3 : return tr("R3"); + case CHIAKI_CONTROLLER_BUTTON_OPTIONS : return tr("Options"); + case CHIAKI_CONTROLLER_BUTTON_SHARE : return tr("Share"); + case CHIAKI_CONTROLLER_BUTTON_TOUCHPAD : return tr("Touchpad"); + case CHIAKI_CONTROLLER_BUTTON_PS : return tr("PS"); + case CHIAKI_CONTROLLER_ANALOG_BUTTON_L2 : return tr("L2"); + case CHIAKI_CONTROLLER_ANALOG_BUTTON_R2 : return tr("R2"); + case static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_X_UP) : return tr("Left Stick Right"); + case static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_Y_UP) : return tr("Left Stick Up"); + case static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_X_UP) : return tr("Right Stick Right"); + case static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_Y_UP) : return tr("Right Stick Up"); + case static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_X_DOWN) : return tr("Left Stick Left"); + case static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_Y_DOWN) : return tr("Left Stick Down"); + case static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_X_DOWN) : return tr("Right Stick Left"); + case static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_Y_DOWN) : return tr("Right Stick Down"); + default: return "Unknown"; + } +} + +void Settings::SetControllerButtonMapping(int chiaki_button, Qt::Key key) +{ + auto button_name = GetChiakiControllerButtonName(chiaki_button).replace(' ', '_').toLower(); + settings.setValue("keymap/" + button_name, QKeySequence(key).toString()); +} + +QMap Settings::GetControllerMapping() +{ + // Initialize with default values + QMap result = + { + {CHIAKI_CONTROLLER_BUTTON_CROSS , Qt::Key::Key_Return}, + {CHIAKI_CONTROLLER_BUTTON_MOON , Qt::Key::Key_Backspace}, + {CHIAKI_CONTROLLER_BUTTON_BOX , Qt::Key::Key_Backslash}, + {CHIAKI_CONTROLLER_BUTTON_PYRAMID , Qt::Key::Key_C}, + {CHIAKI_CONTROLLER_BUTTON_DPAD_LEFT , Qt::Key::Key_Left}, + {CHIAKI_CONTROLLER_BUTTON_DPAD_RIGHT, Qt::Key::Key_Right}, + {CHIAKI_CONTROLLER_BUTTON_DPAD_UP , Qt::Key::Key_Up}, + {CHIAKI_CONTROLLER_BUTTON_DPAD_DOWN , Qt::Key::Key_Down}, + {CHIAKI_CONTROLLER_BUTTON_L1 , Qt::Key::Key_2}, + {CHIAKI_CONTROLLER_BUTTON_R1 , Qt::Key::Key_3}, + {CHIAKI_CONTROLLER_BUTTON_L3 , Qt::Key::Key_5}, + {CHIAKI_CONTROLLER_BUTTON_R3 , Qt::Key::Key_6}, + {CHIAKI_CONTROLLER_BUTTON_OPTIONS , Qt::Key::Key_O}, + {CHIAKI_CONTROLLER_BUTTON_SHARE , Qt::Key::Key_F}, + {CHIAKI_CONTROLLER_BUTTON_TOUCHPAD , Qt::Key::Key_T}, + {CHIAKI_CONTROLLER_BUTTON_PS , Qt::Key::Key_Escape}, + {CHIAKI_CONTROLLER_ANALOG_BUTTON_L2 , Qt::Key::Key_1}, + {CHIAKI_CONTROLLER_ANALOG_BUTTON_R2 , Qt::Key::Key_4}, + {static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_X_UP) , Qt::Key::Key_BracketRight}, + {static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_X_DOWN) , Qt::Key::Key_BracketLeft}, + {static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_Y_UP) , Qt::Key::Key_Insert}, + {static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_Y_DOWN) , Qt::Key::Key_Delete}, + {static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_X_UP) , Qt::Key::Key_Equal}, + {static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_X_DOWN), Qt::Key::Key_Minus}, + {static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_Y_UP) , Qt::Key::Key_PageUp}, + {static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_Y_DOWN), Qt::Key::Key_PageDown} + }; + + // Then fill in from settings + auto chiaki_buttons = result.keys(); + for(auto chiaki_button : chiaki_buttons) + { + auto button_name = GetChiakiControllerButtonName(chiaki_button).replace(' ', '_').toLower(); + if(settings.contains("keymap/" + button_name)) + result[static_cast(chiaki_button)] = Qt::Key(QKeySequence(settings.value("keymap/" + button_name).toString())[0]); + } + + return result; +} + +QMap Settings::GetControllerMappingForDecoding() +{ + auto map = GetControllerMapping(); + QMap result; + for(auto it = map.begin(); it != map.end(); ++it) + { + result[it.value()] = it.key(); + } + return result; +} diff --git a/gui/src/settingsdialog.cpp b/gui/src/settingsdialog.cpp index 86b47d9..30dc00a 100644 --- a/gui/src/settingsdialog.cpp +++ b/gui/src/settingsdialog.cpp @@ -17,9 +17,11 @@ #include #include +#include #include #include +#include #include #include #include @@ -32,7 +34,6 @@ #include #include - const char * const about_string = "

Chiaki

by thestr4ng3r, version " CHIAKI_VERSION "" @@ -46,21 +47,28 @@ const char * const about_string = "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " "GNU General Public License for more details.

"; - SettingsDialog::SettingsDialog(Settings *settings, QWidget *parent) : QDialog(parent) { this->settings = settings; setWindowTitle(tr("Settings")); - auto layout = new QVBoxLayout(this); - setLayout(layout); + auto root_layout = new QVBoxLayout(this); + setLayout(root_layout); + auto horizontal_layout = new QHBoxLayout(); + root_layout->addLayout(horizontal_layout); + + auto left_layout = new QVBoxLayout(); + horizontal_layout->addLayout(left_layout); + + auto right_layout = new QVBoxLayout(); + horizontal_layout->addLayout(right_layout); // General auto general_group_box = new QGroupBox(tr("General")); - layout->addWidget(general_group_box); + left_layout->addWidget(general_group_box); auto general_layout = new QFormLayout(); general_group_box->setLayout(general_layout); @@ -85,7 +93,7 @@ SettingsDialog::SettingsDialog(Settings *settings, QWidget *parent) : QDialog(pa // Stream Settings auto stream_settings_group_box = new QGroupBox(tr("Stream Settings")); - layout->addWidget(stream_settings_group_box); + left_layout->addWidget(stream_settings_group_box); auto stream_settings_layout = new QFormLayout(); stream_settings_group_box->setLayout(stream_settings_layout); @@ -141,7 +149,7 @@ SettingsDialog::SettingsDialog(Settings *settings, QWidget *parent) : QDialog(pa // Registered Consoles auto registered_hosts_group_box = new QGroupBox(tr("Registered Consoles")); - layout->addWidget(registered_hosts_group_box); + left_layout->addWidget(registered_hosts_group_box); auto registered_hosts_layout = new QHBoxLayout(); registered_hosts_group_box->setLayout(registered_hosts_layout); @@ -162,8 +170,43 @@ SettingsDialog::SettingsDialog(Settings *settings, QWidget *parent) : QDialog(pa registered_hosts_buttons_layout->addStretch(); + // Key Settings + auto key_settings_group_box = new QGroupBox(tr("Key Settings")); + right_layout->addWidget(key_settings_group_box); + auto key_horizontal = new QHBoxLayout(); + key_settings_group_box->setLayout(key_horizontal); + key_horizontal->setSpacing(10); + + auto key_left_form = new QFormLayout(); + auto key_right_form = new QFormLayout(); + key_horizontal->addLayout(key_left_form); + key_horizontal->addLayout(key_right_form); + + QMap key_map = this->settings->GetControllerMapping(); + + int i = 0; + for(auto it = key_map.begin(); it != key_map.end(); ++it, ++i) + { + int chiaki_button = it.key(); + auto button = new QPushButton(QKeySequence(it.value()).toString(), this); + button->setAutoDefault(false); + auto form = i % 2 ? key_left_form : key_right_form; + form->addRow(Settings::GetChiakiControllerButtonName(chiaki_button), button); + // Launch key capture dialog on clicked event + connect(button, &QPushButton::clicked, this, [this, chiaki_button, button](){ + auto dialog = new SettingsKeyCaptureDialog(this); + // Store captured key to settings and change button label on KeyCaptured event + connect(dialog, &SettingsKeyCaptureDialog::KeyCaptured, button, [this, button, chiaki_button](Qt::Key key){ + button->setText(QKeySequence(key).toString()); + this->settings->SetControllerButtonMapping(chiaki_button, key); + }); + dialog->exec(); + }); + } + + // Close Button auto button_box = new QDialogButtonBox(QDialogButtonBox::Close, this); - layout->addWidget(button_box); + root_layout->addWidget(button_box); connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); button_box->button(QDialogButtonBox::Close)->setDefault(true); @@ -241,4 +284,4 @@ void SettingsDialog::DeleteRegisteredHost() return; settings->RemoveRegisteredHost(mac); -} \ No newline at end of file +} diff --git a/gui/src/settingskeycapturedialog.cpp b/gui/src/settingskeycapturedialog.cpp new file mode 100644 index 0000000..10e0bb3 --- /dev/null +++ b/gui/src/settingskeycapturedialog.cpp @@ -0,0 +1,45 @@ +/* + * 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 "settingskeycapturedialog.h" + +#include +#include +#include +#include + +SettingsKeyCaptureDialog::SettingsKeyCaptureDialog(QWidget* parent) +{ + setWindowTitle(tr("Key Capture")); + + auto root_layout = new QVBoxLayout(this); + setLayout(root_layout); + + auto label = new QLabel(tr("Press any key to configure button or click close.")); + root_layout->addWidget(label); + + auto button = new QPushButton(tr("Close"), this); + root_layout->addWidget(button); + button->setAutoDefault(false); + connect(button, &QPushButton::clicked, this, &QDialog::accept); +} + +void SettingsKeyCaptureDialog::keyReleaseEvent(QKeyEvent* event) +{ + KeyCaptured(Qt::Key(event->key())); + accept(); +} diff --git a/gui/src/streamsession.cpp b/gui/src/streamsession.cpp index e4edf59..2e427f8 100644 --- a/gui/src/streamsession.cpp +++ b/gui/src/streamsession.cpp @@ -32,15 +32,9 @@ #include #include -StreamSessionConnectInfo::StreamSessionConnectInfo() -{ - log_level_mask = CHIAKI_LOG_ALL; - std::memset(&video_profile, 0, sizeof(video_profile)); - audio_buffer_size = 9600; -} - StreamSessionConnectInfo::StreamSessionConnectInfo(Settings *settings, QString host, QByteArray regist_key, QByteArray morning) { + key_map = settings->GetControllerMappingForDecoding(); log_level_mask = settings->GetLogLevelMask(); log_file = CreateLogFilename(); video_profile = settings->GetVideoProfile(); @@ -104,6 +98,7 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje connect(ControllerManager::GetInstance(), &ControllerManager::AvailableControllersUpdated, this, &StreamSession::UpdateGamepads); #endif + key_map = connect_info.key_map; UpdateGamepads(); } @@ -152,55 +147,55 @@ void StreamSession::HandleMouseEvent(QMouseEvent *event) void StreamSession::HandleKeyboardEvent(QKeyEvent *event) { - uint64_t button_mask; - switch(event->key()) + if(key_map.contains(Qt::Key(event->key())) == false) + return; + + if(event->isAutoRepeat()) + return; + + int button = key_map[Qt::Key(event->key())]; + bool press_event = event->type() == QEvent::Type::KeyPress; + + switch(button) { - case Qt::Key::Key_Left: - button_mask = CHIAKI_CONTROLLER_BUTTON_DPAD_LEFT; + case CHIAKI_CONTROLLER_ANALOG_BUTTON_L2: + keyboard_state.l2_state = press_event ? 0xff : 0; break; - case Qt::Key::Key_Right: - button_mask = CHIAKI_CONTROLLER_BUTTON_DPAD_RIGHT; + case CHIAKI_CONTROLLER_ANALOG_BUTTON_R2: + keyboard_state.r2_state = press_event ? 0xff : 0; break; - case Qt::Key::Key_Up: - button_mask = CHIAKI_CONTROLLER_BUTTON_DPAD_UP; + case static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_Y_UP): + keyboard_state.right_y = press_event ? -0x3fff : 0; break; - case Qt::Key::Key_Down: - button_mask = CHIAKI_CONTROLLER_BUTTON_DPAD_DOWN; + case static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_Y_DOWN): + keyboard_state.right_y = press_event ? 0x3fff : 0; break; - case Qt::Key::Key_Return: - button_mask = CHIAKI_CONTROLLER_BUTTON_CROSS; + case static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_X_UP): + keyboard_state.right_x = press_event ? 0x3fff : 0; break; - case Qt::Key::Key_Backspace: - button_mask = CHIAKI_CONTROLLER_BUTTON_MOON; + case static_cast(ControllerButtonExt::ANALOG_STICK_RIGHT_X_DOWN): + keyboard_state.right_x = press_event ? -0x3fff : 0; break; - case Qt::Key::Key_Backslash: - case Qt::Key::Key_X: - button_mask = CHIAKI_CONTROLLER_BUTTON_BOX; + case static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_Y_UP): + keyboard_state.left_y = press_event ? -0x3fff : 0; break; - case Qt::Key::Key_C: - case Qt::Key::Key_BracketRight: - button_mask = CHIAKI_CONTROLLER_BUTTON_PYRAMID; + case static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_Y_DOWN): + keyboard_state.left_y = press_event ? 0x3fff : 0; break; - case Qt::Key::Key_F: - case Qt::Key::Key_BracketLeft: - button_mask = CHIAKI_CONTROLLER_BUTTON_SHARE; + case static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_X_UP): + keyboard_state.left_x = press_event ? 0x3fff : 0; break; - case Qt::Key::Key_Escape: - button_mask = CHIAKI_CONTROLLER_BUTTON_PS; - break; - case Qt::Key::Key_T: - button_mask = CHIAKI_CONTROLLER_BUTTON_TOUCHPAD; + case static_cast(ControllerButtonExt::ANALOG_STICK_LEFT_X_DOWN): + keyboard_state.left_x = press_event ? -0x3fff : 0; break; default: - // not interested - return; + if(press_event) + keyboard_state.buttons |= button; + else + keyboard_state.buttons &= ~button; + break; } - if(event->type() == QEvent::KeyPress) - keyboard_state.buttons |= button_mask; - else - keyboard_state.buttons &= ~button_mask; - SendFeedbackState(); }