GUI: Add settings for keyboard mapping (#174)

This commit is contained in:
Felipe Lavratti 2020-02-29 11:38:03 -03:00 committed by GitHub
commit a605ba083d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 292 additions and 52 deletions

View file

@ -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

View file

@ -24,6 +24,19 @@
#include <QSettings>
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<int, Qt::Key> GetControllerMapping();
QMap<Qt::Key, int> GetControllerMappingForDecoding();
signals:
void RegisteredHostsUpdated();
void ManualHostsUpdated();

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef CHIAKI_SETTINGSKEYCAPTUREDIALOG_H
#define CHIAKI_SETTINGSKEYCAPTUREDIALOG_H
#include <QDialog>
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

View file

@ -29,6 +29,7 @@
#include <QObject>
#include <QImage>
#include <QMouseEvent>
#include <QTimer>
#if CHIAKI_GUI_ENABLE_QT_GAMEPAD
class QGamepad;
@ -47,6 +48,7 @@ class ChiakiException: public Exception
struct StreamSessionConnectInfo
{
QMap<Qt::Key, int> 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<Qt::Key, int> key_map;
void PushAudioFrame(int16_t *buf, size_t samples_count);
void PushVideoSample(uint8_t *buf, size_t buf_size);
void Event(ChiakiEvent *event);

View file

@ -16,6 +16,7 @@
*/
#include <settings.h>
#include <QKeySequence>
#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<int>(ControllerButtonExt::ANALOG_STICK_LEFT_X_UP) : return tr("Left Stick Right");
case static_cast<int>(ControllerButtonExt::ANALOG_STICK_LEFT_Y_UP) : return tr("Left Stick Up");
case static_cast<int>(ControllerButtonExt::ANALOG_STICK_RIGHT_X_UP) : return tr("Right Stick Right");
case static_cast<int>(ControllerButtonExt::ANALOG_STICK_RIGHT_Y_UP) : return tr("Right Stick Up");
case static_cast<int>(ControllerButtonExt::ANALOG_STICK_LEFT_X_DOWN) : return tr("Left Stick Left");
case static_cast<int>(ControllerButtonExt::ANALOG_STICK_LEFT_Y_DOWN) : return tr("Left Stick Down");
case static_cast<int>(ControllerButtonExt::ANALOG_STICK_RIGHT_X_DOWN) : return tr("Right Stick Left");
case static_cast<int>(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<int, Qt::Key> Settings::GetControllerMapping()
{
// Initialize with default values
QMap<int, Qt::Key> 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<int>(ControllerButtonExt::ANALOG_STICK_LEFT_X_UP) , Qt::Key::Key_BracketRight},
{static_cast<int>(ControllerButtonExt::ANALOG_STICK_LEFT_X_DOWN) , Qt::Key::Key_BracketLeft},
{static_cast<int>(ControllerButtonExt::ANALOG_STICK_LEFT_Y_UP) , Qt::Key::Key_Insert},
{static_cast<int>(ControllerButtonExt::ANALOG_STICK_LEFT_Y_DOWN) , Qt::Key::Key_Delete},
{static_cast<int>(ControllerButtonExt::ANALOG_STICK_RIGHT_X_UP) , Qt::Key::Key_Equal},
{static_cast<int>(ControllerButtonExt::ANALOG_STICK_RIGHT_X_DOWN), Qt::Key::Key_Minus},
{static_cast<int>(ControllerButtonExt::ANALOG_STICK_RIGHT_Y_UP) , Qt::Key::Key_PageUp},
{static_cast<int>(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<int>(chiaki_button)] = Qt::Key(QKeySequence(settings.value("keymap/" + button_name).toString())[0]);
}
return result;
}
QMap<Qt::Key, int> Settings::GetControllerMappingForDecoding()
{
auto map = GetControllerMapping();
QMap<Qt::Key, int> result;
for(auto it = map.begin(); it != map.end(); ++it)
{
result[it.value()] = it.key();
}
return result;
}

View file

@ -17,9 +17,11 @@
#include <settingsdialog.h>
#include <settings.h>
#include <settingskeycapturedialog.h>
#include <registdialog.h>
#include <sessionlog.h>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QListWidget>
@ -32,7 +34,6 @@
#include <QCheckBox>
#include <QLineEdit>
const char * const about_string =
"<h1>Chiaki</h1> 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.</p>";
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<int, Qt::Key> 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);
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#include "settingskeycapturedialog.h"
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QKeyEvent>
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();
}

View file

@ -32,15 +32,9 @@
#include <cstring>
#include <chiaki/session.h>
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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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();
}