diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index 111666055c..d5247b500d 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -103,9 +103,9 @@ public: , _windowManager(CreateWindowManager()) , _keyboardShortcuts(env) { - if (SDL_Init(SDL_INIT_VIDEO) < 0) + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { - SDLException::Throw("SDL_Init(SDL_INIT_VIDEO)"); + SDLException::Throw("SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)"); } _cursorRepository.LoadCursors(); _keyboardShortcuts.Reset(); @@ -567,7 +567,11 @@ public: _textComposition.HandleMessage(&e); break; default: + { + auto& inputManager = GetInputManager(); + inputManager.QueueInputEvent(e); break; + } } } diff --git a/src/openrct2-ui/input/InputManager.cpp b/src/openrct2-ui/input/InputManager.cpp index 1fbf05c834..23caea0e62 100644 --- a/src/openrct2-ui/input/InputManager.cpp +++ b/src/openrct2-ui/input/InputManager.cpp @@ -23,13 +23,76 @@ using namespace OpenRCT2::Ui; +void InputManager::QueueInputEvent(const SDL_Event& e) +{ + switch (e.type) + { + case SDL_JOYHATMOTION: + { + if (e.jhat.value != SDL_HAT_CENTERED) + { + InputEvent ie; + ie.DeviceKind = InputDeviceKind::JoyHat; + ie.Modifiers = SDL_GetModState(); + ie.Button = e.jhat.value; + ie.State = InputEventState::Down; + QueueInputEvent(std::move(ie)); + } + break; + } + case SDL_JOYBUTTONDOWN: + { + InputEvent ie; + ie.DeviceKind = InputDeviceKind::JoyButton; + ie.Modifiers = SDL_GetModState(); + ie.Button = e.jbutton.button; + ie.State = InputEventState::Down; + QueueInputEvent(std::move(ie)); + break; + } + case SDL_JOYBUTTONUP: + { + InputEvent ie; + ie.DeviceKind = InputDeviceKind::JoyButton; + ie.Modifiers = SDL_GetModState(); + ie.Button = e.jbutton.button; + ie.State = InputEventState::Release; + QueueInputEvent(std::move(ie)); + break; + } + } +} + void InputManager::QueueInputEvent(InputEvent&& e) { _events.push(e); } +void InputManager::CheckJoysticks() +{ + constexpr uint32_t CHECK_INTERVAL_MS = 5000; + + auto tick = SDL_GetTicks(); + if (tick > _lastJoystickCheck + CHECK_INTERVAL_MS) + { + _lastJoystickCheck = tick; + + _joysticks.clear(); + auto numJoysticks = SDL_NumJoysticks(); + for (auto i = 0; i < numJoysticks; i++) + { + auto joystick = SDL_JoystickOpen(i); + if (joystick != nullptr) + { + _joysticks.push_back(joystick); + } + } + } +} + void InputManager::Process() { + CheckJoysticks(); HandleModifiers(); ProcessEvents(); HandleViewScrolling(); diff --git a/src/openrct2-ui/input/InputManager.h b/src/openrct2-ui/input/InputManager.h index 67d9536d7e..9ba9ba77b4 100644 --- a/src/openrct2-ui/input/InputManager.h +++ b/src/openrct2-ui/input/InputManager.h @@ -12,13 +12,17 @@ #include #include +typedef struct _SDL_Joystick SDL_Joystick; +typedef union SDL_Event SDL_Event; + namespace OpenRCT2::Ui { enum class InputDeviceKind { Mouse, Keyboard, - Gamepad, + JoyButton, + JoyHat, }; enum class InputEventState @@ -38,9 +42,13 @@ namespace OpenRCT2::Ui class InputManager { private: + uint32_t _lastJoystickCheck; + std::vector _joysticks; std::queue _events; ScreenCoordsXY _viewScroll; + void CheckJoysticks(); + void HandleViewScrolling(); void HandleModifiers(); void ProcessEvents(); @@ -49,6 +57,7 @@ namespace OpenRCT2::Ui void ProcessChat(const InputEvent& e); public: + void QueueInputEvent(const SDL_Event& e); void QueueInputEvent(InputEvent&& e); void Process(); }; diff --git a/src/openrct2-ui/input/ShortcutManager.cpp b/src/openrct2-ui/input/ShortcutManager.cpp index d7de5d426d..2e4f31423a 100644 --- a/src/openrct2-ui/input/ShortcutManager.cpp +++ b/src/openrct2-ui/input/ShortcutManager.cpp @@ -118,8 +118,8 @@ ShortcutInput::ShortcutInput(const std::string_view& value) sepIndex = FindPlus(value, index); } - auto kind = ShortcutInputKind::Keyboard; - auto key = 0u; + auto kind = InputDeviceKind::Keyboard; + auto button = 0u; auto colonIndex = value.find(':', index); if (colonIndex != std::string::npos) { @@ -127,18 +127,18 @@ ShortcutInput::ShortcutInput(const std::string_view& value) if (device == "MOUSE") { auto rem = std::string(value.substr(colonIndex + 1)); - kind = ShortcutInputKind::Mouse; - key = atoi(rem.c_str()); + kind = InputDeviceKind::Mouse; + button = atoi(rem.c_str()); } } else { - key = ParseKey(value.substr(index)); + button = ParseKey(value.substr(index)); } Kind = kind; Modifiers = modifiers; - Key = key; + Button = button; } std::string ShortcutInput::ToString() const @@ -149,9 +149,9 @@ std::string ShortcutInput::ToString() const AppendModifier(result, "ALT", KMOD_LALT, KMOD_RALT); AppendModifier(result, "GUI", KMOD_LGUI, KMOD_RGUI); - if (Kind == ShortcutInputKind::Keyboard) + if (Kind == InputDeviceKind::Keyboard) { - switch (Key) + switch (Button) { case 0: break; @@ -227,21 +227,21 @@ std::string ShortcutInput::ToString() const break; default: - if (Key & SDLK_SCANCODE_MASK) + if (Button & SDLK_SCANCODE_MASK) { - auto name = SDL_GetScancodeName(static_cast(Key & ~SDLK_SCANCODE_MASK)); + auto name = SDL_GetScancodeName(static_cast(Button & ~SDLK_SCANCODE_MASK)); result += name; } else { - String::AppendCodepoint(result, std::toupper(Key)); + String::AppendCodepoint(result, std::toupper(Button)); } break; } } - else if (Kind == ShortcutInputKind::Mouse) + else if (Kind == InputDeviceKind::Mouse) { - switch (Key) + switch (Button) { case 0: result += "LMB"; @@ -251,10 +251,28 @@ std::string ShortcutInput::ToString() const break; default: result += "MOUSE "; - result += std::to_string(Key); + result += std::to_string(Button + 1); break; } } + else if (Kind == InputDeviceKind::JoyButton) + { + result += "JOY "; + result += std::to_string(Button + 1); + } + else if (Kind == InputDeviceKind::JoyHat) + { + if (Button & SDL_HAT_LEFT) + result += "JOY LEFT"; + else if (Button & SDL_HAT_RIGHT) + result += "JOY RIGHT"; + else if (Button & SDL_HAT_UP) + result += "JOY UP"; + else if (Button & SDL_HAT_DOWN) + result += "JOY DOWN"; + else + result += "JOY ?"; + } return result; } @@ -315,19 +333,9 @@ bool ShortcutInput::Matches(const InputEvent& e) const { if (CompareModifiers(Modifiers, e.Modifiers)) { - if (e.DeviceKind == InputDeviceKind::Mouse) + if (e.DeviceKind == Kind && Button == e.Button) { - if (Kind == ShortcutInputKind::Mouse && Key == e.Button) - { - return true; - } - } - else if (e.DeviceKind == InputDeviceKind::Keyboard) - { - if (Kind == ShortcutInputKind::Keyboard && Key == e.Button) - { - return true; - } + return true; } } return false; @@ -335,24 +343,21 @@ bool ShortcutInput::Matches(const InputEvent& e) const std::optional ShortcutInput::FromInputEvent(const InputEvent& e) { + // Assume any side modifier (more specific configurations can be done by manually editing config file) auto modifiers = e.Modifiers & UsefulModifiers; - if (e.DeviceKind == InputDeviceKind::Mouse) + for (auto mod : { KMOD_CTRL, KMOD_SHIFT, KMOD_ALT, KMOD_GUI }) { - ShortcutInput result; - result.Kind = ShortcutInputKind::Mouse; - result.Modifiers = modifiers; - result.Key = e.Button; - return result; + if (modifiers & mod) + { + modifiers |= mod; + } } - else if (e.DeviceKind == InputDeviceKind::Keyboard) - { - ShortcutInput result; - result.Kind = ShortcutInputKind::Keyboard; - result.Modifiers = modifiers; - result.Key = e.Button; - return result; - } - return {}; + + ShortcutInput result; + result.Kind = e.DeviceKind; + result.Modifiers = modifiers; + result.Button = e.Button; + return result; } std::string_view RegisteredShortcut::GetGroup() const @@ -378,6 +383,12 @@ bool RegisteredShortcut::Matches(const InputEvent& e) const bool RegisteredShortcut::IsSuitableInputEvent(const InputEvent& e) const { + // Do not intercept button releases + if (e.State == InputEventState::Release) + { + return false; + } + if (e.DeviceKind == InputDeviceKind::Mouse) { // Do not allow LMB or RMB to be shortcut @@ -385,18 +396,24 @@ bool RegisteredShortcut::IsSuitableInputEvent(const InputEvent& e) const { return false; } - if (e.State == InputEventState::Down) - { - return false; - } } else if (e.DeviceKind == InputDeviceKind::Keyboard) { - if (e.State == InputEventState::Down) + // Do not allow modifier keys alone + switch (e.Button) { - return false; + case SDLK_LCTRL: + case SDLK_RCTRL: + case SDLK_LSHIFT: + case SDLK_RSHIFT: + case SDLK_LALT: + case SDLK_RALT: + case SDLK_LGUI: + case SDLK_RGUI: + return false; } } + return true; } diff --git a/src/openrct2-ui/input/ShortcutManager.h b/src/openrct2-ui/input/ShortcutManager.h index 53f9da87b9..5ecfed0d67 100644 --- a/src/openrct2-ui/input/ShortcutManager.h +++ b/src/openrct2-ui/input/ShortcutManager.h @@ -22,18 +22,12 @@ namespace OpenRCT2::Ui { - enum class ShortcutInputKind - { - Keyboard, - Mouse, - }; - struct ShortcutInput { public: - ShortcutInputKind Kind{}; + InputDeviceKind Kind{}; uint32_t Modifiers{}; - uint32_t Key{}; + uint32_t Button{}; ShortcutInput() = default; ShortcutInput(const std::string_view& value);