| 728 | } |
| 729 | |
| 730 | std::optional<InputBindingKey> SDLInputSource::ParseKeyString(const std::string_view device, const std::string_view binding) |
| 731 | { |
| 732 | if (!device.starts_with("SDL-") || binding.empty()) |
| 733 | return std::nullopt; |
| 734 | |
| 735 | const std::optional<s32> player_id = StringUtil::FromChars<s32>(device.substr(4)); |
| 736 | if (!player_id.has_value() || player_id.value() < 0) |
| 737 | return std::nullopt; |
| 738 | |
| 739 | InputBindingKey key = {}; |
| 740 | key.source_type = InputSourceType::SDL; |
| 741 | key.source_index = static_cast<u32>(player_id.value()); |
| 742 | |
| 743 | // SDL2-SDL3 migrations |
| 744 | static constexpr const char* sdl_button_legacy_names[] = { |
| 745 | "A", // SDL_CONTROLLER_BUTTON_A |
| 746 | "B", // SDL_CONTROLLER_BUTTON_B |
| 747 | "X", // SDL_CONTROLLER_BUTTON_X |
| 748 | "Y", // SDL_CONTROLLER_BUTTON_Y |
| 749 | }; |
| 750 | |
| 751 | for (u32 i = 0; i < std::size(sdl_button_legacy_names); i++) |
| 752 | { |
| 753 | if (binding == sdl_button_legacy_names[i]) |
| 754 | { |
| 755 | key.source_subtype = InputSubclass::ControllerButton; |
| 756 | key.data = i; |
| 757 | |
| 758 | // SDL2 would map A/B/X/Y based on the button's label |
| 759 | // We need to convert this to a positional binding for SDL3 |
| 760 | static constexpr SDL_GamepadButton face_button_pos[] = { |
| 761 | SDL_GAMEPAD_BUTTON_SOUTH, |
| 762 | SDL_GAMEPAD_BUTTON_EAST, |
| 763 | SDL_GAMEPAD_BUTTON_WEST, |
| 764 | SDL_GAMEPAD_BUTTON_NORTH, |
| 765 | }; |
| 766 | |
| 767 | // This migrations needs to inspect the controller |
| 768 | { |
| 769 | std::lock_guard lock(m_controllers_key_mutex); |
| 770 | |
| 771 | auto it = GetControllerDataForPlayerId(key.source_index); |
| 772 | if (it != m_controllers.end() && it->gamepad) |
| 773 | { |
| 774 | static bool shown_prompt = false; |
| 775 | for (u32 pos = 0; pos < std::size(face_button_pos); pos++) |
| 776 | { |
| 777 | // A/B/X/Y are equal to 1/2/3/4 in SDL_GamepadButtonLabel |
| 778 | // PS controllers have positional A/B/X/Y, so don't need adjusting |
| 779 | // Controllers with unknown labels are assumed to have positional A/B/X/Y |
| 780 | const SDL_GamepadButtonLabel label = SDL_GetGamepadButtonLabel(it->gamepad, face_button_pos[pos]); |
| 781 | if (key.data == (label - 1)) |
| 782 | { |
| 783 | if (key.data != pos) |
| 784 | { |
| 785 | if (!shown_prompt) |
| 786 | { |
| 787 | shown_prompt = true; |