From af1a4ab07a6a9cc4360edf84ce84912315d24631 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 11 Dec 2023 01:28:55 +1000 Subject: [PATCH] FullscreenUI: Use PromptFont for keyboard/controller icons --- 3rdparty/include/IconsPromptFont.h | 328 +++++++ bin/resources/fonts/promptfont-license | 91 ++ bin/resources/fonts/promptfont.otf | Bin 0 -> 202632 bytes pcsx2-gsrunner/Main.cpp | 5 + pcsx2-qt/QtKeyCodes.cpp | 900 +++++++++--------- pcsx2/Config.h | 1 + pcsx2/GS/GS.cpp | 9 +- pcsx2/GS/GS.h | 5 +- pcsx2/ImGui/FullscreenUI.cpp | 154 ++- pcsx2/ImGui/ImGuiFullscreen.cpp | 1 - pcsx2/ImGui/ImGuiFullscreen.h | 1 + pcsx2/ImGui/ImGuiManager.cpp | 59 +- pcsx2/ImGui/ImGuiOverlays.cpp | 64 +- pcsx2/Input/DInputSource.cpp | 16 +- pcsx2/Input/DInputSource.h | 3 +- pcsx2/Input/InputManager.cpp | 116 ++- pcsx2/Input/InputManager.h | 6 + pcsx2/Input/InputSource.h | 4 +- pcsx2/Input/SDLInputSource.cpp | 107 ++- pcsx2/Input/SDLInputSource.h | 3 +- pcsx2/Input/XInputSource.cpp | 72 +- pcsx2/Input/XInputSource.h | 45 +- pcsx2/SIO/Pad/PadDualshock2.cpp | 60 +- pcsx2/SIO/Pad/PadGuitar.cpp | 24 +- pcsx2/SIO/Pad/PadNotConnected.cpp | 2 +- pcsx2/SIO/Pad/PadTypes.h | 1 + pcsx2/USB/usb-hid/usb-hid.cpp | 10 +- pcsx2/USB/usb-lightgun/guncon2.cpp | 32 +- pcsx2/USB/usb-pad/usb-pad.cpp | 212 ++--- pcsx2/USB/usb-pad/usb-seamic.cpp | 36 +- pcsx2/USB/usb-pad/usb-turntable.cpp | 48 +- tests/ctest/core/StubHost.cpp | 5 + ...erate_fullscreen_ui_translation_strings.py | 2 +- tools/generate_update_fa_glyph_ranges.py | 71 +- 34 files changed, 1637 insertions(+), 856 deletions(-) create mode 100644 3rdparty/include/IconsPromptFont.h create mode 100644 bin/resources/fonts/promptfont-license create mode 100644 bin/resources/fonts/promptfont.otf diff --git a/3rdparty/include/IconsPromptFont.h b/3rdparty/include/IconsPromptFont.h new file mode 100644 index 0000000000..d627eee353 --- /dev/null +++ b/3rdparty/include/IconsPromptFont.h @@ -0,0 +1,328 @@ +#pragma once +#define ICON_PF_EXCHANGE "\xE2\x86\x94" +#define ICON_PF_REVERSE "\xE2\x86\x95" +#define ICON_PF_LEFT_TRIGGER_LT "\xE2\x86\x96" +#define ICON_PF_RIGHT_TRIGGER_RT "\xE2\x86\x97" +#define ICON_PF_LEFT_SHOULDER_LB "\xE2\x86\x98" +#define ICON_PF_RIGHT_SHOULDER_RB "\xE2\x86\x99" +#define ICON_PF_LEFT_TRIGGER_ZL "\xE2\x86\x9A" +#define ICON_PF_RIGHT_TRIGGER_ZR "\xE2\x86\x9B" +#define ICON_PF_LEFT_SHOULDER_L "\xE2\x86\x9C" +#define ICON_PF_RIGHT_SHOULDER_R "\xE2\x86\x9D" +#define ICON_PF_DPAD_LEFT "\xE2\x86\x9E" +#define ICON_PF_DPAD_UP "\xE2\x86\x9F" +#define ICON_PF_DPAD_RIGHT "\xE2\x86\xA0" +#define ICON_PF_DPAD_DOWN "\xE2\x86\xA1" +#define ICON_PF_DPAD_LEFT_RIGHT "\xE2\x86\xA2" +#define ICON_PF_DPAD_UP_DOWN "\xE2\x86\xA3" +#define ICON_PF_BUTTON_LEFT_X "\xE2\x86\xA4" +#define ICON_PF_BUTTON_UP_Y "\xE2\x86\xA5" +#define ICON_PF_BUTTON_RIGHT_B "\xE2\x86\xA6" +#define ICON_PF_BUTTON_DOWN_A "\xE2\x86\xA7" +#define ICON_PF_LEFT_ANALOG_CLOCKWISE "\xE2\x86\xA9" +#define ICON_PF_LEFT_ANALOG_COUNTER "\xE2\x86\xAA" +#define ICON_PF_RIGHT_ANALOG_CLOCKWISE "\xE2\x86\xAB" +#define ICON_PF_RIGHT_ANALOG_COUNTER "\xE2\x86\xAC" +#define ICON_PF_BOTH_ANALOG_CLOCKWISE "\xE2\x86\xAD" +#define ICON_PF_BOTH_ANALOG_COUNTER "\xE2\x86\xAE" +#define ICON_PF_LEFT_SHOULDER_L1 "\xE2\x86\xB0" +#define ICON_PF_RIGHT_SHOULDER_R1 "\xE2\x86\xB1" +#define ICON_PF_LEFT_TRIGGER_L2 "\xE2\x86\xB2" +#define ICON_PF_RIGHT_TRIGGER_R2 "\xE2\x86\xB3" +#define ICON_PF_DPAD_LEFT_DOWN "\xE2\x86\xB4" +#define ICON_PF_DPAD_UP_RIGHT "\xE2\x86\xB5" +#define ICON_PF_ANALOG_CLOCKWISE "\xE2\x86\xB6" +#define ICON_PF_ANALOG_COUNTER "\xE2\x86\xB7" +#define ICON_PF_BOTH_ANALOG_CLICK "\xE2\x86\xB9" +#define ICON_PF_LEFT_ANALOG_CLICK "\xE2\x86\xBA" +#define ICON_PF_RIGHT_ANALOG_CLICK "\xE2\x86\xBB" +#define ICON_PF_LEFT_ANALOG_LEFT "\xE2\x86\xBC" +#define ICON_PF_RIGHT_ANALOG_LEFT "\xE2\x86\xBD" +#define ICON_PF_LEFT_ANALOG_UP "\xE2\x86\xBE" +#define ICON_PF_RIGHT_ANALOG_UP "\xE2\x86\xBF" +#define ICON_PF_LEFT_ANALOG_RIGHT "\xE2\x87\x80" +#define ICON_PF_RIGHT_ANALOG_RIGHT "\xE2\x87\x81" +#define ICON_PF_LEFT_ANALOG_DOWN "\xE2\x87\x82" +#define ICON_PF_RIGHT_ANALOG_DOWN "\xE2\x87\x83" +#define ICON_PF_LEFT_ANALOG_LEFT_RIGHT "\xE2\x87\x84" +#define ICON_PF_LEFT_ANALOG_UP_DOWN "\xE2\x87\x85" +#define ICON_PF_RIGHT_ANALOG_LEFT_RIGHT "\xE2\x87\x86" +#define ICON_PF_ANALOG_LEFT "\xE2\x87\x87" +#define ICON_PF_ANALOG_UP "\xE2\x87\x88" +#define ICON_PF_ANALOG_RIGHT "\xE2\x87\x89" +#define ICON_PF_ANALOG_DOWN "\xE2\x87\x8A" +#define ICON_PF_LEFT_ANALOG "\xE2\x87\x8B" +#define ICON_PF_RIGHT_ANALOG "\xE2\x87\x8C" +#define ICON_PF_DPAD "\xE2\x87\x8E" +#define ICON_PF_BUTTON_X "\xE2\x87\x90" +#define ICON_PF_BUTTON_Y "\xE2\x87\x91" +#define ICON_PF_BUTTON_B "\xE2\x87\x92" +#define ICON_PF_BUTTON_A "\xE2\x87\x93" +#define ICON_PF_ANALOG_LEFT_RIGHT "\xE2\x87\x94" +#define ICON_PF_ANALOG_UP_DOWN "\xE2\x87\x95" +#define ICON_PF_ANALOG_UP_LEFT "\xE2\x87\x96" +#define ICON_PF_ANALOG_UP_RIGHT "\xE2\x87\x97" +#define ICON_PF_ANALOG_DOWN_RIGHT "\xE2\x87\x98" +#define ICON_PF_ANALOG_DOWN_LEFT "\xE2\x87\x99" +#define ICON_PF_LEFT_ANALOG_TOUCH "\xE2\x87\x9A" +#define ICON_PF_RIGHT_ANALOG_TOUCH "\xE2\x87\x9B" +#define ICON_PF_LEFT_TRIGGER_PULL "\xE2\x87\x9C" +#define ICON_PF_RIGHT_TRIGGER_PULL "\xE2\x87\x9D" +#define ICON_PF_DPAD_RIGHT_DOWN "\xE2\x87\x9E" +#define ICON_PF_DPAD_LEFT_UP "\xE2\x87\x9F" +#define ICON_PF_BUTTON_SQUARE "\xE2\x87\xA0" +#define ICON_PF_BUTTON_TRIANGLE "\xE2\x87\xA1" +#define ICON_PF_BUTTON_CIRCLE "\xE2\x87\xA2" +#define ICON_PF_BUTTON_CROSS "\xE2\x87\xA3" +#define ICON_PF_STEAM_MENU "\xE2\x87\xA4" +#define ICON_PF_OPTIONS_MENU "\xE2\x87\xA5" +#define ICON_PF_DUALSHOCK_SHARE "\xE2\x87\xA6" +#define ICON_PF_DUALSHOCK_TOUCHPAD "\xE2\x87\xA7" +#define ICON_PF_DUALSHOCK_OPTIONS "\xE2\x87\xA8" +#define ICON_PF_GAMECUBE_Z "\xE2\x87\xA9" +#define ICON_PF_BACK_TRIGGER_Z "\xE2\x87\xAA" +#define ICON_PF_BUTTON_C "\xE2\x87\xAB" +#define ICON_PF_BUTTON_Z "\xE2\x87\xAC" +#define ICON_PF_BUTTON_ALT_1 "\xE2\x87\xAD" +#define ICON_PF_BUTTON_ALT_2 "\xE2\x87\xAE" +#define ICON_PF_LEFT_ANALOG_ANY "\xE2\x87\xB1" +#define ICON_PF_RIGHT_ANALOG_ANY "\xE2\x87\xB2" +#define ICON_PF_ANALOG_ANY "\xE2\x87\xB3" +#define ICON_PF_RIGHT_ANALOG_UP_DOWN "\xE2\x87\xB5" +#define ICON_PF_SELECT_SHARE "\xE2\x87\xB7" +#define ICON_PF_START "\xE2\x87\xB8" +#define ICON_PF_HOME_MENU "\xE2\x87\xB9" +#define ICON_PF_SHARE_CAPTURE "\xE2\x87\xBA" +#define ICON_PF_BURGER_MENU "\xE2\x87\xBB" +#define ICON_PF_MINUS "\xE2\x87\xBD" +#define ICON_PF_PLUS "\xE2\x87\xBE" +#define ICON_PF_JOYCON_DPAD_LEFT "\xE2\x87\xBF" +#define ICON_PF_JOYCON_DPAD_UP "\xE2\x88\x80" +#define ICON_PF_JOYCON_DPAD_RIGHT "\xE2\x88\x81" +#define ICON_PF_JOYCON_DPAD_DOWN "\xE2\x88\x82" +#define ICON_PF_JOYCON_SL "\xE2\x88\x83" +#define ICON_PF_JOYCON_SR "\xE2\x88\x84" +#define ICON_PF_LENOVO_LEGION_QUICK_SETTINGS "\xE2\x88\x85" +#define ICON_PF_DUALSENSE_SHARE "\xE2\x88\x86" +#define ICON_PF_DUALSENSE_TOUCHPAD "\xE2\x88\x87" +#define ICON_PF_DUALSENSE_OPTIONS "\xE2\x88\x88" +#define ICON_PF_AYANEO_LC "\xE2\x88\x89" +#define ICON_PF_AYANEO_RC "\xE2\x88\x8A" +#define ICON_PF_AYANEO_WAVE "\xE2\x88\x8B" +#define ICON_PF_AYN_HOME "\xE2\x88\x8C" +#define ICON_PF_AYN_LCC "\xE2\x88\x8D" +#define ICON_PF_GPD_C1 "\xE2\x88\x8E" +#define ICON_PF_GPD_C2 "\xE2\x88\x8F" +#define ICON_PF_ONEXPLAYER_KEYBOARD "\xE2\x88\x90" +#define ICON_PF_ONEXPLAYER_TURBO "\xE2\x88\x91" +#define ICON_PF_ROG_ALLY_M1 "\xE2\x88\x92" +#define ICON_PF_ROG_ALLY_M2 "\xE2\x88\x93" +#define ICON_PF_LEFT_TRACKPAD_ANY "\xE2\x89\xA4" +#define ICON_PF_RIGHT_TRACKPAD_ANY "\xE2\x89\xA5" +#define ICON_PF_LEFT_TRACKPAD_CLICK "\xE2\x89\xA6" +#define ICON_PF_RIGHT_TRACKPAD_CLICK "\xE2\x89\xA7" +#define ICON_PF_LEFT_TRACKPAD_TOUCH "\xE2\x89\xA8" +#define ICON_PF_RIGHT_TRACKPAD_TOUCH "\xE2\x89\xA9" +#define ICON_PF_LEFT_TRACKPAD_LEFT "\xE2\x89\xAE" +#define ICON_PF_RIGHT_TRACKPAD_LEFT "\xE2\x89\xAF" +#define ICON_PF_LEFT_TRACKPAD_UP "\xE2\x89\xB0" +#define ICON_PF_RIGHT_TRACKPAD_UP "\xE2\x89\xB1" +#define ICON_PF_LEFT_TRACKPAD_RIGHT "\xE2\x89\xB2" +#define ICON_PF_RIGHT_TRACKPAD_RIGHT "\xE2\x89\xB3" +#define ICON_PF_LEFT_TRACKPAD_DOWN "\xE2\x89\xB4" +#define ICON_PF_RIGHT_TRACKPAD_DOWN "\xE2\x89\xB5" +#define ICON_PF_STEAMDECK_L4 "\xE2\x89\xB6" +#define ICON_PF_STEAMDECK_R4 "\xE2\x89\xB7" +#define ICON_PF_STEAMDECK_L5 "\xE2\x89\xB8" +#define ICON_PF_STEAMDECK_R5 "\xE2\x89\xB9" +#define ICON_PF_XBOX_DPAD_LEFT "\xE2\x89\xBA" +#define ICON_PF_XBOX_DPAD_UP "\xE2\x89\xBB" +#define ICON_PF_XBOX_DPAD_RIGHT "\xE2\x89\xBC" +#define ICON_PF_XBOX_DPAD_DOWN "\xE2\x89\xBD" +#define ICON_PF_XBOX_DPAD_LEFT_RIGHT "\xE2\x89\xBE" +#define ICON_PF_XBOX_DPAD_UP_DOWN "\xE2\x89\xBF" +#define ICON_PF_XBOX_DPAD_LEFT_UP "\xE2\x8A\x80" +#define ICON_PF_XBOX_DPAD_RIGHT_UP "\xE2\x8A\x81" +#define ICON_PF_XBOX_DPAD_LEFT_DOWN "\xE2\x8A\x82" +#define ICON_PF_XBOX_DPAD_RIGHT_DOWN "\xE2\x8A\x83" +#define ICON_PF_XBOX_DPAD "\xE2\x8A\x84" +#define ICON_PF_PIN "\xE2\x8C\x96" +#define ICON_PF_TABS "\xE2\x8F\x8D" +#define ICON_PF_BACK "\xE2\x8F\x8E" +#define ICON_PF_HOME_SCREEN "\xE2\x8F\x8F" +#define ICON_PF_HORIZONTAL_DOTS "\xE2\x8F\x90" +#define ICON_PF_VERTICAL_DOTS "\xE2\x8F\x91" +#define ICON_PF_HAMBURGER_MENU "\xE2\x8F\x92" +#define ICON_PF_ARROW_LEFT "\xE2\x8F\xB4" +#define ICON_PF_ARROW_UP "\xE2\x8F\xB5" +#define ICON_PF_ARROW_RIGHT "\xE2\x8F\xB6" +#define ICON_PF_ARROW_DOWN "\xE2\x8F\xB7" +#define ICON_PF_WASD "\xE2\x90\xA3" +#define ICON_PF_ARROW_KEYS "\xE2\x90\xA4" +#define ICON_PF_IJKL "\xE2\x90\xA5" +#define ICON_PF_FN "\xE2\x90\xA6" +#define ICON_PF_CTRL "\xE2\x90\xA7" +#define ICON_PF_ALT "\xE2\x90\xA8" +#define ICON_PF_SHIFT "\xE2\x90\xA9" +#define ICON_PF_SUPER "\xE2\x90\xAA" +#define ICON_PF_TAB "\xE2\x90\xAB" +#define ICON_PF_CAPS "\xE2\x90\xAC" +#define ICON_PF_BACKSPACE "\xE2\x90\xAD" +#define ICON_PF_ENTER "\xE2\x90\xAE" +#define ICON_PF_ESC "\xE2\x90\xAF" +#define ICON_PF_PRTSC "\xE2\x90\xB0" +#define ICON_PF_SCRLK "\xE2\x90\xB1" +#define ICON_PF_PAUSE "\xE2\x90\xB2" +#define ICON_PF_NUMLOCK "\xE2\x90\xB3" +#define ICON_PF_INSERT "\xE2\x90\xB4" +#define ICON_PF_HOME "\xE2\x90\xB5" +#define ICON_PF_PAGE_UP "\xE2\x90\xB6" +#define ICON_PF_DELETE "\xE2\x90\xB7" +#define ICON_PF_END "\xE2\x90\xB8" +#define ICON_PF_PAGE_DOWN "\xE2\x90\xB9" +#define ICON_PF_SPACE "\xE2\x90\xBA" +#define ICON_PF_GAMEPAD "\xE2\x90\xBC" +#define ICON_PF_KEYBOARD "\xE2\x90\xBD" +#define ICON_PF_MOUSE "\xE2\x90\xBE" +#define ICON_PF_MOUSE_AND_KEYBOARD "\xE2\x90\xBF" +#define ICON_PF_F1 "\xE2\x91\xA0" +#define ICON_PF_F2 "\xE2\x91\xA1" +#define ICON_PF_F3 "\xE2\x91\xA2" +#define ICON_PF_F4 "\xE2\x91\xA3" +#define ICON_PF_F5 "\xE2\x91\xA4" +#define ICON_PF_F6 "\xE2\x91\xA5" +#define ICON_PF_F7 "\xE2\x91\xA6" +#define ICON_PF_F8 "\xE2\x91\xA7" +#define ICON_PF_F9 "\xE2\x91\xA8" +#define ICON_PF_F10 "\xE2\x91\xA9" +#define ICON_PF_F11 "\xE2\x91\xAA" +#define ICON_PF_F12 "\xE2\x91\xAB" +#define ICON_PF_EMPTY_KEYCAP "\xE2\x92\x8F" +#define ICON_PF_1 "\xE2\x93\xB5" +#define ICON_PF_2 "\xE2\x93\xB6" +#define ICON_PF_3 "\xE2\x93\xB7" +#define ICON_PF_4 "\xE2\x93\xB8" +#define ICON_PF_5 "\xE2\x93\xB9" +#define ICON_PF_6 "\xE2\x93\xBA" +#define ICON_PF_7 "\xE2\x93\xBB" +#define ICON_PF_8 "\xE2\x93\xBC" +#define ICON_PF_9 "\xE2\x93\xBD" +#define ICON_PF_0 "\xE2\x93\xBF" +#define ICON_PF_STAR "\xE2\x98\x85" +#define ICON_PF_SKULL "\xE2\x98\xA0" +#define ICON_PF_FROWN "\xE2\x98\xB9" +#define ICON_PF_SMILE "\xE2\x98\xBA" +#define ICON_PF_EMPTY_HEART "\xE2\x99\xA1" +#define ICON_PF_HEART "\xE2\x99\xA5" +#define ICON_PF_D4 "\xE2\x99\xB3" +#define ICON_PF_D6 "\xE2\x99\xB4" +#define ICON_PF_D8 "\xE2\x99\xB5" +#define ICON_PF_D10 "\xE2\x99\xB6" +#define ICON_PF_D12 "\xE2\x99\xB7" +#define ICON_PF_D20 "\xE2\x99\xB8" +#define ICON_PF_D6_1 "\xE2\x9A\x80" +#define ICON_PF_D6_2 "\xE2\x9A\x81" +#define ICON_PF_D6_3 "\xE2\x9A\x82" +#define ICON_PF_D6_4 "\xE2\x9A\x83" +#define ICON_PF_D6_5 "\xE2\x9A\x84" +#define ICON_PF_D6_6 "\xE2\x9A\x85" +#define ICON_PF_FLAG "\xE2\x9A\x91" +#define ICON_PF_GEARS_OPTIONS_SETTINGS "\xE2\x9A\x99" +#define ICON_PF_CROSS "\xE2\x9C\x97" +#define ICON_PF_QUESTION "\xE2\x9D\x93" +#define ICON_PF_EXCLAMATION "\xE2\x9D\x97" +#define ICON_PF_MOUSE_BUTTON_1 "\xE2\x9E\x8A" +#define ICON_PF_MOUSE_BUTTON_2 "\xE2\x9E\x8B" +#define ICON_PF_MOUSE_BUTTON_3 "\xE2\x9E\x8C" +#define ICON_PF_MOUSE_BUTTON_4 "\xE2\x9E\x8D" +#define ICON_PF_MOUSE_BUTTON_5 "\xE2\x9E\x8E" +#define ICON_PF_MOUSE_BUTTON_6 "\xE2\x9E\x8F" +#define ICON_PF_MOUSE_BUTTON_7 "\xE2\x9E\x90" +#define ICON_PF_MOUSE_BUTTON_8 "\xE2\x9E\x91" +#define ICON_PF_SCROLL_UP "\xE2\x9F\xB0" +#define ICON_PF_SCROLL_DOWN "\xE2\x9F\xB1" +#define ICON_PF_LEFT_CLICK "\xE2\x9F\xB5" +#define ICON_PF_RIGHT_CLICK "\xE2\x9F\xB6" +#define ICON_PF_MIDDLE_CLICK "\xE2\x9F\xB7" +#define ICON_PF_MOUSE_LEFT_RIGHT "\xE2\x9F\xBA" +#define ICON_PF_MOUSE_UP_DOWN "\xE2\x9F\xBB" +#define ICON_PF_MOUSE_ANY "\xE2\x9F\xBC" +#define ICON_PF_BOX_CRATE "\xE2\xAC\x9B" +#define ICON_PF_PLAYSTATION "\xEE\x80\x80" +#define ICON_PF_XBOX "\xEE\x80\x81" +#define ICON_PF_NINTENDO_SWITCH "\xEE\x80\x82" +#define ICON_PF_AYANEO "\xEE\x80\x83" +#define ICON_PF_LENOVO_LEGION "\xEE\x80\x84" +#define ICON_PF_ROG_ALLY_ARMOURY "\xEE\x80\x85" +#define ICON_PF_ROG_ALLOY_COMMAND "\xEE\x80\x86" +#define ICON_PF_APPLE_MAC "\xEE\x80\x87" +#define ICON_PF_WINDOWS "\xEE\x80\x88" +#define ICON_PF_LINUX "\xEE\x80\x89" +#define ICON_PF_BSD "\xEE\x80\x8A" +#define ICON_PF_KEY_0 "\xEF\xBC\x90" +#define ICON_PF_KEY_1 "\xEF\xBC\x91" +#define ICON_PF_KEY_2 "\xEF\xBC\x92" +#define ICON_PF_KEY_3 "\xEF\xBC\x93" +#define ICON_PF_KEY_4 "\xEF\xBC\x94" +#define ICON_PF_KEY_5 "\xEF\xBC\x95" +#define ICON_PF_KEY_6 "\xEF\xBC\x96" +#define ICON_PF_KEY_7 "\xEF\xBC\x97" +#define ICON_PF_KEY_8 "\xEF\xBC\x98" +#define ICON_PF_KEY_9 "\xEF\xBC\x99" +#define ICON_PF_KEY_A "\xEF\xBC\xA1" +#define ICON_PF_KEY_B "\xEF\xBC\xA2" +#define ICON_PF_KEY_C "\xEF\xBC\xA3" +#define ICON_PF_KEY_D "\xEF\xBC\xA4" +#define ICON_PF_KEY_E "\xEF\xBC\xA5" +#define ICON_PF_KEY_F "\xEF\xBC\xA6" +#define ICON_PF_KEY_G "\xEF\xBC\xA7" +#define ICON_PF_KEY_H "\xEF\xBC\xA8" +#define ICON_PF_KEY_I "\xEF\xBC\xA9" +#define ICON_PF_KEY_J "\xEF\xBC\xAA" +#define ICON_PF_KEY_K "\xEF\xBC\xAB" +#define ICON_PF_KEY_L "\xEF\xBC\xAC" +#define ICON_PF_KEY_M "\xEF\xBC\xAD" +#define ICON_PF_KEY_N "\xEF\xBC\xAE" +#define ICON_PF_KEY_O "\xEF\xBC\xAF" +#define ICON_PF_KEY_P "\xEF\xBC\xB0" +#define ICON_PF_KEY_Q "\xEF\xBC\xB1" +#define ICON_PF_KEY_R "\xEF\xBC\xB2" +#define ICON_PF_KEY_S "\xEF\xBC\xB3" +#define ICON_PF_KEY_T "\xEF\xBC\xB4" +#define ICON_PF_KEY_U "\xEF\xBC\xB5" +#define ICON_PF_KEY_V "\xEF\xBC\xB6" +#define ICON_PF_KEY_W "\xEF\xBC\xB7" +#define ICON_PF_KEY_X "\xEF\xBC\xB8" +#define ICON_PF_KEY_Y "\xEF\xBC\xB9" +#define ICON_PF_KEY_Z "\xEF\xBC\xBA" +#define ICON_PF_HEADPHONES "\xF0\x9F\x8E\xA7" +#define ICON_PF_MUSIC "\xF0\x9F\x8E\xB6" +#define ICON_PF_FISH "\xF0\x9F\x90\x9F" +#define ICON_PF_DANCE_PAD "\xF0\x9F\x92\x83" +#define ICON_PF_LAPTOP "\xF0\x9F\x92\xBB" +#define ICON_PF_DISKETTE "\xF0\x9F\x92\xBE" +#define ICON_PF_WRITE "\xF0\x9F\x93\x9D" +#define ICON_PF_PHONE "\xF0\x9F\x93\xB1" +#define ICON_PF_CAMERA "\xF0\x9F\x93\xB7" +#define ICON_PF_SPEAKER "\xF0\x9F\x94\x88" +#define ICON_PF_LIGHT_GUN "\xF0\x9F\x94\xAB" +#define ICON_PF_SFX_SOUND_EFFECT_NOISE "\xF0\x9F\x95\xAC" +#define ICON_PF_STEERING_WHEEL "\xF0\x9F\x95\xB8" +#define ICON_PF_FIGHT_STICK_JOYSTICK "\xF0\x9F\x95\xB9" +#define ICON_PF_VR_HEADSET "\xF0\x9F\x95\xBB" +#define ICON_PF_VR_CONTROLLER "\xF0\x9F\x95\xBC" +#define ICON_PF_FLIGHT_STICK "\xF0\x9F\x95\xBD" +#define ICON_PF_CPU_PROCESSOR "\xF0\x9F\x96\xA5" +#define ICON_PF_WEB_INTERNET_LINK "\xF0\x9F\x96\xA7" +#define ICON_PF_GPU_GRAPHICS_CARD "\xF0\x9F\x96\xA8" +#define ICON_PF_RAM_MEMORY "\xF0\x9F\x96\xAA" +#define ICON_PF_USB_STICK "\xF0\x9F\x96\xAB" +#define ICON_PF_DATABASE "\xF0\x9F\x96\xAC" +#define ICON_PF_HARD_DISK_DRIVE "\xF0\x9F\x96\xB4" +#define ICON_PF_SCREEN_VIDEO "\xF0\x9F\x96\xB5" +#define ICON_PF_TEXT_ENTRY_EDIT "\xF0\x9F\x96\xB9" +#define ICON_PF_SPEAKING_VOICE "\xF0\x9F\x97\xA3" +#define ICON_PF_LANGUAGE "\xF0\x9F\x97\xA9" +#define ICON_PF_EXIT_QUIT_LEAVE "\xF0\x9F\x9A\xAA" +#define ICON_PF_INFORMATION "\xF0\x9F\x9B\x88" +#define ICON_PF_SHOPPING_CART "\xF0\x9F\x9B\x92" diff --git a/bin/resources/fonts/promptfont-license b/bin/resources/fonts/promptfont-license new file mode 100644 index 0000000000..ec29af783c --- /dev/null +++ b/bin/resources/fonts/promptfont-license @@ -0,0 +1,91 @@ +This Font Software is licensed under the SIL Open Font License, +Version 1.1. This license is copied below, and is also available +with a FAQ at + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/bin/resources/fonts/promptfont.otf b/bin/resources/fonts/promptfont.otf new file mode 100644 index 0000000000000000000000000000000000000000..9295e2e5180138e02126087326650618fce3cd94 GIT binary patch literal 202632 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 KR&}Kp4gMGW2(+vK literal 0 HcmV?d00001 diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index 222b7ac068..4f2a52c95c 100644 --- a/pcsx2-gsrunner/Main.cpp +++ b/pcsx2-gsrunner/Main.cpp @@ -393,6 +393,11 @@ std::optional InputManager::ConvertHostKeyboardCodeToString(u32 cod return std::nullopt; } +const char* InputManager::ConvertHostKeyboardCodeToIcon(u32 code) +{ + return nullptr; +} + BEGIN_HOTKEY_LIST(g_host_hotkeys) END_HOTKEY_LIST() diff --git a/pcsx2-qt/QtKeyCodes.cpp b/pcsx2-qt/QtKeyCodes.cpp index 324a026d0c..d10aa8a938 100644 --- a/pcsx2-qt/QtKeyCodes.cpp +++ b/pcsx2-qt/QtKeyCodes.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2022 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -21,457 +21,460 @@ #include "common/StringUtil.h" +#include "IconsPromptFont.h" + #include 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 const 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) { - std::string compare_name(str); + std::string_view compare_name = str; u32 modifier_bits = 0; if (StringUtil::StartsWith(compare_name, "Numpad")) { @@ -511,6 +514,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) { int key = ev->key(); diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 62a18c5cff..9ee68b253d 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -97,6 +97,7 @@ struct InputBindingInfo const char* name; const char* display_name; + const char* icon_name; Type bind_type; u16 bind_index; GenericInputBinding generic_mapping; diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index 5a9f1b1807..64b707c272 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -58,6 +58,7 @@ #include "common/Console.h" #include "common/FileSystem.h" #include "common/Path.h" +#include "common/SmallString.h" #include "common/StringUtil.h" #include "fmt/format.h" @@ -619,7 +620,7 @@ void GSgetInternalResolution(int* width, int* height) *height = res.y; } -void GSgetStats(std::string& info) +void GSgetStats(SmallStringBase& info) { GSPerfMon& pm = g_perfmon; const char* api_name = GSDevice::RenderAPIToString(g_gs_device->GetRenderAPI()); @@ -627,7 +628,7 @@ void GSgetStats(std::string& info) { const double fps = GetVerticalFrequency(); const double fillrate = pm.Get(GSPerfMon::Fillrate); - fmt::format_to(std::back_inserter(info), "{} SW | {} S | {} P | {} D | {:.2f} U | {:.2f} D | {:.2f} mpps", + info.fmt("{} SW | {} S | {} P | {} D | {:.2f} U | {:.2f} D | {:.2f} mpps", api_name, (int)pm.Get(GSPerfMon::SyncPoint), (int)pm.Get(GSPerfMon::Prim), @@ -642,7 +643,7 @@ void GSgetStats(std::string& info) } else { - fmt::format_to(std::back_inserter(info), "{} HW | {} P | {} D | {} DC | {} B | {} RP | {} RB | {} TC | {} TU", + info.fmt("{} HW | {} P | {} D | {} DC | {} B | {} RP | {} RB | {} TC | {} TU", api_name, (int)pm.Get(GSPerfMon::Prim), (int)pm.Get(GSPerfMon::Draw), @@ -655,7 +656,7 @@ void GSgetStats(std::string& info) } } -void GSgetMemoryStats(std::string& info) +void GSgetMemoryStats(SmallStringBase& info) { if (!g_texture_cache) return; diff --git a/pcsx2/GS/GS.h b/pcsx2/GS/GS.h index d44160a0dd..ff0a994f28 100644 --- a/pcsx2/GS/GS.h +++ b/pcsx2/GS/GS.h @@ -64,6 +64,7 @@ enum class GSDisplayAlignment extern Pcsx2Config::GSOptions GSConfig; class HostDisplay; +class SmallStringBase; // Returns the ID for the specified function, otherwise -1. s16 GSLookupGetSkipCountFunctionId(const std::string_view& name); @@ -107,8 +108,8 @@ void GSGetAdaptersAndFullscreenModes( GSRendererType renderer, std::vector* adapters, std::vector* fullscreen_modes); GSVideoMode GSgetDisplayMode(); void GSgetInternalResolution(int* width, int* height); -void GSgetStats(std::string& info); -void GSgetMemoryStats(std::string& info); +void GSgetStats(SmallStringBase& info); +void GSgetMemoryStats(SmallStringBase& info); void GSgetTitleStats(std::string& info); /// Converts window position to normalized display coordinates (0..1). A value less than 0 or greater than 1 is diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp index 8b5a958142..ebb0efb263 100644 --- a/pcsx2/ImGui/FullscreenUI.cpp +++ b/pcsx2/ImGui/FullscreenUI.cpp @@ -378,8 +378,7 @@ namespace FullscreenUI static void BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, const std::string_view& section, const std::string_view& key, const std::string_view& display_name); static void DrawInputBindingWindow(); - static void DrawInputBindingButton(SettingsInterface* bsi, InputBindingInfo::Type type, const char* section, const char* name, - const char* display_name, bool show_type = true); + static void DrawInputBindingButton(SettingsInterface* bsi, InputBindingInfo::Type type, const char* section, const char* name, const char* display_name, const char* icon_name, bool show_type = true); static void ClearInputBindingVariables(); static void StartAutomaticBinding(u32 port); static void DrawSettingInfoSetting(SettingsInterface* bsi, const char* section, const char* key, const SettingInfo& si, @@ -1155,53 +1154,87 @@ s32 FullscreenUI::GetEffectiveIntSetting(SettingsInterface* bsi, const char* sec } void FullscreenUI::DrawInputBindingButton( - SettingsInterface* bsi, InputBindingInfo::Type type, const char* section, const char* name, const char* display_name, bool show_type) + SettingsInterface* bsi, InputBindingInfo::Type type, const char* section, const char* name, const char* display_name, const char* icon_name, bool show_type) { - std::string title(fmt::format("{}/{}", section, name)); + TinyString title; + title.fmt("{}/{}", section, name); + + std::string value = bsi->GetStringValue(section, name); + const bool oneline = (std::count_if(value.begin(), value.end(), [](char ch) { return (ch == '&'); }) <= 1); ImRect bb; bool visible, hovered, clicked; - clicked = MenuButtonFrame(title.c_str(), true, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, &visible, &hovered, &bb.Min, &bb.Max); + clicked = MenuButtonFrame(title, true, + oneline ? ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY : + ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, + &visible, &hovered, &bb.Min, &bb.Max); if (!visible) return; - const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f); - const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint)); - const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max); + if (oneline) + InputManager::PrettifyInputBinding(value); if (show_type) { - switch (type) + if (icon_name) { - case InputBindingInfo::Type::Button: - title = fmt::format(ICON_FA_DOT_CIRCLE " {}", display_name); - break; - case InputBindingInfo::Type::Axis: - case InputBindingInfo::Type::HalfAxis: - title = fmt::format(ICON_FA_BULLSEYE " {}", display_name); - break; - case InputBindingInfo::Type::Motor: - title = fmt::format(ICON_FA_BELL " {}", display_name); - break; - case InputBindingInfo::Type::Macro: - title = fmt::format(ICON_FA_PIZZA_SLICE " {}", display_name); - break; - default: - title = display_name; - break; + title.fmt("{} {}", icon_name, display_name); + } + else + { + switch (type) + { + case InputBindingInfo::Type::Button: + title.fmt(ICON_FA_DOT_CIRCLE " {}", display_name); + break; + case InputBindingInfo::Type::Axis: + case InputBindingInfo::Type::HalfAxis: + title.fmt(ICON_FA_BULLSEYE " {}", display_name); + break; + case InputBindingInfo::Type::Motor: + title.fmt(ICON_FA_BELL " {}", display_name); + break; + case InputBindingInfo::Type::Macro: + title.fmt(ICON_FA_PIZZA_SLICE " {}", display_name); + break; + default: + title = display_name; + break; + } } } - ImGui::PushFont(g_large_font); - 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::PopFont(); + const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f); - const std::string value(bsi->GetStringValue(section, name)); - ImGui::PushFont(g_medium_font); - ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, value.empty() ? FSUI_CSTR("No Binding") : value.c_str(), nullptr, nullptr, - ImVec2(0.0f, 0.0f), &summary_bb); - ImGui::PopFont(); + if (oneline) + { + ImGui::PushFont(g_large_font); + + const ImVec2 value_size(ImGui::CalcTextSize(value.empty() ? FSUI_CSTR("-") : value.c_str(), nullptr)); + const float text_end = bb.Max.x - value_size.x; + const ImRect title_bb(bb.Min, ImVec2(text_end, midpoint)); + + 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::PopFont(); + } + else + { + const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint)); + const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max); + + ImGui::PushFont(g_large_font); + 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::PopFont(); + + ImGui::PushFont(g_medium_font); + ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, value.empty() ? FSUI_CSTR("No Binding") : value.c_str(), + nullptr, nullptr, ImVec2(0.0f, 0.0f), &summary_bb); + ImGui::PopFont(); + } if (clicked) { @@ -4023,7 +4056,7 @@ void FullscreenUI::DrawControllerSettingsPage() StartAutomaticBinding(global_slot); for (const InputBindingInfo& bi : ci->bindings) - DrawInputBindingButton(bsi, bi.bind_type, section, bi.name, Host::TranslateToCString("Pad", bi.display_name), true); + DrawInputBindingButton(bsi, bi.bind_type, section, bi.name, Host::TranslateToCString("Pad", bi.display_name), bi.icon_name, true); if (mtap_enabled[mtap_port]) { @@ -4047,11 +4080,28 @@ void FullscreenUI::DrawControllerSettingsPage() continue; DrawInputBindingButton( - bsi, InputBindingInfo::Type::Macro, section, TinyString::from_fmt("Macro{}", macro_index + 1), "Trigger"); + bsi, InputBindingInfo::Type::Macro, section, TinyString::from_fmt("Macro{}", macro_index + 1), "Trigger", nullptr); std::string binds_string(bsi->GetStringValue(section, fmt::format("Macro{}Binds", macro_index + 1).c_str())); - if (MenuButton(FSUI_ICONSTR(ICON_FA_KEYBOARD, "Buttons"), - binds_string.empty() ? FSUI_CSTR("No Buttons Selected") : binds_string.c_str())) + 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 InputBindingInfo& bi : ci->bindings) + { + if (bind == bi.name) + { + dispname = bi.icon_name ? bi.icon_name : Host::TranslateToCString("Pad", bi.display_name); + break; + } + } + pretty_binds_string.append_fmt("{}{}", pretty_binds_string.empty() ? "" : " ", dispname); + } + } + if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_KEYBOARD, "Buttons"), 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; @@ -4114,12 +4164,12 @@ void FullscreenUI::DrawControllerSettingsPage() } const TinyString freq_key = TinyString::from_fmt("Macro{}Frequency", macro_index + 1); - const TinyString freq_label = TinyString::from_fmt(FSUI_FSTR("Macro {} Frequency"), macro_index + 1); + const TinyString freq_label = TinyString::from_fmt(ICON_FA_CLOCK " {}##macro_{}_frequency", FSUI_VSTR("Frequency"), macro_index + 1); s32 frequency = bsi->GetIntValue(section, freq_key.c_str(), 0); const SmallString freq_summary = - ((frequency == 0) ? TinyString(FSUI_VSTR("Macro will not auto-toggle.")) : - TinyString::from_fmt(FSUI_FSTR("Macro will toggle every {} frames."), frequency)); - if (MenuButton(FSUI_ICONSTR(ICON_FA_LIGHTBULB, "Frequency"), freq_summary.c_str())) + ((frequency == 0) ? TinyString(FSUI_VSTR("Disabled")) : + TinyString::from_fmt(FSUI_FSTR("{} Frames"), frequency)); + if (MenuButtonWithValue(freq_label, FSUI_CSTR("Determines the frequency at which the macro will toggle the buttons on and off (aka auto fire)."), freq_summary, true)) ImGui::OpenPopup(freq_label.c_str()); const std::string pressure_key(fmt::format("Macro{}Pressure", macro_index + 1)); @@ -4264,7 +4314,7 @@ void FullscreenUI::DrawControllerSettingsPage() for (const InputBindingInfo& bi : bindings) { DrawInputBindingButton(bsi, bi.bind_type, section.c_str(), USB::GetConfigSubKey(type, bi.name).c_str(), - Host::TranslateToCString("USB", bi.display_name)); + Host::TranslateToCString("USB", bi.display_name), bi.icon_name); } } @@ -4299,7 +4349,7 @@ void FullscreenUI::DrawHotkeySettingsPage() } DrawInputBindingButton( - bsi, InputBindingInfo::Type::Button, "Hotkeys", hotkey->name, Host::TranslateToCString("Hotkeys", hotkey->display_name), false); + bsi, InputBindingInfo::Type::Button, "Hotkeys", hotkey->name, Host::TranslateToCString("Hotkeys", hotkey->display_name), nullptr, false); } EndMenuButtons(); @@ -5512,7 +5562,7 @@ void FullscreenUI::DrawGameListWindow() default: break; } - + if (VMManager::GetState() != VMState::Shutdown) { // Dummy window to prevent interacting with the game list while loading. @@ -6133,9 +6183,9 @@ void FullscreenUI::DrawAboutWindow() if (ImGui::BeginPopupModal(FSUI_CSTR("About PCSX2"), &s_about_window_open, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize)) { ImGui::TextWrapped("%s", FSUI_CSTR( - "PCSX2 is a free and open-source PlayStation 2 (PS2) emulator. Its purpose is to emulate the PS2's hardware, using a " - "combination of MIPS CPU Interpreters, Recompilers and a Virtual Machine which manages hardware states and PS2 system memory. " - "This allows you to play PS2 games on your PC, with many additional features and benefits.")); + "PCSX2 is a free and open-source PlayStation 2 (PS2) emulator. Its purpose is to emulate the PS2's hardware, using a " + "combination of MIPS CPU Interpreters, Recompilers and a Virtual Machine which manages hardware states and PS2 system memory. " + "This allows you to play PS2 games on your PC, with many additional features and benefits.")); ImGui::NewLine(); @@ -6428,6 +6478,7 @@ TRANSLATE_NOOP("FullscreenUI", "Start the console without any disc inserted."); TRANSLATE_NOOP("FullscreenUI", "Start a game from a disc in your PC's DVD drive."); TRANSLATE_NOOP("FullscreenUI", "Change settings for the emulator."); TRANSLATE_NOOP("FullscreenUI", "Exits the program."); +TRANSLATE_NOOP("FullscreenUI", "-"); TRANSLATE_NOOP("FullscreenUI", "No Binding"); TRANSLATE_NOOP("FullscreenUI", "Setting %s binding %s."); TRANSLATE_NOOP("FullscreenUI", "Push a controller button or axis now."); @@ -6710,7 +6761,7 @@ TRANSLATE_NOOP("FullscreenUI", "The XInput source provides support for XBox 360/ TRANSLATE_NOOP("FullscreenUI", "Multitap"); TRANSLATE_NOOP("FullscreenUI", "Enables an additional three controller slots. Not supported in all games."); TRANSLATE_NOOP("FullscreenUI", "Attempts to map the selected port to a chosen controller."); -TRANSLATE_NOOP("FullscreenUI", "No Buttons Selected"); +TRANSLATE_NOOP("FullscreenUI", "Determines the frequency at which the macro will toggle the buttons on and off (aka auto fire)."); TRANSLATE_NOOP("FullscreenUI", "Determines how much pressure is simulated when macro is active."); TRANSLATE_NOOP("FullscreenUI", "Determines the pressure required to activate the macro."); TRANSLATE_NOOP("FullscreenUI", "Toggle every %d frames"); @@ -6866,8 +6917,7 @@ TRANSLATE_NOOP("FullscreenUI", "Input profile '{}' saved."); TRANSLATE_NOOP("FullscreenUI", "Failed to save input profile '{}'."); TRANSLATE_NOOP("FullscreenUI", "Port {} Controller Type"); TRANSLATE_NOOP("FullscreenUI", "Select Macro {} Binds"); -TRANSLATE_NOOP("FullscreenUI", "Macro {} Frequency"); -TRANSLATE_NOOP("FullscreenUI", "Macro will toggle every {} frames."); +TRANSLATE_NOOP("FullscreenUI", "{} Frames"); TRANSLATE_NOOP("FullscreenUI", "Port {} Device"); TRANSLATE_NOOP("FullscreenUI", "Port {} Subtype"); TRANSLATE_NOOP("FullscreenUI", "{} unlabelled patch codes will automatically activate."); @@ -7091,6 +7141,7 @@ TRANSLATE_NOOP("FullscreenUI", "CRC"); TRANSLATE_NOOP("FullscreenUI", "Time Played"); TRANSLATE_NOOP("FullscreenUI", "Last Played"); TRANSLATE_NOOP("FullscreenUI", "Size"); +TRANSLATE_NOOP("FullscreenUI", "Frequency"); TRANSLATE_NOOP("FullscreenUI", "Select Disc Image"); TRANSLATE_NOOP("FullscreenUI", "Select Disc Drive"); TRANSLATE_NOOP("FullscreenUI", "Start File"); @@ -7165,7 +7216,6 @@ TRANSLATE_NOOP("FullscreenUI", "Controller Port {}{} Macros"); TRANSLATE_NOOP("FullscreenUI", "Controller Port {} Macros"); TRANSLATE_NOOP("FullscreenUI", "Macro Button {}"); TRANSLATE_NOOP("FullscreenUI", "Buttons"); -TRANSLATE_NOOP("FullscreenUI", "Frequency"); TRANSLATE_NOOP("FullscreenUI", "Pressure"); TRANSLATE_NOOP("FullscreenUI", "Deadzone"); TRANSLATE_NOOP("FullscreenUI", "Controller Port {}{} Settings"); diff --git a/pcsx2/ImGui/ImGuiFullscreen.cpp b/pcsx2/ImGui/ImGuiFullscreen.cpp index de5efb4c7e..e0ee7e5da8 100644 --- a/pcsx2/ImGui/ImGuiFullscreen.cpp +++ b/pcsx2/ImGui/ImGuiFullscreen.cpp @@ -55,7 +55,6 @@ namespace ImGuiFullscreen static void DrawBackgroundProgressDialogs(ImVec2& position, float spacing); static void DrawNotifications(ImVec2& position, float spacing); static void DrawToast(); - static void GetMenuButtonFrameBounds(float height, ImVec2* pos, ImVec2* size); static bool MenuButtonFrame(const char* str_id, bool enabled, float height, bool* visible, bool* hovered, ImRect* bb, ImGuiButtonFlags flags = 0, float hover_alpha = 1.0f); static void PopulateFileSelectorItems(); diff --git a/pcsx2/ImGui/ImGuiFullscreen.h b/pcsx2/ImGui/ImGuiFullscreen.h index 7ed9e26a8a..1deff01906 100644 --- a/pcsx2/ImGui/ImGuiFullscreen.h +++ b/pcsx2/ImGui/ImGuiFullscreen.h @@ -154,6 +154,7 @@ namespace ImGuiFullscreen void BeginMenuButtons(u32 num_items = 0, float y_align = 0.0f, float x_padding = LAYOUT_MENU_BUTTON_X_PADDING, float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING, float item_height = LAYOUT_MENU_BUTTON_HEIGHT); void EndMenuButtons(); + void GetMenuButtonFrameBounds(float height, ImVec2* pos, ImVec2* size); bool MenuButtonFrame(const char* str_id, bool enabled, float height, bool* visible, bool* hovered, ImVec2* min, ImVec2* max, ImGuiButtonFlags flags = 0, float hover_alpha = 1.0f); void MenuHeading(const char* title, bool draw_line = true); diff --git a/pcsx2/ImGui/ImGuiManager.cpp b/pcsx2/ImGui/ImGuiManager.cpp index 97fbd12faf..7bf793ef8d 100644 --- a/pcsx2/ImGui/ImGuiManager.cpp +++ b/pcsx2/ImGui/ImGuiManager.cpp @@ -90,7 +90,8 @@ static ImFont* s_large_font; static std::vector s_standard_font_data; static std::vector s_fixed_font_data; -static std::vector s_icon_font_data; +static std::vector s_icon_fa_font_data; +static std::vector s_icon_pf_font_data; static float s_window_width; static float s_window_height; @@ -425,14 +426,24 @@ bool ImGuiManager::LoadFontData() s_fixed_font_data = std::move(font_data.value()); } - if (s_icon_font_data.empty()) + if (s_icon_fa_font_data.empty()) { std::optional> font_data = FileSystem::ReadBinaryFile(Path::Combine(EmuFolders::Resources, "fonts" FS_OSPATH_SEPARATOR_STR "fa-solid-900.ttf").c_str()); if (!font_data.has_value()) return false; - s_icon_font_data = std::move(font_data.value()); + s_icon_fa_font_data = std::move(font_data.value()); + } + + if (s_icon_pf_font_data.empty()) + { + std::optional> font_data = + FileSystem::ReadBinaryFile(Path::Combine(EmuFolders::Resources, "fonts" FS_OSPATH_SEPARATOR_STR "promptfont.otf").c_str()); + if (!font_data.has_value()) + return false; + + s_icon_pf_font_data = std::move(font_data.value()); } return true; @@ -442,7 +453,8 @@ void ImGuiManager::UnloadFontData() { std::vector().swap(s_standard_font_data); std::vector().swap(s_fixed_font_data); - std::vector().swap(s_icon_font_data); + std::vector().swap(s_icon_fa_font_data); + std::vector().swap(s_icon_pf_font_data); } ImFont* ImGuiManager::AddTextFont(float size) @@ -486,17 +498,40 @@ bool ImGuiManager::AddIconFonts(float size) { // clang-format off static constexpr ImWchar range_fa[] = { 0xf002,0xf002,0xf005,0xf005,0xf007,0xf007,0xf00c,0xf00e,0xf011,0xf011,0xf013,0xf013,0xf017,0xf017,0xf019,0xf019,0xf01c,0xf01c,0xf021,0xf021,0xf023,0xf023,0xf025,0xf025,0xf027,0xf028,0xf02e,0xf02e,0xf030,0xf030,0xf03a,0xf03a,0xf03d,0xf03d,0xf04a,0xf04c,0xf04e,0xf04e,0xf050,0xf050,0xf052,0xf052,0xf059,0xf059,0xf05e,0xf05e,0xf063,0xf063,0xf065,0xf065,0xf067,0xf067,0xf06a,0xf06a,0xf071,0xf071,0xf077,0xf078,0xf07b,0xf07c,0xf084,0xf085,0xf091,0xf091,0xf0ac,0xf0ad,0xf0b0,0xf0b0,0xf0c5,0xf0c5,0xf0c7,0xf0c9,0xf0cb,0xf0cb,0xf0d0,0xf0d0,0xf0dc,0xf0dc,0xf0e2,0xf0e2,0xf0eb,0xf0eb,0xf0f1,0xf0f1,0xf0f3,0xf0f3,0xf0fe,0xf0fe,0xf110,0xf110,0xf119,0xf119,0xf11b,0xf11c,0xf121,0xf121,0xf133,0xf133,0xf140,0xf140,0xf144,0xf144,0xf14a,0xf14a,0xf15b,0xf15b,0xf15d,0xf15d,0xf188,0xf188,0xf191,0xf192,0xf1c9,0xf1c9,0xf1dd,0xf1de,0xf1e6,0xf1e6,0xf1ea,0xf1eb,0xf1f8,0xf1f8,0xf1fc,0xf1fc,0xf242,0xf242,0xf245,0xf245,0xf26c,0xf26c,0xf279,0xf279,0xf2d0,0xf2d0,0xf2db,0xf2db,0xf2f2,0xf2f2,0xf2f5,0xf2f5,0xf302,0xf302,0xf3c1,0xf3c1,0xf3fd,0xf3fd,0xf410,0xf410,0xf466,0xf466,0xf500,0xf500,0xf517,0xf517,0xf51f,0xf51f,0xf543,0xf543,0xf545,0xf545,0xf547,0xf548,0xf552,0xf552,0xf5a2,0xf5a2,0xf5e7,0xf5e7,0xf65d,0xf65e,0xf6a9,0xf6a9,0xf756,0xf756,0xf7c2,0xf7c2,0xf807,0xf807,0xf815,0xf815,0xf818,0xf818,0xf84c,0xf84c,0xf8cc,0xf8cc,0xf8d9,0xf8d9,0x0,0x0 }; + static constexpr ImWchar range_pf[] = { 0x2198,0x2199,0x219e,0x21a1,0x21b0,0x21b3,0x21ba,0x21c3,0x21d0,0x21d4,0x21dc,0x21dd,0x21e0,0x21e3,0x21f3,0x21f3,0x21f7,0x21f8,0x21fa,0x21fb,0x227a,0x227d,0x23f4,0x23f7,0x2427,0x243a,0x243c,0x243c,0x2460,0x246b,0x24f5,0x24fd,0x24ff,0x24ff,0x278a,0x278e,0xe001,0xe001,0xff21,0xff3a,0x0,0x0 }; // clang-format on - ImFontConfig cfg; - cfg.MergeMode = true; - cfg.PixelSnapH = true; - cfg.GlyphMinAdvanceX = size; - cfg.GlyphMaxAdvanceX = size; - cfg.FontDataOwnedByAtlas = false; + { + ImFontConfig cfg; + cfg.MergeMode = true; + cfg.PixelSnapH = true; + cfg.GlyphMinAdvanceX = size; + cfg.GlyphMaxAdvanceX = size; + cfg.FontDataOwnedByAtlas = false; - return (ImGui::GetIO().Fonts->AddFontFromMemoryTTF( - s_icon_font_data.data(), static_cast(s_icon_font_data.size()), size * 0.75f, &cfg, range_fa) != nullptr); + if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF( + s_icon_fa_font_data.data(), static_cast(s_icon_fa_font_data.size()), size * 0.75f, &cfg, range_fa)) + { + return false; + } + } + + { + ImFontConfig cfg; + cfg.MergeMode = true; + cfg.PixelSnapH = true; + cfg.GlyphMinAdvanceX = size; + cfg.GlyphMaxAdvanceX = size; + cfg.FontDataOwnedByAtlas = false; + + if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF( + s_icon_pf_font_data.data(), static_cast(s_icon_pf_font_data.size()), size * 1.2f, &cfg, range_pf)) + { + return false; + } + } + + return true; } bool ImGuiManager::AddImGuiFonts(bool fullscreen_fonts) diff --git a/pcsx2/ImGui/ImGuiOverlays.cpp b/pcsx2/ImGui/ImGuiOverlays.cpp index 68fba171d1..df41b5692e 100644 --- a/pcsx2/ImGui/ImGuiOverlays.cpp +++ b/pcsx2/ImGui/ImGuiOverlays.cpp @@ -53,7 +53,7 @@ namespace ImGuiManager { - static void FormatProcessorStat(std::string& text, double usage, double time); + static void FormatProcessorStat(SmallStringBase& text, double usage, double time); static void DrawPerformanceOverlay(float& position_y); static void DrawSettingsOverlay(); static void DrawInputsOverlay(); @@ -86,15 +86,15 @@ static std::tuple GetMinMax(std::span values) return std::tie(min, max); } -void ImGuiManager::FormatProcessorStat(std::string& text, double usage, double time) +void ImGuiManager::FormatProcessorStat(SmallStringBase& text, double usage, double time) { // Some values, such as GPU (and even CPU to some extent) can be out of phase with the wall clock, // which the processor time is divided by to get a utilization percentage. Let's clamp it at 100%, // so that people don't get confused, and remove the decimal places when it's there while we're at it. if (usage >= 99.95) - fmt::format_to(std::back_inserter(text), "100% ({:.2f}ms)", time); + text.append_fmt("100% ({:.2f}ms)", time); else - fmt::format_to(std::back_inserter(text), "{:.1f}% ({:.2f}ms)", usage, time); + text.append_fmt("{:.1f}% ({:.2f}ms)", usage, time); } void ImGuiManager::DrawPerformanceOverlay(float& position_y) @@ -108,11 +108,9 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y) ImFont* const standard_font = ImGuiManager::GetStandardFont(); ImDrawList* dl = ImGui::GetBackgroundDrawList(); - std::string text; + SmallString text; ImVec2 text_size; - text.reserve(128); - #define DRAW_LINE(font, text, color) \ do \ { \ @@ -136,31 +134,31 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y) switch (PerformanceMetrics::GetInternalFPSMethod()) { case PerformanceMetrics::InternalFPSMethod::GSPrivilegedRegister: - fmt::format_to(std::back_inserter(text), "G: {:.2f} [P] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(), + text.append_fmt("G: {:.2f} [P] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(), PerformanceMetrics::GetFPS()); break; case PerformanceMetrics::InternalFPSMethod::DISPFBBlit: - fmt::format_to(std::back_inserter(text), "G: {:.2f} [B] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(), + text.append_fmt("G: {:.2f} [B] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(), PerformanceMetrics::GetFPS()); break; case PerformanceMetrics::InternalFPSMethod::None: default: - fmt::format_to(std::back_inserter(text), "V: {:.2f}", PerformanceMetrics::GetFPS()); + text.append_fmt("V: {:.2f}", PerformanceMetrics::GetFPS()); break; } first = false; } if (GSConfig.OsdShowSpeed) { - fmt::format_to(std::back_inserter(text), "{}{}%", first ? "" : " | ", static_cast(std::round(speed))); + text.append_fmt("{}{}%", first ? "" : " | ", static_cast(std::round(speed))); const float target_speed = VMManager::GetTargetSpeed(); if (target_speed == 0.0f) - text += " (Max)"; + text.append(" (Max)"); else - fmt::format_to(std::back_inserter(text), " ({:.0f}%)", target_speed * 100.0f); + text.append_fmt(" ({:.0f}%)", target_speed * 100.0f); } if (!text.empty()) { @@ -193,20 +191,20 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y) GSgetInternalResolution(&width, &height); text.clear(); - fmt::format_to(std::back_inserter(text), "{}x{} {} {}", width, height, ReportVideoMode(), ReportInterlaceMode()); + text.append_fmt("{}x{} {} {}", width, height, ReportVideoMode(), ReportInterlaceMode()); DRAW_LINE(fixed_font, text.c_str(), IM_COL32(255, 255, 255, 255)); } if (GSConfig.OsdShowCPU) { text.clear(); - fmt::format_to(std::back_inserter(text), "{:.2f}ms | {:.2f}ms | {:.2f}ms", PerformanceMetrics::GetMinimumFrameTime(), + text.append_fmt("{:.2f}ms | {:.2f}ms | {:.2f}ms", PerformanceMetrics::GetMinimumFrameTime(), PerformanceMetrics::GetAverageFrameTime(), PerformanceMetrics::GetMaximumFrameTime()); DRAW_LINE(fixed_font, text.c_str(), IM_COL32(255, 255, 255, 255)); text.clear(); if (EmuConfig.Speedhacks.EECycleRate != 0 || EmuConfig.Speedhacks.EECycleSkip != 0) - fmt::format_to(std::back_inserter(text), "EE[{}/{}]: ", EmuConfig.Speedhacks.EECycleRate, EmuConfig.Speedhacks.EECycleSkip); + text.append_fmt("EE[{}/{}]: ", EmuConfig.Speedhacks.EECycleRate, EmuConfig.Speedhacks.EECycleSkip); else text = "EE: "; FormatProcessorStat(text, PerformanceMetrics::GetCPUThreadUsage(), PerformanceMetrics::GetCPUThreadAverageTime()); @@ -220,7 +218,7 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y) for (u32 i = 0; i < gs_sw_threads; i++) { text.clear(); - fmt::format_to(std::back_inserter(text), "SW-{}: ", i); + text.append_fmt("SW-{}: ", i); FormatProcessorStat(text, PerformanceMetrics::GetGSSWThreadUsage(i), PerformanceMetrics::GetGSSWThreadAverageTime(i)); DRAW_LINE(fixed_font, text.c_str(), IM_COL32(255, 255, 255, 255)); } @@ -297,7 +295,7 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y) const ImVec2 wpos(ImGui::GetCurrentWindow()->Pos); text.clear(); - fmt::format_to(std::back_inserter(text), "{:.1f} ms", max); + text.append_fmt("{:.1f} ms", max); text_size = fixed_font->CalcTextSizeA(fixed_font->FontSize, FLT_MAX, 0.0f, text.c_str(), text.c_str() + text.length()); win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing + shadow_offset, wpos.y + shadow_offset), IM_COL32(0, 0, 0, 100), text.c_str(), text.c_str() + text.length()); @@ -305,7 +303,7 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y) text.c_str() + text.length()); text.clear(); - fmt::format_to(std::back_inserter(text), "{:.1f} ms", min); + text.append_fmt("{:.1f} ms", min); text_size = fixed_font->CalcTextSizeA(fixed_font->FontSize, FLT_MAX, 0.0f, text.c_str(), text.c_str() + text.length()); win_dl->AddText(ImVec2(wpos.x + history_size.x - text_size.x - spacing + shadow_offset, wpos.y + history_size.y - fixed_font->FontSize + shadow_offset), @@ -470,7 +468,7 @@ void ImGuiManager::DrawInputsOverlay() const float shadow_offset = 1.0f * scale; const float margin = 10.0f * scale; const float spacing = 5.0f * scale; - ImFont* font = ImGuiManager::GetFixedFont(); + ImFont* font = ImGuiManager::GetStandardFont(); static constexpr u32 text_color = IM_COL32(0xff, 0xff, 0xff, 255); static constexpr u32 shadow_color = IM_COL32(0x00, 0x00, 0x00, 100); @@ -497,8 +495,7 @@ void ImGuiManager::DrawInputsOverlay() const ImVec4 clip_rect(current_x, current_y, display_size.x - margin, display_size.y - margin); - std::string text; - text.reserve(256); + SmallString text; for (u32 slot = 0; slot < Pad::NUM_CONTROLLER_PORTS; slot++) { @@ -507,10 +504,12 @@ void ImGuiManager::DrawInputsOverlay() if (ctype == Pad::ControllerType::NotConnected) continue; - text.clear(); - fmt::format_to(std::back_inserter(text), "P{} |", slot + 1u); - const Pad::ControllerInfo& cinfo = pad->GetInfo(); + if (cinfo.icon_name) + text.fmt("{} {}", cinfo.icon_name, slot + 1u); + else + text.fmt("{} |", slot + 1u); + for (u32 bind = 0; bind < static_cast(cinfo.bindings.size()); bind++) { const InputBindingInfo& bi = cinfo.bindings[bind]; @@ -522,9 +521,9 @@ void ImGuiManager::DrawInputsOverlay() // axes are always shown const float value = static_cast(pad->GetRawInput(bind)) * (1.0f / 255.0f); if (value >= (254.0f / 255.0f)) - fmt::format_to(std::back_inserter(text), " {}", bi.name); + text.append_fmt(" {}", bi.icon_name ? bi.icon_name : bi.name); else if (value > (1.0f / 255.0f)) - fmt::format_to(std::back_inserter(text), " {}: {:.2f}", bi.name, value); + text.append_fmt(" {}: {:.2f}", bi.icon_name ? bi.icon_name : bi.name, value); } break; @@ -533,7 +532,7 @@ void ImGuiManager::DrawInputsOverlay() // buttons only shown when active const float value = static_cast(pad->GetRawInput(bind)) * (1.0f / 255.0f); if (value >= 0.5f) - fmt::format_to(std::back_inserter(text), " {}", bi.name); + text.append_fmt(" {}", bi.icon_name ? bi.icon_name : bi.name); } break; @@ -562,8 +561,7 @@ void ImGuiManager::DrawInputsOverlay() if (bindings.empty()) continue; - text.clear(); - fmt::format_to(std::back_inserter(text), "USB{} |", port + 1u); + text.fmt("USB{} |", port + 1u); for (const InputBindingInfo& bi : bindings) { @@ -575,9 +573,9 @@ void ImGuiManager::DrawInputsOverlay() // axes are always shown const float value = static_cast(USB::GetDeviceBindValue(port, bi.bind_index)); if (value >= (254.0f / 255.0f)) - fmt::format_to(std::back_inserter(text), " {}", bi.name); + text.append_fmt(" {}", bi.icon_name ? bi.icon_name : bi.name); else if (value > (1.0f / 255.0f)) - fmt::format_to(std::back_inserter(text), " {}: {:.2f}", bi.name, value); + text.append_fmt(" {}: {:.2f}", bi.icon_name ? bi.icon_name : bi.name, value); } break; @@ -586,7 +584,7 @@ void ImGuiManager::DrawInputsOverlay() // buttons only shown when active const float value = static_cast(USB::GetDeviceBindValue(port, bi.bind_index)); if (value >= 0.5f) - fmt::format_to(std::back_inserter(text), " {}", bi.name); + text.append_fmt(" {}", bi.icon_name ? bi.icon_name : bi.name); } break; diff --git a/pcsx2/Input/DInputSource.cpp b/pcsx2/Input/DInputSource.cpp index e3afb700e5..cd1a4e00dc 100644 --- a/pcsx2/Input/DInputSource.cpp +++ b/pcsx2/Input/DInputSource.cpp @@ -397,32 +397,38 @@ 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) { if (key.source_subtype == InputSubclass::ControllerAxis) { 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 && !m_ignore_inversion) ? "~" : ""); + ret.fmt("DInput-{}/{}Axis{}{}", u32(key.source_index), modifier, u32(key.data), (key.invert && !m_ignore_inversion) ? "~" : ""); } 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 DIJOYSTATE2& new_state) { ControllerData& cd = m_controllers[index]; diff --git a/pcsx2/Input/DInputSource.h b/pcsx2/Input/DInputSource.h index daca234dea..e279d43443 100644 --- a/pcsx2/Input/DInputSource.h +++ b/pcsx2/Input/DInputSource.h @@ -60,7 +60,8 @@ public: void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; 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 ControllerData diff --git a/pcsx2/Input/InputManager.cpp b/pcsx2/Input/InputManager.cpp index 111300c498..04cceb4298 100644 --- a/pcsx2/Input/InputManager.cpp +++ b/pcsx2/Input/InputManager.cpp @@ -27,6 +27,8 @@ #include "common/StringUtil.h" #include "common/Timer.h" +#include "IconsPromptFont.h" + #include "fmt/core.h" #include @@ -104,6 +106,7 @@ namespace InputManager 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 AddBinding(const std::string_view& binding, const InputEventHandler& handler); static void AddBindings(const std::vector& bindings, const InputEventHandler& handler); static bool ParseBindingAndGetSource(const std::string_view& binding, InputBindingKey* key, InputSource** source); @@ -325,7 +328,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)); } } @@ -358,6 +361,117 @@ 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 + { + 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::AddBinding(const std::string_view& binding, const InputEventHandler& handler) { std::shared_ptr ibinding; diff --git a/pcsx2/Input/InputManager.h b/pcsx2/Input/InputManager.h index acae4eec2c..5e7bd4eaff 100644 --- a/pcsx2/Input/InputManager.h +++ b/pcsx2/Input/InputManager.h @@ -196,6 +196,9 @@ namespace InputManager /// 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); @@ -215,6 +218,9 @@ namespace InputManager /// Converts a chord of binding keys to a string. 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/pcsx2/Input/InputSource.h b/pcsx2/Input/InputSource.h index 4900877b60..48b3603163 100644 --- a/pcsx2/Input/InputSource.h +++ b/pcsx2/Input/InputSource.h @@ -21,6 +21,7 @@ #include #include "common/Pcsx2Defs.h" +#include "common/SmallString.h" #include "Input/InputManager.h" class SettingsInterface; @@ -39,7 +40,8 @@ public: virtual void PollEvents() = 0; 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/pcsx2/Input/SDLInputSource.cpp b/pcsx2/Input/SDLInputSource.cpp index f94e29697e..e76cb81681 100644 --- a/pcsx2/Input/SDLInputSource.cpp +++ b/pcsx2/Input/SDLInputSource.cpp @@ -26,6 +26,8 @@ #include "common/Path.h" #include "common/StringUtil.h" +#include "IconsPromptFont.h" + #include #include @@ -39,6 +41,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 @@ -71,6 +81,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 @@ -168,38 +195,38 @@ void SDLInputSource::LoadSettings(SettingsInterface& si) m_controller_raw_mode = si.GetBoolValue("InputSources", "SDLRawInput", false); m_sdl_hints = si.GetKeyValueList("SDLHints"); - for (u32 i = 0; i < MAX_LED_COLORS; i++) - { - const u32 color = GetRGBForPlayerId(si, i); - if (m_led_colors[i] == color) - continue; + for (u32 i = 0; i < MAX_LED_COLORS; i++) + { + const u32 color = GetRGBForPlayerId(si, i); + if (m_led_colors[i] == color) + continue; - m_led_colors[i] = color; + m_led_colors[i] = color; - const auto it = GetControllerDataForPlayerId(i); - if (it == m_controllers.end() || !it->game_controller || !SDL_GameControllerHasLED(it->game_controller)) - continue; + const auto it = GetControllerDataForPlayerId(i); + if (it == m_controllers.end() || !it->game_controller || !SDL_GameControllerHasLED(it->game_controller)) + continue; - SetControllerRGBLED(it->game_controller, color); - } + SetControllerRGBLED(it->game_controller, color); + } } u32 SDLInputSource::GetRGBForPlayerId(SettingsInterface& si, u32 player_id) { - return ParseRGBForPlayerId( - si.GetStringValue("SDLExtra", fmt::format("Player{}LED", player_id).c_str(), s_sdl_default_led_colors[player_id]), - player_id); + return ParseRGBForPlayerId( + si.GetStringValue("SDLExtra", fmt::format("Player{}LED", player_id).c_str(), s_sdl_default_led_colors[player_id]), + player_id); } u32 SDLInputSource::ParseRGBForPlayerId(const std::string_view& str, u32 player_id) { - if (player_id >= MAX_LED_COLORS) - return 0; + if (player_id >= MAX_LED_COLORS) + return 0; - const u32 default_color = StringUtil::FromChars(s_sdl_default_led_colors[player_id], 16).value_or(0); - const u32 color = StringUtil::FromChars(str, 16).value_or(default_color); + const u32 default_color = StringUtil::FromChars(s_sdl_default_led_colors[player_id], 16).value_or(0); + const u32 color = StringUtil::FromChars(str, 16).value_or(default_color); - return color; + return color; } void SDLInputSource::SetHints() @@ -420,9 +447,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) { @@ -430,30 +457,54 @@ std::string SDLInputSource::ConvertKeyToString(InputBindingKey key) { const char* modifier = (key.modifier == InputModifier::FullAxis ? "Full" : (key.modifier == InputModifier::Negate ? "-" : "+")); if (key.data < std::size(s_sdl_axis_names)) - ret = StringUtil::StdStringFromFormat("SDL-%u/%s%s", 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 = StringUtil::StdStringFromFormat("SDL-%u/%sAxis%u%s", key.source_index, modifier, key.data, key.invert ? "~" : ""); + ret.fmt("SDL-{}/{}Axis{}{}", static_cast(key.source_index), modifier, key.data, key.invert ? "~" : ""); } else if (key.source_subtype == InputSubclass::ControllerButton) { if (key.data < std::size(s_sdl_button_names)) - ret = StringUtil::StdStringFromFormat("SDL-%u/%s", 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 = StringUtil::StdStringFromFormat("SDL-%u/Button%u", key.source_index, key.data); + ret.fmt("SDL-{}/Button{}", static_cast(key.source_index), key.data); } 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 = StringUtil::StdStringFromFormat("SDL-%u/Hat%u%s", 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 = StringUtil::StdStringFromFormat("SDL-%u/%sMotor", 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 = StringUtil::StdStringFromFormat("SDL-%u/Haptic", 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/pcsx2/Input/SDLInputSource.h b/pcsx2/Input/SDLInputSource.h index 8014413a2c..c54c8e4d2f 100644 --- a/pcsx2/Input/SDLInputSource.h +++ b/pcsx2/Input/SDLInputSource.h @@ -44,7 +44,8 @@ public: void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; 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/pcsx2/Input/XInputSource.cpp b/pcsx2/Input/XInputSource.cpp index d25141ba48..91cb4c3c80 100644 --- a/pcsx2/Input/XInputSource.cpp +++ b/pcsx2/Input/XInputSource.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2022 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -14,14 +14,19 @@ */ #include "PrecompiledHeader.h" + #include "Input/XInputSource.h" #include "Input/InputManager.h" + #include "common/Assertions.h" #include "common/StringUtil.h" #include "common/Console.h" + +#include "IconsPromptFont.h" + #include -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 @@ -29,6 +34,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 @@ -38,7 +51,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 @@ -55,12 +68,29 @@ 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, XINPUT_GAMEPAD_DPAD_RIGHT, XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, 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 @@ -299,24 +329,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 = StringUtil::StdStringFromFormat("XInput-%u/%c%s", 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 = StringUtil::StdStringFromFormat("XInput-%u/%s", 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 = StringUtil::StdStringFromFormat("XInput-%u/%sMotor", 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]); } } diff --git a/pcsx2/Input/XInputSource.h b/pcsx2/Input/XInputSource.h index b42db78822..2bcf31616d 100644 --- a/pcsx2/Input/XInputSource.h +++ b/pcsx2/Input/XInputSource.h @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2023 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -63,25 +63,6 @@ class SettingsInterface; class XInputSource final : public InputSource { public: - XInputSource(); - ~XInputSource(); - - bool Initialize(SettingsInterface& si, std::unique_lock& settings_lock) override; - void UpdateSettings(SettingsInterface& si, std::unique_lock& settings_lock) override; - bool ReloadDevices() override; - void Shutdown() override; - - void PollEvents() override; - std::vector> EnumerateDevices() override; - std::vector EnumerateMotors() override; - bool GetGenericBindingMapping(const std::string_view& device, InputManager::GenericInputBindingMapping* mapping) override; - void UpdateMotorState(InputBindingKey key, float intensity) override; - void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; - - std::optional ParseKeyString(const std::string_view& device, const std::string_view& binding) override; - std::string ConvertKeyToString(InputBindingKey key) override; - -private: enum : u32 { NUM_CONTROLLERS = XUSER_MAX_COUNT, // 4 @@ -99,6 +80,26 @@ private: NUM_AXES, }; + XInputSource(); + ~XInputSource(); + + bool Initialize(SettingsInterface& si, std::unique_lock& settings_lock) override; + void UpdateSettings(SettingsInterface& si, std::unique_lock& settings_lock) override; + bool ReloadDevices() override; + void Shutdown() override; + + void PollEvents() override; + std::vector> EnumerateDevices() override; + std::vector EnumerateMotors() override; + bool GetGenericBindingMapping(const std::string_view& device, InputManager::GenericInputBindingMapping* mapping) override; + void UpdateMotorState(InputBindingKey key, float intensity) override; + void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; + + std::optional ParseKeyString(const std::string_view& device, const std::string_view& binding) override; + TinyString ConvertKeyToString(InputBindingKey key) override; + TinyString ConvertKeyToIcon(InputBindingKey key) override; + +private: struct ControllerData { union @@ -126,8 +127,4 @@ private: DWORD(WINAPI* m_xinput_set_state)(DWORD, XINPUT_VIBRATION*); DWORD(WINAPI* m_xinput_get_capabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); DWORD(WINAPI* m_xinput_get_extended)(DWORD, SCP_EXTN*); - - static const char* s_axis_names[NUM_AXES]; - static const char* s_button_names[NUM_BUTTONS]; - static const u16 s_button_masks[NUM_BUTTONS]; }; diff --git a/pcsx2/SIO/Pad/PadDualshock2.cpp b/pcsx2/SIO/Pad/PadDualshock2.cpp index 9a6e98c291..cd7ae21743 100644 --- a/pcsx2/SIO/Pad/PadDualshock2.cpp +++ b/pcsx2/SIO/Pad/PadDualshock2.cpp @@ -24,36 +24,38 @@ #include "Input/InputManager.h" #include "Host.h" +#include "IconsPromptFont.h" + static const InputBindingInfo s_bindings[] = { // clang-format off - {"Up", TRANSLATE_NOOP("Pad", "D-Pad Up"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_UP, GenericInputBinding::DPadUp}, - {"Right", TRANSLATE_NOOP("Pad", "D-Pad Right"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_RIGHT, GenericInputBinding::DPadRight}, - {"Down", TRANSLATE_NOOP("Pad", "D-Pad Down"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_DOWN, GenericInputBinding::DPadDown}, - {"Left", TRANSLATE_NOOP("Pad", "D-Pad Left"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_LEFT, GenericInputBinding::DPadLeft}, - {"Triangle", TRANSLATE_NOOP("Pad", "Triangle"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_TRIANGLE, GenericInputBinding::Triangle}, - {"Circle", TRANSLATE_NOOP("Pad", "Circle"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_CIRCLE, GenericInputBinding::Circle}, - {"Cross", TRANSLATE_NOOP("Pad", "Cross"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_CROSS, GenericInputBinding::Cross}, - {"Square", TRANSLATE_NOOP("Pad", "Square"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_SQUARE, GenericInputBinding::Square}, - {"Select", TRANSLATE_NOOP("Pad", "Select"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_SELECT, GenericInputBinding::Select}, - {"Start", TRANSLATE_NOOP("Pad", "Start"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_START, GenericInputBinding::Start}, - {"L1", TRANSLATE_NOOP("Pad", "L1 (Left Bumper)"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_L1, GenericInputBinding::L1}, - {"L2", TRANSLATE_NOOP("Pad", "L2 (Left Trigger)"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L2, GenericInputBinding::L2}, - {"R1", TRANSLATE_NOOP("Pad", "R1 (Right Bumper)"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_R1, GenericInputBinding::R1}, - {"R2", TRANSLATE_NOOP("Pad", "R2 (Right Trigger)"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R2, GenericInputBinding::R2}, - {"L3", TRANSLATE_NOOP("Pad", "L3 (Left Stick Button)"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_L3, GenericInputBinding::L3}, - {"R3", TRANSLATE_NOOP("Pad", "R3 (Right Stick Button)"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_R3, GenericInputBinding::R3}, - {"Analog", TRANSLATE_NOOP("Pad", "Analog Toggle"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_ANALOG, GenericInputBinding::System}, - {"Pressure", TRANSLATE_NOOP("Pad", "Apply Pressure"), InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_PRESSURE, GenericInputBinding::Unknown}, - {"LUp", TRANSLATE_NOOP("Pad", "Left Stick Up"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_UP, GenericInputBinding::LeftStickUp}, - {"LRight", TRANSLATE_NOOP("Pad", "Left Stick Right"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_RIGHT, GenericInputBinding::LeftStickRight}, - {"LDown", TRANSLATE_NOOP("Pad", "Left Stick Down"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_DOWN, GenericInputBinding::LeftStickDown}, - {"LLeft", TRANSLATE_NOOP("Pad", "Left Stick Left"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_LEFT, GenericInputBinding::LeftStickLeft}, - {"RUp", TRANSLATE_NOOP("Pad", "Right Stick Up"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_UP, GenericInputBinding::RightStickUp}, - {"RRight", TRANSLATE_NOOP("Pad", "Right Stick Right"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_RIGHT, GenericInputBinding::RightStickRight}, - {"RDown", TRANSLATE_NOOP("Pad", "Right Stick Down"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_DOWN, GenericInputBinding::RightStickDown}, - {"RLeft", TRANSLATE_NOOP("Pad", "Right Stick Left"), InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_LEFT, GenericInputBinding::RightStickLeft}, - {"LargeMotor", TRANSLATE_NOOP("Pad", "Large (Low Frequency) Motor"), InputBindingInfo::Type::Motor, 0, GenericInputBinding::LargeMotor}, - {"SmallMotor", TRANSLATE_NOOP("Pad", "Small (High Frequency) Motor"), InputBindingInfo::Type::Motor, 0, GenericInputBinding::SmallMotor}, + {"Up", TRANSLATE_NOOP("Pad", "D-Pad Up"), ICON_PF_DPAD_UP, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_UP, GenericInputBinding::DPadUp}, + {"Right", TRANSLATE_NOOP("Pad", "D-Pad Right"), ICON_PF_DPAD_RIGHT, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_RIGHT, GenericInputBinding::DPadRight}, + {"Down", TRANSLATE_NOOP("Pad", "D-Pad Down"), ICON_PF_DPAD_DOWN, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_DOWN, GenericInputBinding::DPadDown}, + {"Left", TRANSLATE_NOOP("Pad", "D-Pad Left"), ICON_PF_DPAD_LEFT, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_LEFT, GenericInputBinding::DPadLeft}, + {"Triangle", TRANSLATE_NOOP("Pad", "Triangle"), ICON_PF_BUTTON_TRIANGLE, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_TRIANGLE, GenericInputBinding::Triangle}, + {"Circle", TRANSLATE_NOOP("Pad", "Circle"), ICON_PF_BUTTON_CIRCLE, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_CIRCLE, GenericInputBinding::Circle}, + {"Cross", TRANSLATE_NOOP("Pad", "Cross"), ICON_PF_BUTTON_CROSS, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_CROSS, GenericInputBinding::Cross}, + {"Square", TRANSLATE_NOOP("Pad", "Square"), ICON_PF_BUTTON_SQUARE, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_SQUARE, GenericInputBinding::Square}, + {"Select", TRANSLATE_NOOP("Pad", "Select"), ICON_PF_SELECT_SHARE, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_SELECT, GenericInputBinding::Select}, + {"Start", TRANSLATE_NOOP("Pad", "Start"), ICON_PF_START, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_START, GenericInputBinding::Start}, + {"L1", TRANSLATE_NOOP("Pad", "L1 (Left Bumper)"), ICON_PF_LEFT_SHOULDER_L1, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_L1, GenericInputBinding::L1}, + {"L2", TRANSLATE_NOOP("Pad", "L2 (Left Trigger)"), ICON_PF_LEFT_TRIGGER_L2, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L2, GenericInputBinding::L2}, + {"R1", TRANSLATE_NOOP("Pad", "R1 (Right Bumper)"), ICON_PF_RIGHT_SHOULDER_R1, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_R1, GenericInputBinding::R1}, + {"R2", TRANSLATE_NOOP("Pad", "R2 (Right Trigger)"), ICON_PF_RIGHT_TRIGGER_R2, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R2, GenericInputBinding::R2}, + {"L3", TRANSLATE_NOOP("Pad", "L3 (Left Stick Button)"), ICON_PF_LEFT_ANALOG_CLICK, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_L3, GenericInputBinding::L3}, + {"R3", TRANSLATE_NOOP("Pad", "R3 (Right Stick Button)"), ICON_PF_RIGHT_ANALOG_CLICK, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_R3, GenericInputBinding::R3}, + {"Analog", TRANSLATE_NOOP("Pad", "Analog Toggle"), ICON_PF_ANALOG_LEFT_RIGHT, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_ANALOG, GenericInputBinding::System}, + {"Pressure", TRANSLATE_NOOP("Pad", "Apply Pressure"), ICON_PF_ANALOG_ANY, InputBindingInfo::Type::Button, PadDualshock2::Inputs::PAD_PRESSURE, GenericInputBinding::Unknown}, + {"LUp", TRANSLATE_NOOP("Pad", "Left Stick Up"), ICON_PF_LEFT_ANALOG_UP, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_UP, GenericInputBinding::LeftStickUp}, + {"LRight", TRANSLATE_NOOP("Pad", "Left Stick Right"), ICON_PF_LEFT_ANALOG_RIGHT, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_RIGHT, GenericInputBinding::LeftStickRight}, + {"LDown", TRANSLATE_NOOP("Pad", "Left Stick Down"), ICON_PF_LEFT_ANALOG_DOWN, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_DOWN, GenericInputBinding::LeftStickDown}, + {"LLeft", TRANSLATE_NOOP("Pad", "Left Stick Left"), ICON_PF_LEFT_ANALOG_LEFT, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_L_LEFT, GenericInputBinding::LeftStickLeft}, + {"RUp", TRANSLATE_NOOP("Pad", "Right Stick Up"), ICON_PF_RIGHT_ANALOG_UP, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_UP, GenericInputBinding::RightStickUp}, + {"RRight", TRANSLATE_NOOP("Pad", "Right Stick Right"), ICON_PF_RIGHT_ANALOG_RIGHT, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_RIGHT, GenericInputBinding::RightStickRight}, + {"RDown", TRANSLATE_NOOP("Pad", "Right Stick Down"), ICON_PF_RIGHT_ANALOG_RIGHT, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_DOWN, GenericInputBinding::RightStickDown}, + {"RLeft", TRANSLATE_NOOP("Pad", "Right Stick Left"), ICON_PF_RIGHT_ANALOG_LEFT, InputBindingInfo::Type::HalfAxis, PadDualshock2::Inputs::PAD_R_LEFT, GenericInputBinding::RightStickLeft}, + {"LargeMotor", TRANSLATE_NOOP("Pad", "Large (Low Frequency) Motor"), nullptr, InputBindingInfo::Type::Motor, 0, GenericInputBinding::LargeMotor}, + {"SmallMotor", TRANSLATE_NOOP("Pad", "Small (High Frequency) Motor"), nullptr, InputBindingInfo::Type::Motor, 0, GenericInputBinding::SmallMotor}, // clang-format on }; @@ -93,7 +95,7 @@ static const SettingInfo s_settings[] = { }; const Pad::ControllerInfo PadDualshock2::ControllerInfo = {Pad::ControllerType::DualShock2, "DualShock2", - TRANSLATE_NOOP("Pad", "DualShock 2"), s_bindings, s_settings, Pad::VibrationCapabilities::LargeSmallMotors}; + TRANSLATE_NOOP("Pad", "DualShock 2"), ICON_PF_GAMEPAD, s_bindings, s_settings, Pad::VibrationCapabilities::LargeSmallMotors}; void PadDualshock2::ConfigLog() { diff --git a/pcsx2/SIO/Pad/PadGuitar.cpp b/pcsx2/SIO/Pad/PadGuitar.cpp index e57c46bf7b..be773bdef7 100644 --- a/pcsx2/SIO/Pad/PadGuitar.cpp +++ b/pcsx2/SIO/Pad/PadGuitar.cpp @@ -25,17 +25,17 @@ // would do what actions, if you played Guitar Hero on a PS2 with a DS2 instead of a controller. static const InputBindingInfo s_bindings[] = { // clang-format off - {"Up", TRANSLATE_NOOP("Pad", "Strum Up"), InputBindingInfo::Type::Button, PadGuitar::Inputs::STRUM_UP, GenericInputBinding::DPadUp}, - {"Down", TRANSLATE_NOOP("Pad", "Strum Down"), InputBindingInfo::Type::Button, PadGuitar::Inputs::STRUM_DOWN, GenericInputBinding::DPadDown}, - {"Select", TRANSLATE_NOOP("Pad", "Select"), InputBindingInfo::Type::Button, PadGuitar::Inputs::SELECT, GenericInputBinding::Select}, - {"Start", TRANSLATE_NOOP("Pad", "Start"), InputBindingInfo::Type::Button, PadGuitar::Inputs::START, GenericInputBinding::Start}, - {"Green", TRANSLATE_NOOP("Pad", "Green Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::GREEN, GenericInputBinding::R2}, - {"Red", TRANSLATE_NOOP("Pad", "Red Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::RED, GenericInputBinding::Circle}, - {"Yellow", TRANSLATE_NOOP("Pad", "Yellow Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::YELLOW, GenericInputBinding::Triangle}, - {"Blue", TRANSLATE_NOOP("Pad", "Blue Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::BLUE, GenericInputBinding::Cross}, - {"Orange", TRANSLATE_NOOP("Pad", "Orange Fret"), InputBindingInfo::Type::Button, PadGuitar::Inputs::ORANGE, GenericInputBinding::Square}, - {"Whammy", TRANSLATE_NOOP("Pad", "Whammy Bar"), InputBindingInfo::Type::HalfAxis, PadGuitar::Inputs::WHAMMY, GenericInputBinding::LeftStickUp}, - {"Tilt", TRANSLATE_NOOP("Pad", "Tilt Up"), InputBindingInfo::Type::Button, PadGuitar::Inputs::TILT, GenericInputBinding::L2}, + {"Up", TRANSLATE_NOOP("Pad", "Strum Up"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::STRUM_UP, GenericInputBinding::DPadUp}, + {"Down", TRANSLATE_NOOP("Pad", "Strum Down"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::STRUM_DOWN, GenericInputBinding::DPadDown}, + {"Select", TRANSLATE_NOOP("Pad", "Select"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::SELECT, GenericInputBinding::Select}, + {"Start", TRANSLATE_NOOP("Pad", "Start"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::START, GenericInputBinding::Start}, + {"Green", TRANSLATE_NOOP("Pad", "Green Fret"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::GREEN, GenericInputBinding::R2}, + {"Red", TRANSLATE_NOOP("Pad", "Red Fret"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::RED, GenericInputBinding::Circle}, + {"Yellow", TRANSLATE_NOOP("Pad", "Yellow Fret"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::YELLOW, GenericInputBinding::Triangle}, + {"Blue", TRANSLATE_NOOP("Pad", "Blue Fret"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::BLUE, GenericInputBinding::Cross}, + {"Orange", TRANSLATE_NOOP("Pad", "Orange Fret"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::ORANGE, GenericInputBinding::Square}, + {"Whammy", TRANSLATE_NOOP("Pad", "Whammy Bar"), nullptr, InputBindingInfo::Type::HalfAxis, PadGuitar::Inputs::WHAMMY, GenericInputBinding::LeftStickUp}, + {"Tilt", TRANSLATE_NOOP("Pad", "Tilt Up"), nullptr, InputBindingInfo::Type::Button, PadGuitar::Inputs::TILT, GenericInputBinding::L2}, // clang-format on }; @@ -49,7 +49,7 @@ static const SettingInfo s_settings[] = { }; const Pad::ControllerInfo PadGuitar::ControllerInfo = {Pad::ControllerType::Guitar, "Guitar", - TRANSLATE_NOOP("Pad", "Guitar"), s_bindings, s_settings, Pad::VibrationCapabilities::NoVibration}; + TRANSLATE_NOOP("Pad", "Guitar"), nullptr, s_bindings, s_settings, Pad::VibrationCapabilities::NoVibration}; void PadGuitar::ConfigLog() { diff --git a/pcsx2/SIO/Pad/PadNotConnected.cpp b/pcsx2/SIO/Pad/PadNotConnected.cpp index b183823fe2..242c3b49f8 100644 --- a/pcsx2/SIO/Pad/PadNotConnected.cpp +++ b/pcsx2/SIO/Pad/PadNotConnected.cpp @@ -20,7 +20,7 @@ #include "Host.h" const Pad::ControllerInfo PadNotConnected::ControllerInfo = {Pad::ControllerType::NotConnected, "None", - TRANSLATE_NOOP("Pad", "Not Connected"), {}, {}, Pad::VibrationCapabilities::NoVibration }; + TRANSLATE_NOOP("Pad", "Not Connected"), nullptr, {}, {}, Pad::VibrationCapabilities::NoVibration }; PadNotConnected::PadNotConnected(u8 unifiedSlot, size_t ejectTicks) : PadBase(unifiedSlot, ejectTicks) diff --git a/pcsx2/SIO/Pad/PadTypes.h b/pcsx2/SIO/Pad/PadTypes.h index a639a1b469..0917a30227 100644 --- a/pcsx2/SIO/Pad/PadTypes.h +++ b/pcsx2/SIO/Pad/PadTypes.h @@ -92,6 +92,7 @@ namespace Pad ControllerType type; const char* name; const char* display_name; + const char* icon_name; std::span bindings; std::span settings; VibrationCapabilities vibration_caps; diff --git a/pcsx2/USB/usb-hid/usb-hid.cpp b/pcsx2/USB/usb-hid/usb-hid.cpp index 35d16224f1..5328f8143c 100644 --- a/pcsx2/USB/usb-hid/usb-hid.cpp +++ b/pcsx2/USB/usb-hid/usb-hid.cpp @@ -951,7 +951,7 @@ namespace usb_hid std::span HIDKbdDevice::Bindings(u32 subtype) const { static constexpr const InputBindingInfo info[] = { - {"Keyboard", TRANSLATE_NOOP("USB", "Keyboard"), InputBindingInfo::Type::Keyboard, 0, GenericInputBinding::Unknown}, + {"Keyboard", TRANSLATE_NOOP("USB", "Keyboard"), nullptr, InputBindingInfo::Type::Keyboard, 0, GenericInputBinding::Unknown}, }; return info; } @@ -1044,10 +1044,10 @@ namespace usb_hid std::span HIDMouseDevice::Bindings(u32 subtype) const { static constexpr const InputBindingInfo info[] = { - {"Pointer", TRANSLATE_NOOP("USB", "Pointer"), InputBindingInfo::Type::Pointer, INPUT_BUTTON__MAX, GenericInputBinding::Unknown}, - {"LeftButton", TRANSLATE_NOOP("USB", "Left Button"), InputBindingInfo::Type::Button, INPUT_BUTTON_LEFT, GenericInputBinding::Unknown}, - {"RightButton", TRANSLATE_NOOP("USB", "Right Button"), InputBindingInfo::Type::Button, INPUT_BUTTON_RIGHT, GenericInputBinding::Unknown}, - {"MiddleButton", TRANSLATE_NOOP("USB", "Middle Button"), InputBindingInfo::Type::Button, INPUT_BUTTON_MIDDLE, GenericInputBinding::Unknown}, + {"Pointer", TRANSLATE_NOOP("USB", "Pointer"), nullptr, InputBindingInfo::Type::Pointer, INPUT_BUTTON__MAX, GenericInputBinding::Unknown}, + {"LeftButton", TRANSLATE_NOOP("USB", "Left Button"), nullptr, InputBindingInfo::Type::Button, INPUT_BUTTON_LEFT, GenericInputBinding::Unknown}, + {"RightButton", TRANSLATE_NOOP("USB", "Right Button"), nullptr, InputBindingInfo::Type::Button, INPUT_BUTTON_RIGHT, GenericInputBinding::Unknown}, + {"MiddleButton", TRANSLATE_NOOP("USB", "Middle Button"), nullptr, InputBindingInfo::Type::Button, INPUT_BUTTON_MIDDLE, GenericInputBinding::Unknown}, }; return info; } diff --git a/pcsx2/USB/usb-lightgun/guncon2.cpp b/pcsx2/USB/usb-lightgun/guncon2.cpp index 0835e38583..40f0add0c5 100644 --- a/pcsx2/USB/usb-lightgun/guncon2.cpp +++ b/pcsx2/USB/usb-lightgun/guncon2.cpp @@ -587,25 +587,25 @@ namespace usb_lightgun { static constexpr const InputBindingInfo bindings[] = { //{"pointer", "Pointer/Aiming", InputBindingInfo::Type::Pointer, BID_POINTER_X, GenericInputBinding::Unknown}, - {"Up", TRANSLATE_NOOP("USB", "D-Pad Up"), InputBindingInfo::Type::Button, BID_DPAD_UP, GenericInputBinding::DPadUp}, - {"Down", TRANSLATE_NOOP("USB", "D-Pad Down"), InputBindingInfo::Type::Button, BID_DPAD_DOWN, GenericInputBinding::DPadDown}, - {"Left", TRANSLATE_NOOP("USB", "D-Pad Left"), InputBindingInfo::Type::Button, BID_DPAD_LEFT, GenericInputBinding::DPadLeft}, - {"Right", TRANSLATE_NOOP("USB", "D-Pad Right"), InputBindingInfo::Type::Button, BID_DPAD_RIGHT, + {"Up", TRANSLATE_NOOP("USB", "D-Pad Up"), nullptr, InputBindingInfo::Type::Button, BID_DPAD_UP, GenericInputBinding::DPadUp}, + {"Down", TRANSLATE_NOOP("USB", "D-Pad Down"), nullptr, InputBindingInfo::Type::Button, BID_DPAD_DOWN, GenericInputBinding::DPadDown}, + {"Left", TRANSLATE_NOOP("USB", "D-Pad Left"), nullptr, InputBindingInfo::Type::Button, BID_DPAD_LEFT, GenericInputBinding::DPadLeft}, + {"Right", TRANSLATE_NOOP("USB", "D-Pad Right"), nullptr, InputBindingInfo::Type::Button, BID_DPAD_RIGHT, GenericInputBinding::DPadRight}, - {"Trigger", TRANSLATE_NOOP("USB", "Trigger"), InputBindingInfo::Type::Button, BID_TRIGGER, GenericInputBinding::R2}, - {"ShootOffscreen", TRANSLATE_NOOP("USB", "Shoot Offscreen"), InputBindingInfo::Type::Button, BID_SHOOT_OFFSCREEN, + {"Trigger", TRANSLATE_NOOP("USB", "Trigger"), nullptr, InputBindingInfo::Type::Button, BID_TRIGGER, GenericInputBinding::R2}, + {"ShootOffscreen", TRANSLATE_NOOP("USB", "Shoot Offscreen"), nullptr, InputBindingInfo::Type::Button, BID_SHOOT_OFFSCREEN, GenericInputBinding::R1}, - {"Recalibrate", TRANSLATE_NOOP("USB", "Calibration Shot"), InputBindingInfo::Type::Button, BID_RECALIBRATE, + {"Recalibrate", TRANSLATE_NOOP("USB", "Calibration Shot"), nullptr, InputBindingInfo::Type::Button, BID_RECALIBRATE, GenericInputBinding::Unknown}, - {"A", TRANSLATE_NOOP("USB", "A"), InputBindingInfo::Type::Button, BID_A, GenericInputBinding::Cross}, - {"B", TRANSLATE_NOOP("USB", "B"), InputBindingInfo::Type::Button, BID_B, GenericInputBinding::Circle}, - {"C", TRANSLATE_NOOP("USB", "C"), InputBindingInfo::Type::Button, BID_C, GenericInputBinding::Triangle}, - {"Select", TRANSLATE_NOOP("USB", "Select"), InputBindingInfo::Type::Button, BID_SELECT, GenericInputBinding::Select}, - {"Start", TRANSLATE_NOOP("USB", "Start"), InputBindingInfo::Type::Button, BID_START, GenericInputBinding::Start}, - {"RelativeLeft", TRANSLATE_NOOP("USB", "Relative Left"), InputBindingInfo::Type::HalfAxis, BID_RELATIVE_LEFT, GenericInputBinding::Unknown}, - {"RelativeRight", TRANSLATE_NOOP("USB", "Relative Right"), InputBindingInfo::Type::HalfAxis, BID_RELATIVE_RIGHT, GenericInputBinding::Unknown}, - {"RelativeUp", TRANSLATE_NOOP("USB", "Relative Up"), InputBindingInfo::Type::HalfAxis, BID_RELATIVE_UP, GenericInputBinding::Unknown}, - {"RelativeDown", TRANSLATE_NOOP("USB", "Relative Down"), InputBindingInfo::Type::HalfAxis, BID_RELATIVE_DOWN, GenericInputBinding::Unknown}, + {"A", TRANSLATE_NOOP("USB", "A"), nullptr, InputBindingInfo::Type::Button, BID_A, GenericInputBinding::Cross}, + {"B", TRANSLATE_NOOP("USB", "B"), nullptr, InputBindingInfo::Type::Button, BID_B, GenericInputBinding::Circle}, + {"C", TRANSLATE_NOOP("USB", "C"), nullptr, InputBindingInfo::Type::Button, BID_C, GenericInputBinding::Triangle}, + {"Select", TRANSLATE_NOOP("USB", "Select"), nullptr, InputBindingInfo::Type::Button, BID_SELECT, GenericInputBinding::Select}, + {"Start", TRANSLATE_NOOP("USB", "Start"), nullptr, InputBindingInfo::Type::Button, BID_START, GenericInputBinding::Start}, + {"RelativeLeft", TRANSLATE_NOOP("USB", "Relative Left"), nullptr, InputBindingInfo::Type::HalfAxis, BID_RELATIVE_LEFT, GenericInputBinding::Unknown}, + {"RelativeRight", TRANSLATE_NOOP("USB", "Relative Right"), nullptr, InputBindingInfo::Type::HalfAxis, BID_RELATIVE_RIGHT, GenericInputBinding::Unknown}, + {"RelativeUp", TRANSLATE_NOOP("USB", "Relative Up"), nullptr, InputBindingInfo::Type::HalfAxis, BID_RELATIVE_UP, GenericInputBinding::Unknown}, + {"RelativeDown", TRANSLATE_NOOP("USB", "Relative Down"), nullptr, InputBindingInfo::Type::HalfAxis, BID_RELATIVE_DOWN, GenericInputBinding::Unknown}, }; return bindings; diff --git a/pcsx2/USB/usb-pad/usb-pad.cpp b/pcsx2/USB/usb-pad/usb-pad.cpp index fdd5aaaa70..575098b31d 100644 --- a/pcsx2/USB/usb-pad/usb-pad.cpp +++ b/pcsx2/USB/usb-pad/usb-pad.cpp @@ -86,25 +86,25 @@ namespace usb_pad case WT_GENERIC: { static constexpr const InputBindingInfo bindings[] = { - {"SteeringLeft", TRANSLATE_NOOP("USB", "Steering Left"), InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, - {"SteeringRight", TRANSLATE_NOOP("USB", "Steering Right"), InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, - {"Throttle", TRANSLATE_NOOP("USB", "Throttle"), InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::R2}, - {"Brake", TRANSLATE_NOOP("USB", "Brake"), InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::L2}, - {"DPadUp", TRANSLATE_NOOP("USB", "D-Pad Up"), InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp}, - {"DPadDown", TRANSLATE_NOOP("USB", "D-Pad Down"), InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown}, - {"DPadLeft", TRANSLATE_NOOP("USB", "D-Pad Left"), InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft}, - {"DPadRight", TRANSLATE_NOOP("USB", "D-Pad Right"), InputBindingInfo::Type::Button, CID_DPAD_RIGHT, GenericInputBinding::DPadRight}, - {"Cross", TRANSLATE_NOOP("USB", "Cross"), InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross}, - {"Square", TRANSLATE_NOOP("USB", "Square"), InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square}, - {"Circle", TRANSLATE_NOOP("USB", "Circle"), InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, - {"Triangle", TRANSLATE_NOOP("USB", "Triangle"), InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, - {"L1", TRANSLATE_NOOP("USB", "L1"), InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::L1}, - {"R1", TRANSLATE_NOOP("USB", "R1"), InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::R1}, - {"L2", TRANSLATE_NOOP("USB", "L2"), InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::Unknown}, // used L2 for brake - {"R2", TRANSLATE_NOOP("USB", "R2"), InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, // used R2 for throttle - {"Select", TRANSLATE_NOOP("USB", "Select"), InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, - {"Start", TRANSLATE_NOOP("USB", "Start"), InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, - {"FFDevice", TRANSLATE_NOOP("USB", "Force Feedback"), InputBindingInfo::Type::Device, 0, GenericInputBinding::Unknown}, + {"SteeringLeft", TRANSLATE_NOOP("USB", "Steering Left"), nullptr, InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, + {"SteeringRight", TRANSLATE_NOOP("USB", "Steering Right"), nullptr, InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, + {"Throttle", TRANSLATE_NOOP("USB", "Throttle"), nullptr, InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::R2}, + {"Brake", TRANSLATE_NOOP("USB", "Brake"), nullptr, InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::L2}, + {"DPadUp", TRANSLATE_NOOP("USB", "D-Pad Up"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp}, + {"DPadDown", TRANSLATE_NOOP("USB", "D-Pad Down"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown}, + {"DPadLeft", TRANSLATE_NOOP("USB", "D-Pad Left"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft}, + {"DPadRight", TRANSLATE_NOOP("USB", "D-Pad Right"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_RIGHT, GenericInputBinding::DPadRight}, + {"Cross", TRANSLATE_NOOP("USB", "Cross"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross}, + {"Square", TRANSLATE_NOOP("USB", "Square"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square}, + {"Circle", TRANSLATE_NOOP("USB", "Circle"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, + {"Triangle", TRANSLATE_NOOP("USB", "Triangle"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, + {"L1", TRANSLATE_NOOP("USB", "L1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::L1}, + {"R1", TRANSLATE_NOOP("USB", "R1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::R1}, + {"L2", TRANSLATE_NOOP("USB", "L2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::Unknown}, // used L2 for brake + {"R2", TRANSLATE_NOOP("USB", "R2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, // used R2 for throttle + {"Select", TRANSLATE_NOOP("USB", "Select"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, + {"Start", TRANSLATE_NOOP("USB", "Start"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, + {"FFDevice", TRANSLATE_NOOP("USB", "Force Feedback"), nullptr, InputBindingInfo::Type::Device, 0, GenericInputBinding::Unknown}, }; return bindings; @@ -114,27 +114,27 @@ namespace usb_pad case WT_DRIVING_FORCE_PRO_1102: { static constexpr const InputBindingInfo bindings[] = { - {"SteeringLeft", TRANSLATE_NOOP("USB", "Steering Left"), InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, - {"SteeringRight", TRANSLATE_NOOP("USB", "Steering Right"), InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, - {"Throttle", TRANSLATE_NOOP("USB", "Throttle"), InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::R2}, - {"Brake", TRANSLATE_NOOP("USB", "Brake"), InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::L2}, - {"DPadUp", TRANSLATE_NOOP("USB", "D-Pad Up"), InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp}, - {"DPadDown", TRANSLATE_NOOP("USB", "D-Pad Down"), InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown}, - {"DPadLeft", TRANSLATE_NOOP("USB", "D-Pad Left"), InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft}, - {"DPadRight", TRANSLATE_NOOP("USB", "D-Pad Right"), InputBindingInfo::Type::Button, CID_DPAD_RIGHT, GenericInputBinding::DPadRight}, - {"Cross", TRANSLATE_NOOP("USB", "Cross"), InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross}, - {"Square", TRANSLATE_NOOP("USB", "Square"), InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square}, - {"Circle", TRANSLATE_NOOP("USB", "Circle"), InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, - {"Triangle", TRANSLATE_NOOP("USB", "Triangle"), InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, - {"R1", TRANSLATE_NOOP("USB", "Shift Up / R1"), InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::R1}, - {"L1", TRANSLATE_NOOP("USB", "Shift Down / L1"), InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::L1}, - {"Select", TRANSLATE_NOOP("USB", "Select"), InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, - {"Start", TRANSLATE_NOOP("USB", "Start"), InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, - {"L2", TRANSLATE_NOOP("USB", "L2"), InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::Unknown}, // used L2 for brake - {"R2", TRANSLATE_NOOP("USB", "R2"), InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, // used R2 for throttle - {"L3", TRANSLATE_NOOP("USB", "L3"), InputBindingInfo::Type::Button, CID_BUTTON11, GenericInputBinding::L3}, - {"R3", TRANSLATE_NOOP("USB", "R3"), InputBindingInfo::Type::Button, CID_BUTTON10, GenericInputBinding::R3}, - {"FFDevice", "Force Feedback", InputBindingInfo::Type::Device, 0, GenericInputBinding::Unknown}, + {"SteeringLeft", TRANSLATE_NOOP("USB", "Steering Left"), nullptr, InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, + {"SteeringRight", TRANSLATE_NOOP("USB", "Steering Right"), nullptr, InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, + {"Throttle", TRANSLATE_NOOP("USB", "Throttle"), nullptr, InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::R2}, + {"Brake", TRANSLATE_NOOP("USB", "Brake"), nullptr, InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::L2}, + {"DPadUp", TRANSLATE_NOOP("USB", "D-Pad Up"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp}, + {"DPadDown", TRANSLATE_NOOP("USB", "D-Pad Down"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown}, + {"DPadLeft", TRANSLATE_NOOP("USB", "D-Pad Left"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft}, + {"DPadRight", TRANSLATE_NOOP("USB", "D-Pad Right"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_RIGHT, GenericInputBinding::DPadRight}, + {"Cross", TRANSLATE_NOOP("USB", "Cross"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross}, + {"Square", TRANSLATE_NOOP("USB", "Square"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square}, + {"Circle", TRANSLATE_NOOP("USB", "Circle"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, + {"Triangle", TRANSLATE_NOOP("USB", "Triangle"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, + {"R1", TRANSLATE_NOOP("USB", "Shift Up / R1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::R1}, + {"L1", TRANSLATE_NOOP("USB", "Shift Down / L1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::L1}, + {"Select", TRANSLATE_NOOP("USB", "Select"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, + {"Start", TRANSLATE_NOOP("USB", "Start"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, + {"L2", TRANSLATE_NOOP("USB", "L2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::Unknown}, // used L2 for brake + {"R2", TRANSLATE_NOOP("USB", "R2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, // used R2 for throttle + {"L3", TRANSLATE_NOOP("USB", "L3"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON11, GenericInputBinding::L3}, + {"R3", TRANSLATE_NOOP("USB", "R3"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON10, GenericInputBinding::R3}, + {"FFDevice", "Force Feedback", nullptr, InputBindingInfo::Type::Device, 0, GenericInputBinding::Unknown}, }; return bindings; @@ -143,17 +143,17 @@ namespace usb_pad case WT_GT_FORCE: { static constexpr const InputBindingInfo bindings[] = { - {"SteeringLeft", TRANSLATE_NOOP("USB", "Steering Left"), InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, - {"SteeringRight", TRANSLATE_NOOP("USB", "Steering Right"), InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, - {"Throttle", TRANSLATE_NOOP("USB", "Throttle"), InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::R2}, - {"Brake", TRANSLATE_NOOP("USB", "Brake"), InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::L2}, - {"MenuUp", TRANSLATE_NOOP("USB", "Menu Up"), InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::DPadUp}, - {"MenuDown", TRANSLATE_NOOP("USB", "Menu Down"), InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::DPadDown}, - {"X", TRANSLATE_NOOP("USB", "X"), InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Square}, - {"Y", TRANSLATE_NOOP("USB", "Y"), InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, - {"A", TRANSLATE_NOOP("USB", "A"), InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Cross}, - {"B", TRANSLATE_NOOP("USB", "B"), InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::Circle}, - {"FFDevice", TRANSLATE_NOOP("USB", "Force Feedback"), InputBindingInfo::Type::Device, 0, GenericInputBinding::Unknown}, + {"SteeringLeft", TRANSLATE_NOOP("USB", "Steering Left"), nullptr, InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, + {"SteeringRight", TRANSLATE_NOOP("USB", "Steering Right"), nullptr, InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, + {"Throttle", TRANSLATE_NOOP("USB", "Throttle"), nullptr, InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::R2}, + {"Brake", TRANSLATE_NOOP("USB", "Brake"), nullptr, InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::L2}, + {"MenuUp", TRANSLATE_NOOP("USB", "Menu Up"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::DPadUp}, + {"MenuDown", TRANSLATE_NOOP("USB", "Menu Down"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::DPadDown}, + {"X", TRANSLATE_NOOP("USB", "X"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Square}, + {"Y", TRANSLATE_NOOP("USB", "Y"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, + {"A", TRANSLATE_NOOP("USB", "A"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Cross}, + {"B", TRANSLATE_NOOP("USB", "B"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::Circle}, + {"FFDevice", TRANSLATE_NOOP("USB", "Force Feedback"), nullptr, InputBindingInfo::Type::Device, 0, GenericInputBinding::Unknown}, }; return bindings; @@ -941,13 +941,13 @@ namespace usb_pad std::span RBDrumKitDevice::Bindings(u32 subtype) const { static constexpr const InputBindingInfo bindings[] = { - {"Blue", TRANSLATE_NOOP("USB", "Blue"), InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::R1}, - {"Green", TRANSLATE_NOOP("USB", "Green"), InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Triangle}, - {"Red", TRANSLATE_NOOP("USB", "Red"), InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, - {"Yellow", TRANSLATE_NOOP("USB", "Yellow"), InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Square}, - {"Orange", TRANSLATE_NOOP("USB", "Orange"), InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Cross}, - {"Select", TRANSLATE_NOOP("USB", "Select"), InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, - {"Start", TRANSLATE_NOOP("USB", "Start"), InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, + {"Blue", TRANSLATE_NOOP("USB", "Blue"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::R1}, + {"Green", TRANSLATE_NOOP("USB", "Green"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Triangle}, + {"Red", TRANSLATE_NOOP("USB", "Red"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, + {"Yellow", TRANSLATE_NOOP("USB", "Yellow"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Square}, + {"Orange", TRANSLATE_NOOP("USB", "Orange"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Cross}, + {"Select", TRANSLATE_NOOP("USB", "Select"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, + {"Start", TRANSLATE_NOOP("USB", "Start"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, }; return bindings; @@ -978,29 +978,29 @@ namespace usb_pad std::span BuzzDevice::Bindings(u32 subtype) const { static constexpr const InputBindingInfo bindings[] = { - {"Red1", TRANSLATE_NOOP("USB", "Player 1 Red"), InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Circle}, - {"Blue1", TRANSLATE_NOOP("USB", "Player 1 Blue"), InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::R1}, - {"Orange1", TRANSLATE_NOOP("USB", "Player 1 Orange"), InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Cross}, - {"Green1", TRANSLATE_NOOP("USB", "Player 1 Green"), InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Triangle}, - {"Yellow1", TRANSLATE_NOOP("USB", "Player 1 Yellow"), InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square}, + {"Red1", TRANSLATE_NOOP("USB", "Player 1 Red"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Circle}, + {"Blue1", TRANSLATE_NOOP("USB", "Player 1 Blue"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::R1}, + {"Orange1", TRANSLATE_NOOP("USB", "Player 1 Orange"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Cross}, + {"Green1", TRANSLATE_NOOP("USB", "Player 1 Green"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Triangle}, + {"Yellow1", TRANSLATE_NOOP("USB", "Player 1 Yellow"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square}, - {"Red2", TRANSLATE_NOOP("USB", "Player 2 Red"), InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::Unknown}, - {"Blue2", TRANSLATE_NOOP("USB", "Player 2 Blue"), InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Unknown}, - {"Orange2", TRANSLATE_NOOP("USB", "Player 2 Orange"), InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Unknown}, - {"Green2", TRANSLATE_NOOP("USB", "Player 2 Green"), InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::Unknown}, - {"Yellow2", TRANSLATE_NOOP("USB", "Player 2 Yellow"), InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, + {"Red2", TRANSLATE_NOOP("USB", "Player 2 Red"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::Unknown}, + {"Blue2", TRANSLATE_NOOP("USB", "Player 2 Blue"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Unknown}, + {"Orange2", TRANSLATE_NOOP("USB", "Player 2 Orange"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Unknown}, + {"Green2", TRANSLATE_NOOP("USB", "Player 2 Green"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::Unknown}, + {"Yellow2", TRANSLATE_NOOP("USB", "Player 2 Yellow"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, - {"Red3", TRANSLATE_NOOP("USB", "Player 3 Red"), InputBindingInfo::Type::Button, CID_BUTTON10, GenericInputBinding::Unknown}, - {"Blue3", TRANSLATE_NOOP("USB", "Player 3 Blue"), InputBindingInfo::Type::Button, CID_BUTTON14, GenericInputBinding::Unknown}, - {"Orange3", TRANSLATE_NOOP("USB", "Player 3 Orange"), InputBindingInfo::Type::Button, CID_BUTTON13, GenericInputBinding::Unknown}, - {"Green3", TRANSLATE_NOOP("USB", "Player 3 Green"), InputBindingInfo::Type::Button, CID_BUTTON12, GenericInputBinding::Unknown}, - {"Yellow3", TRANSLATE_NOOP("USB", "Player 3 Yellow"), InputBindingInfo::Type::Button, CID_BUTTON11, GenericInputBinding::Unknown}, + {"Red3", TRANSLATE_NOOP("USB", "Player 3 Red"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON10, GenericInputBinding::Unknown}, + {"Blue3", TRANSLATE_NOOP("USB", "Player 3 Blue"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON14, GenericInputBinding::Unknown}, + {"Orange3", TRANSLATE_NOOP("USB", "Player 3 Orange"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON13, GenericInputBinding::Unknown}, + {"Green3", TRANSLATE_NOOP("USB", "Player 3 Green"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON12, GenericInputBinding::Unknown}, + {"Yellow3", TRANSLATE_NOOP("USB", "Player 3 Yellow"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON11, GenericInputBinding::Unknown}, - {"Red4", TRANSLATE_NOOP("USB", "Player 4 Red"), InputBindingInfo::Type::Button, CID_BUTTON15, GenericInputBinding::Unknown}, - {"Blue4", TRANSLATE_NOOP("USB", "Player 4 Blue"), InputBindingInfo::Type::Button, CID_BUTTON19, GenericInputBinding::Unknown}, - {"Orange4", TRANSLATE_NOOP("USB", "Player 4 Orange"), InputBindingInfo::Type::Button, CID_BUTTON18, GenericInputBinding::Unknown}, - {"Green4", TRANSLATE_NOOP("USB", "Player 4 Green"), InputBindingInfo::Type::Button, CID_BUTTON17, GenericInputBinding::Unknown}, - {"Yellow4", TRANSLATE_NOOP("USB", "Player 4 Yellow"), InputBindingInfo::Type::Button, CID_BUTTON16, GenericInputBinding::Unknown}, + {"Red4", TRANSLATE_NOOP("USB", "Player 4 Red"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON15, GenericInputBinding::Unknown}, + {"Blue4", TRANSLATE_NOOP("USB", "Player 4 Blue"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON19, GenericInputBinding::Unknown}, + {"Orange4", TRANSLATE_NOOP("USB", "Player 4 Orange"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON18, GenericInputBinding::Unknown}, + {"Green4", TRANSLATE_NOOP("USB", "Player 4 Green"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON17, GenericInputBinding::Unknown}, + {"Yellow4", TRANSLATE_NOOP("USB", "Player 4 Yellow"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON16, GenericInputBinding::Unknown}, }; return bindings; @@ -1052,35 +1052,35 @@ namespace usb_pad std::span KeyboardmaniaDevice::Bindings(u32 subtype) const { static constexpr const InputBindingInfo bindings[] = { - {"C1", TRANSLATE_NOOP("USB", "C 1"), InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Unknown}, - {"CSharp1", TRANSLATE_NOOP("USB", "C# 1"), InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Unknown}, - {"D1", TRANSLATE_NOOP("USB", "D 1"), InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Unknown}, - {"DSharp1", TRANSLATE_NOOP("USB", "D# 1"), InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Unknown}, - {"E1", TRANSLATE_NOOP("USB", "E 1"), InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Unknown}, - {"F1", TRANSLATE_NOOP("USB", "F 1"), InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::Unknown}, - {"FSharp1", TRANSLATE_NOOP("USB", "F# 1"), InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, - {"G1", TRANSLATE_NOOP("USB", "G 1"), InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Unknown}, - {"GSharp1", TRANSLATE_NOOP("USB", "G# 1"), InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Unknown}, - {"A1", TRANSLATE_NOOP("USB", "A 1"), InputBindingInfo::Type::Button, CID_BUTTON10, GenericInputBinding::Unknown}, - {"ASharp1", TRANSLATE_NOOP("USB", "A# 1"), InputBindingInfo::Type::Button, CID_BUTTON11, GenericInputBinding::Unknown}, - {"B1", TRANSLATE_NOOP("USB", "B 1"), InputBindingInfo::Type::Button, CID_BUTTON12, GenericInputBinding::Unknown}, - {"C2", TRANSLATE_NOOP("USB", "C 2"), InputBindingInfo::Type::Button, CID_BUTTON13, GenericInputBinding::Unknown}, - {"CSharp2", TRANSLATE_NOOP("USB", "C# 2"), InputBindingInfo::Type::Button, CID_BUTTON16, GenericInputBinding::Unknown}, - {"D2", TRANSLATE_NOOP("USB", "D 2"), InputBindingInfo::Type::Button, CID_BUTTON17, GenericInputBinding::Unknown}, - {"DSharp2", TRANSLATE_NOOP("USB", "D# 2"), InputBindingInfo::Type::Button, CID_BUTTON18, GenericInputBinding::Unknown}, - {"E2", TRANSLATE_NOOP("USB", "E 2"), InputBindingInfo::Type::Button, CID_BUTTON19, GenericInputBinding::Unknown}, - {"F2", TRANSLATE_NOOP("USB", "F 2"), InputBindingInfo::Type::Button, CID_BUTTON20, GenericInputBinding::Unknown}, - {"FSharp2", TRANSLATE_NOOP("USB", "F# 2"), InputBindingInfo::Type::Button, CID_BUTTON21, GenericInputBinding::Unknown}, - {"G2", TRANSLATE_NOOP("USB", "G 2"), InputBindingInfo::Type::Button, CID_BUTTON24, GenericInputBinding::Unknown}, - {"GSharp2", TRANSLATE_NOOP("USB", "G# 2"), InputBindingInfo::Type::Button, CID_BUTTON25, GenericInputBinding::Unknown}, - {"A2", TRANSLATE_NOOP("USB", "A 2"), InputBindingInfo::Type::Button, CID_BUTTON26, GenericInputBinding::Unknown}, - {"ASharp2", TRANSLATE_NOOP("USB", "A# 2"), InputBindingInfo::Type::Button, CID_BUTTON27, GenericInputBinding::Unknown}, - {"B2", TRANSLATE_NOOP("USB", "B 2"), InputBindingInfo::Type::Button, CID_BUTTON28, GenericInputBinding::Unknown}, + {"C1", TRANSLATE_NOOP("USB", "C 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Unknown}, + {"CSharp1", TRANSLATE_NOOP("USB", "C# 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Unknown}, + {"D1", TRANSLATE_NOOP("USB", "D 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Unknown}, + {"DSharp1", TRANSLATE_NOOP("USB", "D# 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Unknown}, + {"E1", TRANSLATE_NOOP("USB", "E 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Unknown}, + {"F1", TRANSLATE_NOOP("USB", "F 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::Unknown}, + {"FSharp1", TRANSLATE_NOOP("USB", "F# 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, + {"G1", TRANSLATE_NOOP("USB", "G 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Unknown}, + {"GSharp1", TRANSLATE_NOOP("USB", "G# 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Unknown}, + {"A1", TRANSLATE_NOOP("USB", "A 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON10, GenericInputBinding::Unknown}, + {"ASharp1", TRANSLATE_NOOP("USB", "A# 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON11, GenericInputBinding::Unknown}, + {"B1", TRANSLATE_NOOP("USB", "B 1"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON12, GenericInputBinding::Unknown}, + {"C2", TRANSLATE_NOOP("USB", "C 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON13, GenericInputBinding::Unknown}, + {"CSharp2", TRANSLATE_NOOP("USB", "C# 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON16, GenericInputBinding::Unknown}, + {"D2", TRANSLATE_NOOP("USB", "D 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON17, GenericInputBinding::Unknown}, + {"DSharp2", TRANSLATE_NOOP("USB", "D# 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON18, GenericInputBinding::Unknown}, + {"E2", TRANSLATE_NOOP("USB", "E 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON19, GenericInputBinding::Unknown}, + {"F2", TRANSLATE_NOOP("USB", "F 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON20, GenericInputBinding::Unknown}, + {"FSharp2", TRANSLATE_NOOP("USB", "F# 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON21, GenericInputBinding::Unknown}, + {"G2", TRANSLATE_NOOP("USB", "G 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON24, GenericInputBinding::Unknown}, + {"GSharp2", TRANSLATE_NOOP("USB", "G# 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON25, GenericInputBinding::Unknown}, + {"A2", TRANSLATE_NOOP("USB", "A 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON26, GenericInputBinding::Unknown}, + {"ASharp2", TRANSLATE_NOOP("USB", "A# 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON27, GenericInputBinding::Unknown}, + {"B2", TRANSLATE_NOOP("USB", "B 2"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON28, GenericInputBinding::Unknown}, - {"Start", TRANSLATE_NOOP("USB", "Start"), InputBindingInfo::Type::Button, CID_BUTTON22, GenericInputBinding::Unknown}, - {"Select", TRANSLATE_NOOP("USB", "Select"), InputBindingInfo::Type::Button, CID_BUTTON14, GenericInputBinding::Unknown}, - {"WheelUp", TRANSLATE_NOOP("USB", "Wheel Up"), InputBindingInfo::Type::Button, CID_BUTTON29, GenericInputBinding::Unknown}, - {"WheelDown", TRANSLATE_NOOP("USB", "Wheel Down"), InputBindingInfo::Type::Button, CID_BUTTON30, GenericInputBinding::Unknown}, + {"Start", TRANSLATE_NOOP("USB", "Start"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON22, GenericInputBinding::Unknown}, + {"Select", TRANSLATE_NOOP("USB", "Select"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON14, GenericInputBinding::Unknown}, + {"WheelUp", TRANSLATE_NOOP("USB", "Wheel Up"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON29, GenericInputBinding::Unknown}, + {"WheelDown", TRANSLATE_NOOP("USB", "Wheel Down"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON30, GenericInputBinding::Unknown}, }; return bindings; diff --git a/pcsx2/USB/usb-pad/usb-seamic.cpp b/pcsx2/USB/usb-pad/usb-seamic.cpp index 0e9a11517f..8aac62820d 100644 --- a/pcsx2/USB/usb-pad/usb-seamic.cpp +++ b/pcsx2/USB/usb-pad/usb-seamic.cpp @@ -362,24 +362,24 @@ namespace usb_pad { // TODO: This is likely wrong. Someone who cares can fix it. static constexpr const InputBindingInfo bindings[] = { - {"StickLeft", TRANSLATE_NOOP("USB", "Stick Left"), InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, - {"StickRight", TRANSLATE_NOOP("USB", "Stick Right"), InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, - {"StickUp", TRANSLATE_NOOP("USB", "Stick Up"), InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::LeftStickUp}, - {"StickDown", TRANSLATE_NOOP("USB", "Stick Down"), InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::LeftStickDown}, - {"A", TRANSLATE_NOOP("USB", "A"), InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross}, - {"B", TRANSLATE_NOOP("USB", "B"), InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Circle}, - {"C", TRANSLATE_NOOP("USB", "C"), InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::R2}, - {"X", TRANSLATE_NOOP("USB", "X"), InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Square}, - {"Y", TRANSLATE_NOOP("USB", "Y"), InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Triangle}, - {"Z", TRANSLATE_NOOP("USB", "Z"), InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::L2}, - {"L", TRANSLATE_NOOP("USB", "L"), InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::L1}, - {"R", TRANSLATE_NOOP("USB", "R"), InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::R1}, - {"Select", TRANSLATE_NOOP("USB", "Select"), InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, - {"Start", TRANSLATE_NOOP("USB", "Start"), InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, - {"DPadUp", TRANSLATE_NOOP("USB", "D-Pad Up"), InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp}, - {"DPadDown", TRANSLATE_NOOP("USB", "D-Pad Down"), InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown}, - {"DPadLeft", TRANSLATE_NOOP("USB", "D-Pad Left"), InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft}, - {"DPadRight", TRANSLATE_NOOP("USB", "D-Pad Right"), InputBindingInfo::Type::Button, CID_DPAD_RIGHT, GenericInputBinding::DPadRight}, + {"StickLeft", TRANSLATE_NOOP("USB", "Stick Left"), nullptr, InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, + {"StickRight", TRANSLATE_NOOP("USB", "Stick Right"), nullptr, InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, + {"StickUp", TRANSLATE_NOOP("USB", "Stick Up"), nullptr, InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::LeftStickUp}, + {"StickDown", TRANSLATE_NOOP("USB", "Stick Down"), nullptr, InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::LeftStickDown}, + {"A", TRANSLATE_NOOP("USB", "A"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross}, + {"B", TRANSLATE_NOOP("USB", "B"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Circle}, + {"C", TRANSLATE_NOOP("USB", "C"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::R2}, + {"X", TRANSLATE_NOOP("USB", "X"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Square}, + {"Y", TRANSLATE_NOOP("USB", "Y"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Triangle}, + {"Z", TRANSLATE_NOOP("USB", "Z"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::L2}, + {"L", TRANSLATE_NOOP("USB", "L"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::L1}, + {"R", TRANSLATE_NOOP("USB", "R"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::R1}, + {"Select", TRANSLATE_NOOP("USB", "Select"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, + {"Start", TRANSLATE_NOOP("USB", "Start"), nullptr, InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, + {"DPadUp", TRANSLATE_NOOP("USB", "D-Pad Up"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp}, + {"DPadDown", TRANSLATE_NOOP("USB", "D-Pad Down"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown}, + {"DPadLeft", TRANSLATE_NOOP("USB", "D-Pad Left"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft}, + {"DPadRight", TRANSLATE_NOOP("USB", "D-Pad Right"), nullptr, InputBindingInfo::Type::Button, CID_DPAD_RIGHT, GenericInputBinding::DPadRight}, }; return bindings; diff --git a/pcsx2/USB/usb-pad/usb-turntable.cpp b/pcsx2/USB/usb-pad/usb-turntable.cpp index 297227476f..861ddbe918 100644 --- a/pcsx2/USB/usb-pad/usb-turntable.cpp +++ b/pcsx2/USB/usb-pad/usb-turntable.cpp @@ -444,30 +444,30 @@ namespace usb_pad std::span DJTurntableDevice::Bindings(u32 subtype) const { static constexpr const InputBindingInfo bindings[] = { - {"DPadUp", TRANSLATE_NOOP("USB", "D-Pad Up"), InputBindingInfo::Type::Button, CID_DJ_DPAD_UP, GenericInputBinding::DPadUp}, - {"DPadDown", TRANSLATE_NOOP("USB", "D-Pad Down"), InputBindingInfo::Type::Button, CID_DJ_DPAD_DOWN, GenericInputBinding::DPadDown}, - {"DPadLeft", TRANSLATE_NOOP("USB", "D-Pad Left"), InputBindingInfo::Type::Button, CID_DJ_DPAD_LEFT, GenericInputBinding::DPadLeft}, - {"DPadRight", TRANSLATE_NOOP("USB", "D-Pad Right"), InputBindingInfo::Type::Button, CID_DJ_DPAD_RIGHT, GenericInputBinding::DPadRight}, - {"Square", TRANSLATE_NOOP("USB", "Square"), InputBindingInfo::Type::Button, CID_DJ_SQUARE, GenericInputBinding::Unknown}, - {"Cross", TRANSLATE_NOOP("USB", "Cross"), InputBindingInfo::Type::Button, CID_DJ_CROSS, GenericInputBinding::Unknown}, - {"Circle", TRANSLATE_NOOP("USB", "Circle"), InputBindingInfo::Type::Button, CID_DJ_CIRCLE, GenericInputBinding::Unknown}, - {"Triangle", TRANSLATE_NOOP("USB", "Triangle / Euphoria"), InputBindingInfo::Type::Button, CID_DJ_TRIANGLE, GenericInputBinding::Triangle}, - {"Select", TRANSLATE_NOOP("USB", "Select"), InputBindingInfo::Type::Button, CID_DJ_SELECT, GenericInputBinding::Select}, - {"Start", TRANSLATE_NOOP("USB", "Start"), InputBindingInfo::Type::Button, CID_DJ_START, GenericInputBinding::Start}, - {"CrossFaderLeft", TRANSLATE_NOOP("USB", "Crossfader Left"), InputBindingInfo::Type::HalfAxis, CID_DJ_CROSSFADER_LEFT, GenericInputBinding::RightStickDown}, - {"CrossFaderRight", TRANSLATE_NOOP("USB", "Crossfader Right"), InputBindingInfo::Type::HalfAxis, CID_DJ_CROSSFADER_RIGHT, GenericInputBinding::RightStickUp}, - {"EffectsKnobLeft", TRANSLATE_NOOP("USB", "Effects Knob Left"), InputBindingInfo::Type::HalfAxis, CID_DJ_EFFECTSKNOB_LEFT, GenericInputBinding::RightStickLeft}, - {"EffectsKnobRight", TRANSLATE_NOOP("USB", "Effects Knob Right"), InputBindingInfo::Type::HalfAxis, CID_DJ_EFFECTSKNOB_RIGHT, GenericInputBinding::RightStickRight}, - {"LeftTurntableCW", TRANSLATE_NOOP("USB", "Left Turntable Clockwise"), InputBindingInfo::Type::HalfAxis, CID_DJ_LEFT_TURNTABLE_CW, GenericInputBinding::LeftStickRight}, - {"LeftTurntableCCW", TRANSLATE_NOOP("USB", "Left Turntable Counterclockwise"), InputBindingInfo::Type::HalfAxis, CID_DJ_LEFT_TURNTABLE_CCW, GenericInputBinding::LeftStickLeft}, - {"RightTurntableCW", TRANSLATE_NOOP("USB", "Right Turntable Clockwise"), InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_CW, GenericInputBinding::LeftStickDown}, - {"RightTurntableCCW", TRANSLATE_NOOP("USB", "Right Turntable Counterclockwise"), InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_CCW, GenericInputBinding::LeftStickUp}, - {"LeftTurntableGreen", TRANSLATE_NOOP("USB", "Left Turntable Green"), InputBindingInfo::Type::Button, CID_DJ_LEFT_GREEN, GenericInputBinding::Unknown}, - {"LeftTurntableRed", TRANSLATE_NOOP("USB", "Left Turntable Red"), InputBindingInfo::Type::Button, CID_DJ_LEFT_RED, GenericInputBinding::Unknown}, - {"LeftTurntableBlue", TRANSLATE_NOOP("USB", "Left Turntable Blue"), InputBindingInfo::Type::Button, CID_DJ_LEFT_BLUE, GenericInputBinding::Unknown}, - {"RightTurntableGreen", TRANSLATE_NOOP("USB", "Right Turntable Green"), InputBindingInfo::Type::Button, CID_DJ_RIGHT_GREEN, GenericInputBinding::Cross}, - {"RightTurntableRed", TRANSLATE_NOOP("USB", "Right Turntable Red "), InputBindingInfo::Type::Button, CID_DJ_RIGHT_RED, GenericInputBinding::Circle}, - {"RightTurntableBlue", TRANSLATE_NOOP("USB", "Right Turntable Blue"), InputBindingInfo::Type::Button, CID_DJ_RIGHT_BLUE, GenericInputBinding::Square} + {"DPadUp", TRANSLATE_NOOP("USB", "D-Pad Up"), nullptr, InputBindingInfo::Type::Button, CID_DJ_DPAD_UP, GenericInputBinding::DPadUp}, + {"DPadDown", TRANSLATE_NOOP("USB", "D-Pad Down"), nullptr, InputBindingInfo::Type::Button, CID_DJ_DPAD_DOWN, GenericInputBinding::DPadDown}, + {"DPadLeft", TRANSLATE_NOOP("USB", "D-Pad Left"), nullptr, InputBindingInfo::Type::Button, CID_DJ_DPAD_LEFT, GenericInputBinding::DPadLeft}, + {"DPadRight", TRANSLATE_NOOP("USB", "D-Pad Right"), nullptr, InputBindingInfo::Type::Button, CID_DJ_DPAD_RIGHT, GenericInputBinding::DPadRight}, + {"Square", TRANSLATE_NOOP("USB", "Square"), nullptr, InputBindingInfo::Type::Button, CID_DJ_SQUARE, GenericInputBinding::Unknown}, + {"Cross", TRANSLATE_NOOP("USB", "Cross"), nullptr, InputBindingInfo::Type::Button, CID_DJ_CROSS, GenericInputBinding::Unknown}, + {"Circle", TRANSLATE_NOOP("USB", "Circle"), nullptr, InputBindingInfo::Type::Button, CID_DJ_CIRCLE, GenericInputBinding::Unknown}, + {"Triangle", TRANSLATE_NOOP("USB", "Triangle / Euphoria"), nullptr, InputBindingInfo::Type::Button, CID_DJ_TRIANGLE, GenericInputBinding::Triangle}, + {"Select", TRANSLATE_NOOP("USB", "Select"), nullptr, InputBindingInfo::Type::Button, CID_DJ_SELECT, GenericInputBinding::Select}, + {"Start", TRANSLATE_NOOP("USB", "Start"), nullptr, InputBindingInfo::Type::Button, CID_DJ_START, GenericInputBinding::Start}, + {"CrossFaderLeft", TRANSLATE_NOOP("USB", "Crossfader Left"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_CROSSFADER_LEFT, GenericInputBinding::RightStickDown}, + {"CrossFaderRight", TRANSLATE_NOOP("USB", "Crossfader Right"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_CROSSFADER_RIGHT, GenericInputBinding::RightStickUp}, + {"EffectsKnobLeft", TRANSLATE_NOOP("USB", "Effects Knob Left"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_EFFECTSKNOB_LEFT, GenericInputBinding::RightStickLeft}, + {"EffectsKnobRight", TRANSLATE_NOOP("USB", "Effects Knob Right"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_EFFECTSKNOB_RIGHT, GenericInputBinding::RightStickRight}, + {"LeftTurntableCW", TRANSLATE_NOOP("USB", "Left Turntable Clockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_LEFT_TURNTABLE_CW, GenericInputBinding::LeftStickRight}, + {"LeftTurntableCCW", TRANSLATE_NOOP("USB", "Left Turntable Counterclockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_LEFT_TURNTABLE_CCW, GenericInputBinding::LeftStickLeft}, + {"RightTurntableCW", TRANSLATE_NOOP("USB", "Right Turntable Clockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_CW, GenericInputBinding::LeftStickDown}, + {"RightTurntableCCW", TRANSLATE_NOOP("USB", "Right Turntable Counterclockwise"), nullptr, InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_CCW, GenericInputBinding::LeftStickUp}, + {"LeftTurntableGreen", TRANSLATE_NOOP("USB", "Left Turntable Green"), nullptr, InputBindingInfo::Type::Button, CID_DJ_LEFT_GREEN, GenericInputBinding::Unknown}, + {"LeftTurntableRed", TRANSLATE_NOOP("USB", "Left Turntable Red"), nullptr, InputBindingInfo::Type::Button, CID_DJ_LEFT_RED, GenericInputBinding::Unknown}, + {"LeftTurntableBlue", TRANSLATE_NOOP("USB", "Left Turntable Blue"), nullptr, InputBindingInfo::Type::Button, CID_DJ_LEFT_BLUE, GenericInputBinding::Unknown}, + {"RightTurntableGreen", TRANSLATE_NOOP("USB", "Right Turntable Green"), nullptr, InputBindingInfo::Type::Button, CID_DJ_RIGHT_GREEN, GenericInputBinding::Cross}, + {"RightTurntableRed", TRANSLATE_NOOP("USB", "Right Turntable Red "), nullptr, InputBindingInfo::Type::Button, CID_DJ_RIGHT_RED, GenericInputBinding::Circle}, + {"RightTurntableBlue", TRANSLATE_NOOP("USB", "Right Turntable Blue"), nullptr, InputBindingInfo::Type::Button, CID_DJ_RIGHT_BLUE, GenericInputBinding::Square} }; diff --git a/tests/ctest/core/StubHost.cpp b/tests/ctest/core/StubHost.cpp index 11ee4fe00e..721f1d7f87 100644 --- a/tests/ctest/core/StubHost.cpp +++ b/tests/ctest/core/StubHost.cpp @@ -226,5 +226,10 @@ std::optional InputManager::ConvertHostKeyboardCodeToString(u32 cod return std::nullopt; } +const char* InputManager::ConvertHostKeyboardCodeToIcon(u32 code) +{ + return nullptr; +} + BEGIN_HOTKEY_LIST(g_host_hotkeys) END_HOTKEY_LIST() \ No newline at end of file diff --git a/tools/generate_fullscreen_ui_translation_strings.py b/tools/generate_fullscreen_ui_translation_strings.py index 119717be08..bfc944d425 100644 --- a/tools/generate_fullscreen_ui_translation_strings.py +++ b/tools/generate_fullscreen_ui_translation_strings.py @@ -9,7 +9,7 @@ with open(src_file, "r") as f: full_source = f.read() strings = [] -for token in ["FSUI_STR", "FSUI_CSTR", "FSUI_FSTR", "FSUI_NSTR", "FSUI_ICONSTR", "FSUI_ICONSTR_S"]: +for token in ["FSUI_STR", "FSUI_CSTR", "FSUI_FSTR", "FSUI_NSTR", "FSUI_VSTR", "FSUI_ICONSTR", "FSUI_ICONSTR_S"]: token_len = len(token) last_pos = 0 while True: diff --git a/tools/generate_update_fa_glyph_ranges.py b/tools/generate_update_fa_glyph_ranges.py index 00a5141523..115f4c2b2d 100644 --- a/tools/generate_update_fa_glyph_ranges.py +++ b/tools/generate_update_fa_glyph_ranges.py @@ -24,13 +24,16 @@ import functools src_dirs = [os.path.join(os.path.dirname(__file__), "..", "pcsx2"), os.path.join(os.path.dirname(__file__), "..", "pcsx2-qt")] fa_file = os.path.join(os.path.dirname(__file__), "..", "3rdparty", "include", "IconsFontAwesome5.h") +pf_file = os.path.join(os.path.dirname(__file__), "..", "3rdparty", "include", "IconsPromptFont.h") dst_file = os.path.join(os.path.dirname(__file__), "..", "pcsx2", "ImGui", "ImGuiManager.cpp") + all_source_files = list(functools.reduce(lambda prev, src_dir: prev + glob.glob(os.path.join(src_dir, "**", "*.cpp"), recursive=True) + \ glob.glob(os.path.join(src_dir, "**", "*.h"), recursive=True) + \ glob.glob(os.path.join(src_dir, "**", "*.inl"), recursive=True), src_dirs, [])) tokens = set() +pf_tokens = set() for filename in all_source_files: data = None with open(filename, "r") as f: @@ -40,9 +43,10 @@ for filename in all_source_files: continue tokens = tokens.union(set(re.findall("(ICON_FA_[a-zA-Z0-9_]+)", data))) + pf_tokens = pf_tokens.union(set(re.findall("(ICON_PF_[a-zA-Z0-9_]+)", data))) -print("{} tokens found.".format(len(tokens))) -if len(tokens) == 0: +print("{}/{} tokens found.".format(len(tokens), len(pf_tokens))) +if len(tokens) == 0 and len(pf_tokens) == 0: sys.exit(0) u8_encodings = {} @@ -52,44 +56,53 @@ with open(fa_file, "r") as f: if match is None: continue u8_encodings[match[1]] = bytes.fromhex(match[2].replace("\\x", "")) +with open(pf_file, "r") as f: + for line in f.readlines(): + match = re.match("#define (ICON_PF_[^ ]+) \"([^\"]+)\"", line) + if match is None: + continue + u8_encodings[match[1]] = bytes.fromhex(match[2].replace("\\x", "")) out_pattern = "(static constexpr ImWchar range_fa\[\] = \{)[0-9A-Z_a-z, \n]+(\};)" +out_pf_pattern = "(static constexpr ImWchar range_pf\[\] = \{)[0-9A-Z_a-z, \n]+(\};)" -codepoints = list() -for token in tokens: - u8_bytes = u8_encodings[token] - u8 = str(u8_bytes, "utf-8") - u16 = u8.encode("utf-16le") - if len(u16) > 2: - raise ValueError("{} too long".format(u8_bytes)) +def get_pairs(tokens): + codepoints = list() + for token in tokens: + u8_bytes = u8_encodings[token] + u8 = str(u8_bytes, "utf-8") + u16 = u8.encode("utf-16le") + if len(u16) > 2: + raise ValueError("{} {} too long".format(u8_bytes, token)) - codepoint = int.from_bytes(u16, byteorder="little", signed=False) - codepoints.append(codepoint) -codepoints.sort() -codepoints.append(0) # null terminator + codepoint = int.from_bytes(u16, byteorder="little", signed=False) + codepoints.append(codepoint) + codepoints.sort() + codepoints.append(0) # null terminator -startc = codepoints[0] -endc = None -pairs = [startc] -for codepoint in codepoints: - if endc is not None and (endc + 1) != codepoint: - pairs.append(endc) - pairs.append(codepoint) - startc = codepoint - endc = codepoint - else: - endc = codepoint -pairs.append(endc) + startc = codepoints[0] + endc = None + pairs = [startc] + for codepoint in codepoints: + if endc is not None and (endc + 1) != codepoint: + pairs.append(endc) + pairs.append(codepoint) + startc = codepoint + endc = codepoint + else: + endc = codepoint + pairs.append(endc) -pairs_str = ",".join(map("0x{:x}".format, pairs)) + pairs_str = ",".join(list(map("0x{:x}".format, pairs))) + return pairs_str with open(dst_file, "r") as f: original = f.read() - updated = re.sub(out_pattern, "\\1 " + pairs_str + " \\2", original) + updated = re.sub(out_pattern, "\\1 " + get_pairs(tokens) + " \\2", original) + updated = re.sub(out_pf_pattern, "\\1 " + get_pairs(pf_tokens) + " \\2", original) if original != updated: with open(dst_file, "w") as f: f.write(updated) print("Updated {}".format(dst_file)) else: - print("Skipping updating {}".format(dst_file)) - + print("Skipping updating {}".format(dst_file)) \ No newline at end of file