OpenRCT2/src/openrct2-ui/input/InputManager.cpp

399 lines
11 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2024 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "InputManager.h"
#include "ShortcutIds.h"
#include <SDL.h>
#include <openrct2-ui/UiContext.h>
#include <openrct2-ui/input/ShortcutManager.h>
#include <openrct2-ui/interface/InGameConsole.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Input.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/config/Config.h>
#include <openrct2/interface/Chat.h>
#include <openrct2/interface/Window.h>
#include <openrct2/paint/VirtualFloor.h>
#include <openrct2/ui/UiContext.h>
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();
ProcessHoldEvents();
HandleViewScrolling();
}
void InputManager::HandleViewScrolling()
{
if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)
return;
auto& console = GetInGameConsole();
if (console.IsOpen())
return;
// Shortcut scrolling
auto mainWindow = WindowGetMain();
if (mainWindow != nullptr && (_viewScroll.x != 0 || _viewScroll.y != 0))
{
WindowUnfollowSprite(*mainWindow);
}
InputScrollViewport(_viewScroll);
// Mouse edge scrolling
if (gConfigGeneral.EdgeScrolling)
{
if (InputGetState() != InputState::Normal)
return;
if (gInputPlaceObjectModifier & (PLACE_OBJECT_MODIFIER_SHIFT_Z | PLACE_OBJECT_MODIFIER_COPY_Z))
return;
GameHandleEdgeScroll();
}
}
void InputManager::HandleModifiers()
{
auto modifiers = SDL_GetModState();
gInputPlaceObjectModifier = PLACE_OBJECT_MODIFIER_NONE;
if (modifiers & KMOD_SHIFT)
{
gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_SHIFT_Z;
}
if (modifiers & KMOD_CTRL)
{
gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_COPY_Z;
}
if (modifiers & KMOD_ALT)
{
gInputPlaceObjectModifier |= 4;
}
#ifdef __MACOSX__
if (modifiers & KMOD_GUI)
{
gInputPlaceObjectModifier |= 8;
}
#endif
if (gConfigGeneral.VirtualFloorStyle != VirtualFloorStyles::Off)
{
if (gInputPlaceObjectModifier & (PLACE_OBJECT_MODIFIER_COPY_Z | PLACE_OBJECT_MODIFIER_SHIFT_Z))
VirtualFloorEnable();
else
VirtualFloorDisable();
}
}
void InputManager::ProcessEvents()
{
while (!_events.empty())
{
const auto& e = _events.front();
Process(e);
_events.pop();
}
}
void InputManager::Process(const InputEvent& e)
{
auto& shortcutManager = GetShortcutManager();
if (e.DeviceKind == InputDeviceKind::Keyboard)
{
auto& console = GetInGameConsole();
if (console.IsOpen())
{
if (!shortcutManager.ProcessEventForSpecificShortcut(e, ShortcutId::DebugToggleConsole))
{
ProcessInGameConsole(e);
}
return;
}
if (gChatOpen)
{
ProcessChat(e);
return;
}
if (e.DeviceKind == InputDeviceKind::Keyboard)
{
auto w = WindowFindByClass(WindowClass::Textinput);
if (w != nullptr)
{
if (e.State == InputEventState::Release)
{
WindowTextInputKey(w, e.Button);
}
return;
}
if (gUsingWidgetTextBox)
{
return;
}
}
}
shortcutManager.ProcessEvent(e);
}
void InputManager::ProcessInGameConsole(const InputEvent& e)
{
if (e.DeviceKind == InputDeviceKind::Keyboard && e.State == InputEventState::Release)
{
auto input = ConsoleInput::None;
switch (e.Button)
{
case SDLK_ESCAPE:
input = ConsoleInput::LineClear;
break;
case SDLK_RETURN:
case SDLK_KP_ENTER:
input = ConsoleInput::LineExecute;
break;
case SDLK_UP:
input = ConsoleInput::HistoryPrevious;
break;
case SDLK_DOWN:
input = ConsoleInput::HistoryNext;
break;
case SDLK_PAGEUP:
input = ConsoleInput::ScrollPrevious;
break;
case SDLK_PAGEDOWN:
input = ConsoleInput::ScrollNext;
break;
}
if (input != ConsoleInput::None)
{
auto& console = GetInGameConsole();
console.Input(input);
}
}
}
void InputManager::ProcessChat(const InputEvent& e)
{
if (e.DeviceKind == InputDeviceKind::Keyboard && e.State == InputEventState::Down)
{
auto input = ChatInput::None;
switch (e.Button)
{
case SDLK_ESCAPE:
input = ChatInput::Close;
break;
case SDLK_RETURN:
case SDLK_KP_ENTER:
input = ChatInput::Send;
break;
}
if (input != ChatInput::None)
{
ChatInput(input);
}
}
}
void InputManager::ProcessHoldEvents()
{
// Get mouse state
_mouseState = SDL_GetMouseState(nullptr, nullptr);
// Get keyboard state
int numkeys;
auto keys = SDL_GetKeyboardState(&numkeys);
_keyboardState.resize(numkeys);
std::memcpy(_keyboardState.data(), keys, numkeys);
// Check view scroll shortcuts
_viewScroll.x = 0;
_viewScroll.y = 0;
if (!HasTextInputFocus())
{
auto& shortcutManager = GetShortcutManager();
if (!shortcutManager.IsPendingShortcutChange())
{
ProcessViewScrollEvent(ShortcutId::ViewScrollUp, { 0, -1 });
ProcessViewScrollEvent(ShortcutId::ViewScrollDown, { 0, 1 });
ProcessViewScrollEvent(ShortcutId::ViewScrollLeft, { -1, 0 });
ProcessViewScrollEvent(ShortcutId::ViewScrollRight, { 1, 0 });
}
}
}
void InputManager::ProcessViewScrollEvent(std::string_view shortcutId, const ScreenCoordsXY& delta)
{
auto& shortcutManager = GetShortcutManager();
auto shortcut = shortcutManager.GetShortcut(shortcutId);
if (shortcut != nullptr && GetState(*shortcut))
{
_viewScroll.x += delta.x;
_viewScroll.y += delta.y;
}
}
bool InputManager::GetState(const RegisteredShortcut& shortcut) const
{
for (const auto& i : shortcut.Current)
{
if (GetState(i))
{
return true;
}
}
return false;
}
bool InputManager::GetState(const ShortcutInput& shortcut) const
{
constexpr uint32_t UsefulModifiers = KMOD_SHIFT | KMOD_CTRL | KMOD_ALT | KMOD_GUI;
auto modifiers = SDL_GetModState() & UsefulModifiers;
if ((shortcut.Modifiers & UsefulModifiers) == modifiers)
{
switch (shortcut.Kind)
{
case InputDeviceKind::Mouse:
{
if (_mouseState & (1 << shortcut.Button))
{
return true;
}
break;
}
case InputDeviceKind::Keyboard:
{
auto scanCode = static_cast<size_t>(SDL_GetScancodeFromKey(shortcut.Button));
if (scanCode < _keyboardState.size() && _keyboardState[scanCode])
{
return true;
}
break;
}
case InputDeviceKind::JoyButton:
{
for (auto* joystick : _joysticks)
{
if (SDL_JoystickGetButton(joystick, shortcut.Button))
{
return true;
}
}
break;
}
case InputDeviceKind::JoyHat:
{
for (auto* joystick : _joysticks)
{
auto numHats = SDL_JoystickNumHats(joystick);
for (int i = 0; i < numHats; i++)
{
auto hat = SDL_JoystickGetHat(joystick, i);
if (hat & shortcut.Button)
{
return true;
}
}
}
break;
}
}
}
return false;
}
bool InputManager::HasTextInputFocus() const
{
if (gUsingWidgetTextBox || gChatOpen)
return true;
auto w = WindowFindByClass(WindowClass::Textinput);
if (w != nullptr)
return true;
auto& console = GetInGameConsole();
if (console.IsOpen())
return true;
return false;
}