mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-06-04 23:05:00 -06:00
Merge 790473eb44 into c650473fdc
This commit is contained in:
commit
b927c51346
@ -202,12 +202,15 @@ if (ENABLE_QT)
|
|||||||
"use_touchpad"
|
"use_touchpad"
|
||||||
"controller_touch_device"
|
"controller_touch_device"
|
||||||
"use_touch_from_button"
|
"use_touch_from_button"
|
||||||
|
"input_maptype"
|
||||||
|
"controller_hotkey_maptype"
|
||||||
"touch_from_button_map"
|
"touch_from_button_map"
|
||||||
"touch_from_button_maps" # Why are these two so similar? Basically typo bait
|
"touch_from_button_maps" # Why are these two so similar? Basically typo bait
|
||||||
"nand_directory"
|
"nand_directory"
|
||||||
"sdmc_directory"
|
"sdmc_directory"
|
||||||
"game_id"
|
"game_id"
|
||||||
"KeySeq"
|
"KeySeq"
|
||||||
|
"controller_keyseq"
|
||||||
"gamedirs"
|
"gamedirs"
|
||||||
"libvorbis"
|
"libvorbis"
|
||||||
"Context"
|
"Context"
|
||||||
|
|||||||
BIN
dist/qt_themes/default/icons/256x256/automap_face_buttons.png
vendored
Normal file
BIN
dist/qt_themes/default/icons/256x256/automap_face_buttons.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
1
dist/qt_themes/default/theme_default.qrc
vendored
1
dist/qt_themes/default/theme_default.qrc
vendored
@ -15,6 +15,7 @@
|
|||||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||||
<file alias="128x128/cartridge.png">icons/128x128/cartridge.png</file>
|
<file alias="128x128/cartridge.png">icons/128x128/cartridge.png</file>
|
||||||
<file alias="256x256/azahar.png">icons/256x256/azahar.png</file>
|
<file alias="256x256/azahar.png">icons/256x256/azahar.png</file>
|
||||||
|
<file alias="256x256/automap_face_buttons.png">icons/256x256/automap_face_buttons.png</file>
|
||||||
<file alias="48x48/star.png">icons/48x48/star.png</file>
|
<file alias="48x48/star.png">icons/48x48/star.png</file>
|
||||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
|
|||||||
@ -13,30 +13,28 @@ namespace DefaultINI {
|
|||||||
|
|
||||||
// All of these setting keys are either not currently used by Android or are too niche to bother
|
// All of these setting keys are either not currently used by Android or are too niche to bother
|
||||||
// documenting (people can contribute documentation if they care), or some other explained reason
|
// documenting (people can contribute documentation if they care), or some other explained reason
|
||||||
constexpr std::array android_config_omitted_keys = {
|
constexpr std::array android_config_omitted_keys = {Keys::enable_gamemode,
|
||||||
Keys::enable_gamemode,
|
Keys::use_custom_storage,
|
||||||
Keys::use_custom_storage,
|
Keys::init_time_offset,
|
||||||
Keys::init_time_offset,
|
Keys::physical_device,
|
||||||
Keys::physical_device,
|
Keys::use_gles, // Niche
|
||||||
Keys::use_gles, // Niche
|
Keys::dump_command_buffers,
|
||||||
Keys::dump_command_buffers,
|
Keys::use_display_refresh_rate_detection,
|
||||||
Keys::use_display_refresh_rate_detection,
|
Keys::screen_top_stretch,
|
||||||
Keys::screen_top_stretch,
|
Keys::screen_top_leftright_padding,
|
||||||
Keys::screen_top_leftright_padding,
|
Keys::screen_top_topbottom_padding,
|
||||||
Keys::screen_top_topbottom_padding,
|
Keys::screen_bottom_stretch,
|
||||||
Keys::screen_bottom_stretch,
|
Keys::screen_bottom_leftright_padding,
|
||||||
Keys::screen_bottom_leftright_padding,
|
Keys::screen_bottom_topbottom_padding,
|
||||||
Keys::screen_bottom_topbottom_padding,
|
Keys::mono_render_option,
|
||||||
Keys::mono_render_option,
|
Keys::log_regex_filter, // Niche
|
||||||
Keys::log_regex_filter, // Niche
|
Keys::video_encoder,
|
||||||
Keys::video_encoder,
|
Keys::video_encoder_options,
|
||||||
Keys::video_encoder_options,
|
Keys::video_bitrate,
|
||||||
Keys::video_bitrate,
|
Keys::audio_encoder,
|
||||||
Keys::audio_encoder,
|
Keys::audio_encoder_options,
|
||||||
Keys::audio_encoder_options,
|
Keys::audio_bitrate,
|
||||||
Keys::audio_bitrate,
|
Keys::last_artic_base_addr};
|
||||||
Keys::last_artic_base_addr, // On Android, this value is stored as a "preference"
|
|
||||||
};
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
||||||
|
|||||||
@ -63,6 +63,9 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
|
|||||||
configuration/configure_hotkeys.cpp
|
configuration/configure_hotkeys.cpp
|
||||||
configuration/configure_hotkeys.h
|
configuration/configure_hotkeys.h
|
||||||
configuration/configure_hotkeys.ui
|
configuration/configure_hotkeys.ui
|
||||||
|
configuration/configure_hotkeys_controller.cpp
|
||||||
|
configuration/configure_hotkeys_controller.h
|
||||||
|
configuration/configure_hotkeys_controller.ui
|
||||||
configuration/configure_input.cpp
|
configuration/configure_input.cpp
|
||||||
configuration/configure_input.h
|
configuration/configure_input.h
|
||||||
configuration/configure_input.ui
|
configuration/configure_input.ui
|
||||||
@ -140,6 +143,8 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
|
|||||||
game_list_worker.h
|
game_list_worker.h
|
||||||
hotkeys.cpp
|
hotkeys.cpp
|
||||||
hotkeys.h
|
hotkeys.h
|
||||||
|
hotkey_monitor.cpp
|
||||||
|
hotkey_monitor.h
|
||||||
loading_screen.cpp
|
loading_screen.cpp
|
||||||
loading_screen.h
|
loading_screen.h
|
||||||
loading_screen.ui
|
loading_screen.ui
|
||||||
@ -188,6 +193,8 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
|
|||||||
util/graphics_device_info.h
|
util/graphics_device_info.h
|
||||||
util/sequence_dialog/sequence_dialog.cpp
|
util/sequence_dialog/sequence_dialog.cpp
|
||||||
util/sequence_dialog/sequence_dialog.h
|
util/sequence_dialog/sequence_dialog.h
|
||||||
|
util/sequence_dialog/controller_sequence_dialog.cpp
|
||||||
|
util/sequence_dialog/controller_sequence_dialog.h
|
||||||
util/spinbox.cpp
|
util/spinbox.cpp
|
||||||
util/spinbox.h
|
util/spinbox.h
|
||||||
util/util.cpp
|
util/util.cpp
|
||||||
|
|||||||
@ -778,7 +778,8 @@ void GMainWindow::InitializeSaveStateMenuActions() {
|
|||||||
|
|
||||||
void GMainWindow::InitializeHotkeys() {
|
void GMainWindow::InitializeHotkeys() {
|
||||||
hotkey_registry.LoadHotkeys();
|
hotkey_registry.LoadHotkeys();
|
||||||
|
hotkey_registry.buttonMonitor.start(16);
|
||||||
|
LOG_DEBUG(Frontend, "Initializing hotkeys");
|
||||||
const QString main_window = QStringLiteral("Main Window");
|
const QString main_window = QStringLiteral("Main Window");
|
||||||
const QString fullscreen = QStringLiteral("Fullscreen");
|
const QString fullscreen = QStringLiteral("Fullscreen");
|
||||||
|
|
||||||
@ -791,6 +792,7 @@ void GMainWindow::InitializeHotkeys() {
|
|||||||
this->addAction(action);
|
this->addAction(action);
|
||||||
if (!primary_only)
|
if (!primary_only)
|
||||||
secondary_window->addAction(action);
|
secondary_window->addAction(action);
|
||||||
|
hotkey_registry.SetAction(main_window, action_name, action);
|
||||||
};
|
};
|
||||||
|
|
||||||
link_action_shortcut(ui->action_Load_File, QStringLiteral("Load File"));
|
link_action_shortcut(ui->action_Load_File, QStringLiteral("Load File"));
|
||||||
|
|||||||
@ -58,44 +58,44 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> QtConfi
|
|||||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::array<UISettings::Shortcut, 38> QtConfig::default_hotkeys {{
|
const std::array<UISettings::Shortcut, 38> QtConfig::default_hotkeys {{
|
||||||
{QStringLiteral("Advance Frame"), 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"), Qt::WindowShortcut}},
|
{QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
|
{QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
|
{QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
|
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
|
||||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Decrease 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+-"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Decrease 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+-"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Exit Azahar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
|
{QStringLiteral("Exit Azahar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}},
|
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
|
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Increase 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl++"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Increase 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl++"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
|
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
|
||||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), 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"), Qt::WindowShortcut}},
|
{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"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Multiplayer Browse Public Rooms"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+B"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Multiplayer Create Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+N"), 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"), 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"), 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"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Multiplayer Show Current Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+R"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Quick Save"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
|
{QStringLiteral("Quick Save"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Quick Load"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}},
|
{QStringLiteral("Quick Load"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), 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"), 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"), Qt::WindowShortcut}},
|
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Toggle Per-Application Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), 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"), Qt::WindowShortcut}},
|
{QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
|
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Toggle Turbo Mode"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle Turbo Mode"), QStringLiteral("Main Window"), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
}};
|
}};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@ -336,6 +336,11 @@ void QtConfig::ReadControlValues() {
|
|||||||
|
|
||||||
ReadBasicSetting(Settings::values.use_artic_base_controller);
|
ReadBasicSetting(Settings::values.use_artic_base_controller);
|
||||||
|
|
||||||
|
UISettings::values.controller_hotkey_maptype = static_cast<Settings::InputMappingType>(
|
||||||
|
ReadSetting(Settings::QKeys::controller_hotkey_maptype,
|
||||||
|
static_cast<int>(Settings::InputMappingType::AllControllers))
|
||||||
|
.toInt());
|
||||||
|
|
||||||
int num_touch_from_button_maps =
|
int num_touch_from_button_maps =
|
||||||
qt_config->beginReadArray(Settings::QKeys::touch_from_button_maps);
|
qt_config->beginReadArray(Settings::QKeys::touch_from_button_maps);
|
||||||
|
|
||||||
@ -374,6 +379,8 @@ void QtConfig::ReadControlValues() {
|
|||||||
Settings::InputProfile profile;
|
Settings::InputProfile profile;
|
||||||
profile.name =
|
profile.name =
|
||||||
ReadSetting(Settings::QKeys::name, QStringLiteral("Default")).toString().toStdString();
|
ReadSetting(Settings::QKeys::name, QStringLiteral("Default")).toString().toStdString();
|
||||||
|
profile.maptype = static_cast<Settings::InputMappingType>(
|
||||||
|
ReadSetting(Settings::QKeys::input_maptype, 2).toInt());
|
||||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]),
|
profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]),
|
||||||
@ -741,7 +748,10 @@ void QtConfig::ReadShortcutValues() {
|
|||||||
UISettings::values.shortcuts.push_back(
|
UISettings::values.shortcuts.push_back(
|
||||||
{name,
|
{name,
|
||||||
group,
|
group,
|
||||||
{ReadSetting(Settings::QKeys::KeySeq, shortcut.keyseq).toString(), shortcut.context}});
|
{ReadSetting(Settings::QKeys::KeySeq, shortcut.keyseq).toString(),
|
||||||
|
ReadSetting(Settings::QKeys::controller_keyseq, shortcut.controller_keyseq)
|
||||||
|
.toString(),
|
||||||
|
shortcut.context}});
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
}
|
||||||
@ -983,7 +993,9 @@ void QtConfig::SaveControlValues() {
|
|||||||
qt_config->beginGroup(QStringLiteral("Controls"));
|
qt_config->beginGroup(QStringLiteral("Controls"));
|
||||||
|
|
||||||
WriteBasicSetting(Settings::values.use_artic_base_controller);
|
WriteBasicSetting(Settings::values.use_artic_base_controller);
|
||||||
|
WriteSetting(Settings::QKeys::controller_hotkey_maptype,
|
||||||
|
static_cast<int>(UISettings::values.controller_hotkey_maptype.GetValue()),
|
||||||
|
static_cast<int>(Settings::InputMappingType::GuidPort));
|
||||||
WriteSetting(Settings::QKeys::profile, Settings::values.current_input_profile_index, 0);
|
WriteSetting(Settings::QKeys::profile, Settings::values.current_input_profile_index, 0);
|
||||||
qt_config->beginWriteArray(QStringLiteral("profiles"));
|
qt_config->beginWriteArray(QStringLiteral("profiles"));
|
||||||
for (std::size_t p = 0; p < Settings::values.input_profiles.size(); ++p) {
|
for (std::size_t p = 0; p < Settings::values.input_profiles.size(); ++p) {
|
||||||
@ -991,6 +1003,8 @@ void QtConfig::SaveControlValues() {
|
|||||||
const auto& profile = Settings::values.input_profiles[p];
|
const auto& profile = Settings::values.input_profiles[p];
|
||||||
WriteSetting(Settings::QKeys::name, QString::fromStdString(profile.name),
|
WriteSetting(Settings::QKeys::name, QString::fromStdString(profile.name),
|
||||||
QStringLiteral("default"));
|
QStringLiteral("default"));
|
||||||
|
WriteSetting(Settings::QKeys::input_maptype, static_cast<int>(profile.maptype),
|
||||||
|
static_cast<int>(Settings::InputMappingType::GuidPort));
|
||||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
WriteSetting(QString::fromStdString(Settings::NativeButton::mapping[i]),
|
WriteSetting(QString::fromStdString(Settings::NativeButton::mapping[i]),
|
||||||
@ -1287,6 +1301,8 @@ void QtConfig::SaveShortcutValues() {
|
|||||||
qt_config->beginGroup(name);
|
qt_config->beginGroup(name);
|
||||||
WriteSetting(Settings::QKeys::KeySeq, shortcut.keyseq, default_hotkey.keyseq);
|
WriteSetting(Settings::QKeys::KeySeq, shortcut.keyseq, default_hotkey.keyseq);
|
||||||
WriteSetting(Settings::QKeys::Context, shortcut.context, default_hotkey.context);
|
WriteSetting(Settings::QKeys::Context, shortcut.context, default_hotkey.context);
|
||||||
|
WriteSetting(Settings::QKeys::controller_keyseq, shortcut.controller_keyseq,
|
||||||
|
default_hotkey.controller_keyseq);
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include "citra_qt/configuration/configure_general.h"
|
#include "citra_qt/configuration/configure_general.h"
|
||||||
#include "citra_qt/configuration/configure_graphics.h"
|
#include "citra_qt/configuration/configure_graphics.h"
|
||||||
#include "citra_qt/configuration/configure_hotkeys.h"
|
#include "citra_qt/configuration/configure_hotkeys.h"
|
||||||
|
#include "citra_qt/configuration/configure_hotkeys_controller.h"
|
||||||
#include "citra_qt/configuration/configure_input.h"
|
#include "citra_qt/configuration/configure_input.h"
|
||||||
#include "citra_qt/configuration/configure_layout.h"
|
#include "citra_qt/configuration/configure_layout.h"
|
||||||
#include "citra_qt/configuration/configure_storage.h"
|
#include "citra_qt/configuration/configure_storage.h"
|
||||||
@ -32,6 +33,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor
|
|||||||
system_tab{std::make_unique<ConfigureSystem>(system, this)},
|
system_tab{std::make_unique<ConfigureSystem>(system, this)},
|
||||||
input_tab{std::make_unique<ConfigureInput>(system, this)},
|
input_tab{std::make_unique<ConfigureInput>(system, this)},
|
||||||
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
|
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
|
||||||
|
hotkeys_controller_tab{std::make_unique<ConfigureControllerHotkeys>(this)},
|
||||||
graphics_tab{
|
graphics_tab{
|
||||||
std::make_unique<ConfigureGraphics>(gl_renderer, physical_devices, is_powered_on, this)},
|
std::make_unique<ConfigureGraphics>(gl_renderer, physical_devices, is_powered_on, this)},
|
||||||
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
|
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
|
||||||
@ -48,7 +50,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor
|
|||||||
ui->tabWidget->addTab(general_tab.get(), tr("General"));
|
ui->tabWidget->addTab(general_tab.get(), tr("General"));
|
||||||
ui->tabWidget->addTab(system_tab.get(), tr("System"));
|
ui->tabWidget->addTab(system_tab.get(), tr("System"));
|
||||||
ui->tabWidget->addTab(input_tab.get(), tr("Input"));
|
ui->tabWidget->addTab(input_tab.get(), tr("Input"));
|
||||||
ui->tabWidget->addTab(hotkeys_tab.get(), tr("Hotkeys"));
|
ui->tabWidget->addTab(hotkeys_controller_tab.get(), tr("Controller Hotkeys"));
|
||||||
|
ui->tabWidget->addTab(hotkeys_tab.get(), tr("Keyboard Hotkeys"));
|
||||||
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
|
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
|
||||||
ui->tabWidget->addTab(enhancements_tab.get(), tr("Enhancements"));
|
ui->tabWidget->addTab(enhancements_tab.get(), tr("Enhancements"));
|
||||||
ui->tabWidget->addTab(layout_tab.get(), tr("Layout"));
|
ui->tabWidget->addTab(layout_tab.get(), tr("Layout"));
|
||||||
@ -60,7 +63,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor
|
|||||||
ui->tabWidget->addTab(ui_tab.get(), tr("UI"));
|
ui->tabWidget->addTab(ui_tab.get(), tr("UI"));
|
||||||
|
|
||||||
hotkeys_tab->Populate(registry);
|
hotkeys_tab->Populate(registry);
|
||||||
|
hotkeys_controller_tab->Populate(registry);
|
||||||
PopulateSelectionList();
|
PopulateSelectionList();
|
||||||
|
|
||||||
connect(ui_tab.get(), &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
|
connect(ui_tab.get(), &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
|
||||||
@ -104,6 +107,7 @@ void ConfigureDialog::ApplyConfiguration() {
|
|||||||
input_tab->ApplyConfiguration();
|
input_tab->ApplyConfiguration();
|
||||||
input_tab->ApplyProfile();
|
input_tab->ApplyProfile();
|
||||||
hotkeys_tab->ApplyConfiguration(registry);
|
hotkeys_tab->ApplyConfiguration(registry);
|
||||||
|
hotkeys_controller_tab->ApplyConfiguration(registry);
|
||||||
graphics_tab->ApplyConfiguration();
|
graphics_tab->ApplyConfiguration();
|
||||||
enhancements_tab->ApplyConfiguration();
|
enhancements_tab->ApplyConfiguration();
|
||||||
layout_tab->ApplyConfiguration();
|
layout_tab->ApplyConfiguration();
|
||||||
@ -127,7 +131,7 @@ void ConfigureDialog::PopulateSelectionList() {
|
|||||||
{tr("System"), {system_tab.get(), camera_tab.get(), storage_tab.get()}},
|
{tr("System"), {system_tab.get(), camera_tab.get(), storage_tab.get()}},
|
||||||
{tr("Graphics"), {enhancements_tab.get(), layout_tab.get(), graphics_tab.get()}},
|
{tr("Graphics"), {enhancements_tab.get(), layout_tab.get(), graphics_tab.get()}},
|
||||||
{tr("Audio"), {audio_tab.get()}},
|
{tr("Audio"), {audio_tab.get()}},
|
||||||
{tr("Controls"), {input_tab.get(), hotkeys_tab.get()}}}};
|
{tr("Controls"), {input_tab.get(), hotkeys_controller_tab.get(), hotkeys_tab.get()}}}};
|
||||||
|
|
||||||
for (const auto& entry : items) {
|
for (const auto& entry : items) {
|
||||||
auto* const item = new QListWidgetItem(entry.first);
|
auto* const item = new QListWidgetItem(entry.first);
|
||||||
@ -158,6 +162,7 @@ void ConfigureDialog::RetranslateUI() {
|
|||||||
system_tab->RetranslateUI();
|
system_tab->RetranslateUI();
|
||||||
input_tab->RetranslateUI();
|
input_tab->RetranslateUI();
|
||||||
hotkeys_tab->RetranslateUI();
|
hotkeys_tab->RetranslateUI();
|
||||||
|
hotkeys_controller_tab->RetranslateUI();
|
||||||
graphics_tab->RetranslateUI();
|
graphics_tab->RetranslateUI();
|
||||||
enhancements_tab->RetranslateUI();
|
enhancements_tab->RetranslateUI();
|
||||||
layout_tab->RetranslateUI();
|
layout_tab->RetranslateUI();
|
||||||
@ -174,19 +179,21 @@ void ConfigureDialog::UpdateVisibleTabs() {
|
|||||||
if (items.isEmpty())
|
if (items.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const std::map<QWidget*, QString> widgets = {{general_tab.get(), tr("General")},
|
const std::map<QWidget*, QString> widgets = {
|
||||||
{system_tab.get(), tr("System")},
|
{general_tab.get(), tr("General")},
|
||||||
{input_tab.get(), tr("Input")},
|
{system_tab.get(), tr("System")},
|
||||||
{hotkeys_tab.get(), tr("Hotkeys")},
|
{input_tab.get(), tr("Input")},
|
||||||
{enhancements_tab.get(), tr("Enhancements")},
|
{hotkeys_tab.get(), tr("Keyboard Hotkeys")},
|
||||||
{layout_tab.get(), tr("Layout")},
|
{hotkeys_controller_tab.get(), tr("Controller Hotkeys")},
|
||||||
{graphics_tab.get(), tr("Advanced")},
|
{enhancements_tab.get(), tr("Enhancements")},
|
||||||
{audio_tab.get(), tr("Audio")},
|
{layout_tab.get(), tr("Layout")},
|
||||||
{camera_tab.get(), tr("Camera")},
|
{graphics_tab.get(), tr("Advanced")},
|
||||||
{debug_tab.get(), tr("Debug")},
|
{audio_tab.get(), tr("Audio")},
|
||||||
{storage_tab.get(), tr("Storage")},
|
{camera_tab.get(), tr("Camera")},
|
||||||
{web_tab.get(), tr("Web")},
|
{debug_tab.get(), tr("Debug")},
|
||||||
{ui_tab.get(), tr("UI")}};
|
{storage_tab.get(), tr("Storage")},
|
||||||
|
{web_tab.get(), tr("Web")},
|
||||||
|
{ui_tab.get(), tr("UI")}};
|
||||||
|
|
||||||
ui->tabWidget->clear();
|
ui->tabWidget->clear();
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ class ConfigureGeneral;
|
|||||||
class ConfigureSystem;
|
class ConfigureSystem;
|
||||||
class ConfigureInput;
|
class ConfigureInput;
|
||||||
class ConfigureHotkeys;
|
class ConfigureHotkeys;
|
||||||
|
class ConfigureControllerHotkeys;
|
||||||
class ConfigureGraphics;
|
class ConfigureGraphics;
|
||||||
class ConfigureLayout;
|
class ConfigureLayout;
|
||||||
class ConfigureEnhancements;
|
class ConfigureEnhancements;
|
||||||
@ -65,6 +66,7 @@ private:
|
|||||||
std::unique_ptr<ConfigureSystem> system_tab;
|
std::unique_ptr<ConfigureSystem> system_tab;
|
||||||
std::unique_ptr<ConfigureInput> input_tab;
|
std::unique_ptr<ConfigureInput> input_tab;
|
||||||
std::unique_ptr<ConfigureHotkeys> hotkeys_tab;
|
std::unique_ptr<ConfigureHotkeys> hotkeys_tab;
|
||||||
|
std::unique_ptr<ConfigureControllerHotkeys> hotkeys_controller_tab;
|
||||||
std::unique_ptr<ConfigureGraphics> graphics_tab;
|
std::unique_ptr<ConfigureGraphics> graphics_tab;
|
||||||
std::unique_ptr<ConfigureEnhancements> enhancements_tab;
|
std::unique_ptr<ConfigureEnhancements> enhancements_tab;
|
||||||
std::unique_ptr<ConfigureLayout> layout_tab;
|
std::unique_ptr<ConfigureLayout> layout_tab;
|
||||||
|
|||||||
@ -29,7 +29,7 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
|
|||||||
ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
ui->hotkey_list->setModel(model);
|
ui->hotkey_list->setModel(model);
|
||||||
|
|
||||||
ui->hotkey_list->setColumnWidth(0, 250);
|
ui->hotkey_list->setColumnWidth(0, 300);
|
||||||
ui->hotkey_list->resizeColumnToContents(hotkey_column);
|
ui->hotkey_list->resizeColumnToContents(hotkey_column);
|
||||||
|
|
||||||
connect(ui->button_restore_defaults, &QPushButton::clicked, this,
|
connect(ui->button_restore_defaults, &QPushButton::clicked, this,
|
||||||
@ -63,11 +63,9 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
|
|||||||
QStandardItem* action = new QStandardItem(hotkey.first);
|
QStandardItem* action = new QStandardItem(hotkey.first);
|
||||||
QStandardItem* keyseq =
|
QStandardItem* keyseq =
|
||||||
new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
|
new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
|
||||||
QStandardItem* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
|
|
||||||
action->setEditable(false);
|
action->setEditable(false);
|
||||||
keyseq->setEditable(false);
|
keyseq->setEditable(false);
|
||||||
controller_keyseq->setEditable(false);
|
parent_item->appendRow({action, keyseq});
|
||||||
parent_item->appendRow({action, keyseq, controller_keyseq});
|
|
||||||
}
|
}
|
||||||
model->appendRow(parent_item);
|
model->appendRow(parent_item);
|
||||||
}
|
}
|
||||||
|
|||||||
166
src/citra_qt/configuration/configure_hotkeys_controller.cpp
Normal file
166
src/citra_qt/configuration/configure_hotkeys_controller.cpp
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
#include "citra_qt/configuration/config.h"
|
||||||
|
#include "citra_qt/configuration/configure_hotkeys_controller.h"
|
||||||
|
#include "citra_qt/configuration/configure_input.h"
|
||||||
|
#include "citra_qt/hotkeys.h"
|
||||||
|
#include "citra_qt/util/sequence_dialog/controller_sequence_dialog.h"
|
||||||
|
#include "ui_configure_hotkeys_controller.h"
|
||||||
|
|
||||||
|
constexpr int name_column = 0;
|
||||||
|
constexpr int readable_hotkey_column = 1;
|
||||||
|
constexpr int hotkey_column = 2;
|
||||||
|
|
||||||
|
ConfigureControllerHotkeys::ConfigureControllerHotkeys(QWidget* parent)
|
||||||
|
: QWidget(parent), ui(std::make_unique<Ui::ConfigureControllerHotkeys>()) {
|
||||||
|
ui->setupUi(this);
|
||||||
|
setFocusPolicy(Qt::ClickFocus);
|
||||||
|
ui->comboBoxMappingType->setCurrentIndex(
|
||||||
|
static_cast<int>(UISettings::values.controller_hotkey_maptype.GetValue()));
|
||||||
|
model = new QStandardItemModel(this);
|
||||||
|
model->setColumnCount(2);
|
||||||
|
model->setHorizontalHeaderLabels({tr("Action"), tr("Controller Hotkey")});
|
||||||
|
|
||||||
|
connect(ui->hotkey_list, &QTreeView::doubleClicked, this,
|
||||||
|
&ConfigureControllerHotkeys::Configure);
|
||||||
|
connect(ui->hotkey_list, &QTreeView::customContextMenuRequested, this,
|
||||||
|
&ConfigureControllerHotkeys::PopupContextMenu);
|
||||||
|
ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
ui->hotkey_list->setModel(model);
|
||||||
|
|
||||||
|
ui->hotkey_list->setColumnWidth(0, 300);
|
||||||
|
ui->hotkey_list->resizeColumnToContents(readable_hotkey_column);
|
||||||
|
|
||||||
|
connect(ui->button_clear_all, &QPushButton::clicked, this,
|
||||||
|
&ConfigureControllerHotkeys::ClearAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigureControllerHotkeys::~ConfigureControllerHotkeys() = default;
|
||||||
|
|
||||||
|
void ConfigureControllerHotkeys::Populate(const HotkeyRegistry& registry) {
|
||||||
|
for (const auto& group : registry.hotkey_groups) {
|
||||||
|
QStandardItem* parent_item = new QStandardItem(group.first);
|
||||||
|
parent_item->setEditable(false);
|
||||||
|
for (const auto& hotkey : group.second) {
|
||||||
|
QStandardItem* action = new QStandardItem(hotkey.first);
|
||||||
|
QStandardItem* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
|
||||||
|
QStandardItem* readable_keyseq = new QStandardItem(
|
||||||
|
HotkeyRegistry::SequenceToString(hotkey.second.controller_keyseq));
|
||||||
|
action->setEditable(false);
|
||||||
|
controller_keyseq->setEditable(false);
|
||||||
|
parent_item->appendRow({action, readable_keyseq, controller_keyseq});
|
||||||
|
}
|
||||||
|
model->appendRow(parent_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->hotkey_list->expandAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureControllerHotkeys::Configure(QModelIndex index) {
|
||||||
|
if (!index.parent().isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap to the hotkey column
|
||||||
|
index = index.sibling(index.row(), hotkey_column);
|
||||||
|
QModelIndex readableIndex = index.sibling(index.row(), readable_hotkey_column);
|
||||||
|
|
||||||
|
const auto previous_key = model->data(index);
|
||||||
|
|
||||||
|
ControllerSequenceDialog hotkey_dialog{this};
|
||||||
|
|
||||||
|
const int return_code = hotkey_dialog.exec();
|
||||||
|
const auto key_sequence = hotkey_dialog.GetSequence();
|
||||||
|
if (return_code == QDialog::Rejected || key_sequence.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
model->setData(index, key_sequence);
|
||||||
|
model->setData(readableIndex, HotkeyRegistry::SequenceToString(key_sequence));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureControllerHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
|
||||||
|
Settings::InputMappingType maptype = UISettings::values.controller_hotkey_maptype =
|
||||||
|
static_cast<Settings::InputMappingType>(ui->comboBoxMappingType->currentIndex());
|
||||||
|
|
||||||
|
for (int key_id = 0; key_id < model->rowCount(); key_id++) {
|
||||||
|
QStandardItem* parent = model->item(key_id, 0);
|
||||||
|
for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
|
||||||
|
const QStandardItem* action = parent->child(key_column_id, name_column);
|
||||||
|
const QStandardItem* controller_keyseq = parent->child(key_column_id, hotkey_column);
|
||||||
|
if (controller_keyseq->text().isEmpty())
|
||||||
|
continue;
|
||||||
|
const QStringList sequences = controller_keyseq->text().split(QStringLiteral("||"));
|
||||||
|
std::vector<Common::ParamPackage> params;
|
||||||
|
std::transform(sequences.begin(), sequences.end(), std::back_inserter(params),
|
||||||
|
[](const QString& s) { return Common::ParamPackage(s.toStdString()); });
|
||||||
|
if (maptype == Settings::InputMappingType::AllControllers) {
|
||||||
|
for (auto& param : params)
|
||||||
|
param.Set("maptype", "all");
|
||||||
|
} else if (maptype == Settings::InputMappingType::Guid) {
|
||||||
|
for (auto& param : params)
|
||||||
|
param.Set("maptype", "guid");
|
||||||
|
} else {
|
||||||
|
for (auto& param : params)
|
||||||
|
param.Set("maptype", "guid+port");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& [group, sub_actions] : registry.hotkey_groups) {
|
||||||
|
if (group != parent->text())
|
||||||
|
continue;
|
||||||
|
for (auto& [action_name, hotkey] : sub_actions) {
|
||||||
|
if (action_name == action->text()) {
|
||||||
|
QStringList parts;
|
||||||
|
for (const auto& param : params) {
|
||||||
|
parts.append(QString::fromStdString(param.Serialize()));
|
||||||
|
}
|
||||||
|
hotkey.controller_keyseq = parts.join(QStringLiteral("||"));
|
||||||
|
registry.UpdateControllerHotkey(action_name, hotkey);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registry.SaveHotkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureControllerHotkeys::ClearAll() {
|
||||||
|
for (int r = 0; r < model->rowCount(); ++r) {
|
||||||
|
const QStandardItem* parent = model->item(r, 0);
|
||||||
|
|
||||||
|
for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
|
||||||
|
model->item(r, 0)->child(r2, readable_hotkey_column)->setText(QString{});
|
||||||
|
model->item(r, 0)->child(r2, hotkey_column)->setText(QString{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureControllerHotkeys::PopupContextMenu(const QPoint& menu_location) {
|
||||||
|
const auto index = ui->hotkey_list->indexAt(menu_location);
|
||||||
|
if (!index.parent().isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu context_menu;
|
||||||
|
QAction* clear = context_menu.addAction(tr("Clear"));
|
||||||
|
|
||||||
|
const auto readable_hotkey_index = index.sibling(index.row(), readable_hotkey_column);
|
||||||
|
const auto hotkey_index = index.sibling(index.row(), hotkey_column);
|
||||||
|
connect(clear, &QAction::triggered, this, [this, hotkey_index, readable_hotkey_index] {
|
||||||
|
model->setData(hotkey_index, QString{});
|
||||||
|
model->setData(readable_hotkey_index, QString{});
|
||||||
|
});
|
||||||
|
|
||||||
|
context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureControllerHotkeys::RetranslateUI() {
|
||||||
|
ui->retranslateUi(this);
|
||||||
|
}
|
||||||
42
src/citra_qt/configuration/configure_hotkeys_controller.h
Normal file
42
src/citra_qt/configuration/configure_hotkeys_controller.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ConfigureControllerHotkeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
class HotkeyRegistry;
|
||||||
|
class QStandardItemModel;
|
||||||
|
|
||||||
|
class ConfigureControllerHotkeys : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ConfigureControllerHotkeys(QWidget* parent = nullptr);
|
||||||
|
~ConfigureControllerHotkeys() override;
|
||||||
|
|
||||||
|
void ApplyConfiguration(HotkeyRegistry& registry);
|
||||||
|
void RetranslateUI();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates the hotkey list widget using data from the provided registry.
|
||||||
|
* Called everytime the Configure dialog is opened.
|
||||||
|
* @param registry The HotkeyRegistry whose data is used to populate the list.
|
||||||
|
*/
|
||||||
|
void Populate(const HotkeyRegistry& registry);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Configure(QModelIndex index);
|
||||||
|
void ClearAll();
|
||||||
|
void PopupContextMenu(const QPoint& menu_location);
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::ConfigureControllerHotkeys> ui;
|
||||||
|
|
||||||
|
QStandardItemModel* model;
|
||||||
|
};
|
||||||
135
src/citra_qt/configuration/configure_hotkeys_controller.ui
Normal file
135
src/citra_qt/configuration/configure_hotkeys_controller.ui
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ConfigureControllerHotkeys</class>
|
||||||
|
<widget class="QWidget" name="ConfigureControllerHotkeys">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>933</width>
|
||||||
|
<height>388</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Controller Hotkey Settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Double-click on a binding to change it. You can use two-button chords as well.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="button_clear_all">
|
||||||
|
<property name="text">
|
||||||
|
<string>Clear All</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item alignment="Qt::AlignRight">
|
||||||
|
<widget class="QLabel" name="label_16">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>166</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>1920192</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Some mappings cannot be applied to all controllers, such as back buttons</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Apply Controller Hotkeys To:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboBoxMappingType">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>150</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>All controllers</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Controllers of the mapped type</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Only the mapped controller</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="hotkey_list">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="sortingEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@ -70,88 +70,15 @@ static void SetAnalogButton(const Common::ParamPackage& input_param,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static QString ButtonToText(const Common::ParamPackage& param) {
|
static QString ButtonToText(const Common::ParamPackage& param) {
|
||||||
if (!param.Has("engine")) {
|
if (param.Get("engine", "") == "keyboard") {
|
||||||
return QObject::tr("[not set]");
|
|
||||||
}
|
|
||||||
const auto engine_str = param.Get("engine", "");
|
|
||||||
if (engine_str == "keyboard") {
|
|
||||||
return GetKeyName(param.Get("code", 0));
|
return GetKeyName(param.Get("code", 0));
|
||||||
|
} else {
|
||||||
|
return QString::fromStdString(InputCommon::ButtonToText(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (engine_str == "sdl") {
|
|
||||||
if (param.Has("hat")) {
|
|
||||||
const QString hat_str = QString::fromStdString(param.Get("hat", ""));
|
|
||||||
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
|
||||||
|
|
||||||
return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param.Has("axis")) {
|
|
||||||
const QString axis_str = QString::fromStdString(param.Get("axis", ""));
|
|
||||||
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
|
||||||
|
|
||||||
return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param.Has("button")) {
|
|
||||||
const QString button_str = QString::fromStdString(param.Get("button", ""));
|
|
||||||
|
|
||||||
return QObject::tr("Button %1").arg(button_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (engine_str == "gcpad") {
|
|
||||||
if (param.Has("axis")) {
|
|
||||||
const QString axis_str = QString::fromStdString(param.Get("axis", ""));
|
|
||||||
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
|
||||||
|
|
||||||
return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str);
|
|
||||||
}
|
|
||||||
if (param.Has("button")) {
|
|
||||||
const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
|
|
||||||
return QObject::tr("GC Button %1").arg(button_str);
|
|
||||||
}
|
|
||||||
return GetKeyName(param.Get("code", 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return QObject::tr("[unknown]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
|
static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
|
||||||
if (!param.Has("engine")) {
|
return QString::fromStdString(InputCommon::AnalogToText(param, dir));
|
||||||
return QObject::tr("[not set]");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto engine_str = param.Get("engine", "");
|
|
||||||
if (engine_str == "analog_from_button") {
|
|
||||||
return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString axis_x_str{QString::fromStdString(param.Get("axis_x", ""))};
|
|
||||||
const QString axis_y_str{QString::fromStdString(param.Get("axis_y", ""))};
|
|
||||||
static const QString plus_str{QString::fromStdString("+")};
|
|
||||||
static const QString minus_str{QString::fromStdString("-")};
|
|
||||||
if (engine_str == "sdl" || engine_str == "gcpad") {
|
|
||||||
if (dir == "modifier") {
|
|
||||||
return QObject::tr("[unused]");
|
|
||||||
}
|
|
||||||
if (dir == "left") {
|
|
||||||
return QObject::tr("Axis %1%2").arg(axis_x_str, minus_str);
|
|
||||||
}
|
|
||||||
if (dir == "right") {
|
|
||||||
return QObject::tr("Axis %1%2").arg(axis_x_str, plus_str);
|
|
||||||
}
|
|
||||||
if (dir == "up") {
|
|
||||||
return QObject::tr("Axis %1%2").arg(axis_y_str, plus_str);
|
|
||||||
}
|
|
||||||
if (dir == "down") {
|
|
||||||
return QObject::tr("Axis %1%2").arg(axis_y_str, minus_str);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return QObject::tr("[unknown]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||||
@ -230,7 +157,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
// If the user closes the dialog, the changes are reverted in
|
// If the user closes the dialog, the changes are reverted in
|
||||||
// `GMainWindow::OnConfigure()`
|
// `GMainWindow::OnConfigure()`
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
},
|
},
|
||||||
InputCommon::Polling::DeviceType::Button);
|
InputCommon::Polling::DeviceType::Button);
|
||||||
});
|
});
|
||||||
@ -241,7 +167,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
buttons_param[button_id].Clear();
|
buttons_param[button_id].Clear();
|
||||||
button_map[button_id]->setText(tr("[not set]"));
|
button_map[button_id]->setText(tr("[not set]"));
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
});
|
});
|
||||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||||
buttons_param[button_id] =
|
buttons_param[button_id] =
|
||||||
@ -249,7 +174,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
QtConfig::default_buttons[button_id])};
|
QtConfig::default_buttons[button_id])};
|
||||||
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
});
|
});
|
||||||
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
||||||
});
|
});
|
||||||
@ -269,7 +193,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
SetAnalogButton(params, analogs_param[analog_id],
|
SetAnalogButton(params, analogs_param[analog_id],
|
||||||
analog_sub_buttons[sub_button_id]);
|
analog_sub_buttons[sub_button_id]);
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
},
|
},
|
||||||
InputCommon::Polling::DeviceType::Button);
|
InputCommon::Polling::DeviceType::Button);
|
||||||
});
|
});
|
||||||
@ -281,7 +204,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||||
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
});
|
});
|
||||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||||
@ -291,7 +213,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
|
analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
|
||||||
analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
|
analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
});
|
});
|
||||||
context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
|
context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
|
||||||
menu_location));
|
menu_location));
|
||||||
@ -308,7 +229,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
[this, analog_id](const Common::ParamPackage& params) {
|
[this, analog_id](const Common::ParamPackage& params) {
|
||||||
analogs_param[analog_id] = params;
|
analogs_param[analog_id] = params;
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
},
|
},
|
||||||
InputCommon::Polling::DeviceType::Analog);
|
InputCommon::Polling::DeviceType::Analog);
|
||||||
}
|
}
|
||||||
@ -328,7 +248,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
|
analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
|
||||||
}
|
}
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,7 +262,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
SetAnalogButton(params, analogs_param[analog_id], "modifier");
|
SetAnalogButton(params, analogs_param[analog_id], "modifier");
|
||||||
}
|
}
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
},
|
},
|
||||||
InputCommon::Polling::DeviceType::Button);
|
InputCommon::Polling::DeviceType::Button);
|
||||||
});
|
});
|
||||||
@ -357,7 +275,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
}
|
}
|
||||||
ui->buttonCircleMod->setText(tr("[not set]"));
|
ui->buttonCircleMod->setText(tr("[not set]"));
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||||
@ -371,7 +288,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
AnalogToText(analogs_param[analog_id], "modifier"));
|
AnalogToText(analogs_param[analog_id], "modifier"));
|
||||||
}
|
}
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
});
|
});
|
||||||
context_menu.exec(ui->buttonCircleMod->mapToGlobal(menu_location));
|
context_menu.exec(ui->buttonCircleMod->mapToGlobal(menu_location));
|
||||||
});
|
});
|
||||||
@ -395,7 +311,6 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
|
|
||||||
connect(ui->profile, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int i) {
|
connect(ui->profile, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int i) {
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
|
||||||
Settings::LoadProfile(i);
|
Settings::LoadProfile(i);
|
||||||
LoadConfiguration();
|
LoadConfiguration();
|
||||||
});
|
});
|
||||||
@ -407,7 +322,8 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
|||||||
Common::ParamPackage params;
|
Common::ParamPackage params;
|
||||||
for (auto& poller : device_pollers) {
|
for (auto& poller : device_pollers) {
|
||||||
params = poller->GetNextInput();
|
params = poller->GetNextInput();
|
||||||
if (params.Has("engine")) {
|
// skip button downs and only process button ups to maintain former behavior
|
||||||
|
if (params.Has("engine") && !params.Has("down")) {
|
||||||
SetPollingResult(params, false);
|
SetPollingResult(params, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -423,12 +339,44 @@ void ConfigureInput::ApplyConfiguration() {
|
|||||||
|
|
||||||
Settings::values.use_artic_base_controller = ui->use_artic_controller->isChecked();
|
Settings::values.use_artic_base_controller = ui->use_artic_controller->isChecked();
|
||||||
|
|
||||||
|
Settings::values.current_input_profile.maptype =
|
||||||
|
static_cast<Settings::InputMappingType>(ui->comboBoxMappingType->currentIndex());
|
||||||
|
|
||||||
std::transform(buttons_param.begin(), buttons_param.end(),
|
std::transform(buttons_param.begin(), buttons_param.end(),
|
||||||
Settings::values.current_input_profile.buttons.begin(),
|
Settings::values.current_input_profile.buttons.begin(),
|
||||||
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
[](Common::ParamPackage& param) {
|
||||||
|
if (param.Get("engine", "keyboard") == "sdl") {
|
||||||
|
if (Settings::values.current_input_profile.maptype ==
|
||||||
|
Settings::InputMappingType::AllControllers)
|
||||||
|
param.Set("maptype", "all");
|
||||||
|
else if (Settings::values.current_input_profile.maptype ==
|
||||||
|
Settings::InputMappingType::Guid)
|
||||||
|
param.Set("maptype", "guid");
|
||||||
|
else
|
||||||
|
param.Set("maptype", "guid+port");
|
||||||
|
} else {
|
||||||
|
param.Erase("maptype");
|
||||||
|
}
|
||||||
|
return param.Serialize();
|
||||||
|
});
|
||||||
std::transform(analogs_param.begin(), analogs_param.end(),
|
std::transform(analogs_param.begin(), analogs_param.end(),
|
||||||
Settings::values.current_input_profile.analogs.begin(),
|
Settings::values.current_input_profile.analogs.begin(),
|
||||||
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
[](Common::ParamPackage& param) {
|
||||||
|
if (param.Get("engine", "keyboard") == "sdl") {
|
||||||
|
if (Settings::values.current_input_profile.maptype ==
|
||||||
|
Settings::InputMappingType::AllControllers)
|
||||||
|
param.Set("maptype", "all");
|
||||||
|
else if (Settings::values.current_input_profile.maptype ==
|
||||||
|
Settings::InputMappingType::Guid)
|
||||||
|
param.Set("maptype", "guid");
|
||||||
|
else
|
||||||
|
param.Set("maptype", "guid+port");
|
||||||
|
} else {
|
||||||
|
param.Erase("maptype");
|
||||||
|
}
|
||||||
|
return param.Serialize();
|
||||||
|
});
|
||||||
|
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureInput::ApplyProfile() {
|
void ConfigureInput::ApplyProfile() {
|
||||||
@ -469,6 +417,8 @@ QList<QKeySequence> ConfigureInput::GetUsedKeyboardKeys() {
|
|||||||
void ConfigureInput::LoadConfiguration() {
|
void ConfigureInput::LoadConfiguration() {
|
||||||
|
|
||||||
ui->use_artic_controller->setChecked(Settings::values.use_artic_base_controller.GetValue());
|
ui->use_artic_controller->setChecked(Settings::values.use_artic_base_controller.GetValue());
|
||||||
|
ui->comboBoxMappingType->setCurrentIndex(
|
||||||
|
static_cast<int>(Settings::values.current_input_profile.maptype));
|
||||||
ui->use_artic_controller->setEnabled(!system.IsPoweredOn());
|
ui->use_artic_controller->setEnabled(!system.IsPoweredOn());
|
||||||
|
|
||||||
std::transform(Settings::values.current_input_profile.buttons.begin(),
|
std::transform(Settings::values.current_input_profile.buttons.begin(),
|
||||||
@ -495,7 +445,6 @@ void ConfigureInput::RestoreDefaults() {
|
|||||||
UpdateButtonLabels();
|
UpdateButtonLabels();
|
||||||
|
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureInput::ClearAll() {
|
void ConfigureInput::ClearAll() {
|
||||||
@ -509,7 +458,6 @@ void ConfigureInput::ClearAll() {
|
|||||||
UpdateButtonLabels();
|
UpdateButtonLabels();
|
||||||
|
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureInput::UpdateButtonLabels() {
|
void ConfigureInput::UpdateButtonLabels() {
|
||||||
@ -583,16 +531,21 @@ void ConfigureInput::MapFromButton(const Common::ParamPackage& params) {
|
|||||||
|
|
||||||
void ConfigureInput::AutoMap() {
|
void ConfigureInput::AutoMap() {
|
||||||
ui->buttonAutoMap->setEnabled(false);
|
ui->buttonAutoMap->setEnabled(false);
|
||||||
if (QMessageBox::information(this, tr("Information"),
|
QMessageBox box(this);
|
||||||
tr("After pressing OK, press any button on your joystick"),
|
box.setWindowTitle(tr("Auto map Controller"));
|
||||||
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) {
|
box.setText(tr("After pressing OK, press the A (right) button on your gamepad"));
|
||||||
|
box.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
||||||
|
QPixmap pixmap(QStringLiteral(":/icons/default/256x256/automap_face_buttons.png"));
|
||||||
|
pixmap = pixmap.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
box.setIconPixmap(pixmap);
|
||||||
|
int result = box.exec();
|
||||||
|
if (result == QMessageBox::Cancel) {
|
||||||
ui->buttonAutoMap->setEnabled(true);
|
ui->buttonAutoMap->setEnabled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
input_setter = [this](const Common::ParamPackage& params) {
|
input_setter = [this](const Common::ParamPackage& params) {
|
||||||
MapFromButton(params);
|
MapFromButton(params);
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
};
|
};
|
||||||
device_pollers = InputCommon::Polling::GetPollers(InputCommon::Polling::DeviceType::Button);
|
device_pollers = InputCommon::Polling::GetPollers(InputCommon::Polling::DeviceType::Button);
|
||||||
want_keyboard_keys = false;
|
want_keyboard_keys = false;
|
||||||
@ -690,7 +643,6 @@ void ConfigureInput::NewProfile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ApplyConfiguration();
|
ApplyConfiguration();
|
||||||
Settings::SaveProfile(ui->profile->currentIndex());
|
|
||||||
Settings::CreateProfile(name.toStdString());
|
Settings::CreateProfile(name.toStdString());
|
||||||
ui->profile->addItem(name);
|
ui->profile->addItem(name);
|
||||||
ui->profile->setCurrentIndex(Settings::values.current_input_profile_index);
|
ui->profile->setCurrentIndex(Settings::values.current_input_profile_index);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -43,6 +43,7 @@ public:
|
|||||||
|
|
||||||
/// Save the current input profile index
|
/// Save the current input profile index
|
||||||
void ApplyProfile();
|
void ApplyProfile();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnHotkeysChanged(QList<QKeySequence> new_key_list);
|
void OnHotkeysChanged(QList<QKeySequence> new_key_list);
|
||||||
|
|
||||||
|
|||||||
@ -6,61 +6,163 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>441</width>
|
<width>716</width>
|
||||||
<height>727</height>
|
<height>694</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>ConfigureInput</string>
|
<string>ConfigureInput</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="rootLayout">
|
<layout class="QVBoxLayout" name="rootLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<widget class="QFrame" name="frame">
|
||||||
<item>
|
<property name="frameShape">
|
||||||
<widget class="QLabel" name="label_5">
|
<enum>QFrame::StyledPanel</enum>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>Profile</string>
|
<property name="frameShadow">
|
||||||
</property>
|
<enum>QFrame::Sunken</enum>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
<layout class="QVBoxLayout" name="verticalLayout_40">
|
||||||
<item>
|
<property name="spacing">
|
||||||
<widget class="QComboBox" name="profile"/>
|
<number>4</number>
|
||||||
</item>
|
</property>
|
||||||
<item>
|
<property name="leftMargin">
|
||||||
<spacer name="horizontalSpacer_2">
|
<number>12</number>
|
||||||
<property name="orientation">
|
</property>
|
||||||
<enum>Qt::Horizontal</enum>
|
<property name="topMargin">
|
||||||
</property>
|
<number>8</number>
|
||||||
<property name="sizeHint" stdset="0">
|
</property>
|
||||||
<size>
|
<property name="rightMargin">
|
||||||
<width>40</width>
|
<number>12</number>
|
||||||
<height>20</height>
|
</property>
|
||||||
</size>
|
<property name="bottomMargin">
|
||||||
</property>
|
<number>8</number>
|
||||||
</spacer>
|
</property>
|
||||||
</item>
|
<item>
|
||||||
<item>
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<widget class="QPushButton" name="buttonNew">
|
<item>
|
||||||
<property name="text">
|
<widget class="QLabel" name="label_5">
|
||||||
<string>New</string>
|
<property name="text">
|
||||||
</property>
|
<string>Profile</string>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
</widget>
|
||||||
<item>
|
</item>
|
||||||
<widget class="QPushButton" name="buttonDelete">
|
<item>
|
||||||
<property name="text">
|
<widget class="QComboBox" name="profile"/>
|
||||||
<string>Delete</string>
|
</item>
|
||||||
</property>
|
<item>
|
||||||
</widget>
|
<spacer name="horizontalSpacer_2">
|
||||||
</item>
|
<property name="orientation">
|
||||||
<item>
|
<enum>Qt::Horizontal</enum>
|
||||||
<widget class="QPushButton" name="buttonRename">
|
</property>
|
||||||
<property name="text">
|
<property name="sizeHint" stdset="0">
|
||||||
<string>Rename</string>
|
<size>
|
||||||
</property>
|
<width>40</width>
|
||||||
</widget>
|
<height>20</height>
|
||||||
</item>
|
</size>
|
||||||
</layout>
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonNew">
|
||||||
|
<property name="text">
|
||||||
|
<string>New</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonDelete">
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonRename">
|
||||||
|
<property name="text">
|
||||||
|
<string>Rename</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item alignment="Qt::AlignRight">
|
||||||
|
<widget class="QLabel" name="label_16">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>166</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>1920192</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Some mappings cannot be applied to all controllers, such as back buttons</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Apply Game Controller Maps To:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboBoxMappingType">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>150</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>All controllers</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Controllers of the mapped type</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Only the mapped controller</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QScrollArea" name="scrollArea">
|
<widget class="QScrollArea" name="scrollArea">
|
||||||
@ -68,6 +170,14 @@
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="scrollAreaContents">
|
<widget class="QWidget" name="scrollAreaContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>675</width>
|
||||||
|
<height>996</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_7">
|
<layout class="QGridLayout" name="gridLayout_7">
|
||||||
|
|||||||
92
src/citra_qt/hotkey_monitor.cpp
Normal file
92
src/citra_qt/hotkey_monitor.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QShortcut>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWidget>
|
||||||
|
#include "core/frontend/input.h"
|
||||||
|
#include "hotkey_monitor.h"
|
||||||
|
#include "hotkeys.h"
|
||||||
|
|
||||||
|
struct ControllerHotkeyMonitor::ButtonState {
|
||||||
|
Hotkey* hk;
|
||||||
|
bool lastStatus = false;
|
||||||
|
bool lastStatus2 = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
ControllerHotkeyMonitor::ControllerHotkeyMonitor() {
|
||||||
|
m_buttons = std::make_unique<std::map<QString, ButtonState>>();
|
||||||
|
m_timer = new QTimer();
|
||||||
|
QObject::connect(m_timer, &QTimer::timeout, [this]() { checkAllButtons(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerHotkeyMonitor::start(const int msec) {
|
||||||
|
m_timer->start(msec);
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
// Controller Hotkeys
|
||||||
|
for (auto& [name, it] : *m_buttons) {
|
||||||
|
bool trigger = false;
|
||||||
|
if (!it.hk || !it.hk->button_device)
|
||||||
|
continue;
|
||||||
|
bool currentStatus = it.hk->button_device->GetStatus();
|
||||||
|
if (it.hk->button_device2) {
|
||||||
|
// two buttons, need both pressed and one *just now* pressed
|
||||||
|
bool currentStatus2 = it.hk->button_device2->GetStatus();
|
||||||
|
trigger = currentStatus && currentStatus2 && (!it.lastStatus || !it.lastStatus2);
|
||||||
|
it.lastStatus = currentStatus;
|
||||||
|
it.lastStatus2 = currentStatus2;
|
||||||
|
} else {
|
||||||
|
// if only one button, trigger as soon as pressed
|
||||||
|
trigger = currentStatus && !it.lastStatus;
|
||||||
|
it.lastStatus = currentStatus;
|
||||||
|
}
|
||||||
|
if (trigger) {
|
||||||
|
if (it.hk->action) {
|
||||||
|
it.hk->action->trigger();
|
||||||
|
}
|
||||||
|
for (auto const& [name, hotkey_shortcut] : it.hk->shortcuts) {
|
||||||
|
if (hotkey_shortcut && hotkey_shortcut->isEnabled()) {
|
||||||
|
QWidget* parent = qobject_cast<QWidget*>(hotkey_shortcut->parent());
|
||||||
|
if (!parent)
|
||||||
|
continue;
|
||||||
|
if (name == QStringLiteral("move down")) {
|
||||||
|
std::cout << "move down triggered before context check" << std::endl;
|
||||||
|
}
|
||||||
|
bool shouldFire = true;
|
||||||
|
// Code to honor context, so we can set different contexts and parents
|
||||||
|
// appropriately
|
||||||
|
if (hotkey_shortcut->context() == Qt::WidgetShortcut) {
|
||||||
|
shouldFire = parent == QApplication::focusWidget();
|
||||||
|
} else if (hotkey_shortcut->context() == Qt::WidgetWithChildrenShortcut) {
|
||||||
|
shouldFire = parent == QApplication::focusWidget() ||
|
||||||
|
parent->isAncestorOf(QApplication::focusWidget());
|
||||||
|
} else if (hotkey_shortcut->context() == Qt::WindowShortcut) {
|
||||||
|
shouldFire = parent->window()->isActiveWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFire) {
|
||||||
|
hotkey_shortcut->activated();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/citra_qt/hotkey_monitor.h
Normal file
28
src/citra_qt/hotkey_monitor.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright Citra Emulator Project / 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);
|
||||||
|
void start(const int msec);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void checkAllButtons();
|
||||||
|
struct ButtonState;
|
||||||
|
|
||||||
|
std::unique_ptr<std::map<QString, ButtonState>> m_buttons;
|
||||||
|
QTimer* m_timer;
|
||||||
|
};
|
||||||
@ -1,11 +1,13 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include "citra_qt/hotkeys.h"
|
#include "citra_qt/hotkeys.h"
|
||||||
#include "citra_qt/uisettings.h"
|
#include "citra_qt/uisettings.h"
|
||||||
|
#include "input_common/main.h"
|
||||||
|
|
||||||
HotkeyRegistry::HotkeyRegistry() = default;
|
HotkeyRegistry::HotkeyRegistry() = default;
|
||||||
|
|
||||||
@ -17,22 +19,41 @@ void HotkeyRegistry::SaveHotkeys() {
|
|||||||
for (const auto& hotkey : group.second) {
|
for (const auto& hotkey : group.second) {
|
||||||
UISettings::values.shortcuts.push_back(
|
UISettings::values.shortcuts.push_back(
|
||||||
{hotkey.first, group.first,
|
{hotkey.first, group.first,
|
||||||
UISettings::ContextualShortcut(
|
UISettings::ContextualShortcut({hotkey.second.keyseq.toString(),
|
||||||
{hotkey.second.keyseq.toString(), hotkey.second.context})});
|
hotkey.second.controller_keyseq,
|
||||||
|
hotkey.second.context})});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void HotkeyRegistry::UpdateControllerHotkey(QString name, Hotkey& hk) {
|
||||||
|
if (hk.controller_keyseq.isEmpty()) {
|
||||||
|
buttonMonitor.removeButton(name);
|
||||||
|
} else {
|
||||||
|
QStringList paramList = hk.controller_keyseq.split(QStringLiteral("||"));
|
||||||
|
if (paramList.length() > 0) {
|
||||||
|
hk.button_device =
|
||||||
|
Input::CreateDevice<Input::ButtonDevice>(paramList.value(0).toStdString());
|
||||||
|
if (paramList.length() > 1) {
|
||||||
|
hk.button_device2 =
|
||||||
|
Input::CreateDevice<Input::ButtonDevice>(paramList.value(1).toStdString());
|
||||||
|
}
|
||||||
|
buttonMonitor.addButton(name, &hk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HotkeyRegistry::LoadHotkeys() {
|
void HotkeyRegistry::LoadHotkeys() {
|
||||||
// Make sure NOT to use a reference here because it would become invalid once we call
|
// Make sure NOT to use a reference here because it would become invalid once we call
|
||||||
// beginGroup()
|
// beginGroup()
|
||||||
for (auto shortcut : UISettings::values.shortcuts) {
|
for (auto shortcut : UISettings::values.shortcuts) {
|
||||||
Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
|
Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
|
||||||
if (!shortcut.shortcut.keyseq.isEmpty()) {
|
if (!shortcut.shortcut.keyseq.isEmpty() || !shortcut.shortcut.controller_keyseq.isEmpty()) {
|
||||||
hk.keyseq =
|
hk.keyseq =
|
||||||
QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText);
|
QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText);
|
||||||
hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context);
|
hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context);
|
||||||
|
hk.controller_keyseq = shortcut.shortcut.controller_keyseq;
|
||||||
}
|
}
|
||||||
|
UpdateControllerHotkey(shortcut.name, hk);
|
||||||
|
|
||||||
for (auto const& [_, hotkey_shortcut] : hk.shortcuts) {
|
for (auto const& [_, hotkey_shortcut] : hk.shortcuts) {
|
||||||
if (hotkey_shortcut) {
|
if (hotkey_shortcut) {
|
||||||
hotkey_shortcut->disconnect();
|
hotkey_shortcut->disconnect();
|
||||||
@ -63,3 +84,23 @@ Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group,
|
|||||||
Hotkey& hk = hotkey_groups[group][action];
|
Hotkey& hk = hotkey_groups[group][action];
|
||||||
return hk.context;
|
return hk.context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HotkeyRegistry::SetAction(const QString& group, const QString& action_name, QAction* action) {
|
||||||
|
Hotkey& hk = hotkey_groups[group][action_name];
|
||||||
|
hk.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HotkeyRegistry::SequenceToString(QString controller_keyseq) {
|
||||||
|
if (controller_keyseq.isEmpty())
|
||||||
|
return controller_keyseq;
|
||||||
|
QStringList keys = controller_keyseq.split(QStringLiteral("||"));
|
||||||
|
Common::ParamPackage p1 = Common::ParamPackage(keys.value(0).toStdString());
|
||||||
|
QString output = QString::fromStdString(InputCommon::ButtonToText(p1));
|
||||||
|
|
||||||
|
if (keys.length() > 1) {
|
||||||
|
output.append(QStringLiteral(" + "));
|
||||||
|
p1 = Common::ParamPackage(keys.value(1).toStdString());
|
||||||
|
output.append(QString::fromStdString(InputCommon::ButtonToText(p1)));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,25 +1,41 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <QAction>
|
||||||
#include <QKeySequence>
|
#include <QKeySequence>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include "core/frontend/input.h"
|
||||||
|
#include "hotkey_monitor.h"
|
||||||
|
|
||||||
class QDialog;
|
class QDialog;
|
||||||
class QSettings;
|
class QSettings;
|
||||||
class QShortcut;
|
class QShortcut;
|
||||||
class QWidget;
|
class QWidget;
|
||||||
|
|
||||||
|
struct Hotkey {
|
||||||
|
QKeySequence keyseq;
|
||||||
|
QString controller_keyseq;
|
||||||
|
std::map<QString, QShortcut*> shortcuts;
|
||||||
|
Qt::ShortcutContext context = Qt::ApplicationShortcut;
|
||||||
|
std::unique_ptr<Input::ButtonDevice> button_device = nullptr;
|
||||||
|
std::unique_ptr<Input::ButtonDevice> button_device2 = nullptr;
|
||||||
|
QAction* action = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
class HotkeyRegistry final {
|
class HotkeyRegistry final {
|
||||||
public:
|
public:
|
||||||
friend class ConfigureHotkeys;
|
friend class ConfigureHotkeys;
|
||||||
|
friend class ConfigureControllerHotkeys;
|
||||||
|
|
||||||
explicit HotkeyRegistry();
|
explicit HotkeyRegistry();
|
||||||
~HotkeyRegistry();
|
~HotkeyRegistry();
|
||||||
|
|
||||||
|
ControllerHotkeyMonitor buttonMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads hotkeys from the settings file.
|
* Loads hotkeys from the settings file.
|
||||||
*
|
*
|
||||||
@ -36,6 +52,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void SaveHotkeys();
|
void SaveHotkeys();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the button devices for a hotkey based on the controller_keyseq value
|
||||||
|
*/
|
||||||
|
void UpdateControllerHotkey(QString name, Hotkey& hk);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a QShortcut object whose activated() signal can be connected to other QObjects'
|
* Returns a QShortcut object whose activated() signal can be connected to other QObjects'
|
||||||
* slots.
|
* slots.
|
||||||
@ -43,8 +64,8 @@ public:
|
|||||||
* @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
|
* @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
|
||||||
* @param action Name of the action (e.g. "Start Emulation", "Load Image").
|
* @param action Name of the action (e.g. "Start Emulation", "Load Image").
|
||||||
* @param widget Parent widget of the returned QShortcut.
|
* @param widget Parent widget of the returned QShortcut.
|
||||||
* @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
|
* @warning If multiple QWidgets' call this function for the same action, the returned
|
||||||
* will be the same. Thus, you shouldn't rely on the caller really being the
|
* QShortcut will be the same. Thus, you shouldn't rely on the caller really being the
|
||||||
* QShortcut's parent.
|
* QShortcut's parent.
|
||||||
*/
|
*/
|
||||||
QShortcut* GetHotkey(const QString& group, const QString& action, QObject* widget);
|
QShortcut* GetHotkey(const QString& group, const QString& action, QObject* widget);
|
||||||
@ -67,14 +88,23 @@ public:
|
|||||||
*/
|
*/
|
||||||
Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action);
|
Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action);
|
||||||
|
|
||||||
private:
|
/**
|
||||||
struct Hotkey {
|
* Stores a QAction into the appropriate hotkey, for triggering by controller
|
||||||
QKeySequence keyseq;
|
*
|
||||||
QString controller_keyseq;
|
* @param group General group this shortcut context belongs to
|
||||||
std::map<QString, QShortcut*> shortcuts;
|
* @param action_name Name of the action
|
||||||
Qt::ShortcutContext context = Qt::WindowShortcut;
|
* @param action The QAction to store
|
||||||
};
|
*/
|
||||||
|
void SetAction(const QString& group, const QString& action_name, QAction* action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a controller keysequene for a hotkey and returns a readable string
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static QString SequenceToString(QString controller_keyseq);
|
||||||
|
|
||||||
|
private:
|
||||||
using HotkeyMap = std::map<QString, Hotkey>;
|
using HotkeyMap = std::map<QString, Hotkey>;
|
||||||
using HotkeyGroupMap = std::map<QString, HotkeyMap>;
|
using HotkeyGroupMap = std::map<QString, HotkeyMap>;
|
||||||
|
|
||||||
|
|||||||
@ -13,12 +13,14 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include "citra_qt/setting_qkeys.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
|
||||||
namespace UISettings {
|
namespace UISettings {
|
||||||
|
|
||||||
struct ContextualShortcut {
|
struct ContextualShortcut {
|
||||||
QString keyseq;
|
QString keyseq;
|
||||||
|
QString controller_keyseq;
|
||||||
int context;
|
int context;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -163,6 +165,11 @@ struct Values {
|
|||||||
Settings::Setting<bool> show_console{false, "showConsole"};
|
Settings::Setting<bool> show_console{false, "showConsole"};
|
||||||
|
|
||||||
bool shortcut_already_warned = false;
|
bool shortcut_already_warned = false;
|
||||||
|
|
||||||
|
// this isn't really a UI setting, but it's a citra_qt exclusive setting so here we are
|
||||||
|
Settings::Setting<Settings::InputMappingType> controller_hotkey_maptype{
|
||||||
|
Settings::InputMappingType::AllControllers,
|
||||||
|
Settings::QKeys::controller_hotkey_maptype.toStdString()};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Values values;
|
extern Values values;
|
||||||
|
|||||||
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include "citra_qt/hotkeys.h"
|
||||||
|
#include "common/param_package.h"
|
||||||
|
#include "configuration/configure_hotkeys_controller.h"
|
||||||
|
#include "configuration/configure_input.h"
|
||||||
|
#include "controller_sequence_dialog.h"
|
||||||
|
#include "util/sequence_dialog/controller_sequence_dialog.h"
|
||||||
|
|
||||||
|
ControllerSequenceDialog::ControllerSequenceDialog(QWidget* parent)
|
||||||
|
: QDialog(parent), poll_timer(std::make_unique<QTimer>()) {
|
||||||
|
setWindowTitle(tr("Press then release one or two controller buttons"));
|
||||||
|
|
||||||
|
auto* const buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
buttons->setCenterButtons(true);
|
||||||
|
|
||||||
|
textBox = new QLabel(QStringLiteral("Waiting..."), this);
|
||||||
|
auto* const layout = new QVBoxLayout(this);
|
||||||
|
layout->addWidget(textBox);
|
||||||
|
layout->addWidget(buttons);
|
||||||
|
|
||||||
|
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
|
||||||
|
LaunchPollers();
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerSequenceDialog::~ControllerSequenceDialog() = default;
|
||||||
|
|
||||||
|
QString ControllerSequenceDialog::GetSequence() {
|
||||||
|
return key_sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerSequenceDialog::closeEvent(QCloseEvent*) {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControllerSequenceDialog::focusNextPrevChild(bool next) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControllerSequenceDialog::LaunchPollers() {
|
||||||
|
device_pollers = InputCommon::Polling::GetPollers(InputCommon::Polling::DeviceType::Button);
|
||||||
|
|
||||||
|
for (auto& poller : device_pollers) {
|
||||||
|
poller->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(poll_timer.get(), &QTimer::timeout, this, [this, downCount = 0]() mutable {
|
||||||
|
Common::ParamPackage params;
|
||||||
|
for (auto& poller : device_pollers) {
|
||||||
|
params = poller->GetNextInput();
|
||||||
|
if (params.Has("engine")) {
|
||||||
|
if (params.Has("down")) {
|
||||||
|
downCount++;
|
||||||
|
if (downCount == 1) {
|
||||||
|
// either the first press, or the first new press
|
||||||
|
key_sequence = QStringLiteral("");
|
||||||
|
params1 = params;
|
||||||
|
params2 = Common::ParamPackage();
|
||||||
|
key_sequence = QString::fromStdString(params1.Serialize());
|
||||||
|
textBox->setText(HotkeyRegistry::SequenceToString(key_sequence) +
|
||||||
|
QStringLiteral("..."));
|
||||||
|
} else if (downCount == 2 && !params2.Has("engine")) {
|
||||||
|
// this is a second button, currently only one button saved, so save it
|
||||||
|
params2 = params;
|
||||||
|
key_sequence = QString::fromStdString(params1.Serialize() + "||" +
|
||||||
|
params2.Serialize());
|
||||||
|
textBox->setText(HotkeyRegistry::SequenceToString(key_sequence));
|
||||||
|
}
|
||||||
|
// if downCount == 3 or more, just ignore them - we have saved the first two
|
||||||
|
// presses
|
||||||
|
} else { // button release
|
||||||
|
downCount--;
|
||||||
|
if (downCount <= 0) {
|
||||||
|
// buttons all released, show the saved sequence and prepare to start again
|
||||||
|
textBox->setText(HotkeyRegistry::SequenceToString(key_sequence));
|
||||||
|
params1 = Common::ParamPackage();
|
||||||
|
params2 = Common::ParamPackage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
poll_timer->start(100);
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QLabel>
|
||||||
|
#include "common/param_package.h"
|
||||||
|
#include "input_common/main.h"
|
||||||
|
|
||||||
|
class ControllerSequenceDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ControllerSequenceDialog(QWidget* parent = nullptr);
|
||||||
|
~ControllerSequenceDialog();
|
||||||
|
|
||||||
|
QString GetSequence();
|
||||||
|
void closeEvent(QCloseEvent*) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void LaunchPollers();
|
||||||
|
QLabel* textBox;
|
||||||
|
QString key_sequence;
|
||||||
|
Common::ParamPackage params1, params2;
|
||||||
|
bool focusNextPrevChild(bool next) override;
|
||||||
|
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
|
||||||
|
std::unique_ptr<QTimer> poll_timer;
|
||||||
|
};
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2018 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2018 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
// Copyright 2017 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
@ -23,7 +22,6 @@ public:
|
|||||||
|
|
||||||
ParamPackage& operator=(const ParamPackage& other) = default;
|
ParamPackage& operator=(const ParamPackage& other) = default;
|
||||||
ParamPackage& operator=(ParamPackage&& other) = default;
|
ParamPackage& operator=(ParamPackage&& other) = default;
|
||||||
|
|
||||||
[[nodiscard]] std::string Serialize() const;
|
[[nodiscard]] std::string Serialize() const;
|
||||||
[[nodiscard]] std::string Get(const std::string& key, const std::string& default_value) const;
|
[[nodiscard]] std::string Get(const std::string& key, const std::string& default_value) const;
|
||||||
[[nodiscard]] int Get(const std::string& key, int default_value) const;
|
[[nodiscard]] int Get(const std::string& key, int default_value) const;
|
||||||
|
|||||||
@ -49,6 +49,8 @@ enum class LayoutOption : u32 { // Shouldn't these have set numbers to prevent l
|
|||||||
CustomLayout,
|
CustomLayout,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class InputMappingType : u8 { AllControllers, Guid, GuidPort };
|
||||||
|
|
||||||
/** Defines the layout option for mobile portrait */
|
/** Defines the layout option for mobile portrait */
|
||||||
enum class PortraitLayoutOption : u32 {
|
enum class PortraitLayoutOption : u32 {
|
||||||
// formerly mobile portrait
|
// formerly mobile portrait
|
||||||
@ -449,6 +451,7 @@ struct InputProfile {
|
|||||||
std::string udp_input_address;
|
std::string udp_input_address;
|
||||||
u16 udp_input_port;
|
u16 udp_input_port;
|
||||||
u8 udp_pad_index;
|
u8 udp_pad_index;
|
||||||
|
InputMappingType maptype = Settings::InputMappingType::GuidPort;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TouchFromButtonMap {
|
struct TouchFromButtonMap {
|
||||||
|
|||||||
@ -23,6 +23,7 @@ add_library(input_common STATIC
|
|||||||
if(ENABLE_SDL2)
|
if(ENABLE_SDL2)
|
||||||
target_sources(input_common PRIVATE
|
target_sources(input_common PRIVATE
|
||||||
sdl/sdl_impl.cpp
|
sdl/sdl_impl.cpp
|
||||||
|
sdl/sdl_joystick.cpp
|
||||||
sdl/sdl_impl.h
|
sdl/sdl_impl.h
|
||||||
)
|
)
|
||||||
target_link_libraries(input_common PRIVATE SDL2::SDL2)
|
target_link_libraries(input_common PRIVATE SDL2::SDL2)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2017 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -70,6 +70,104 @@ void Shutdown() {
|
|||||||
udp.reset();
|
udp.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ButtonToText(const Common::ParamPackage& param) {
|
||||||
|
if (!param.Has("engine")) {
|
||||||
|
return "[not set]";
|
||||||
|
}
|
||||||
|
const auto engine_str = param.Get("engine", "");
|
||||||
|
// keyboard should be handled by the frontend
|
||||||
|
if (engine_str == "keyboard") {
|
||||||
|
return "keyboard code " + param.Get("code", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine_str == "sdl") {
|
||||||
|
if (param.Has("hat")) {
|
||||||
|
return "Hat " + param.Get("hat", "") + " " + param.Get("direction", "");
|
||||||
|
} else if (param.Has("button")) {
|
||||||
|
if (param.Get("name", "") != "")
|
||||||
|
return param.Get("name", "");
|
||||||
|
else
|
||||||
|
return "Button " + param.Get("button", "");
|
||||||
|
|
||||||
|
} else if (param.Has("axis")) {
|
||||||
|
auto name = param.Get("name", "");
|
||||||
|
if (name == "LT" || name == "RT")
|
||||||
|
return name;
|
||||||
|
else if (name != "")
|
||||||
|
return name + param.Get("direction", "");
|
||||||
|
else
|
||||||
|
return "Axis " + param.Get("axis", "") + param.Get("direction", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine_str == "gcpad") {
|
||||||
|
if (param.Has("axis")) {
|
||||||
|
const auto axis_str = param.Get("axis", "");
|
||||||
|
const auto direction_str = param.Get("direction", "");
|
||||||
|
|
||||||
|
return "GC Axis " + axis_str + direction_str;
|
||||||
|
}
|
||||||
|
if (param.Has("button")) {
|
||||||
|
const auto button = int(std::log2(param.Get("button", 0)));
|
||||||
|
return "GC Button " + button;
|
||||||
|
}
|
||||||
|
return "keyboard code " + param.Get("code", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "[unknown]";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
|
||||||
|
if (!param.Has("engine")) {
|
||||||
|
return "[not set]";
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto engine_str = param.Get("engine", "");
|
||||||
|
if (engine_str == "analog_from_button") {
|
||||||
|
return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto axis_x_str = param.Get("axis_x", "");
|
||||||
|
const auto axis_y_str = param.Get("axis_y", "");
|
||||||
|
const auto name_x_str = param.Get("name_x", "");
|
||||||
|
const auto name_y_str = param.Get("name_y", "");
|
||||||
|
static const auto plus_str = "+";
|
||||||
|
static const auto minus_str = "-";
|
||||||
|
if (engine_str == "sdl" || engine_str == "gcpad") {
|
||||||
|
if (dir == "modifier") {
|
||||||
|
return "[unused]";
|
||||||
|
}
|
||||||
|
if (dir == "left") {
|
||||||
|
if (name_x_str == "")
|
||||||
|
return "Axis " + axis_x_str + minus_str;
|
||||||
|
else
|
||||||
|
return name_x_str + minus_str;
|
||||||
|
}
|
||||||
|
if (dir == "right") {
|
||||||
|
if (name_x_str == "")
|
||||||
|
return "Axis " + axis_x_str + plus_str;
|
||||||
|
else
|
||||||
|
return name_x_str + plus_str;
|
||||||
|
}
|
||||||
|
if (dir == "up") {
|
||||||
|
if (name_y_str == "")
|
||||||
|
return "Axis " + axis_y_str + plus_str;
|
||||||
|
else
|
||||||
|
return name_y_str + plus_str;
|
||||||
|
}
|
||||||
|
if (dir == "down") {
|
||||||
|
if (name_y_str == "")
|
||||||
|
return "Axis " + axis_y_str + minus_str;
|
||||||
|
else
|
||||||
|
return name_y_str + plus_str;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return "[unknown]";
|
||||||
|
}
|
||||||
|
|
||||||
Keyboard* GetKeyboard() {
|
Keyboard* GetKeyboard() {
|
||||||
return keyboard.get();
|
return keyboard.get();
|
||||||
}
|
}
|
||||||
@ -104,8 +202,8 @@ Common::ParamPackage GetControllerButtonBinds(const Common::ParamPackage& params
|
|||||||
const auto native_button{static_cast<Settings::NativeButton::Values>(button)};
|
const auto native_button{static_cast<Settings::NativeButton::Values>(button)};
|
||||||
const auto engine{params.Get("engine", "")};
|
const auto engine{params.Get("engine", "")};
|
||||||
if (engine == "sdl") {
|
if (engine == "sdl") {
|
||||||
return dynamic_cast<SDL::SDLState*>(sdl.get())->GetSDLControllerButtonBindByGUID(
|
return dynamic_cast<SDL::SDLState*>(sdl.get())->GetSDLControllerButtonBind(params,
|
||||||
params.Get("guid", "0"), params.Get("port", 0), native_button);
|
native_button);
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_GCADAPTER
|
#ifdef ENABLE_GCADAPTER
|
||||||
if (engine == "gcpad") {
|
if (engine == "gcpad") {
|
||||||
|
|||||||
@ -37,6 +37,9 @@ std::string GenerateKeyboardParam(int key_code);
|
|||||||
std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
|
std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
|
||||||
int key_modifier, float modifier_scale);
|
int key_modifier, float modifier_scale);
|
||||||
|
|
||||||
|
std::string AnalogToText(const Common::ParamPackage& param, const std::string& dir);
|
||||||
|
std::string ButtonToText(const Common::ParamPackage& param);
|
||||||
|
|
||||||
Common::ParamPackage GetControllerButtonBinds(const Common::ParamPackage& params, int button);
|
Common::ParamPackage GetControllerButtonBinds(const Common::ParamPackage& params, int button);
|
||||||
Common::ParamPackage GetControllerAnalogBinds(const Common::ParamPackage& params, int analog);
|
Common::ParamPackage GetControllerAnalogBinds(const Common::ParamPackage& params, int analog);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -10,17 +10,10 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/threadsafe_queue.h"
|
#include "common/threadsafe_queue.h"
|
||||||
#include "input_common/sdl/sdl.h"
|
#include "input_common/sdl/sdl_joystick.h"
|
||||||
|
|
||||||
union SDL_Event;
|
|
||||||
using SDL_Joystick = struct _SDL_Joystick;
|
|
||||||
using SDL_JoystickID = s32;
|
|
||||||
using SDL_GameController = struct _SDL_GameController;
|
|
||||||
|
|
||||||
namespace InputCommon::SDL {
|
namespace InputCommon::SDL {
|
||||||
|
|
||||||
class SDLJoystick;
|
|
||||||
class SDLGameController;
|
|
||||||
class SDLButtonFactory;
|
class SDLButtonFactory;
|
||||||
class SDLAnalogFactory;
|
class SDLAnalogFactory;
|
||||||
class SDLMotionFactory;
|
class SDLMotionFactory;
|
||||||
@ -37,11 +30,14 @@ public:
|
|||||||
/// Handle SDL_Events for joysticks from SDL_PollEvent
|
/// Handle SDL_Events for joysticks from SDL_PollEvent
|
||||||
void HandleGameControllerEvent(const SDL_Event& event);
|
void HandleGameControllerEvent(const SDL_Event& event);
|
||||||
|
|
||||||
|
// returns a pointer to a vector of joysticks that match the needs of this device.
|
||||||
|
// will be a pointer because it will be updated when new joysticks are added
|
||||||
|
std::shared_ptr<std::vector<std::shared_ptr<SDLJoystick>>> GetJoysticksByGUID(
|
||||||
|
const std::string& guid);
|
||||||
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
|
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
|
||||||
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
|
|
||||||
|
|
||||||
Common::ParamPackage GetSDLControllerButtonBindByGUID(const std::string& guid, int port,
|
Common::ParamPackage GetSDLControllerButtonBind(const Common::ParamPackage a_button_params,
|
||||||
Settings::NativeButton::Values button);
|
Settings::NativeButton::Values button);
|
||||||
Common::ParamPackage GetSDLControllerAnalogBindByGUID(const std::string& guid, int port,
|
Common::ParamPackage GetSDLControllerAnalogBindByGUID(const std::string& guid, int port,
|
||||||
Settings::NativeAnalog::Values analog);
|
Settings::NativeAnalog::Values analog);
|
||||||
|
|
||||||
@ -54,13 +50,17 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void InitJoystick(int joystick_index);
|
void InitJoystick(int joystick_index);
|
||||||
void CloseJoystick(SDL_Joystick* sdl_joystick);
|
void CloseJoystick(SDL_JoystickID instance_id);
|
||||||
|
|
||||||
/// Needs to be called before SDL_QuitSubSystem.
|
/// Needs to be called before SDL_QuitSubSystem.
|
||||||
void CloseJoysticks();
|
void CloseJoysticks();
|
||||||
|
|
||||||
/// Map of GUID of a list of corresponding virtual Joysticks
|
/// Map of GUID of a list of corresponding virtual Joysticks
|
||||||
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
|
std::unordered_map<std::string, std::shared_ptr<std::vector<std::shared_ptr<SDLJoystick>>>>
|
||||||
|
joystick_map;
|
||||||
|
// This vector keeps a list of all joysticks, ignoring guid
|
||||||
|
std::shared_ptr<std::vector<std::shared_ptr<SDLJoystick>>> joystick_vector =
|
||||||
|
std::make_shared<std::vector<std::shared_ptr<SDLJoystick>>>();
|
||||||
std::mutex joystick_map_mutex;
|
std::mutex joystick_map_mutex;
|
||||||
|
|
||||||
std::shared_ptr<SDLTouchFactory> touch_factory;
|
std::shared_ptr<SDLTouchFactory> touch_factory;
|
||||||
|
|||||||
175
src/input_common/sdl/sdl_joystick.cpp
Normal file
175
src/input_common/sdl/sdl_joystick.cpp
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
#include "sdl_joystick.h"
|
||||||
|
|
||||||
|
namespace InputCommon::SDL {
|
||||||
|
|
||||||
|
SDLJoystick::SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
|
||||||
|
SDL_GameController* game_controller)
|
||||||
|
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
|
||||||
|
sdl_controller{game_controller, &SDL_GameControllerClose} {
|
||||||
|
EnableMotion();
|
||||||
|
CreateControllerButtonMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDLJoystick::IsButtonMappedToController(int button) const {
|
||||||
|
return mapped_joystick_buttons.count(button) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLJoystick::EnableMotion() {
|
||||||
|
if (!sdl_controller) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 14)
|
||||||
|
SDL_GameController* controller = sdl_controller.get();
|
||||||
|
|
||||||
|
if (HasMotion()) {
|
||||||
|
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_FALSE);
|
||||||
|
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_FALSE);
|
||||||
|
}
|
||||||
|
has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
|
||||||
|
has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
|
||||||
|
if (has_accel) {
|
||||||
|
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
|
||||||
|
}
|
||||||
|
if (has_gyro) {
|
||||||
|
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDLJoystick::HasMotion() const {
|
||||||
|
return has_gyro || has_accel;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDLJoystick::GetButton(int button, bool isController) const {
|
||||||
|
if (!sdl_joystick)
|
||||||
|
return false;
|
||||||
|
if (isController)
|
||||||
|
return SDL_GameControllerGetButton(sdl_controller.get(),
|
||||||
|
static_cast<SDL_GameControllerButton>(button));
|
||||||
|
return SDL_JoystickGetButton(sdl_joystick.get(), button) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float SDLJoystick::GetAxis(int axis, bool isController) const {
|
||||||
|
if (!sdl_joystick)
|
||||||
|
return 0.0;
|
||||||
|
if (isController)
|
||||||
|
return SDL_GameControllerGetAxis(sdl_controller.get(),
|
||||||
|
static_cast<SDL_GameControllerAxis>(axis)) /
|
||||||
|
32767.0f;
|
||||||
|
return SDL_JoystickGetAxis(sdl_joystick.get(), axis) / 32767.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<float, float> SDLJoystick::GetAnalog(int axis_x, int axis_y, bool isController) const {
|
||||||
|
float x = GetAxis(axis_x, isController);
|
||||||
|
float y = GetAxis(axis_y, isController);
|
||||||
|
y = -y; // 3DS uses an y-axis inverse from SDL
|
||||||
|
|
||||||
|
// Make sure the coordinates are in the unit circle,
|
||||||
|
// otherwise normalize it.
|
||||||
|
float r = x * x + y * y;
|
||||||
|
if (r > 1.0f) {
|
||||||
|
r = std::sqrt(r);
|
||||||
|
x /= r;
|
||||||
|
y /= r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_tuple(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDLJoystick::GetHatDirection(int hat, Uint8 direction) const {
|
||||||
|
// no need to worry about gamecontroller here - that api treats hats as buttons
|
||||||
|
if (!sdl_joystick)
|
||||||
|
return false;
|
||||||
|
return SDL_JoystickGetHat(sdl_joystick.get(), hat) == direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLJoystick::SetTouchpad(float x, float y, int touchpad, bool down) {
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
state.touchpad[touchpad] = std::make_tuple(x, y, down);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLJoystick::SetAccel(const float x, const float y, const float z) {
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
state.accel.x = x;
|
||||||
|
state.accel.y = y;
|
||||||
|
state.accel.z = z;
|
||||||
|
}
|
||||||
|
void SDLJoystick::SetGyro(const float pitch, const float yaw, const float roll) {
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
state.gyro.x = pitch;
|
||||||
|
state.gyro.y = yaw;
|
||||||
|
state.gyro.z = roll;
|
||||||
|
}
|
||||||
|
std::tuple<Common::Vec3<float>, Common::Vec3<float>> SDLJoystick::GetMotion() const {
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
return std::make_tuple(state.accel, state.gyro);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The guid of the joystick
|
||||||
|
*/
|
||||||
|
const std::string& SDLJoystick::GetGUID() const {
|
||||||
|
return guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of joystick from the same type that were connected before this joystick
|
||||||
|
*/
|
||||||
|
int SDLJoystick::GetPort() const {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<float, float, float> SDLJoystick::GetTouch(int pad) const {
|
||||||
|
return state.touchpad.at(pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Joystick* SDLJoystick::GetSDLJoystick() const {
|
||||||
|
return sdl_joystick.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GameController* SDLJoystick::GetSDLGameController() const {
|
||||||
|
return sdl_controller.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLJoystick::SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
|
||||||
|
sdl_joystick.reset(joystick);
|
||||||
|
sdl_controller.reset(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDLJoystick::CreateControllerButtonMap() {
|
||||||
|
mapped_joystick_buttons.clear();
|
||||||
|
|
||||||
|
if (!sdl_controller) {
|
||||||
|
return; // Not a controller, no mapped buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all controller buttons
|
||||||
|
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) {
|
||||||
|
auto bind = SDL_GameControllerGetBindForButton(sdl_controller.get(),
|
||||||
|
static_cast<SDL_GameControllerButton>(i));
|
||||||
|
|
||||||
|
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
|
||||||
|
mapped_joystick_buttons.insert(bind.value.button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check trigger axes that might be buttons
|
||||||
|
auto lt_bind =
|
||||||
|
SDL_GameControllerGetBindForAxis(sdl_controller.get(), SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||||
|
if (lt_bind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
|
||||||
|
mapped_joystick_buttons.insert(lt_bind.value.button);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rt_bind =
|
||||||
|
SDL_GameControllerGetBindForAxis(sdl_controller.get(), SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||||
|
if (rt_bind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
|
||||||
|
mapped_joystick_buttons.insert(rt_bind.value.button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace InputCommon::SDL
|
||||||
76
src/input_common/sdl/sdl_joystick.h
Normal file
76
src/input_common/sdl/sdl_joystick.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include "common/vector_math.h"
|
||||||
|
#include "input_common/sdl/sdl.h"
|
||||||
|
|
||||||
|
union SDL_Event;
|
||||||
|
using SDL_Joystick = struct _SDL_Joystick;
|
||||||
|
using SDL_JoystickID = s32;
|
||||||
|
using SDL_GameController = struct _SDL_GameController;
|
||||||
|
|
||||||
|
namespace InputCommon::SDL {
|
||||||
|
class SDLJoystick {
|
||||||
|
public:
|
||||||
|
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
|
||||||
|
SDL_GameController* game_controller);
|
||||||
|
|
||||||
|
bool IsButtonMappedToController(int button) const;
|
||||||
|
|
||||||
|
void EnableMotion();
|
||||||
|
bool HasMotion() const;
|
||||||
|
|
||||||
|
bool GetButton(int button, bool isController) const;
|
||||||
|
float GetAxis(int axis, bool isController) const;
|
||||||
|
std::tuple<float, float> GetAnalog(int axis_x, int axis_y, bool isController) const;
|
||||||
|
bool GetHatDirection(int hat, uint8_t direction) const;
|
||||||
|
|
||||||
|
void SetTouchpad(float x, float y, int touchpad, bool down);
|
||||||
|
void SetAccel(const float x, const float y, const float z);
|
||||||
|
void SetGyro(const float pitch, const float yaw, const float roll);
|
||||||
|
|
||||||
|
std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetMotion() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The guid of the joystick
|
||||||
|
*/
|
||||||
|
const std::string& GetGUID() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of joystick from the same type that were connected before this joystick
|
||||||
|
*/
|
||||||
|
int GetPort() const;
|
||||||
|
|
||||||
|
std::tuple<float, float, float> GetTouch(int pad) const;
|
||||||
|
|
||||||
|
SDL_Joystick* GetSDLJoystick() const;
|
||||||
|
SDL_GameController* GetSDLGameController() const;
|
||||||
|
|
||||||
|
void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct State {
|
||||||
|
Common::Vec3<float> accel;
|
||||||
|
Common::Vec3<float> gyro;
|
||||||
|
std::unordered_map<int, std::tuple<float, float, bool>> touchpad;
|
||||||
|
} state;
|
||||||
|
std::string guid;
|
||||||
|
int port;
|
||||||
|
bool has_gyro{false};
|
||||||
|
bool has_accel{false};
|
||||||
|
std::unique_ptr<SDL_Joystick, void (*)(SDL_Joystick*)> sdl_joystick;
|
||||||
|
std::unique_ptr<SDL_GameController, void (*)(SDL_GameController*)> sdl_controller;
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
std::unordered_set<int> mapped_joystick_buttons;
|
||||||
|
void CreateControllerButtonMap();
|
||||||
|
};
|
||||||
|
} // namespace InputCommon::SDL
|
||||||
Loading…
Reference in New Issue
Block a user