From c1d0419361e820a250adf95795b1d405293ac9c5 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 26 Nov 2023 21:34:00 +1000 Subject: [PATCH] FullscreenUI: Use icon font for bindings --- src/core/fullscreen_ui.cpp | 47 +- src/core/imgui_overlays.cpp | 2 +- src/duckstation-nogui/nogui_host.cpp | 5 + src/duckstation-qt/qtkeycodes.cpp | 894 ++++++++++++----------- src/duckstation-regtest/regtest_host.cpp | 5 + src/util/dinput_source.cpp | 15 +- src/util/dinput_source.h | 3 +- src/util/imgui_manager.cpp | 6 +- src/util/input_manager.cpp | 115 ++- src/util/input_manager.h | 6 + src/util/input_source.h | 4 +- src/util/sdl_input_source.cpp | 75 +- src/util/sdl_input_source.h | 3 +- src/util/win32_raw_input_source.cpp | 7 +- src/util/win32_raw_input_source.h | 3 +- src/util/xinput_source.cpp | 78 +- src/util/xinput_source.h | 41 +- 17 files changed, 798 insertions(+), 511 deletions(-) diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 981b74578..dbce45cdd 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -1364,6 +1364,9 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn if (!visible) return; + if (oneline) + InputManager::PrettifyInputBinding(value); + if (show_type) { if (icon_name) @@ -1375,17 +1378,17 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn switch (type) { case InputBindingInfo::Type::Button: - title.fmt(ICON_FA_DOT_CIRCLE "{}", display_name); + title.fmt(ICON_FA_DOT_CIRCLE " {}", display_name); break; case InputBindingInfo::Type::Axis: case InputBindingInfo::Type::HalfAxis: - title.fmt(ICON_FA_BULLSEYE "{}", display_name); + title.fmt(ICON_FA_BULLSEYE " {}", display_name); break; case InputBindingInfo::Type::Motor: - title.fmt(ICON_FA_BELL "{}", display_name); + title.fmt(ICON_FA_BELL " {}", display_name); break; case InputBindingInfo::Type::Macro: - title.fmt(ICON_FA_PIZZA_SLICE "{}", display_name); + title.fmt(ICON_FA_PIZZA_SLICE " {}", display_name); break; default: title = display_name; @@ -1406,8 +1409,8 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, show_type ? title.c_str() : display_name, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); - ImGui::RenderTextClipped(bb.Min, bb.Max, value.empty() ? FSUI_CSTR("-") : value.c_str(), nullptr, - &value_size, ImVec2(1.0f, 0.5f), &bb); + ImGui::RenderTextClipped(bb.Min, bb.Max, value.empty() ? FSUI_CSTR("-") : value.c_str(), nullptr, &value_size, + ImVec2(1.0f, 0.5f), &bb); ImGui::PopFont(); } else @@ -3407,9 +3410,27 @@ void FullscreenUI::DrawControllerSettingsPage() std::string binds_string( bsi->GetStringValue(section.c_str(), fmt::format("Macro{}Binds", macro_index + 1).c_str())); - if (MenuButton( + TinyString pretty_binds_string; + if (!binds_string.empty()) + { + for (const std::string_view& bind : StringUtil::SplitString(binds_string, '&', true)) + { + const char* dispname = nullptr; + for (const Controller::ControllerBindingInfo& bi : ci->bindings) + { + if (bind == bi.name) + { + dispname = bi.icon_name ? bi.icon_name : Host::TranslateToCString(ci->name, bi.display_name); + break; + } + } + pretty_binds_string.append_fmt("{}{}", pretty_binds_string.empty() ? "" : " ", dispname); + } + } + if (MenuButtonWithValue( TinyString::from_fmt(fmt::runtime(FSUI_ICONSTR(ICON_FA_KEYBOARD, "Macro {} Buttons")), macro_index + 1), - binds_string.empty() ? FSUI_CSTR("No Buttons Selected") : binds_string.c_str())) + nullptr, pretty_binds_string.empty() ? FSUI_CSTR("-") : pretty_binds_string.c_str(), true, + LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) { std::vector buttons_split(StringUtil::SplitString(binds_string, '&', true)); ImGuiFullscreen::ChoiceDialogOptions options; @@ -3476,10 +3497,10 @@ void FullscreenUI::DrawControllerSettingsPage() s32 frequency = bsi->GetIntValue(section.c_str(), freq_key.c_str(), 0); SmallString freq_summary; if (frequency == 0) - freq_summary = FSUI_VSTR("Macro will not auto-toggle."); + freq_summary = FSUI_VSTR("Disabled"); else - freq_summary.fmt(FSUI_FSTR("Macro will toggle every {} frames."), frequency); - if (MenuButton(freq_title, freq_summary)) + freq_summary.fmt(FSUI_FSTR("{} Frames"), frequency); + if (MenuButtonWithValue(freq_title, nullptr, freq_summary, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) ImGui::OpenPopup(freq_title); ImGui::SetNextWindowSize(LayoutScale(500.0f, 180.0f)); @@ -6727,6 +6748,7 @@ void FullscreenUI::ProgressCallback::SetCancelled() #if 0 // TRANSLATION-STRING-AREA-BEGIN TRANSLATE_NOOP("FullscreenUI", "${title}: Title of the game.\n${filetitle}: Name component of the game's filename.\n${serial}: Serial of the game."); +TRANSLATE_NOOP("FullscreenUI", "-"); TRANSLATE_NOOP("FullscreenUI", "1 Frame"); TRANSLATE_NOOP("FullscreenUI", "10 Frames"); TRANSLATE_NOOP("FullscreenUI", "100% [60 FPS (NTSC) / 50 FPS (PAL)]"); @@ -7061,7 +7083,6 @@ TRANSLATE_NOOP("FullscreenUI", "Logs messages to duckstation.log in the user dir TRANSLATE_NOOP("FullscreenUI", "Logs messages to the console window."); TRANSLATE_NOOP("FullscreenUI", "Logs messages to the debug console where supported."); TRANSLATE_NOOP("FullscreenUI", "Logs out of RetroAchievements."); -TRANSLATE_NOOP("FullscreenUI", "Macro will toggle every {} frames."); TRANSLATE_NOOP("FullscreenUI", "Macro {} Buttons"); TRANSLATE_NOOP("FullscreenUI", "Macro {} Frequency"); TRANSLATE_NOOP("FullscreenUI", "Macro {} Trigger"); @@ -7081,7 +7102,6 @@ TRANSLATE_NOOP("FullscreenUI", "Mute All Sound"); TRANSLATE_NOOP("FullscreenUI", "Mute CD Audio"); TRANSLATE_NOOP("FullscreenUI", "No"); TRANSLATE_NOOP("FullscreenUI", "No Binding"); -TRANSLATE_NOOP("FullscreenUI", "No Buttons Selected"); TRANSLATE_NOOP("FullscreenUI", "No Game Selected"); TRANSLATE_NOOP("FullscreenUI", "No cheats found for {}."); TRANSLATE_NOOP("FullscreenUI", "No input profiles available."); @@ -7321,6 +7341,7 @@ TRANSLATE_NOOP("FullscreenUI", "Writes textures which can be replaced to the dum TRANSLATE_NOOP("FullscreenUI", "Yes"); TRANSLATE_NOOP("FullscreenUI", "\"Challenge\" mode for achievements, including leaderboard tracking. Disables save state, cheats, and slowdown functions."); TRANSLATE_NOOP("FullscreenUI", "\"PlayStation\" and \"PSX\" are registered trademarks of Sony Interactive Entertainment Europe Limited. This software is not affiliated in any way with Sony Interactive Entertainment."); +TRANSLATE_NOOP("FullscreenUI", "{} Frames"); TRANSLATE_NOOP("FullscreenUI", "{} deleted."); TRANSLATE_NOOP("FullscreenUI", "{} does not exist."); TRANSLATE_NOOP("FullscreenUI", "{} is not a valid disc image."); diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp index ddeea2d04..7ebd53e2b 100644 --- a/src/core/imgui_overlays.cpp +++ b/src/core/imgui_overlays.cpp @@ -615,7 +615,7 @@ void ImGuiManager::DrawInputsOverlay() continue; if (cinfo->icon_name) - text.append_fmt("{} {} |", cinfo->icon_name, port + 1u); + text.append_fmt("{} {}", cinfo->icon_name, port + 1u); else text.append_fmt("{} |", port + 1u); diff --git a/src/duckstation-nogui/nogui_host.cpp b/src/duckstation-nogui/nogui_host.cpp index 8e86d1ca3..777dd8a3f 100644 --- a/src/duckstation-nogui/nogui_host.cpp +++ b/src/duckstation-nogui/nogui_host.cpp @@ -934,6 +934,11 @@ std::optional InputManager::ConvertHostKeyboardCodeToString(u32 cod return g_nogui_window->ConvertHostKeyboardCodeToString(code); } +const char* InputManager::ConvertHostKeyboardCodeToIcon(u32 code) +{ + return nullptr; +} + BEGIN_HOTKEY_LIST(g_host_hotkeys) END_HOTKEY_LIST() diff --git a/src/duckstation-qt/qtkeycodes.cpp b/src/duckstation-qt/qtkeycodes.cpp index a3235b3c4..8de6d928e 100644 --- a/src/duckstation-qt/qtkeycodes.cpp +++ b/src/duckstation-qt/qtkeycodes.cpp @@ -5,6 +5,8 @@ #include "common/string_util.h" +#include "IconsPromptFont.h" + #include "fmt/format.h" #include "util/input_manager.h" @@ -14,446 +16,447 @@ struct KeyCodeName { int code; const char* name; + const char* icon_name; }; -static constexpr KeyCodeName s_qt_key_names[] = {{Qt::Key_Escape, "Escape"}, - {Qt::Key_Tab, "Tab"}, - {Qt::Key_Backtab, "Backtab"}, - {Qt::Key_Backspace, "Backspace"}, - {Qt::Key_Return, "Return"}, - {Qt::Key_Enter, "Enter"}, - {Qt::Key_Insert, "Insert"}, - {Qt::Key_Delete, "Delete"}, - {Qt::Key_Pause, "Pause"}, - {Qt::Key_Print, "Print"}, - {Qt::Key_SysReq, "SysReq"}, - {Qt::Key_Clear, "Clear"}, - {Qt::Key_Home, "Home"}, - {Qt::Key_End, "End"}, - {Qt::Key_Left, "Left"}, - {Qt::Key_Up, "Up"}, - {Qt::Key_Right, "Right"}, - {Qt::Key_Down, "Down"}, - {Qt::Key_PageUp, "PageUp"}, - {Qt::Key_PageDown, "PageDown"}, - {Qt::Key_Shift, "Shift"}, - {Qt::Key_Control, "Control"}, - {Qt::Key_Meta, "Meta"}, - {Qt::Key_Alt, "Alt"}, - {Qt::Key_CapsLock, "CapsLock"}, - {Qt::Key_NumLock, "NumLock"}, - {Qt::Key_ScrollLock, "ScrollLock"}, - {Qt::Key_F1, "F1"}, - {Qt::Key_F2, "F2"}, - {Qt::Key_F3, "F3"}, - {Qt::Key_F4, "F4"}, - {Qt::Key_F5, "F5"}, - {Qt::Key_F6, "F6"}, - {Qt::Key_F7, "F7"}, - {Qt::Key_F8, "F8"}, - {Qt::Key_F9, "F9"}, - {Qt::Key_F10, "F10"}, - {Qt::Key_F11, "F11"}, - {Qt::Key_F12, "F12"}, - {Qt::Key_F13, "F13"}, - {Qt::Key_F14, "F14"}, - {Qt::Key_F15, "F15"}, - {Qt::Key_F16, "F16"}, - {Qt::Key_F17, "F17"}, - {Qt::Key_F18, "F18"}, - {Qt::Key_F19, "F19"}, - {Qt::Key_F20, "F20"}, - {Qt::Key_F21, "F21"}, - {Qt::Key_F22, "F22"}, - {Qt::Key_F23, "F23"}, - {Qt::Key_F24, "F24"}, - {Qt::Key_F25, "F25"}, - {Qt::Key_F26, "F26"}, - {Qt::Key_F27, "F27"}, - {Qt::Key_F28, "F28"}, - {Qt::Key_F29, "F29"}, - {Qt::Key_F30, "F30"}, - {Qt::Key_F31, "F31"}, - {Qt::Key_F32, "F32"}, - {Qt::Key_F33, "F33"}, - {Qt::Key_F34, "F34"}, - {Qt::Key_F35, "F35"}, - {Qt::Key_Super_L, "Super_L"}, - {Qt::Key_Super_R, "Super_R"}, - {Qt::Key_Menu, "Menu"}, - {Qt::Key_Hyper_L, "Hyper_L"}, - {Qt::Key_Hyper_R, "Hyper_R"}, - {Qt::Key_Help, "Help"}, - {Qt::Key_Direction_L, "Direction_L"}, - {Qt::Key_Direction_R, "Direction_R"}, - {Qt::Key_Space, "Space"}, - {Qt::Key_Any, "Any"}, - {Qt::Key_Exclam, "Exclam"}, - {Qt::Key_QuoteDbl, "QuoteDbl"}, - {Qt::Key_NumberSign, "NumberSign"}, - {Qt::Key_Dollar, "Dollar"}, - {Qt::Key_Percent, "Percent"}, - {Qt::Key_Ampersand, "Ampersand"}, - {Qt::Key_Apostrophe, "Apostrophe"}, - {Qt::Key_ParenLeft, "ParenLeft"}, - {Qt::Key_ParenRight, "ParenRight"}, - {Qt::Key_Asterisk, "Asterisk"}, - {Qt::Key_Plus, "Plus"}, - {Qt::Key_Comma, "Comma"}, - {Qt::Key_Minus, "Minus"}, - {Qt::Key_Period, "Period"}, - {Qt::Key_Slash, "Slash"}, - {Qt::Key_0, "0"}, - {Qt::Key_1, "1"}, - {Qt::Key_2, "2"}, - {Qt::Key_3, "3"}, - {Qt::Key_4, "4"}, - {Qt::Key_5, "5"}, - {Qt::Key_6, "6"}, - {Qt::Key_7, "7"}, - {Qt::Key_8, "8"}, - {Qt::Key_9, "9"}, - {Qt::Key_Colon, "Colon"}, - {Qt::Key_Semicolon, "Semicolon"}, - {Qt::Key_Less, "Less"}, - {Qt::Key_Equal, "Equal"}, - {Qt::Key_Greater, "Greater"}, - {Qt::Key_Question, "Question"}, - {Qt::Key_At, "At"}, - {Qt::Key_A, "A"}, - {Qt::Key_B, "B"}, - {Qt::Key_C, "C"}, - {Qt::Key_D, "D"}, - {Qt::Key_E, "E"}, - {Qt::Key_F, "F"}, - {Qt::Key_G, "G"}, - {Qt::Key_H, "H"}, - {Qt::Key_I, "I"}, - {Qt::Key_J, "J"}, - {Qt::Key_K, "K"}, - {Qt::Key_L, "L"}, - {Qt::Key_M, "M"}, - {Qt::Key_N, "N"}, - {Qt::Key_O, "O"}, - {Qt::Key_P, "P"}, - {Qt::Key_Q, "Q"}, - {Qt::Key_R, "R"}, - {Qt::Key_S, "S"}, - {Qt::Key_T, "T"}, - {Qt::Key_U, "U"}, - {Qt::Key_V, "V"}, - {Qt::Key_W, "W"}, - {Qt::Key_X, "X"}, - {Qt::Key_Y, "Y"}, - {Qt::Key_Z, "Z"}, - {Qt::Key_BracketLeft, "BracketLeft"}, - {Qt::Key_Backslash, "Backslash"}, - {Qt::Key_BracketRight, "BracketRight"}, - {Qt::Key_AsciiCircum, "AsciiCircum"}, - {Qt::Key_Underscore, "Underscore"}, - {Qt::Key_QuoteLeft, "QuoteLeft"}, - {Qt::Key_BraceLeft, "BraceLeft"}, - {Qt::Key_Bar, "Bar"}, - {Qt::Key_BraceRight, "BraceRight"}, - {Qt::Key_AsciiTilde, "AsciiTilde"}, - {Qt::Key_nobreakspace, "nobreakspace"}, - {Qt::Key_exclamdown, "exclamdown"}, - {Qt::Key_cent, "cent"}, - {Qt::Key_sterling, "sterling"}, - {Qt::Key_currency, "currency"}, - {Qt::Key_yen, "yen"}, - {Qt::Key_brokenbar, "brokenbar"}, - {Qt::Key_section, "section"}, - {Qt::Key_diaeresis, "diaeresis"}, - {Qt::Key_copyright, "copyright"}, - {Qt::Key_ordfeminine, "ordfeminine"}, - {Qt::Key_guillemotleft, "guillemotleft"}, - {Qt::Key_notsign, "notsign"}, - {Qt::Key_hyphen, "hyphen"}, - {Qt::Key_registered, "registered"}, - {Qt::Key_macron, "macron"}, - {Qt::Key_degree, "degree"}, - {Qt::Key_plusminus, "plusminus"}, - {Qt::Key_twosuperior, "twosuperior"}, - {Qt::Key_threesuperior, "threesuperior"}, - {Qt::Key_acute, "acute"}, - {Qt::Key_mu, "mu"}, - {Qt::Key_paragraph, "paragraph"}, - {Qt::Key_periodcentered, "periodcentered"}, - {Qt::Key_cedilla, "cedilla"}, - {Qt::Key_onesuperior, "onesuperior"}, - {Qt::Key_masculine, "masculine"}, - {Qt::Key_guillemotright, "guillemotright"}, - {Qt::Key_onequarter, "onequarter"}, - {Qt::Key_onehalf, "onehalf"}, - {Qt::Key_threequarters, "threequarters"}, - {Qt::Key_questiondown, "questiondown"}, - {Qt::Key_Agrave, "Agrave"}, - {Qt::Key_Aacute, "Aacute"}, - {Qt::Key_Acircumflex, "Acircumflex"}, - {Qt::Key_Atilde, "Atilde"}, - {Qt::Key_Adiaeresis, "Adiaeresis"}, - {Qt::Key_Aring, "Aring"}, - {Qt::Key_AE, "AE"}, - {Qt::Key_Ccedilla, "Ccedilla"}, - {Qt::Key_Egrave, "Egrave"}, - {Qt::Key_Eacute, "Eacute"}, - {Qt::Key_Ecircumflex, "Ecircumflex"}, - {Qt::Key_Ediaeresis, "Ediaeresis"}, - {Qt::Key_Igrave, "Igrave"}, - {Qt::Key_Iacute, "Iacute"}, - {Qt::Key_Icircumflex, "Icircumflex"}, - {Qt::Key_Idiaeresis, "Idiaeresis"}, - {Qt::Key_ETH, "ETH"}, - {Qt::Key_Ntilde, "Ntilde"}, - {Qt::Key_Ograve, "Ograve"}, - {Qt::Key_Oacute, "Oacute"}, - {Qt::Key_Ocircumflex, "Ocircumflex"}, - {Qt::Key_Otilde, "Otilde"}, - {Qt::Key_Odiaeresis, "Odiaeresis"}, - {Qt::Key_multiply, "multiply"}, - {Qt::Key_Ooblique, "Ooblique"}, - {Qt::Key_Ugrave, "Ugrave"}, - {Qt::Key_Uacute, "Uacute"}, - {Qt::Key_Ucircumflex, "Ucircumflex"}, - {Qt::Key_Udiaeresis, "Udiaeresis"}, - {Qt::Key_Yacute, "Yacute"}, - {Qt::Key_THORN, "THORN"}, - {Qt::Key_ssharp, "ssharp"}, - {Qt::Key_division, "division"}, - {Qt::Key_ydiaeresis, "ydiaeresis"}, - {Qt::Key_AltGr, "AltGr"}, - {Qt::Key_Multi_key, "Multi_key"}, - {Qt::Key_Codeinput, "Codeinput"}, - {Qt::Key_SingleCandidate, "SingleCandidate"}, - {Qt::Key_MultipleCandidate, "MultipleCandidate"}, - {Qt::Key_PreviousCandidate, "PreviousCandidate"}, - {Qt::Key_Mode_switch, "Mode_switch"}, - {Qt::Key_Kanji, "Kanji"}, - {Qt::Key_Muhenkan, "Muhenkan"}, - {Qt::Key_Henkan, "Henkan"}, - {Qt::Key_Romaji, "Romaji"}, - {Qt::Key_Hiragana, "Hiragana"}, - {Qt::Key_Katakana, "Katakana"}, - {Qt::Key_Hiragana_Katakana, "Hiragana_Katakana"}, - {Qt::Key_Zenkaku, "Zenkaku"}, - {Qt::Key_Hankaku, "Hankaku"}, - {Qt::Key_Zenkaku_Hankaku, "Zenkaku_Hankaku"}, - {Qt::Key_Touroku, "Touroku"}, - {Qt::Key_Massyo, "Massyo"}, - {Qt::Key_Kana_Lock, "Kana_Lock"}, - {Qt::Key_Kana_Shift, "Kana_Shift"}, - {Qt::Key_Eisu_Shift, "Eisu_Shift"}, - {Qt::Key_Eisu_toggle, "Eisu_toggle"}, - {Qt::Key_Hangul, "Hangul"}, - {Qt::Key_Hangul_Start, "Hangul_Start"}, - {Qt::Key_Hangul_End, "Hangul_End"}, - {Qt::Key_Hangul_Hanja, "Hangul_Hanja"}, - {Qt::Key_Hangul_Jamo, "Hangul_Jamo"}, - {Qt::Key_Hangul_Romaja, "Hangul_Romaja"}, - {Qt::Key_Hangul_Jeonja, "Hangul_Jeonja"}, - {Qt::Key_Hangul_Banja, "Hangul_Banja"}, - {Qt::Key_Hangul_PreHanja, "Hangul_PreHanja"}, - {Qt::Key_Hangul_PostHanja, "Hangul_PostHanja"}, - {Qt::Key_Hangul_Special, "Hangul_Special"}, - {Qt::Key_Dead_Grave, "Dead_Grave"}, - {Qt::Key_Dead_Acute, "Dead_Acute"}, - {Qt::Key_Dead_Circumflex, "Dead_Circumflex"}, - {Qt::Key_Dead_Tilde, "Dead_Tilde"}, - {Qt::Key_Dead_Macron, "Dead_Macron"}, - {Qt::Key_Dead_Breve, "Dead_Breve"}, - {Qt::Key_Dead_Abovedot, "Dead_Abovedot"}, - {Qt::Key_Dead_Diaeresis, "Dead_Diaeresis"}, - {Qt::Key_Dead_Abovering, "Dead_Abovering"}, - {Qt::Key_Dead_Doubleacute, "Dead_Doubleacute"}, - {Qt::Key_Dead_Caron, "Dead_Caron"}, - {Qt::Key_Dead_Cedilla, "Dead_Cedilla"}, - {Qt::Key_Dead_Ogonek, "Dead_Ogonek"}, - {Qt::Key_Dead_Iota, "Dead_Iota"}, - {Qt::Key_Dead_Voiced_Sound, "Dead_Voiced_Sound"}, - {Qt::Key_Dead_Semivoiced_Sound, "Dead_Semivoiced_Sound"}, - {Qt::Key_Dead_Belowdot, "Dead_Belowdot"}, - {Qt::Key_Dead_Hook, "Dead_Hook"}, - {Qt::Key_Dead_Horn, "Dead_Horn"}, - {Qt::Key_Back, "Back"}, - {Qt::Key_Forward, "Forward"}, - {Qt::Key_Stop, "Stop"}, - {Qt::Key_Refresh, "Refresh"}, - {Qt::Key_VolumeDown, "VolumeDown"}, - {Qt::Key_VolumeMute, "VolumeMute"}, - {Qt::Key_VolumeUp, "VolumeUp"}, - {Qt::Key_BassBoost, "BassBoost"}, - {Qt::Key_BassUp, "BassUp"}, - {Qt::Key_BassDown, "BassDown"}, - {Qt::Key_TrebleUp, "TrebleUp"}, - {Qt::Key_TrebleDown, "TrebleDown"}, - {Qt::Key_MediaPlay, "MediaPlay"}, - {Qt::Key_MediaStop, "MediaStop"}, - {Qt::Key_MediaPrevious, "MediaPrevious"}, - {Qt::Key_MediaNext, "MediaNext"}, - {Qt::Key_MediaRecord, "MediaRecord"}, - {Qt::Key_MediaPause, "MediaPause"}, - {Qt::Key_MediaTogglePlayPause, "MediaTogglePlayPause"}, - {Qt::Key_HomePage, "HomePage"}, - {Qt::Key_Favorites, "Favorites"}, - {Qt::Key_Search, "Search"}, - {Qt::Key_Standby, "Standby"}, - {Qt::Key_OpenUrl, "OpenUrl"}, - {Qt::Key_LaunchMail, "LaunchMail"}, - {Qt::Key_LaunchMedia, "LaunchMedia"}, - {Qt::Key_Launch0, "Launch0"}, - {Qt::Key_Launch1, "Launch1"}, - {Qt::Key_Launch2, "Launch2"}, - {Qt::Key_Launch3, "Launch3"}, - {Qt::Key_Launch4, "Launch4"}, - {Qt::Key_Launch5, "Launch5"}, - {Qt::Key_Launch6, "Launch6"}, - {Qt::Key_Launch7, "Launch7"}, - {Qt::Key_Launch8, "Launch8"}, - {Qt::Key_Launch9, "Launch9"}, - {Qt::Key_LaunchA, "LaunchA"}, - {Qt::Key_LaunchB, "LaunchB"}, - {Qt::Key_LaunchC, "LaunchC"}, - {Qt::Key_LaunchD, "LaunchD"}, - {Qt::Key_LaunchE, "LaunchE"}, - {Qt::Key_LaunchF, "LaunchF"}, - {Qt::Key_MonBrightnessUp, "MonBrightnessUp"}, - {Qt::Key_MonBrightnessDown, "MonBrightnessDown"}, - {Qt::Key_KeyboardLightOnOff, "KeyboardLightOnOff"}, - {Qt::Key_KeyboardBrightnessUp, "KeyboardBrightnessUp"}, - {Qt::Key_KeyboardBrightnessDown, "KeyboardBrightnessDown"}, - {Qt::Key_PowerOff, "PowerOff"}, - {Qt::Key_WakeUp, "WakeUp"}, - {Qt::Key_Eject, "Eject"}, - {Qt::Key_ScreenSaver, "ScreenSaver"}, - {Qt::Key_WWW, "WWW"}, - {Qt::Key_Memo, "Memo"}, - {Qt::Key_LightBulb, "LightBulb"}, - {Qt::Key_Shop, "Shop"}, - {Qt::Key_History, "History"}, - {Qt::Key_AddFavorite, "AddFavorite"}, - {Qt::Key_HotLinks, "HotLinks"}, - {Qt::Key_BrightnessAdjust, "BrightnessAdjust"}, - {Qt::Key_Finance, "Finance"}, - {Qt::Key_Community, "Community"}, - {Qt::Key_AudioRewind, "AudioRewind"}, - {Qt::Key_BackForward, "BackForward"}, - {Qt::Key_ApplicationLeft, "ApplicationLeft"}, - {Qt::Key_ApplicationRight, "ApplicationRight"}, - {Qt::Key_Book, "Book"}, - {Qt::Key_CD, "CD"}, - {Qt::Key_Calculator, "Calculator"}, - {Qt::Key_ToDoList, "ToDoList"}, - {Qt::Key_ClearGrab, "ClearGrab"}, - {Qt::Key_Close, "Close"}, - {Qt::Key_Copy, "Copy"}, - {Qt::Key_Cut, "Cut"}, - {Qt::Key_Display, "Display"}, - {Qt::Key_DOS, "DOS"}, - {Qt::Key_Documents, "Documents"}, - {Qt::Key_Excel, "Excel"}, - {Qt::Key_Explorer, "Explorer"}, - {Qt::Key_Game, "Game"}, - {Qt::Key_Go, "Go"}, - {Qt::Key_iTouch, "iTouch"}, - {Qt::Key_LogOff, "LogOff"}, - {Qt::Key_Market, "Market"}, - {Qt::Key_Meeting, "Meeting"}, - {Qt::Key_MenuKB, "MenuKB"}, - {Qt::Key_MenuPB, "MenuPB"}, - {Qt::Key_MySites, "MySites"}, - {Qt::Key_News, "News"}, - {Qt::Key_OfficeHome, "OfficeHome"}, - {Qt::Key_Option, "Option"}, - {Qt::Key_Paste, "Paste"}, - {Qt::Key_Phone, "Phone"}, - {Qt::Key_Calendar, "Calendar"}, - {Qt::Key_Reply, "Reply"}, - {Qt::Key_Reload, "Reload"}, - {Qt::Key_RotateWindows, "RotateWindows"}, - {Qt::Key_RotationPB, "RotationPB"}, - {Qt::Key_RotationKB, "RotationKB"}, - {Qt::Key_Save, "Save"}, - {Qt::Key_Send, "Send"}, - {Qt::Key_Spell, "Spell"}, - {Qt::Key_SplitScreen, "SplitScreen"}, - {Qt::Key_Support, "Support"}, - {Qt::Key_TaskPane, "TaskPane"}, - {Qt::Key_Terminal, "Terminal"}, - {Qt::Key_Tools, "Tools"}, - {Qt::Key_Travel, "Travel"}, - {Qt::Key_Video, "Video"}, - {Qt::Key_Word, "Word"}, - {Qt::Key_Xfer, "Xfer"}, - {Qt::Key_ZoomIn, "ZoomIn"}, - {Qt::Key_ZoomOut, "ZoomOut"}, - {Qt::Key_Away, "Away"}, - {Qt::Key_Messenger, "Messenger"}, - {Qt::Key_WebCam, "WebCam"}, - {Qt::Key_MailForward, "MailForward"}, - {Qt::Key_Pictures, "Pictures"}, - {Qt::Key_Music, "Music"}, - {Qt::Key_Battery, "Battery"}, - {Qt::Key_Bluetooth, "Bluetooth"}, - {Qt::Key_WLAN, "WLAN"}, - {Qt::Key_UWB, "UWB"}, - {Qt::Key_AudioForward, "AudioForward"}, - {Qt::Key_AudioRepeat, "AudioRepeat"}, - {Qt::Key_AudioRandomPlay, "AudioRandomPlay"}, - {Qt::Key_Subtitle, "Subtitle"}, - {Qt::Key_AudioCycleTrack, "AudioCycleTrack"}, - {Qt::Key_Time, "Time"}, - {Qt::Key_Hibernate, "Hibernate"}, - {Qt::Key_View, "View"}, - {Qt::Key_TopMenu, "TopMenu"}, - {Qt::Key_PowerDown, "PowerDown"}, - {Qt::Key_Suspend, "Suspend"}, - {Qt::Key_ContrastAdjust, "ContrastAdjust"}, - {Qt::Key_LaunchG, "LaunchG"}, - {Qt::Key_LaunchH, "LaunchH"}, - {Qt::Key_TouchpadToggle, "TouchpadToggle"}, - {Qt::Key_TouchpadOn, "TouchpadOn"}, - {Qt::Key_TouchpadOff, "TouchpadOff"}, - {Qt::Key_MicMute, "MicMute"}, - {Qt::Key_Red, "Red"}, - {Qt::Key_Green, "Green"}, - {Qt::Key_Yellow, "Yellow"}, - {Qt::Key_Blue, "Blue"}, - {Qt::Key_ChannelUp, "ChannelUp"}, - {Qt::Key_ChannelDown, "ChannelDown"}, - {Qt::Key_Guide, "Guide"}, - {Qt::Key_Info, "Info"}, - {Qt::Key_Settings, "Settings"}, - {Qt::Key_MicVolumeUp, "MicVolumeUp"}, - {Qt::Key_MicVolumeDown, "MicVolumeDown"}, - {Qt::Key_New, "New"}, - {Qt::Key_Open, "Open"}, - {Qt::Key_Find, "Find"}, - {Qt::Key_Undo, "Undo"}, - {Qt::Key_Redo, "Redo"}, - {Qt::Key_MediaLast, "MediaLast"}, - {Qt::Key_Select, "Select"}, - {Qt::Key_Yes, "Yes"}, - {Qt::Key_No, "No"}, - {Qt::Key_Cancel, "Cancel"}, - {Qt::Key_Printer, "Printer"}, - {Qt::Key_Execute, "Execute"}, - {Qt::Key_Sleep, "Sleep"}, - {Qt::Key_Play, "Play"}, - {Qt::Key_Zoom, "Zoom"}, - {Qt::Key_Exit, "Exit"}, - {Qt::Key_Context1, "Context1"}, - {Qt::Key_Context2, "Context2"}, - {Qt::Key_Context3, "Context3"}, - {Qt::Key_Context4, "Context4"}, - {Qt::Key_Call, "Call"}, - {Qt::Key_Hangup, "Hangup"}, - {Qt::Key_Flip, "Flip"}, - {Qt::Key_ToggleCallHangup, "ToggleCallHangup"}, - {Qt::Key_VoiceDial, "VoiceDial"}, - {Qt::Key_LastNumberRedial, "LastNumberRedial"}, - {Qt::Key_Camera, "Camera"}, - {Qt::Key_CameraFocus, "CameraFocus"}}; +static constexpr KeyCodeName s_qt_key_names[] = {{Qt::Key_Escape, "Escape", ICON_PF_ESC}, + {Qt::Key_Tab, "Tab", ICON_PF_TAB}, + {Qt::Key_Backtab, "Backtab", nullptr}, + {Qt::Key_Backspace, "Backspace", ICON_PF_BACKSPACE}, + {Qt::Key_Return, "Return", ICON_PF_ENTER}, + {Qt::Key_Enter, "Enter", ICON_PF_ENTER}, + {Qt::Key_Insert, "Insert", ICON_PF_INSERT}, + {Qt::Key_Delete, "Delete", ICON_PF_DELETE}, + {Qt::Key_Pause, "Pause", ICON_PF_PAUSE}, + {Qt::Key_Print, "Print", ICON_PF_PRTSC}, + {Qt::Key_SysReq, "SysReq", ICON_PF_PAUSE}, + {Qt::Key_Clear, "Clear", nullptr}, + {Qt::Key_Home, "Home", ICON_PF_HOME}, + {Qt::Key_End, "End", ICON_PF_END}, + {Qt::Key_Left, "Left", ICON_PF_ARROW_LEFT}, + {Qt::Key_Up, "Up", ICON_PF_ARROW_UP}, + {Qt::Key_Right, "Right", ICON_PF_ARROW_RIGHT}, + {Qt::Key_Down, "Down", ICON_PF_ARROW_DOWN}, + {Qt::Key_PageUp, "PageUp", ICON_PF_PAGE_UP}, + {Qt::Key_PageDown, "PageDown", ICON_PF_PAGE_DOWN}, + {Qt::Key_Shift, "Shift", ICON_PF_SHIFT}, + {Qt::Key_Control, "Control", ICON_PF_CTRL}, + {Qt::Key_Meta, "Meta", ICON_PF_SUPER}, + {Qt::Key_Alt, "Alt", ICON_PF_ALT}, + {Qt::Key_CapsLock, "CapsLock", ICON_PF_CAPS}, + {Qt::Key_NumLock, "NumLock", ICON_PF_NUMLOCK}, + {Qt::Key_ScrollLock, "ScrollLock", ICON_PF_SCRLK}, + {Qt::Key_F1, "F1", ICON_PF_F1}, + {Qt::Key_F2, "F2", ICON_PF_F2}, + {Qt::Key_F3, "F3", ICON_PF_F3}, + {Qt::Key_F4, "F4", ICON_PF_F4}, + {Qt::Key_F5, "F5", ICON_PF_F5}, + {Qt::Key_F6, "F6", ICON_PF_F6}, + {Qt::Key_F7, "F7", ICON_PF_F7}, + {Qt::Key_F8, "F8", ICON_PF_F8}, + {Qt::Key_F9, "F9", ICON_PF_F9}, + {Qt::Key_F10, "F10", ICON_PF_F10}, + {Qt::Key_F11, "F11", ICON_PF_F11}, + {Qt::Key_F12, "F12", ICON_PF_F12}, + {Qt::Key_F13, "F13", nullptr}, + {Qt::Key_F14, "F14", nullptr}, + {Qt::Key_F15, "F15", nullptr}, + {Qt::Key_F16, "F16", nullptr}, + {Qt::Key_F17, "F17", nullptr}, + {Qt::Key_F18, "F18", nullptr}, + {Qt::Key_F19, "F19", nullptr}, + {Qt::Key_F20, "F20", nullptr}, + {Qt::Key_F21, "F21", nullptr}, + {Qt::Key_F22, "F22", nullptr}, + {Qt::Key_F23, "F23", nullptr}, + {Qt::Key_F24, "F24", nullptr}, + {Qt::Key_F25, "F25", nullptr}, + {Qt::Key_F26, "F26", nullptr}, + {Qt::Key_F27, "F27", nullptr}, + {Qt::Key_F28, "F28", nullptr}, + {Qt::Key_F29, "F29", nullptr}, + {Qt::Key_F30, "F30", nullptr}, + {Qt::Key_F31, "F31", nullptr}, + {Qt::Key_F32, "F32", nullptr}, + {Qt::Key_F33, "F33", nullptr}, + {Qt::Key_F34, "F34", nullptr}, + {Qt::Key_F35, "F35", nullptr}, + {Qt::Key_Super_L, "Super_L", nullptr}, + {Qt::Key_Super_R, "Super_R", nullptr}, + {Qt::Key_Menu, "Menu", nullptr}, + {Qt::Key_Hyper_L, "Hyper_L", nullptr}, + {Qt::Key_Hyper_R, "Hyper_R", nullptr}, + {Qt::Key_Help, "Help", nullptr}, + {Qt::Key_Direction_L, "Direction_L", nullptr}, + {Qt::Key_Direction_R, "Direction_R", nullptr}, + {Qt::Key_Space, "Space", ICON_PF_SPACE}, + {Qt::Key_Any, "Any", nullptr}, + {Qt::Key_Exclam, "Exclam", nullptr}, + {Qt::Key_QuoteDbl, "QuoteDbl", nullptr}, + {Qt::Key_NumberSign, "NumberSign", nullptr}, + {Qt::Key_Dollar, "Dollar", nullptr}, + {Qt::Key_Percent, "Percent", nullptr}, + {Qt::Key_Ampersand, "Ampersand", nullptr}, + {Qt::Key_Apostrophe, "Apostrophe", nullptr}, + {Qt::Key_ParenLeft, "ParenLeft", nullptr}, + {Qt::Key_ParenRight, "ParenRight", nullptr}, + {Qt::Key_Asterisk, "Asterisk", nullptr}, + {Qt::Key_Plus, "Plus", nullptr}, + {Qt::Key_Comma, "Comma", nullptr}, + {Qt::Key_Minus, "Minus", nullptr}, + {Qt::Key_Period, "Period", nullptr}, + {Qt::Key_Slash, "Slash", nullptr}, + {Qt::Key_0, "0", ICON_PF_0}, + {Qt::Key_1, "1", ICON_PF_1}, + {Qt::Key_2, "2", ICON_PF_2}, + {Qt::Key_3, "3", ICON_PF_3}, + {Qt::Key_4, "4", ICON_PF_4}, + {Qt::Key_5, "5", ICON_PF_5}, + {Qt::Key_6, "6", ICON_PF_6}, + {Qt::Key_7, "7", ICON_PF_7}, + {Qt::Key_8, "8", ICON_PF_8}, + {Qt::Key_9, "9", ICON_PF_9}, + {Qt::Key_Colon, "Colon", nullptr}, + {Qt::Key_Semicolon, "Semicolon", nullptr}, + {Qt::Key_Less, "Less", nullptr}, + {Qt::Key_Equal, "Equal", nullptr}, + {Qt::Key_Greater, "Greater", nullptr}, + {Qt::Key_Question, "Question", nullptr}, + {Qt::Key_At, "At", nullptr}, + {Qt::Key_A, "A", ICON_PF_KEY_A}, + {Qt::Key_B, "B", ICON_PF_KEY_B}, + {Qt::Key_C, "C", ICON_PF_KEY_C}, + {Qt::Key_D, "D", ICON_PF_KEY_D}, + {Qt::Key_E, "E", ICON_PF_KEY_E}, + {Qt::Key_F, "F", ICON_PF_KEY_F}, + {Qt::Key_G, "G", ICON_PF_KEY_G}, + {Qt::Key_H, "H", ICON_PF_KEY_H}, + {Qt::Key_I, "I", ICON_PF_KEY_I}, + {Qt::Key_J, "J", ICON_PF_KEY_J}, + {Qt::Key_K, "K", ICON_PF_KEY_K}, + {Qt::Key_L, "L", ICON_PF_KEY_L}, + {Qt::Key_M, "M", ICON_PF_KEY_M}, + {Qt::Key_N, "N", ICON_PF_KEY_N}, + {Qt::Key_O, "O", ICON_PF_KEY_O}, + {Qt::Key_P, "P", ICON_PF_KEY_P}, + {Qt::Key_Q, "Q", ICON_PF_KEY_Q}, + {Qt::Key_R, "R", ICON_PF_KEY_R}, + {Qt::Key_S, "S", ICON_PF_KEY_S}, + {Qt::Key_T, "T", ICON_PF_KEY_T}, + {Qt::Key_U, "U", ICON_PF_KEY_U}, + {Qt::Key_V, "V", ICON_PF_KEY_V}, + {Qt::Key_W, "W", ICON_PF_KEY_W}, + {Qt::Key_X, "X", ICON_PF_KEY_X}, + {Qt::Key_Y, "Y", ICON_PF_KEY_Y}, + {Qt::Key_Z, "Z", ICON_PF_KEY_Z}, + {Qt::Key_BracketLeft, "BracketLeft", nullptr}, + {Qt::Key_Backslash, "Backslash", nullptr}, + {Qt::Key_BracketRight, "BracketRight", nullptr}, + {Qt::Key_AsciiCircum, "AsciiCircum", nullptr}, + {Qt::Key_Underscore, "Underscore", nullptr}, + {Qt::Key_QuoteLeft, "QuoteLeft", nullptr}, + {Qt::Key_BraceLeft, "BraceLeft", nullptr}, + {Qt::Key_Bar, "Bar", nullptr}, + {Qt::Key_BraceRight, "BraceRight", nullptr}, + {Qt::Key_AsciiTilde, "AsciiTilde", nullptr}, + {Qt::Key_nobreakspace, "nobreakspace", nullptr}, + {Qt::Key_exclamdown, "exclamdown", nullptr}, + {Qt::Key_cent, "cent", nullptr}, + {Qt::Key_sterling, "sterling", nullptr}, + {Qt::Key_currency, "currency", nullptr}, + {Qt::Key_yen, "yen", nullptr}, + {Qt::Key_brokenbar, "brokenbar", nullptr}, + {Qt::Key_section, "section", nullptr}, + {Qt::Key_diaeresis, "diaeresis", nullptr}, + {Qt::Key_copyright, "copyright", nullptr}, + {Qt::Key_ordfeminine, "ordfeminine", nullptr}, + {Qt::Key_guillemotleft, "guillemotleft", nullptr}, + {Qt::Key_notsign, "notsign", nullptr}, + {Qt::Key_hyphen, "hyphen", nullptr}, + {Qt::Key_registered, "registered", nullptr}, + {Qt::Key_macron, "macron", nullptr}, + {Qt::Key_degree, "degree", nullptr}, + {Qt::Key_plusminus, "plusminus", nullptr}, + {Qt::Key_twosuperior, "twosuperior", nullptr}, + {Qt::Key_threesuperior, "threesuperior", nullptr}, + {Qt::Key_acute, "acute", nullptr}, + {Qt::Key_mu, "mu", nullptr}, + {Qt::Key_paragraph, "paragraph", nullptr}, + {Qt::Key_periodcentered, "periodcentered", nullptr}, + {Qt::Key_cedilla, "cedilla", nullptr}, + {Qt::Key_onesuperior, "onesuperior", nullptr}, + {Qt::Key_masculine, "masculine", nullptr}, + {Qt::Key_guillemotright, "guillemotright", nullptr}, + {Qt::Key_onequarter, "onequarter", nullptr}, + {Qt::Key_onehalf, "onehalf", nullptr}, + {Qt::Key_threequarters, "threequarters", nullptr}, + {Qt::Key_questiondown, "questiondown", nullptr}, + {Qt::Key_Agrave, "Agrave", nullptr}, + {Qt::Key_Aacute, "Aacute", nullptr}, + {Qt::Key_Acircumflex, "Acircumflex", nullptr}, + {Qt::Key_Atilde, "Atilde", nullptr}, + {Qt::Key_Adiaeresis, "Adiaeresis", nullptr}, + {Qt::Key_Aring, "Aring", nullptr}, + {Qt::Key_AE, "AE", nullptr}, + {Qt::Key_Ccedilla, "Ccedilla", nullptr}, + {Qt::Key_Egrave, "Egrave", nullptr}, + {Qt::Key_Eacute, "Eacute", nullptr}, + {Qt::Key_Ecircumflex, "Ecircumflex", nullptr}, + {Qt::Key_Ediaeresis, "Ediaeresis", nullptr}, + {Qt::Key_Igrave, "Igrave", nullptr}, + {Qt::Key_Iacute, "Iacute", nullptr}, + {Qt::Key_Icircumflex, "Icircumflex", nullptr}, + {Qt::Key_Idiaeresis, "Idiaeresis", nullptr}, + {Qt::Key_ETH, "ETH", nullptr}, + {Qt::Key_Ntilde, "Ntilde", nullptr}, + {Qt::Key_Ograve, "Ograve", nullptr}, + {Qt::Key_Oacute, "Oacute", nullptr}, + {Qt::Key_Ocircumflex, "Ocircumflex", nullptr}, + {Qt::Key_Otilde, "Otilde", nullptr}, + {Qt::Key_Odiaeresis, "Odiaeresis", nullptr}, + {Qt::Key_multiply, "multiply", nullptr}, + {Qt::Key_Ooblique, "Ooblique", nullptr}, + {Qt::Key_Ugrave, "Ugrave", nullptr}, + {Qt::Key_Uacute, "Uacute", nullptr}, + {Qt::Key_Ucircumflex, "Ucircumflex", nullptr}, + {Qt::Key_Udiaeresis, "Udiaeresis", nullptr}, + {Qt::Key_Yacute, "Yacute", nullptr}, + {Qt::Key_THORN, "THORN", nullptr}, + {Qt::Key_ssharp, "ssharp", nullptr}, + {Qt::Key_division, "division", nullptr}, + {Qt::Key_ydiaeresis, "ydiaeresis", nullptr}, + {Qt::Key_AltGr, "AltGr", nullptr}, + {Qt::Key_Multi_key, "Multi_key", nullptr}, + {Qt::Key_Codeinput, "Codeinput", nullptr}, + {Qt::Key_SingleCandidate, "SingleCandidate", nullptr}, + {Qt::Key_MultipleCandidate, "MultipleCandidate", nullptr}, + {Qt::Key_PreviousCandidate, "PreviousCandidate", nullptr}, + {Qt::Key_Mode_switch, "Mode_switch", nullptr}, + {Qt::Key_Kanji, "Kanji", nullptr}, + {Qt::Key_Muhenkan, "Muhenkan", nullptr}, + {Qt::Key_Henkan, "Henkan", nullptr}, + {Qt::Key_Romaji, "Romaji", nullptr}, + {Qt::Key_Hiragana, "Hiragana", nullptr}, + {Qt::Key_Katakana, "Katakana", nullptr}, + {Qt::Key_Hiragana_Katakana, "Hiragana_Katakana", nullptr}, + {Qt::Key_Zenkaku, "Zenkaku", nullptr}, + {Qt::Key_Hankaku, "Hankaku", nullptr}, + {Qt::Key_Zenkaku_Hankaku, "Zenkaku_Hankaku", nullptr}, + {Qt::Key_Touroku, "Touroku", nullptr}, + {Qt::Key_Massyo, "Massyo", nullptr}, + {Qt::Key_Kana_Lock, "Kana_Lock", nullptr}, + {Qt::Key_Kana_Shift, "Kana_Shift", nullptr}, + {Qt::Key_Eisu_Shift, "Eisu_Shift", nullptr}, + {Qt::Key_Eisu_toggle, "Eisu_toggle", nullptr}, + {Qt::Key_Hangul, "Hangul", nullptr}, + {Qt::Key_Hangul_Start, "Hangul_Start", nullptr}, + {Qt::Key_Hangul_End, "Hangul_End", nullptr}, + {Qt::Key_Hangul_Hanja, "Hangul_Hanja", nullptr}, + {Qt::Key_Hangul_Jamo, "Hangul_Jamo", nullptr}, + {Qt::Key_Hangul_Romaja, "Hangul_Romaja", nullptr}, + {Qt::Key_Hangul_Jeonja, "Hangul_Jeonja", nullptr}, + {Qt::Key_Hangul_Banja, "Hangul_Banja", nullptr}, + {Qt::Key_Hangul_PreHanja, "Hangul_PreHanja", nullptr}, + {Qt::Key_Hangul_PostHanja, "Hangul_PostHanja", nullptr}, + {Qt::Key_Hangul_Special, "Hangul_Special", nullptr}, + {Qt::Key_Dead_Grave, "Dead_Grave", nullptr}, + {Qt::Key_Dead_Acute, "Dead_Acute", nullptr}, + {Qt::Key_Dead_Circumflex, "Dead_Circumflex", nullptr}, + {Qt::Key_Dead_Tilde, "Dead_Tilde", nullptr}, + {Qt::Key_Dead_Macron, "Dead_Macron", nullptr}, + {Qt::Key_Dead_Breve, "Dead_Breve", nullptr}, + {Qt::Key_Dead_Abovedot, "Dead_Abovedot", nullptr}, + {Qt::Key_Dead_Diaeresis, "Dead_Diaeresis", nullptr}, + {Qt::Key_Dead_Abovering, "Dead_Abovering", nullptr}, + {Qt::Key_Dead_Doubleacute, "Dead_Doubleacute", nullptr}, + {Qt::Key_Dead_Caron, "Dead_Caron", nullptr}, + {Qt::Key_Dead_Cedilla, "Dead_Cedilla", nullptr}, + {Qt::Key_Dead_Ogonek, "Dead_Ogonek", nullptr}, + {Qt::Key_Dead_Iota, "Dead_Iota", nullptr}, + {Qt::Key_Dead_Voiced_Sound, "Dead_Voiced_Sound", nullptr}, + {Qt::Key_Dead_Semivoiced_Sound, "Dead_Semivoiced_Sound", nullptr}, + {Qt::Key_Dead_Belowdot, "Dead_Belowdot", nullptr}, + {Qt::Key_Dead_Hook, "Dead_Hook", nullptr}, + {Qt::Key_Dead_Horn, "Dead_Horn", nullptr}, + {Qt::Key_Back, "Back", nullptr}, + {Qt::Key_Forward, "Forward", nullptr}, + {Qt::Key_Stop, "Stop", nullptr}, + {Qt::Key_Refresh, "Refresh", nullptr}, + {Qt::Key_VolumeDown, "VolumeDown", nullptr}, + {Qt::Key_VolumeMute, "VolumeMute", nullptr}, + {Qt::Key_VolumeUp, "VolumeUp", nullptr}, + {Qt::Key_BassBoost, "BassBoost", nullptr}, + {Qt::Key_BassUp, "BassUp", nullptr}, + {Qt::Key_BassDown, "BassDown", nullptr}, + {Qt::Key_TrebleUp, "TrebleUp", nullptr}, + {Qt::Key_TrebleDown, "TrebleDown", nullptr}, + {Qt::Key_MediaPlay, "MediaPlay", nullptr}, + {Qt::Key_MediaStop, "MediaStop", nullptr}, + {Qt::Key_MediaPrevious, "MediaPrevious", nullptr}, + {Qt::Key_MediaNext, "MediaNext", nullptr}, + {Qt::Key_MediaRecord, "MediaRecord", nullptr}, + {Qt::Key_MediaPause, "MediaPause", nullptr}, + {Qt::Key_MediaTogglePlayPause, "MediaTogglePlayPause", nullptr}, + {Qt::Key_HomePage, "HomePage", nullptr}, + {Qt::Key_Favorites, "Favorites", nullptr}, + {Qt::Key_Search, "Search", nullptr}, + {Qt::Key_Standby, "Standby", nullptr}, + {Qt::Key_OpenUrl, "OpenUrl", nullptr}, + {Qt::Key_LaunchMail, "LaunchMail", nullptr}, + {Qt::Key_LaunchMedia, "LaunchMedia", nullptr}, + {Qt::Key_Launch0, "Launch0", nullptr}, + {Qt::Key_Launch1, "Launch1", nullptr}, + {Qt::Key_Launch2, "Launch2", nullptr}, + {Qt::Key_Launch3, "Launch3", nullptr}, + {Qt::Key_Launch4, "Launch4", nullptr}, + {Qt::Key_Launch5, "Launch5", nullptr}, + {Qt::Key_Launch6, "Launch6", nullptr}, + {Qt::Key_Launch7, "Launch7", nullptr}, + {Qt::Key_Launch8, "Launch8", nullptr}, + {Qt::Key_Launch9, "Launch9", nullptr}, + {Qt::Key_LaunchA, "LaunchA", nullptr}, + {Qt::Key_LaunchB, "LaunchB", nullptr}, + {Qt::Key_LaunchC, "LaunchC", nullptr}, + {Qt::Key_LaunchD, "LaunchD", nullptr}, + {Qt::Key_LaunchE, "LaunchE", nullptr}, + {Qt::Key_LaunchF, "LaunchF", nullptr}, + {Qt::Key_MonBrightnessUp, "MonBrightnessUp", nullptr}, + {Qt::Key_MonBrightnessDown, "MonBrightnessDown", nullptr}, + {Qt::Key_KeyboardLightOnOff, "KeyboardLightOnOff", nullptr}, + {Qt::Key_KeyboardBrightnessUp, "KeyboardBrightnessUp", nullptr}, + {Qt::Key_KeyboardBrightnessDown, "KeyboardBrightnessDown", nullptr}, + {Qt::Key_PowerOff, "PowerOff", nullptr}, + {Qt::Key_WakeUp, "WakeUp", nullptr}, + {Qt::Key_Eject, "Eject", nullptr}, + {Qt::Key_ScreenSaver, "ScreenSaver", nullptr}, + {Qt::Key_WWW, "WWW", nullptr}, + {Qt::Key_Memo, "Memo", nullptr}, + {Qt::Key_LightBulb, "LightBulb", nullptr}, + {Qt::Key_Shop, "Shop", nullptr}, + {Qt::Key_History, "History", nullptr}, + {Qt::Key_AddFavorite, "AddFavorite", nullptr}, + {Qt::Key_HotLinks, "HotLinks", nullptr}, + {Qt::Key_BrightnessAdjust, "BrightnessAdjust", nullptr}, + {Qt::Key_Finance, "Finance", nullptr}, + {Qt::Key_Community, "Community", nullptr}, + {Qt::Key_AudioRewind, "AudioRewind", nullptr}, + {Qt::Key_BackForward, "BackForward", nullptr}, + {Qt::Key_ApplicationLeft, "ApplicationLeft", nullptr}, + {Qt::Key_ApplicationRight, "ApplicationRight", nullptr}, + {Qt::Key_Book, "Book", nullptr}, + {Qt::Key_CD, "CD", nullptr}, + {Qt::Key_Calculator, "Calculator", nullptr}, + {Qt::Key_ToDoList, "ToDoList", nullptr}, + {Qt::Key_ClearGrab, "ClearGrab", nullptr}, + {Qt::Key_Close, "Close", nullptr}, + {Qt::Key_Copy, "Copy", nullptr}, + {Qt::Key_Cut, "Cut", nullptr}, + {Qt::Key_Display, "Display", nullptr}, + {Qt::Key_DOS, "DOS", nullptr}, + {Qt::Key_Documents, "Documents", nullptr}, + {Qt::Key_Excel, "Excel", nullptr}, + {Qt::Key_Explorer, "Explorer", nullptr}, + {Qt::Key_Game, "Game", nullptr}, + {Qt::Key_Go, "Go", nullptr}, + {Qt::Key_iTouch, "iTouch", nullptr}, + {Qt::Key_LogOff, "LogOff", nullptr}, + {Qt::Key_Market, "Market", nullptr}, + {Qt::Key_Meeting, "Meeting", nullptr}, + {Qt::Key_MenuKB, "MenuKB", nullptr}, + {Qt::Key_MenuPB, "MenuPB", nullptr}, + {Qt::Key_MySites, "MySites", nullptr}, + {Qt::Key_News, "News", nullptr}, + {Qt::Key_OfficeHome, "OfficeHome", nullptr}, + {Qt::Key_Option, "Option", nullptr}, + {Qt::Key_Paste, "Paste", nullptr}, + {Qt::Key_Phone, "Phone", nullptr}, + {Qt::Key_Calendar, "Calendar", nullptr}, + {Qt::Key_Reply, "Reply", nullptr}, + {Qt::Key_Reload, "Reload", nullptr}, + {Qt::Key_RotateWindows, "RotateWindows", nullptr}, + {Qt::Key_RotationPB, "RotationPB", nullptr}, + {Qt::Key_RotationKB, "RotationKB", nullptr}, + {Qt::Key_Save, "Save", nullptr}, + {Qt::Key_Send, "Send", nullptr}, + {Qt::Key_Spell, "Spell", nullptr}, + {Qt::Key_SplitScreen, "SplitScreen", nullptr}, + {Qt::Key_Support, "Support", nullptr}, + {Qt::Key_TaskPane, "TaskPane", nullptr}, + {Qt::Key_Terminal, "Terminal", nullptr}, + {Qt::Key_Tools, "Tools", nullptr}, + {Qt::Key_Travel, "Travel", nullptr}, + {Qt::Key_Video, "Video", nullptr}, + {Qt::Key_Word, "Word", nullptr}, + {Qt::Key_Xfer, "Xfer", nullptr}, + {Qt::Key_ZoomIn, "ZoomIn", nullptr}, + {Qt::Key_ZoomOut, "ZoomOut", nullptr}, + {Qt::Key_Away, "Away", nullptr}, + {Qt::Key_Messenger, "Messenger", nullptr}, + {Qt::Key_WebCam, "WebCam", nullptr}, + {Qt::Key_MailForward, "MailForward", nullptr}, + {Qt::Key_Pictures, "Pictures", nullptr}, + {Qt::Key_Music, "Music", nullptr}, + {Qt::Key_Battery, "Battery", nullptr}, + {Qt::Key_Bluetooth, "Bluetooth", nullptr}, + {Qt::Key_WLAN, "WLAN", nullptr}, + {Qt::Key_UWB, "UWB", nullptr}, + {Qt::Key_AudioForward, "AudioForward", nullptr}, + {Qt::Key_AudioRepeat, "AudioRepeat", nullptr}, + {Qt::Key_AudioRandomPlay, "AudioRandomPlay", nullptr}, + {Qt::Key_Subtitle, "Subtitle", nullptr}, + {Qt::Key_AudioCycleTrack, "AudioCycleTrack", nullptr}, + {Qt::Key_Time, "Time", nullptr}, + {Qt::Key_Hibernate, "Hibernate", nullptr}, + {Qt::Key_View, "View", nullptr}, + {Qt::Key_TopMenu, "TopMenu", nullptr}, + {Qt::Key_PowerDown, "PowerDown", nullptr}, + {Qt::Key_Suspend, "Suspend", nullptr}, + {Qt::Key_ContrastAdjust, "ContrastAdjust", nullptr}, + {Qt::Key_LaunchG, "LaunchG", nullptr}, + {Qt::Key_LaunchH, "LaunchH", nullptr}, + {Qt::Key_TouchpadToggle, "TouchpadToggle", nullptr}, + {Qt::Key_TouchpadOn, "TouchpadOn", nullptr}, + {Qt::Key_TouchpadOff, "TouchpadOff", nullptr}, + {Qt::Key_MicMute, "MicMute", nullptr}, + {Qt::Key_Red, "Red", nullptr}, + {Qt::Key_Green, "Green", nullptr}, + {Qt::Key_Yellow, "Yellow", nullptr}, + {Qt::Key_Blue, "Blue", nullptr}, + {Qt::Key_ChannelUp, "ChannelUp", nullptr}, + {Qt::Key_ChannelDown, "ChannelDown", nullptr}, + {Qt::Key_Guide, "Guide", nullptr}, + {Qt::Key_Info, "Info", nullptr}, + {Qt::Key_Settings, "Settings", nullptr}, + {Qt::Key_MicVolumeUp, "MicVolumeUp", nullptr}, + {Qt::Key_MicVolumeDown, "MicVolumeDown", nullptr}, + {Qt::Key_New, "New", nullptr}, + {Qt::Key_Open, "Open", nullptr}, + {Qt::Key_Find, "Find", nullptr}, + {Qt::Key_Undo, "Undo", nullptr}, + {Qt::Key_Redo, "Redo", nullptr}, + {Qt::Key_MediaLast, "MediaLast", nullptr}, + {Qt::Key_Select, "Select", nullptr}, + {Qt::Key_Yes, "Yes", nullptr}, + {Qt::Key_No, "No", nullptr}, + {Qt::Key_Cancel, "Cancel", nullptr}, + {Qt::Key_Printer, "Printer", nullptr}, + {Qt::Key_Execute, "Execute", nullptr}, + {Qt::Key_Sleep, "Sleep", nullptr}, + {Qt::Key_Play, "Play", nullptr}, + {Qt::Key_Zoom, "Zoom", nullptr}, + {Qt::Key_Exit, "Exit", nullptr}, + {Qt::Key_Context1, "Context1", nullptr}, + {Qt::Key_Context2, "Context2", nullptr}, + {Qt::Key_Context3, "Context3", nullptr}, + {Qt::Key_Context4, "Context4", nullptr}, + {Qt::Key_Call, "Call", nullptr}, + {Qt::Key_Hangup, "Hangup", nullptr}, + {Qt::Key_Flip, "Flip", nullptr}, + {Qt::Key_ToggleCallHangup, "ToggleCallHangup", nullptr}, + {Qt::Key_VoiceDial, "VoiceDial", nullptr}, + {Qt::Key_LastNumberRedial, "LastNumberRedial", nullptr}, + {Qt::Key_Camera, "Camera", nullptr}, + {Qt::Key_CameraFocus, "CameraFocus", nullptr}}; std::optional InputManager::ConvertHostKeyboardStringToCode(const std::string_view& str) { @@ -497,6 +500,21 @@ std::optional InputManager::ConvertHostKeyboardCodeToString(u32 cod return ret; } +const char* InputManager::ConvertHostKeyboardCodeToIcon(u32 code) +{ + if (code & Qt::KeyboardModifierMask) + return nullptr; + + const u32 masked_code = (code & ~Qt::KeyboardModifierMask); + for (const KeyCodeName& name : s_qt_key_names) + { + if (static_cast(masked_code) == name.code) + return name.icon_name; + } + + return nullptr; +} + u32 QtUtils::KeyEventToCode(const QKeyEvent* ev) { return static_cast(ev->key()) | (static_cast(ev->modifiers()) & static_cast(Qt::KeypadModifier)); diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index 6c264ca97..eac8aec7a 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -363,6 +363,11 @@ std::optional InputManager::ConvertHostKeyboardCodeToString(u32 cod return std::nullopt; } +const char* InputManager::ConvertHostKeyboardCodeToIcon(u32 code) +{ + return nullptr; +} + void Host::AddFixedInputBindings(SettingsInterface& si) { // noop diff --git a/src/util/dinput_source.cpp b/src/util/dinput_source.cpp index 7a89c5372..4995a1dd3 100644 --- a/src/util/dinput_source.cpp +++ b/src/util/dinput_source.cpp @@ -398,9 +398,9 @@ std::optional DInputSource::ParseKeyString(const std::string_vi return std::nullopt; } -std::string DInputSource::ConvertKeyToString(InputBindingKey key) +TinyString DInputSource::ConvertKeyToString(InputBindingKey key) { - std::string ret; + TinyString ret; if (key.source_type == InputSourceType::DInput) { @@ -408,23 +408,28 @@ std::string DInputSource::ConvertKeyToString(InputBindingKey key) { const char* modifier = (key.modifier == InputModifier::FullAxis ? "Full" : (key.modifier == InputModifier::Negate ? "-" : "+")); - ret = fmt::format("DInput-{}/{}Axis{}{}", u32(key.source_index), modifier, u32(key.data), key.invert ? "~" : ""); + ret.fmt("DInput-{}/{}Axis{}{}", u32(key.source_index), modifier, u32(key.data), key.invert ? "~" : ""); } else if (key.source_subtype == InputSubclass::ControllerButton && key.data >= MAX_NUM_BUTTONS) { const u32 hat_num = (key.data - MAX_NUM_BUTTONS) / NUM_HAT_DIRECTIONS; const u32 hat_dir = (key.data - MAX_NUM_BUTTONS) % NUM_HAT_DIRECTIONS; - ret = fmt::format("DInput-{}/Hat{}{}", u32(key.source_index), hat_num, s_hat_directions[hat_dir]); + ret.fmt("DInput-{}/Hat{}{}", u32(key.source_index), hat_num, s_hat_directions[hat_dir]); } else if (key.source_subtype == InputSubclass::ControllerButton) { - ret = fmt::format("DInput-{}/Button{}", u32(key.source_index), u32(key.data)); + ret.fmt("DInput-{}/Button{}", u32(key.source_index), u32(key.data)); } } return ret; } +TinyString DInputSource::ConvertKeyToIcon(InputBindingKey key) +{ + return {}; +} + void DInputSource::CheckForStateChanges(size_t index, const DIJOYSTATE& new_state) { ControllerData& cd = m_controllers[index]; diff --git a/src/util/dinput_source.h b/src/util/dinput_source.h index 9151a26be..def698ebb 100644 --- a/src/util/dinput_source.h +++ b/src/util/dinput_source.h @@ -48,7 +48,8 @@ public: std::optional ParseKeyString(const std::string_view& device, const std::string_view& binding) override; - std::string ConvertKeyToString(InputBindingKey key) override; + TinyString ConvertKeyToString(InputBindingKey key) override; + TinyString ConvertKeyToIcon(InputBindingKey key) override; private: template diff --git a/src/util/imgui_manager.cpp b/src/util/imgui_manager.cpp index c3fd09b5d..fc9f764d2 100644 --- a/src/util/imgui_manager.cpp +++ b/src/util/imgui_manager.cpp @@ -554,7 +554,11 @@ bool ImGuiManager::AddIconFonts(float size) 0xf545, 0xf545, 0xf547, 0xf548, 0xf552, 0xf552, 0xf57a, 0xf57a, 0xf5a2, 0xf5a2, 0xf5aa, 0xf5aa, 0xf5e7, 0xf5e7, 0xf65d, 0xf65e, 0xf6a9, 0xf6a9, 0xf7c2, 0xf7c2, 0xf807, 0xf807, 0xf815, 0xf815, 0xf818, 0xf818, 0xf84c, 0xf84c, 0xf8cc, 0xf8cc, 0x0, 0x0}; - static constexpr ImWchar range_pf[] = { 0x2196,0x2199,0x219e,0x21a1,0x21b0,0x21b3,0x21ba,0x21c3,0x21c7,0x21ca,0x21d0,0x21d4,0x21e0,0x21e3,0x21ed,0x21ee,0x21f7,0x21f8,0x220b,0x220b,0x227a,0x227d,0x23ce,0x23ce,0x23f4,0x23f7,0x2427,0x243a,0x243c,0x243c,0x243e,0x243e,0x2460,0x246b,0x24f5,0x24fd,0x24ff,0x24ff,0x278a,0x278b,0x27fc,0x27fc,0xff21,0xff3a,0x0,0x0 }; + static constexpr ImWchar range_pf[] = {0x2196, 0x2199, 0x219e, 0x21a1, 0x21b0, 0x21b3, 0x21ba, 0x21c3, 0x21c7, 0x21ca, + 0x21d0, 0x21d4, 0x21dc, 0x21dd, 0x21e0, 0x21e3, 0x21ed, 0x21ee, 0x21f7, 0x21f8, + 0x21fa, 0x21fb, 0x227a, 0x227d, 0x23f4, 0x23f7, 0x2427, 0x243a, 0x243c, 0x243c, + 0x243e, 0x243e, 0x2460, 0x246b, 0x24f5, 0x24fd, 0x24ff, 0x24ff, 0x278a, 0x278e, + 0x27fc, 0x27fc, 0xe001, 0xe001, 0xff21, 0xff3a, 0x0, 0x0}; { ImFontConfig cfg; diff --git a/src/util/input_manager.cpp b/src/util/input_manager.cpp index 28c001c78..8a76acfaa 100644 --- a/src/util/input_manager.cpp +++ b/src/util/input_manager.cpp @@ -14,6 +14,8 @@ #include "imgui_manager.h" #include "input_source.h" +#include "IconsPromptFont.h" + #include "fmt/core.h" #include @@ -105,6 +107,7 @@ static std::optional ParseSensorKey(const std::string_view& sou static std::vector SplitChord(const std::string_view& binding); static bool SplitBinding(const std::string_view& binding, std::string_view* source, std::string_view* sub_binding); +static void PrettifyInputBindingPart(const std::string_view binding, SmallString& ret, bool& changed); static void AddBindings(const std::vector& bindings, const InputEventHandler& handler); static bool IsAxisHandler(const InputEventHandler& handler); @@ -333,7 +336,7 @@ std::string InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type } else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast(key.source_type)]) { - return s_input_sources[static_cast(key.source_type)]->ConvertKeyToString(key); + return std::string(s_input_sources[static_cast(key.source_type)]->ConvertKeyToString(key)); } } @@ -367,6 +370,116 @@ std::string InputManager::ConvertInputBindingKeysToString(InputBindingInfo::Type return ss.str(); } +bool InputManager::PrettifyInputBinding(std::string& binding) +{ + if (binding.empty()) + return false; + + const std::string_view binding_view(binding); + + SmallString ret; + bool changed = false; + + std::string_view::size_type last = 0; + std::string_view::size_type next; + while ((next = binding_view.find('&', last)) != std::string_view::npos) + { + if (last != next) + { + const std::string_view part = StringUtil::StripWhitespace(binding_view.substr(last, next - last)); + if (!part.empty()) + { + if (!ret.empty()) + ret.append(" + "); + PrettifyInputBindingPart(part, ret, changed); + } + } + last = next + 1; + } + if (last < (binding_view.size() - 1)) + { + const std::string_view part = StringUtil::StripWhitespace(binding_view.substr(last)); + if (!part.empty()) + { + if (!ret.empty()) + ret.append(" + "); + PrettifyInputBindingPart(part, ret, changed); + } + } + + if (changed) + binding = ret.view(); + + return changed; +} + +void InputManager::PrettifyInputBindingPart(const std::string_view binding, SmallString& ret, bool& changed) +{ + std::string_view source, sub_binding; + if (!SplitBinding(binding, &source, &sub_binding)) + return; + + // lameee, string matching + if (StringUtil::StartsWith(source, "Keyboard")) + { + std::optional key = ParseHostKeyboardKey(source, sub_binding); + const char* icon = key.has_value() ? ConvertHostKeyboardCodeToIcon(key->data) : nullptr; + if (icon) + { + ret.append(icon); + changed = true; + return; + } + } + else if (StringUtil::StartsWith(source, "Pointer")) + { + const std::optional key = ParsePointerKey(source, sub_binding); + if (key.has_value()) + { + if (key->source_subtype == InputSubclass::PointerButton) + { + static constexpr const char* button_icons[] = { + ICON_PF_MOUSE_BUTTON_1, ICON_PF_MOUSE_BUTTON_2, ICON_PF_MOUSE_BUTTON_3, + ICON_PF_MOUSE_BUTTON_4, ICON_PF_MOUSE_BUTTON_5, + }; + if (key->data < std::size(button_icons)) + { + ret.append(button_icons[key->data]); + changed = true; + return; + } + } + } + } + else if (StringUtil::StartsWith(source, "Sensor")) + { + } + else + { + for (u32 i = FIRST_EXTERNAL_INPUT_SOURCE; i < LAST_EXTERNAL_INPUT_SOURCE; i++) + { + if (s_input_sources[i]) + { + std::optional key = s_input_sources[i]->ParseKeyString(source, sub_binding); + if (key.has_value()) + { + const TinyString icon = s_input_sources[i]->ConvertKeyToIcon(key.value()); + if (!icon.empty()) + { + ret.append(icon); + changed = true; + return; + } + + break; + } + } + } + } + + ret.append(binding); +} + void InputManager::AddBindings(const std::vector& bindings, const InputEventHandler& handler) { for (const std::string& binding : bindings) diff --git a/src/util/input_manager.h b/src/util/input_manager.h index 9d68b8de8..8a327564a 100644 --- a/src/util/input_manager.h +++ b/src/util/input_manager.h @@ -207,6 +207,9 @@ std::optional ConvertHostKeyboardStringToCode(const std::string_view& str); /// Converts a key code from an identifier to a human-readable string. std::optional ConvertHostKeyboardCodeToString(u32 code); +/// Converts a key code from an identifier to an icon which can be drawn. +const char* ConvertHostKeyboardCodeToIcon(u32 code); + /// Creates a key for a host-specific key code. InputBindingKey MakeHostKeyboardKey(u32 key_code); @@ -230,6 +233,9 @@ std::string ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, std::string ConvertInputBindingKeysToString(InputBindingInfo::Type binding_type, const InputBindingKey* keys, size_t num_keys); +/// Represents a binding with icon fonts, if available. +bool PrettifyInputBinding(std::string& binding); + /// Returns a list of all hotkeys. std::vector GetHotkeyList(); diff --git a/src/util/input_source.h b/src/util/input_source.h index 1edb3e40f..0888b4556 100644 --- a/src/util/input_source.h +++ b/src/util/input_source.h @@ -10,6 +10,7 @@ #include #include +#include "common/small_string.h" #include "common/types.h" #include "input_manager.h" @@ -30,7 +31,8 @@ public: virtual std::optional ParseKeyString(const std::string_view& device, const std::string_view& binding) = 0; - virtual std::string ConvertKeyToString(InputBindingKey key) = 0; + virtual TinyString ConvertKeyToString(InputBindingKey key) = 0; + virtual TinyString ConvertKeyToIcon(InputBindingKey key) = 0; /// Enumerates available devices. Returns a pair of the prefix (e.g. SDL-0) and the device name. virtual std::vector> EnumerateDevices() = 0; diff --git a/src/util/sdl_input_source.cpp b/src/util/sdl_input_source.cpp index 675cec78e..02cbbc3c4 100644 --- a/src/util/sdl_input_source.cpp +++ b/src/util/sdl_input_source.cpp @@ -14,6 +14,8 @@ #include "common/path.h" #include "common/string_util.h" +#include "IconsPromptFont.h" + #include #ifdef __APPLE__ @@ -32,6 +34,14 @@ static constexpr const char* s_sdl_axis_names[] = { "LeftTrigger", // SDL_CONTROLLER_AXIS_TRIGGERLEFT "RightTrigger", // SDL_CONTROLLER_AXIS_TRIGGERRIGHT }; +static constexpr const char* s_sdl_axis_icons[][2] = { + {ICON_PF_LEFT_ANALOG_LEFT, ICON_PF_LEFT_ANALOG_RIGHT}, // SDL_CONTROLLER_AXIS_LEFTX + {ICON_PF_LEFT_ANALOG_UP, ICON_PF_LEFT_ANALOG_DOWN}, // SDL_CONTROLLER_AXIS_LEFTY + {ICON_PF_RIGHT_ANALOG_LEFT, ICON_PF_RIGHT_ANALOG_RIGHT}, // SDL_CONTROLLER_AXIS_RIGHTX + {ICON_PF_RIGHT_ANALOG_UP, ICON_PF_RIGHT_ANALOG_DOWN}, // SDL_CONTROLLER_AXIS_RIGHTY + {nullptr, ICON_PF_LEFT_TRIGGER_PULL}, // SDL_CONTROLLER_AXIS_TRIGGERLEFT + {nullptr, ICON_PF_RIGHT_TRIGGER_PULL}, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT +}; static constexpr const GenericInputBinding s_sdl_generic_binding_axis_mapping[][2] = { {GenericInputBinding::LeftStickLeft, GenericInputBinding::LeftStickRight}, // SDL_CONTROLLER_AXIS_LEFTX {GenericInputBinding::LeftStickUp, GenericInputBinding::LeftStickDown}, // SDL_CONTROLLER_AXIS_LEFTY @@ -64,6 +74,23 @@ static constexpr const char* s_sdl_button_names[] = { "Paddle4", // SDL_CONTROLLER_BUTTON_PADDLE4 "Touchpad", // SDL_CONTROLLER_BUTTON_TOUCHPAD }; +static constexpr const char* s_sdl_button_icons[] = { + ICON_PF_BUTTON_A, // SDL_CONTROLLER_BUTTON_A + ICON_PF_BUTTON_B, // SDL_CONTROLLER_BUTTON_B + ICON_PF_BUTTON_X, // SDL_CONTROLLER_BUTTON_X + ICON_PF_BUTTON_Y, // SDL_CONTROLLER_BUTTON_Y + ICON_PF_SHARE_CAPTURE, // SDL_CONTROLLER_BUTTON_BACK + ICON_PF_XBOX, // SDL_CONTROLLER_BUTTON_GUIDE + ICON_PF_BURGER_MENU, // SDL_CONTROLLER_BUTTON_START + ICON_PF_LEFT_ANALOG_CLICK, // SDL_CONTROLLER_BUTTON_LEFTSTICK + ICON_PF_RIGHT_ANALOG_CLICK, // SDL_CONTROLLER_BUTTON_RIGHTSTICK + ICON_PF_LEFT_SHOULDER_LB, // SDL_CONTROLLER_BUTTON_LEFTSHOULDER + ICON_PF_RIGHT_SHOULDER_RB, // SDL_CONTROLLER_BUTTON_RIGHTSHOULDER + ICON_PF_XBOX_DPAD_UP, // SDL_CONTROLLER_BUTTON_DPAD_UP + ICON_PF_XBOX_DPAD_DOWN, // SDL_CONTROLLER_BUTTON_DPAD_DOWN + ICON_PF_XBOX_DPAD_LEFT, // SDL_CONTROLLER_BUTTON_DPAD_LEFT + ICON_PF_XBOX_DPAD_RIGHT, // SDL_CONTROLLER_BUTTON_DPAD_RIGHT +}; static constexpr const GenericInputBinding s_sdl_generic_binding_button_mapping[] = { GenericInputBinding::Cross, // SDL_CONTROLLER_BUTTON_A GenericInputBinding::Circle, // SDL_CONTROLLER_BUTTON_B @@ -416,9 +443,9 @@ std::optional SDLInputSource::ParseKeyString(const std::string_ return std::nullopt; } -std::string SDLInputSource::ConvertKeyToString(InputBindingKey key) +TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key) { - std::string ret; + TinyString ret; if (key.source_type == InputSourceType::SDL) { @@ -428,40 +455,64 @@ std::string SDLInputSource::ConvertKeyToString(InputBindingKey key) (key.modifier == InputModifier::FullAxis ? "Full" : (key.modifier == InputModifier::Negate ? "-" : "+")); if (key.data < std::size(s_sdl_axis_names)) { - ret = fmt::format("SDL-{}/{}{}", static_cast(key.source_index), modifier, s_sdl_axis_names[key.data]); + ret.fmt("SDL-{}/{}{}", static_cast(key.source_index), modifier, s_sdl_axis_names[key.data]); } else { - ret = fmt::format("SDL-{}/{}Axis{}{}", static_cast(key.source_index), modifier, - key.data - static_cast(std::size(s_sdl_axis_names)), key.invert ? "~" : ""); + ret.fmt("SDL-{}/{}Axis{}{}", static_cast(key.source_index), modifier, + key.data - static_cast(std::size(s_sdl_axis_names)), key.invert ? "~" : ""); } } else if (key.source_subtype == InputSubclass::ControllerButton) { if (key.data < std::size(s_sdl_button_names)) { - ret = fmt::format("SDL-{}/{}", static_cast(key.source_index), s_sdl_button_names[key.data]); + ret.fmt("SDL-{}/{}", static_cast(key.source_index), s_sdl_button_names[key.data]); } else { - ret = fmt::format("SDL-{}/Button{}", static_cast(key.source_index), - key.data - static_cast(std::size(s_sdl_button_names))); + ret.fmt("SDL-{}/Button{}", static_cast(key.source_index), + key.data - static_cast(std::size(s_sdl_button_names))); } } else if (key.source_subtype == InputSubclass::ControllerHat) { const u32 hat_index = key.data / static_cast(std::size(s_sdl_hat_direction_names)); const u32 hat_direction = key.data % static_cast(std::size(s_sdl_hat_direction_names)); - ret = fmt::format("SDL-{}/Hat{}{}", static_cast(key.source_index), hat_index, - s_sdl_hat_direction_names[hat_direction]); + ret.fmt("SDL-{}/Hat{}{}", static_cast(key.source_index), hat_index, + s_sdl_hat_direction_names[hat_direction]); } else if (key.source_subtype == InputSubclass::ControllerMotor) { - ret = fmt::format("SDL-{}/{}Motor", static_cast(key.source_index), key.data ? "Large" : "Small"); + ret.fmt("SDL-{}/{}Motor", static_cast(key.source_index), key.data ? "Large" : "Small"); } else if (key.source_subtype == InputSubclass::ControllerHaptic) { - ret = fmt::format("SDL-{}/Haptic", static_cast(key.source_index)); + ret.fmt("SDL-{}/Haptic", static_cast(key.source_index)); + } + } + + return ret; +} + +TinyString SDLInputSource::ConvertKeyToIcon(InputBindingKey key) +{ + TinyString ret; + + if (key.source_type == InputSourceType::SDL) + { + if (key.source_subtype == InputSubclass::ControllerAxis) + { + if (key.data < std::size(s_sdl_axis_icons) && key.modifier != InputModifier::FullAxis) + { + ret.fmt("SDL-{} {}", static_cast(key.source_index), + s_sdl_axis_icons[key.data][key.modifier == InputModifier::None]); + } + } + else if (key.source_subtype == InputSubclass::ControllerButton) + { + if (key.data < std::size(s_sdl_button_icons)) + ret.fmt("SDL-{} {}", static_cast(key.source_index), s_sdl_button_icons[key.data]); } } diff --git a/src/util/sdl_input_source.h b/src/util/sdl_input_source.h index 5cd1db13f..3c8aebab8 100644 --- a/src/util/sdl_input_source.h +++ b/src/util/sdl_input_source.h @@ -34,7 +34,8 @@ public: std::optional ParseKeyString(const std::string_view& device, const std::string_view& binding) override; - std::string ConvertKeyToString(InputBindingKey key) override; + TinyString ConvertKeyToString(InputBindingKey key) override; + TinyString ConvertKeyToIcon(InputBindingKey key) override; bool ProcessSDLEvent(const SDL_Event* event); diff --git a/src/util/win32_raw_input_source.cpp b/src/util/win32_raw_input_source.cpp index 8bdc015ab..ade9f59b7 100644 --- a/src/util/win32_raw_input_source.cpp +++ b/src/util/win32_raw_input_source.cpp @@ -89,7 +89,12 @@ std::optional Win32RawInputSource::ParseKeyString(const std::st return std::nullopt; } -std::string Win32RawInputSource::ConvertKeyToString(InputBindingKey key) +TinyString Win32RawInputSource::ConvertKeyToString(InputBindingKey key) +{ + return {}; +} + +TinyString Win32RawInputSource::ConvertKeyToIcon(InputBindingKey key) { return {}; } diff --git a/src/util/win32_raw_input_source.h b/src/util/win32_raw_input_source.h index 7cc09cfcb..4ed8833c4 100644 --- a/src/util/win32_raw_input_source.h +++ b/src/util/win32_raw_input_source.h @@ -32,7 +32,8 @@ public: std::optional ParseKeyString(const std::string_view& device, const std::string_view& binding) override; - std::string ConvertKeyToString(InputBindingKey key) override; + TinyString ConvertKeyToString(InputBindingKey key) override; + TinyString ConvertKeyToIcon(InputBindingKey key) override; private: struct MouseState diff --git a/src/util/xinput_source.cpp b/src/util/xinput_source.cpp index 36f351674..675de0ec8 100644 --- a/src/util/xinput_source.cpp +++ b/src/util/xinput_source.cpp @@ -1,16 +1,20 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "xinput_source.h" +#include "input_manager.h" + #include "common/assert.h" #include "common/log.h" #include "common/string_util.h" -#include "core/host.h" -#include "input_manager.h" + +#include "IconsPromptFont.h" + #include + Log_SetChannel(XInputSource); -const char* XInputSource::s_axis_names[XInputSource::NUM_AXES] = { +static const char* s_axis_names[XInputSource::NUM_AXES] = { "LeftX", // AXIS_LEFTX "LeftY", // AXIS_LEFTY "RightX", // AXIS_RIGHTX @@ -18,6 +22,14 @@ const char* XInputSource::s_axis_names[XInputSource::NUM_AXES] = { "LeftTrigger", // AXIS_TRIGGERLEFT "RightTrigger", // AXIS_TRIGGERRIGHT }; +static constexpr const char* s_axis_icons[][2] = { + {ICON_PF_LEFT_ANALOG_LEFT, ICON_PF_LEFT_ANALOG_RIGHT}, // AXIS_LEFTX + {ICON_PF_LEFT_ANALOG_UP, ICON_PF_LEFT_ANALOG_DOWN}, // AXIS_LEFTY + {ICON_PF_RIGHT_ANALOG_LEFT, ICON_PF_RIGHT_ANALOG_RIGHT}, // AXIS_RIGHTX + {ICON_PF_RIGHT_ANALOG_UP, ICON_PF_RIGHT_ANALOG_DOWN}, // AXIS_RIGHTY + {nullptr, ICON_PF_LEFT_TRIGGER_PULL}, // AXIS_TRIGGERLEFT + {nullptr, ICON_PF_RIGHT_TRIGGER_PULL}, // AXIS_TRIGGERRIGHT +}; static const GenericInputBinding s_xinput_generic_binding_axis_mapping[][2] = { {GenericInputBinding::LeftStickLeft, GenericInputBinding::LeftStickRight}, // AXIS_LEFTX {GenericInputBinding::LeftStickUp, GenericInputBinding::LeftStickDown}, // AXIS_LEFTY @@ -27,7 +39,7 @@ static const GenericInputBinding s_xinput_generic_binding_axis_mapping[][2] = { {GenericInputBinding::Unknown, GenericInputBinding::R2}, // AXIS_TRIGGERRIGHT }; -const char* XInputSource::s_button_names[XInputSource::NUM_BUTTONS] = { +static const char* s_button_names[XInputSource::NUM_BUTTONS] = { "DPadUp", // XINPUT_GAMEPAD_DPAD_UP "DPadDown", // XINPUT_GAMEPAD_DPAD_DOWN "DPadLeft", // XINPUT_GAMEPAD_DPAD_LEFT @@ -44,7 +56,7 @@ const char* XInputSource::s_button_names[XInputSource::NUM_BUTTONS] = { "Y", // XINPUT_GAMEPAD_Y "Guide", // XINPUT_GAMEPAD_GUIDE }; -const u16 XInputSource::s_button_masks[XInputSource::NUM_BUTTONS] = { +static const u16 s_button_masks[XInputSource::NUM_BUTTONS] = { XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, @@ -61,6 +73,23 @@ const u16 XInputSource::s_button_masks[XInputSource::NUM_BUTTONS] = { XINPUT_GAMEPAD_Y, 0x400, // XINPUT_GAMEPAD_GUIDE }; +static constexpr const char* s_button_icons[] = { + ICON_PF_XBOX_DPAD_UP, // XINPUT_GAMEPAD_DPAD_UP + ICON_PF_XBOX_DPAD_DOWN, // XINPUT_GAMEPAD_DPAD_DOWN + ICON_PF_XBOX_DPAD_LEFT, // XINPUT_GAMEPAD_DPAD_LEFT + ICON_PF_XBOX_DPAD_RIGHT, // XINPUT_GAMEPAD_DPAD_RIGHT + ICON_PF_BURGER_MENU, // XINPUT_GAMEPAD_START + ICON_PF_SHARE_CAPTURE, // XINPUT_GAMEPAD_BACK + ICON_PF_LEFT_ANALOG_CLICK, // XINPUT_GAMEPAD_LEFT_THUMB + ICON_PF_RIGHT_ANALOG_CLICK, // XINPUT_GAMEPAD_RIGHT_THUMB + ICON_PF_LEFT_SHOULDER_LB, // XINPUT_GAMEPAD_LEFT_SHOULDER + ICON_PF_RIGHT_SHOULDER_RB, // XINPUT_GAMEPAD_RIGHT_SHOULDER + ICON_PF_BUTTON_A, // XINPUT_GAMEPAD_A + ICON_PF_BUTTON_B, // XINPUT_GAMEPAD_B + ICON_PF_BUTTON_X, // XINPUT_GAMEPAD_X + ICON_PF_BUTTON_Y, // XINPUT_GAMEPAD_Y + ICON_PF_XBOX, // XINPUT_GAMEPAD_GUIDE +}; static const GenericInputBinding s_xinput_generic_binding_button_mapping[] = { GenericInputBinding::DPadUp, // XINPUT_GAMEPAD_DPAD_UP GenericInputBinding::DPadDown, // XINPUT_GAMEPAD_DPAD_DOWN @@ -287,24 +316,48 @@ std::optional XInputSource::ParseKeyString(const std::string_vi return std::nullopt; } -std::string XInputSource::ConvertKeyToString(InputBindingKey key) +TinyString XInputSource::ConvertKeyToString(InputBindingKey key) { - std::string ret; + TinyString ret; if (key.source_type == InputSourceType::XInput) { if (key.source_subtype == InputSubclass::ControllerAxis && key.data < std::size(s_axis_names)) { const char modifier = key.modifier == InputModifier::Negate ? '-' : '+'; - ret = fmt::format("XInput-{}/{}{}", static_cast(key.source_index), modifier, s_axis_names[key.data]); + ret.fmt("XInput-{}/{}{}", static_cast(key.source_index), modifier, s_axis_names[key.data]); } else if (key.source_subtype == InputSubclass::ControllerButton && key.data < std::size(s_button_names)) { - ret = fmt::format("XInput-{}/{}", static_cast(key.source_index), s_button_names[key.data]); + ret.fmt("XInput-{}/{}", static_cast(key.source_index), s_button_names[key.data]); } else if (key.source_subtype == InputSubclass::ControllerMotor) { - ret = fmt::format("XInput-{}/{}Motor", static_cast(key.source_index), key.data ? "Large" : "Small"); + ret.fmt("XInput-{}/{}Motor", static_cast(key.source_index), key.data ? "Large" : "Small"); + } + } + + return ret; +} + +TinyString XInputSource::ConvertKeyToIcon(InputBindingKey key) +{ + TinyString ret; + + if (key.source_type == InputSourceType::SDL) + { + if (key.source_subtype == InputSubclass::ControllerAxis) + { + if (key.data < std::size(s_axis_icons) && key.modifier != InputModifier::FullAxis) + { + ret.fmt("XInput-{} {}", static_cast(key.source_index), + s_axis_icons[key.data][key.modifier == InputModifier::None]); + } + } + else if (key.source_subtype == InputSubclass::ControllerButton) + { + if (key.data < std::size(s_button_icons)) + ret.fmt("XInput-{} {}", static_cast(key.source_index), s_button_icons[key.data]); } } @@ -384,8 +437,7 @@ void XInputSource::HandleControllerConnection(u32 index) cd.has_small_motor = caps.Vibration.wRightMotorSpeed != 0; cd.last_state = {}; - InputManager::OnInputDeviceConnected(fmt::format("XInput-{}", index), - fmt::format("XInput Controller {}", index)); + InputManager::OnInputDeviceConnected(fmt::format("XInput-{}", index), fmt::format("XInput Controller {}", index)); } void XInputSource::HandleControllerDisconnection(u32 index) diff --git a/src/util/xinput_source.h b/src/util/xinput_source.h index ec06aa791..f52ca32c8 100644 --- a/src/util/xinput_source.h +++ b/src/util/xinput_source.h @@ -15,6 +15,23 @@ class SettingsInterface; class XInputSource final : public InputSource { public: + enum : u32 + { + NUM_CONTROLLERS = XUSER_MAX_COUNT, // 4 + NUM_BUTTONS = 15, + }; + + enum : u32 + { + AXIS_LEFTX, + AXIS_LEFTY, + AXIS_RIGHTX, + AXIS_RIGHTY, + AXIS_LEFTTRIGGER, + AXIS_RIGHTTRIGGER, + NUM_AXES, + }; + XInputSource(); ~XInputSource(); @@ -33,26 +50,10 @@ public: std::optional ParseKeyString(const std::string_view& device, const std::string_view& binding) override; - std::string ConvertKeyToString(InputBindingKey key) override; + TinyString ConvertKeyToString(InputBindingKey key) override; + TinyString ConvertKeyToIcon(InputBindingKey key) override; private: - enum : u32 - { - NUM_CONTROLLERS = XUSER_MAX_COUNT, // 4 - NUM_BUTTONS = 15, - }; - - enum : u32 - { - AXIS_LEFTX, - AXIS_LEFTY, - AXIS_RIGHTX, - AXIS_RIGHTY, - AXIS_LEFTTRIGGER, - AXIS_RIGHTTRIGGER, - NUM_AXES, - }; - struct ControllerData { XINPUT_STATE last_state; @@ -74,8 +75,4 @@ private: DWORD(WINAPI* m_xinput_get_state)(DWORD, XINPUT_STATE*) = nullptr; DWORD(WINAPI* m_xinput_set_state)(DWORD, XINPUT_VIBRATION*) = nullptr; DWORD(WINAPI* m_xinput_get_capabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*) = nullptr; - - static const char* s_axis_names[NUM_AXES]; - static const char* s_button_names[NUM_BUTTONS]; - static const u16 s_button_masks[NUM_BUTTONS]; };