aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/qt_sdl/InputConfigDialog.cpp
diff options
context:
space:
mode:
authorNadia Holmquist Pedersen <nadia@nhp.sh>2020-06-03 13:54:28 +0200
committerNadia Holmquist Pedersen <nadia@nhp.sh>2020-06-03 13:54:28 +0200
commitf9f366e296a68be0aa2eb7bfd0552da31896a18c (patch)
treef52760d5505b84074a9c1b50b4fe16e40035ba06 /src/frontend/qt_sdl/InputConfigDialog.cpp
parentb746c0b7279d1f5b4bcfd02c115b09d583e9df4f (diff)
parentd6332f96f162849ad0dde2738cacd3fae6e76e5d (diff)
Merge remote-tracking branch 'remotes/upstream/master' into feature/qt-platform
# Conflicts: # src/frontend/qt_sdl/CMakeLists.txt # src/frontend/qt_sdl/Platform.cpp # src/frontend/qt_sdl/main.cpp
Diffstat (limited to 'src/frontend/qt_sdl/InputConfigDialog.cpp')
-rw-r--r--src/frontend/qt_sdl/InputConfigDialog.cpp494
1 files changed, 494 insertions, 0 deletions
diff --git a/src/frontend/qt_sdl/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfigDialog.cpp
new file mode 100644
index 0000000..81baa65
--- /dev/null
+++ b/src/frontend/qt_sdl/InputConfigDialog.cpp
@@ -0,0 +1,494 @@
+/*
+ Copyright 2016-2020 Arisotura
+
+ This file is part of melonDS.
+
+ melonDS 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.
+
+ melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#include <QGroupBox>
+#include <QLabel>
+#include <QKeyEvent>
+
+#include <SDL2/SDL.h>
+
+#include "types.h"
+#include "Config.h"
+#include "PlatformConfig.h"
+
+#include "Input.h"
+#include "InputConfigDialog.h"
+#include "ui_InputConfigDialog.h"
+
+
+InputConfigDialog* InputConfigDialog::currentDlg = nullptr;
+
+const int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 2, 3};
+const char* dskeylabels[12] = {"A", "B", "X", "Y", "Left", "Right", "Up", "Down", "L", "R", "Select", "Start"};
+
+const int hk_addons[] =
+{
+ HK_SolarSensorIncrease,
+ HK_SolarSensorDecrease,
+};
+
+const char* hk_addons_labels[] =
+{
+ "[Boktai] Sunlight + ",
+ "[Boktai] Sunlight - ",
+};
+
+const int hk_general[] =
+{
+ HK_Pause,
+ HK_Reset,
+ HK_FastForward,
+ HK_FastForwardToggle,
+ HK_Lid,
+ HK_Mic,
+};
+
+const char* hk_general_labels[] =
+{
+ "Pause/resume",
+ "Reset",
+ "Fast forward",
+ "Toggle FPS limit",
+ "Close/open lid",
+ "Microphone",
+};
+
+
+InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new Ui::InputConfigDialog)
+{
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ for (int i = 0; i < 12; i++)
+ {
+ keypadKeyMap[i] = Config::KeyMapping[dskeyorder[i]];
+ keypadJoyMap[i] = Config::JoyMapping[dskeyorder[i]];
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ addonsKeyMap[i] = Config::HKKeyMapping[hk_addons[i]];
+ addonsJoyMap[i] = Config::HKJoyMapping[hk_addons[i]];
+ }
+
+ for (int i = 0; i < 6; i++)
+ {
+ hkGeneralKeyMap[i] = Config::HKKeyMapping[hk_general[i]];
+ hkGeneralJoyMap[i] = Config::HKJoyMapping[hk_general[i]];
+ }
+
+ populatePage(ui->tabInput, 12, dskeylabels, keypadKeyMap, keypadJoyMap);
+ populatePage(ui->tabAddons, 2, hk_addons_labels, addonsKeyMap, addonsJoyMap);
+ populatePage(ui->tabHotkeysGeneral, 6, hk_general_labels, hkGeneralKeyMap, hkGeneralJoyMap);
+
+ int njoy = SDL_NumJoysticks();
+ if (njoy > 0)
+ {
+ for (int i = 0; i < njoy; i++)
+ {
+ const char* name = SDL_JoystickNameForIndex(i);
+ ui->cbxJoystick->addItem(QString(name));
+ }
+ ui->cbxJoystick->setCurrentIndex(Input::JoystickID);
+ }
+ else
+ {
+ ui->cbxJoystick->addItem("(no joysticks available)");
+ ui->cbxJoystick->setEnabled(false);
+ }
+}
+
+InputConfigDialog::~InputConfigDialog()
+{
+ delete ui;
+}
+
+void InputConfigDialog::populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap)
+{
+ // kind of a hack
+ bool ishotkey = (page != ui->tabInput);
+
+ QHBoxLayout* main_layout = new QHBoxLayout();
+
+ QGroupBox* group;
+ QGridLayout* group_layout;
+
+ group = new QGroupBox("Keyboard mappings:");
+ main_layout->addWidget(group);
+ group_layout = new QGridLayout();
+ group_layout->setSpacing(1);
+ for (int i = 0; i < num; i++)
+ {
+ QLabel* label = new QLabel(QString(labels[i])+":");
+ KeyMapButton* btn = new KeyMapButton(&keymap[i], ishotkey);
+
+ group_layout->addWidget(label, i, 0);
+ group_layout->addWidget(btn, i, 1);
+ }
+ group_layout->setRowStretch(num, 1);
+ group->setLayout(group_layout);
+ group->setMinimumWidth(275);
+
+ group = new QGroupBox("Joystick mappings:");
+ main_layout->addWidget(group);
+ group_layout = new QGridLayout();
+ group_layout->setSpacing(1);
+ for (int i = 0; i < num; i++)
+ {
+ QLabel* label = new QLabel(QString(labels[i])+":");
+ JoyMapButton* btn = new JoyMapButton(&joymap[i], ishotkey);
+
+ group_layout->addWidget(label, i, 0);
+ group_layout->addWidget(btn, i, 1);
+ }
+ group_layout->setRowStretch(num, 1);
+ group->setLayout(group_layout);
+ group->setMinimumWidth(275);
+
+ page->setLayout(main_layout);
+}
+
+void InputConfigDialog::on_InputConfigDialog_accepted()
+{
+ for (int i = 0; i < 12; i++)
+ {
+ Config::KeyMapping[dskeyorder[i]] = keypadKeyMap[i];
+ Config::JoyMapping[dskeyorder[i]] = keypadJoyMap[i];
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ Config::HKKeyMapping[hk_addons[i]] = addonsKeyMap[i];
+ Config::HKJoyMapping[hk_addons[i]] = addonsJoyMap[i];
+ }
+
+ for (int i = 0; i < 6; i++)
+ {
+ Config::HKKeyMapping[hk_general[i]] = hkGeneralKeyMap[i];
+ Config::HKJoyMapping[hk_general[i]] = hkGeneralJoyMap[i];
+ }
+
+ Config::JoystickID = Input::JoystickID;
+ Config::Save();
+
+ closeDlg();
+}
+
+void InputConfigDialog::on_InputConfigDialog_rejected()
+{
+ Input::JoystickID = Config::JoystickID;
+ Input::OpenJoystick();
+
+ closeDlg();
+}
+
+void InputConfigDialog::on_cbxJoystick_currentIndexChanged(int id)
+{
+ // prevent a spurious change
+ if (ui->cbxJoystick->count() < 2) return;
+
+ Input::JoystickID = id;
+ Input::OpenJoystick();
+}
+
+
+KeyMapButton::KeyMapButton(int* mapping, bool hotkey) : QPushButton()
+{
+ this->mapping = mapping;
+ this->isHotkey = hotkey;
+
+ setCheckable(true);
+ setText(mappingText());
+
+ connect(this, &KeyMapButton::clicked, this, &KeyMapButton::onClick);
+}
+
+KeyMapButton::~KeyMapButton()
+{
+}
+
+void KeyMapButton::keyPressEvent(QKeyEvent* event)
+{
+ if (!isChecked()) return QPushButton::keyPressEvent(event);
+
+ printf("KEY PRESSED = %08X %08X | %08X %08X %08X\n", event->key(), event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode());
+
+ int key = event->key();
+ int mod = event->modifiers();
+ bool ismod = (key == Qt::Key_Control ||
+ key == Qt::Key_Alt ||
+ key == Qt::Key_AltGr ||
+ key == Qt::Key_Shift ||
+ key == Qt::Key_Meta);
+
+ if (!mod)
+ {
+ if (key == Qt::Key_Escape) { click(); return; }
+ if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; }
+ }
+
+ if (isHotkey)
+ {
+ if (ismod)
+ return;
+ }
+
+ if (!ismod)
+ key |= mod;
+ else if (Input::IsRightModKey(event))
+ key |= (1<<31);
+
+ *mapping = key;
+ click();
+}
+
+void KeyMapButton::focusOutEvent(QFocusEvent* event)
+{
+ if (isChecked())
+ {
+ // if we lost the focus while mapping, consider it 'done'
+ click();
+ }
+
+ QPushButton::focusOutEvent(event);
+}
+
+void KeyMapButton::onClick()
+{
+ if (isChecked())
+ {
+ setText("[press key]");
+ }
+ else
+ {
+ setText(mappingText());
+ }
+}
+
+QString KeyMapButton::mappingText()
+{
+ int key = *mapping;
+
+ if (key == -1) return "None";
+
+ QString isright = (key & (1<<31)) ? "Right " : "Left ";
+ key &= ~(1<<31);
+
+ switch (key)
+ {
+ case Qt::Key_Control: return isright + "Ctrl";
+ case Qt::Key_Alt: return "Alt";
+ case Qt::Key_AltGr: return "AltGr";
+ case Qt::Key_Shift: return isright + "Shift";
+ case Qt::Key_Meta: return "Meta";
+ }
+
+ QKeySequence seq(key);
+ QString ret = seq.toString();
+
+ // weak attempt at detecting garbage key names
+ if (ret.length() == 2 && ret[0].unicode() > 0xFF)
+ return QString("[%1]").arg(key, 8, 16);
+
+ return ret.replace("&", "&&");
+}
+
+
+JoyMapButton::JoyMapButton(int* mapping, bool hotkey) : QPushButton()
+{
+ this->mapping = mapping;
+ this->isHotkey = hotkey;
+
+ setCheckable(true);
+ setText(mappingText());
+
+ connect(this, &JoyMapButton::clicked, this, &JoyMapButton::onClick);
+
+ timerID = 0;
+}
+
+JoyMapButton::~JoyMapButton()
+{
+}
+
+void JoyMapButton::keyPressEvent(QKeyEvent* event)
+{
+ if (!isChecked()) return QPushButton::keyPressEvent(event);
+
+ int key = event->key();
+ int mod = event->modifiers();
+
+ if (!mod)
+ {
+ if (key == Qt::Key_Escape) { click(); return; }
+ if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; }
+ }
+}
+
+void JoyMapButton::focusOutEvent(QFocusEvent* event)
+{
+ if (isChecked())
+ {
+ // if we lost the focus while mapping, consider it 'done'
+ click();
+ }
+
+ QPushButton::focusOutEvent(event);
+}
+
+void JoyMapButton::timerEvent(QTimerEvent* event)
+{
+ SDL_Joystick* joy = Input::Joystick;
+ if (!joy) { click(); return; }
+ if (!SDL_JoystickGetAttached(joy)) { click(); return; }
+
+ int oldmap;
+ if (*mapping == -1) oldmap = 0xFFFF;
+ else oldmap = *mapping;
+
+ int nbuttons = SDL_JoystickNumButtons(joy);
+ for (int i = 0; i < nbuttons; i++)
+ {
+ if (SDL_JoystickGetButton(joy, i))
+ {
+ *mapping = (oldmap & 0xFFFF0000) | i;
+ click();
+ return;
+ }
+ }
+
+ int nhats = SDL_JoystickNumHats(joy);
+ if (nhats > 16) nhats = 16;
+ for (int i = 0; i < nhats; i++)
+ {
+ Uint8 blackhat = SDL_JoystickGetHat(joy, i);
+ if (blackhat)
+ {
+ if (blackhat & 0x1) blackhat = 0x1;
+ else if (blackhat & 0x2) blackhat = 0x2;
+ else if (blackhat & 0x4) blackhat = 0x4;
+ else blackhat = 0x8;
+
+ *mapping = (oldmap & 0xFFFF0000) | 0x100 | blackhat | (i << 4);
+ click();
+ return;
+ }
+ }
+
+ int naxes = SDL_JoystickNumAxes(joy);
+ if (naxes > 16) naxes = 16;
+ for (int i = 0; i < naxes; i++)
+ {
+ Sint16 axisval = SDL_JoystickGetAxis(joy, i);
+ int diff = abs(axisval - axesRest[i]);
+
+ if (axesRest[i] < -16384 && axisval >= 0)
+ {
+ *mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
+ click();
+ return;
+ }
+ else if (diff > 16384)
+ {
+ int axistype;
+ if (axisval > 0) axistype = 0;
+ else axistype = 1;
+
+ *mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
+ click();
+ return;
+ }
+ }
+}
+
+void JoyMapButton::onClick()
+{
+ if (isChecked())
+ {
+ setText("[press button/axis]");
+ timerID = startTimer(50);
+
+ memset(axesRest, 0, sizeof(axesRest));
+ if (Input::Joystick && SDL_JoystickGetAttached(Input::Joystick))
+ {
+ int naxes = SDL_JoystickNumAxes(Input::Joystick);
+ if (naxes > 16) naxes = 16;
+ for (int a = 0; a < naxes; a++)
+ {
+ axesRest[a] = SDL_JoystickGetAxis(Input::Joystick, a);
+ }
+ }
+ }
+ else
+ {
+ setText(mappingText());
+ if (timerID) { killTimer(timerID); timerID = 0; }
+ }
+}
+
+QString JoyMapButton::mappingText()
+{
+ int id = *mapping;
+
+ if (id == -1) return "None";
+
+ bool hasbtn = ((id & 0xFFFF) != 0xFFFF);
+ QString str;
+
+ if (hasbtn)
+ {
+ if (id & 0x100)
+ {
+ int hatnum = ((id >> 4) & 0xF) + 1;
+
+ switch (id & 0xF)
+ {
+ case 0x1: str = "Hat %1 up"; break;
+ case 0x2: str = "Hat %1 right"; break;
+ case 0x4: str = "Hat %1 down"; break;
+ case 0x8: str = "Hat %1 left"; break;
+ }
+
+ str = str.arg(hatnum);
+ }
+ else
+ {
+ str = QString("Button %1").arg((id & 0xFFFF) + 1);
+ }
+ }
+ else
+ {
+ str = "";
+ }
+
+ if (id & 0x10000)
+ {
+ int axisnum = ((id >> 24) & 0xF) + 1;
+
+ if (hasbtn) str += " / ";
+
+ switch ((id >> 20) & 0xF)
+ {
+ case 0: str += QString("Axis %1 +").arg(axisnum); break;
+ case 1: str += QString("Axis %1 -").arg(axisnum); break;
+ case 2: str += QString("Trigger %1").arg(axisnum); break;
+ }
+ }
+
+ return str;
+}