controller hotkey initial support

This commit is contained in:
David Griswold 2026-02-02 21:27:29 +03:00
parent 37e688f82d
commit 5c53e376b9
7 changed files with 141 additions and 47 deletions

View File

@ -137,6 +137,8 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
game_list_worker.h
hotkeys.cpp
hotkeys.h
hotkey_monitor.cpp
hotkey_monitor.h
loading_screen.cpp
loading_screen.h
loading_screen.ui

View File

@ -55,44 +55,44 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> QtConfi
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<UISettings::Shortcut, 38> QtConfig::default_hotkeys {{
{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
{QStringLiteral("Decrease 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+-"), Qt::ApplicationShortcut}},
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
{QStringLiteral("Exit Azahar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}},
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
{QStringLiteral("Increase 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl++"), Qt::ApplicationShortcut}},
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load from Newest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
{QStringLiteral("Multiplayer Browse Public Rooms"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+B"), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Create Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+N"), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Direct Connect to Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Shift"), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Leave Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+L"), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Show Current Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+R"), Qt::ApplicationShortcut}},
{QStringLiteral("Quick Save"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Quick Load"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
{QStringLiteral("Save to Oldest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}},
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
{QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Per-Application Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::WindowShortcut}},
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
{QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Turbo Mode"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Decrease 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+-"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Exit Azahar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Increase 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl++"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load from Newest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Multiplayer Browse Public Rooms"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+B"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Create Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+N"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Direct Connect to Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Shift"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Leave Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+L"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Multiplayer Show Current Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+R"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Quick Save"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Quick Load"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Save to Oldest Non-Quicksave Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Per-Application Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
{QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Turbo Mode"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut}},
}};
// clang-format on
@ -732,6 +732,8 @@ void QtConfig::ReadShortcutValues() {
{name,
group,
{ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
ReadSetting(QStringLiteral("ControllerKeySeq"), shortcut.controller_keyseq)
.toString(),
shortcut.context}});
qt_config->endGroup();
qt_config->endGroup();
@ -1273,6 +1275,8 @@ void QtConfig::SaveShortcutValues() {
qt_config->beginGroup(name);
WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq);
WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
WriteSetting(QStringLiteral("ControllerKeySeq"), shortcut.controller_keyseq,
default_hotkey.controller_keyseq);
qt_config->endGroup();
qt_config->endGroup();
}

View File

@ -0,0 +1,48 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QShortcut>
#include <QTimer>
#include "hotkey_monitor.h"
#include "hotkeys.h"
struct ControllerHotkeyMonitor::ButtonState {
Hotkey* hk;
bool lastStatus;
};
ControllerHotkeyMonitor::ControllerHotkeyMonitor() {
m_buttons = std::make_unique<std::map<QString, ButtonState>>();
m_timer = new QTimer();
QObject::connect(m_timer, &QTimer::timeout, [this]() { checkAllButtons(); });
m_timer->start(16);
}
ControllerHotkeyMonitor::~ControllerHotkeyMonitor() {
delete m_timer;
}
void ControllerHotkeyMonitor::addButton(const QString& name, Hotkey* hk) {
(*m_buttons)[name] = {hk, false};
}
void ControllerHotkeyMonitor::removeButton(const QString& name) {
m_buttons->erase(name);
}
void ControllerHotkeyMonitor::checkAllButtons() {
for (auto& [name, it] : *m_buttons) {
bool currentStatus = it.hk->button_device.GetStatus();
if (currentStatus != it.lastStatus) {
if (it.lastStatus && !currentStatus) {
for (auto const& [_, hotkey_shortcut] : it.hk->shortcuts) {
if (hotkey_shortcut) {
hotkey_shortcut->activated();
}
}
}
it.lastStatus = currentStatus;
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright 2025 Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <memory>
#include <QString>
class QTimer;
struct Hotkey;
class ControllerHotkeyMonitor {
public:
explicit ControllerHotkeyMonitor();
~ControllerHotkeyMonitor();
void addButton(const QString& name, Hotkey* hk);
void removeButton(const QString& name);
private:
void checkAllButtons();
struct ButtonState;
std::unique_ptr<std::map<QString, ButtonState>> m_buttons;
QTimer* m_timer;
};

View File

@ -17,8 +17,9 @@ void HotkeyRegistry::SaveHotkeys() {
for (const auto& hotkey : group.second) {
UISettings::values.shortcuts.push_back(
{hotkey.first, group.first,
UISettings::ContextualShortcut(
{hotkey.second.keyseq.toString(), hotkey.second.context})});
UISettings::ContextualShortcut({hotkey.second.keyseq.toString(),
hotkey.second.controller_keyseq,
hotkey.second.context})});
}
}
}
@ -32,6 +33,12 @@ void HotkeyRegistry::LoadHotkeys() {
hk.keyseq =
QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText);
hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context);
hk.controller_keyseq = shortcut.shortcut.controller_keyseq;
}
if (!hk.controller_keyseq.isEmpty()) {
hk.button_device =
*Input::CreateDevice<Input::ButtonDevice>(hk.controller_keyseq.toStdString());
buttonMonitor.addButton(shortcut.name, &hk);
}
for (auto const& [_, hotkey_shortcut] : hk.shortcuts) {
if (hotkey_shortcut) {

View File

@ -7,12 +7,22 @@
#include <map>
#include <QKeySequence>
#include <QString>
#include "core/frontend/input.h"
#include "hotkey_monitor.h"
class QDialog;
class QSettings;
class QShortcut;
class QWidget;
struct Hotkey {
QKeySequence keyseq;
QString controller_keyseq;
std::map<QString, QShortcut*> shortcuts;
Qt::ShortcutContext context = Qt::WindowShortcut;
Input::ButtonDevice button_device;
};
class HotkeyRegistry final {
public:
friend class ConfigureHotkeys;
@ -20,6 +30,8 @@ public:
explicit HotkeyRegistry();
~HotkeyRegistry();
ControllerHotkeyMonitor buttonMonitor;
/**
* Loads hotkeys from the settings file.
*
@ -68,13 +80,6 @@ public:
Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action);
private:
struct Hotkey {
QKeySequence keyseq;
QString controller_keyseq;
std::map<QString, QShortcut*> shortcuts;
Qt::ShortcutContext context = Qt::WindowShortcut;
};
using HotkeyMap = std::map<QString, Hotkey>;
using HotkeyGroupMap = std::map<QString, HotkeyMap>;

View File

@ -19,6 +19,7 @@ namespace UISettings {
struct ContextualShortcut {
QString keyseq;
QString controller_keyseq;
int context;
};