diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index e6faa88ce..6432c765f 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -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 diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 0172f6bb1..3e295c11b 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -55,44 +55,44 @@ const std::array, Settings::NativeAnalog::NumAnalogs> QtConfi // UISetting::values.shortcuts, which is alphabetically ordered. // clang-format off const std::array 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(); } diff --git a/src/citra_qt/hotkey_monitor.cpp b/src/citra_qt/hotkey_monitor.cpp new file mode 100644 index 000000000..cd80914c1 --- /dev/null +++ b/src/citra_qt/hotkey_monitor.cpp @@ -0,0 +1,48 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "hotkey_monitor.h" +#include "hotkeys.h" + +struct ControllerHotkeyMonitor::ButtonState { + Hotkey* hk; + bool lastStatus; +}; + +ControllerHotkeyMonitor::ControllerHotkeyMonitor() { + m_buttons = std::make_unique>(); + 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; + } + } +} \ No newline at end of file diff --git a/src/citra_qt/hotkey_monitor.h b/src/citra_qt/hotkey_monitor.h new file mode 100644 index 000000000..0f72eb150 --- /dev/null +++ b/src/citra_qt/hotkey_monitor.h @@ -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 +#include +#include + +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> m_buttons; + QTimer* m_timer; +}; \ No newline at end of file diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp index 9b0fe4598..450ae5dd0 100644 --- a/src/citra_qt/hotkeys.cpp +++ b/src/citra_qt/hotkeys.cpp @@ -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(shortcut.shortcut.context); + hk.controller_keyseq = shortcut.shortcut.controller_keyseq; + } + if (!hk.controller_keyseq.isEmpty()) { + hk.button_device = + *Input::CreateDevice(hk.controller_keyseq.toStdString()); + buttonMonitor.addButton(shortcut.name, &hk); } for (auto const& [_, hotkey_shortcut] : hk.shortcuts) { if (hotkey_shortcut) { diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h index 639d5db51..f6ff2ac3a 100644 --- a/src/citra_qt/hotkeys.h +++ b/src/citra_qt/hotkeys.h @@ -7,12 +7,22 @@ #include #include #include +#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 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 shortcuts; - Qt::ShortcutContext context = Qt::WindowShortcut; - }; - using HotkeyMap = std::map; using HotkeyGroupMap = std::map; diff --git a/src/citra_qt/uisettings.h b/src/citra_qt/uisettings.h index ef87b3fe3..f90b7576b 100644 --- a/src/citra_qt/uisettings.h +++ b/src/citra_qt/uisettings.h @@ -19,6 +19,7 @@ namespace UISettings { struct ContextualShortcut { QString keyseq; + QString controller_keyseq; int context; };