diff --git a/src/openrct2-ui/TextComposition.cpp b/src/openrct2-ui/TextComposition.cpp new file mode 100644 index 0000000000..b8536a2b53 --- /dev/null +++ b/src/openrct2-ui/TextComposition.cpp @@ -0,0 +1,300 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** +* OpenRCT2, an open source clone of Roller Coaster Tycoon 2. +* +* OpenRCT2 is the work of many authors, a full list can be found in contributors.md +* For more information, visit https://github.com/OpenRCT2/OpenRCT2 +* +* OpenRCT2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* A full copy of the GNU General Public License can be found in licence.txt +*****************************************************************************/ +#pragma endregion + +#include +#include +#include +#include +#include +#include +#include "TextComposition.h" + +extern "C" +{ + #include + #include +} + +#ifdef __MACOSX__ + // macOS uses COMMAND rather than CTRL for many keyboard shortcuts + #define KEYBOARD_PRIMARY_MODIFIER KMOD_GUI +#else + #define KEYBOARD_PRIMARY_MODIFIER KMOD_CTRL +#endif + +using namespace OpenRCT2; +using namespace OpenRCT2::Ui; + +bool TextComposition::IsActive() +{ + return SDL_IsTextInputActive() && _session.Buffer != nullptr; +} + +const TextInputSession * TextComposition::Start(utf8 * buffer, size_t bufferSize) +{ + Guard::ArgumentNotNull(buffer); + + // TODO This doesn't work, and position could be improved to where text entry is + SDL_Rect rect = { 10, 10, 100, 100 }; + SDL_SetTextInputRect(&rect); + SDL_StartTextInput(); + + _session.Buffer = buffer; + _session.BufferSize = bufferSize - 1; + _session.Size = strlen(buffer); + _session.SelectionStart = _session.Size; + _session.SelectionSize = 0; + RecalculateLength(); + return &_session; +} + +void TextComposition::Stop() +{ + SDL_StopTextInput(); + _session.Buffer = nullptr; + _imeActive = false; +} + +void TextComposition::HandleMessage(const SDL_Event * e) +{ + switch (e->type) { + case SDL_TEXTEDITING: + // When inputting Korean characters, `edit.length` is always zero + String::Set(_imeBuffer, sizeof(_imeBuffer), e->edit.text); + _imeStart = e->edit.start; + _imeLength = e->edit.length; + _imeActive = ((e->edit.length != 0 || String::SizeOf(e->edit.text) != 0) && _imeBuffer[0] != '\0'); + break; + case SDL_TEXTINPUT: + // will receive an `SDL_TEXTINPUT` event when a composition is committed + _imeActive = false; + if (_session.Buffer != nullptr) + { + // HACK ` will close console, so don't input any text + if (e->text.text[0] == '`' && gConsoleOpen) { + break; + } + + utf8 * newText = String::Duplicate(e->text.text); + utf8_remove_formatting(newText, false); + Insert(newText); + Memory::Free(newText); + + console_refresh_caret(); + window_update_textbox(); + } + break; + case SDL_KEYDOWN: + { + if (_imeActive) + { + break; + } + + uint16 modifier = e->key.keysym.mod; + SDL_Keycode key = e->key.keysym.sym; + if (key == SDLK_KP_ENTER) + { + // Map Keypad enter to regular enter. + key = SDLK_RETURN; + } + + // _lastKeyPressed = e.key.keysym.sym; + // _keysPressed[e.key.keysym.scancode] = 1; + + // Text input + if (_session.Buffer == nullptr) + { + break; + } + + // Clear the input on Backspace (Windows/Linux) or Backspace (macOS) + if (key == SDLK_BACKSPACE && (modifier & KEYBOARD_PRIMARY_MODIFIER)) + { + Clear(); + console_refresh_caret(); + window_update_textbox(); + } + + switch (key) { + case SDLK_BACKSPACE: + // If backspace and we have input text with a cursor position none zero + if (_session.SelectionStart > 0) + { + size_t endOffset = _session.SelectionStart; + CursorLeft(); + _session.SelectionSize = endOffset - _session.SelectionStart; + Delete(); + + console_refresh_caret(); + window_update_textbox(); + } + break; + case SDLK_HOME: + CursorHome(); + console_refresh_caret(); + break; + case SDLK_END: + CursorEnd(); + console_refresh_caret(); + break; + case SDLK_DELETE: + { + size_t startOffset = _session.SelectionStart; + CursorRight(); + _session.SelectionSize = _session.SelectionStart - startOffset; + _session.SelectionStart = startOffset; + Delete(); + console_refresh_caret(); + window_update_textbox(); + break; + } + case SDLK_RETURN: + window_cancel_textbox(); + break; + case SDLK_LEFT: + CursorLeft(); + console_refresh_caret(); + break; + case SDLK_RIGHT: + CursorRight(); + console_refresh_caret(); + break; + case SDLK_v: + if ((modifier & KEYBOARD_PRIMARY_MODIFIER) && SDL_HasClipboardText()) + { + utf8 * text = SDL_GetClipboardText(); + utf8_remove_formatting(text, false); + Insert(text); + SDL_free(text); + window_update_textbox(); + } + break; + } + } + } +} + +void TextComposition::CursorHome() +{ + _session.SelectionStart = 0; +} + +void TextComposition::CursorEnd() +{ + _session.SelectionStart = _session.SelectionSize; +} + +void TextComposition::CursorLeft() +{ + size_t selectionOffset = _session.SelectionStart; + if (selectionOffset > 0) + { + const utf8 * ch = _session.Buffer + selectionOffset; + do + { + ch--; + selectionOffset--; + } + while (!utf8_is_codepoint_start(ch) && selectionOffset > 0); + + _session.SelectionStart = selectionOffset; + } +} + +void TextComposition::CursorRight() +{ + size_t selectionOffset = _session.SelectionStart; + size_t selectionMaxOffset = _session.Size; + if (selectionOffset < selectionMaxOffset) + { + const utf8 * ch = _session.Buffer + _session.SelectionStart; + do + { + ch++; + selectionOffset++; + } + while (!utf8_is_codepoint_start(ch) && selectionOffset < selectionMaxOffset); + + _session.SelectionSize = Math::Max(0, _session.SelectionSize - (selectionOffset - _session.SelectionStart)); + _session.SelectionStart = selectionOffset; + } +} + +void TextComposition::Insert(const utf8 * text) +{ + const utf8 * ch = text; + uint32 codepoint; + while ((codepoint = utf8_get_next(ch, &ch)) != 0) + { + InsertCodepoint(codepoint); + } +} + +void TextComposition::InsertCodepoint(codepoint_t codepoint) +{ + size_t codepointLength = utf8_get_codepoint_length(codepoint); + size_t remainingSize = _session.BufferSize - _session.Size; + if (codepointLength <= remainingSize) + { + utf8 * buffer = (utf8 *)_session.Buffer; + utf8 * insertPtr = buffer + _session.SelectionStart; + if (_session.SelectionStart < _session.Size) + { + // Shift bytes (including null terminator) right to make room for new codepoint + utf8 * targetShiftPtr = insertPtr + codepointLength; + size_t shiftSize = _session.Size - _session.SelectionStart + 1; + memmove(targetShiftPtr, insertPtr, shiftSize); + } + else + { + // Character is appended onto the end, so set byte after it to null terminator + buffer[_session.Size + codepointLength] = 0; + } + + utf8_write_codepoint(insertPtr, codepoint); + _session.SelectionStart += codepointLength; + _session.Size += codepointLength; + _session.Length++; + } +} + +void TextComposition::Clear() +{ + utf8 * buffer = (utf8 *)_session.Buffer; + buffer[0] = 0; + _session.Size = 0; + _session.Length = 0; + _session.SelectionStart = 0; + _session.SelectionSize = 0; +} + +void TextComposition::Delete() +{ + utf8 * buffer = (utf8 *)_session.Buffer; + utf8 * targetShiftPtr = buffer + _session.SelectionStart; + utf8 * sourceShiftPtr = targetShiftPtr + _session.SelectionSize; + size_t shiftSize = _session.SelectionSize - _session.SelectionStart - _session.SelectionSize + 1; + memmove(targetShiftPtr, sourceShiftPtr, shiftSize); + _session.SelectionSize = 0; + RecalculateLength(); +} + +void TextComposition::RecalculateLength() +{ + _session.Size = String::SizeOf(_session.Buffer); + _session.Length = String::LengthOf(_session.Buffer); +} diff --git a/src/openrct2-ui/TextComposition.h b/src/openrct2-ui/TextComposition.h new file mode 100644 index 0000000000..fe9aeddcd1 --- /dev/null +++ b/src/openrct2-ui/TextComposition.h @@ -0,0 +1,57 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** +* OpenRCT2, an open source clone of Roller Coaster Tycoon 2. +* +* OpenRCT2 is the work of many authors, a full list can be found in contributors.md +* For more information, visit https://github.com/OpenRCT2/OpenRCT2 +* +* OpenRCT2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* A full copy of the GNU General Public License can be found in licence.txt +*****************************************************************************/ +#pragma endregion + +#include +#include + +union SDL_Event; + +namespace OpenRCT2 +{ + namespace Ui + { + /** + * Represents a + */ + class TextComposition + { + private: + TextInputSession _session = { 0 }; + + bool _imeActive = false; + sint32 _imeStart = 0; + sint32 _imeLength = 0; + utf8 _imeBuffer[32] = { 0 }; + + public: + bool IsActive(); + const TextInputSession * TextComposition::Start(utf8 * buffer, size_t bufferSize); + void Stop(); + void HandleMessage(const SDL_Event * e); + + private: + void CursorHome(); + void CursorEnd(); + void CursorLeft(); + void CursorRight(); + void Insert(const utf8 * text); + void InsertCodepoint(codepoint_t codepoint); + void Clear(); + void Delete(); + void RecalculateLength(); + }; + } +} diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index 711c495f7f..cd7666d4a8 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -26,15 +27,31 @@ #include #include "CursorRepository.h" #include "drawing/engines/DrawingEngines.h" +#include "TextComposition.h" #include "UiContext.h" +extern "C" +{ + #include + #include +} + using namespace OpenRCT2; using namespace OpenRCT2::Drawing; using namespace OpenRCT2::Ui; +#ifdef __MACOSX__ + // macOS uses COMMAND rather than CTRL for many keyboard shortcuts + #define KEYBOARD_PRIMARY_MODIFIER KMOD_GUI +#else + #define KEYBOARD_PRIMARY_MODIFIER KMOD_CTRL +#endif + class UiContext : public IUiContext { private: + constexpr static uint32 TOUCH_DOUBLE_TIMEOUT = 300; + IPlatformUiContext * const _platformUiContext; CursorRepository _cursorRepository; @@ -48,6 +65,15 @@ private: bool _steamOverlayActive = false; + // Input + TextComposition _textComposition; + CursorState _cursorState; + uint32 _lastKeyPressed; + const uint8 * _keysState; + uint8 * _keysPressed; + uint32 _lastGestureTimestamp; + float _gestureRadius; + public: UiContext(IPlatformUiContext * platformUiContext) : _platformUiContext(platformUiContext) @@ -60,16 +86,6 @@ public: } // Window - CURSOR_ID GetCursor() override - { - return _cursorRepository.GetCurrentCursor(); - } - - void SetCursor(CURSOR_ID cursor) override - { - _cursorRepository.SetCurrentCursor(cursor); - } - void * GetWindow() override { return _window; @@ -122,6 +138,52 @@ public: return _fsResolutions; } + bool IsSteamOverlayActive() override + { + return _steamOverlayActive; + } + + // Input + const CursorState * GetCursorState() override + { + return &_cursorState; + } + + const uint8 * GetKeysState() override + { + return _keysState; + } + + const uint8 * GetKeysPressed() override + { + return _keysPressed; + } + + CURSOR_ID GetCursor() override + { + return _cursorRepository.GetCurrentCursor(); + } + + void SetCursor(CURSOR_ID cursor) override + { + _cursorRepository.SetCurrentCursor(cursor); + } + + void SetCursorVisible(bool value) override + { + SDL_ShowCursor(value ? SDL_ENABLE : SDL_DISABLE); + } + + void GetCursorPosition(sint32 * x, sint32 * y) override + { + SDL_GetMouseState(x, y); + } + + void SetCursorPosition(sint32 x, sint32 y) override + { + SDL_WarpMouseInWindow(nullptr, x, y); + } + // Drawing IDrawingEngine * CreateDrawingEngine(DRAWING_ENGINE_TYPE type) override { @@ -140,9 +202,215 @@ public: } // Text input - bool IsTextInputActive() override { return false; } - const TextInputSession * StartTextInput(utf8 * buffer, sint32 bufferSize) override { return nullptr; } - void StopTextInput() override { } + bool IsTextInputActive() override + { + return _textComposition.IsActive(); + } + + const TextInputSession * StartTextInput(utf8 * buffer, size_t bufferSize) override + { + return _textComposition.Start(buffer, bufferSize); + } + + void StopTextInput() override + { + _textComposition.Stop(); + } + + void ProcessMessages() + { + _lastKeyPressed = 0; + _cursorState.left &= ~CURSOR_CHANGED; + _cursorState.middle &= ~CURSOR_CHANGED; + _cursorState.right &= ~CURSOR_CHANGED; + _cursorState.old = 0; + _cursorState.touch = false; + + SDL_Event e; + while (SDL_PollEvent(&e)) + { + switch (e.type) { + case SDL_QUIT: + rct2_quit(); + break; + case SDL_WINDOWEVENT: + // HACK: Fix #2158, OpenRCT2 does not draw if it does not think that the window is + // visible - due a bug in SDL 2.0.3 this hack is required if the + // window is maximised, minimised and then restored again. + if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) + { + if (SDL_GetWindowFlags(_window) & SDL_WINDOW_MAXIMIZED) + { + SDL_RestoreWindow(_window); + SDL_MaximizeWindow(_window); + } + if ((SDL_GetWindowFlags(_window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) + { + SDL_RestoreWindow(_window); + SDL_SetWindowFullscreen(_window, SDL_WINDOW_FULLSCREEN_DESKTOP); + } + } + + if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + OnResize(e.window.data1, e.window.data2); + } + if (gConfigSound.audio_focus && gConfigSound.sound_enabled) + { + if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) + { + Mixer_SetVolume(1); + } + if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST) + { + Mixer_SetVolume(0); + } + } + break; + case SDL_MOUSEMOTION: + _cursorState.x = (sint32)(e.motion.x / gConfigGeneral.window_scale); + _cursorState.y = (sint32)(e.motion.y / gConfigGeneral.window_scale); + break; + case SDL_MOUSEWHEEL: + if (gConsoleOpen) + { + console_scroll(e.wheel.y); + break; + } + _cursorState.wheel += e.wheel.y * 128; + break; + case SDL_MOUSEBUTTONDOWN: + { + sint32 x = (sint32)(e.button.x / gConfigGeneral.window_scale); + sint32 y = (sint32)(e.button.y / gConfigGeneral.window_scale); + switch (e.button.button) { + case SDL_BUTTON_LEFT: + store_mouse_input(MOUSE_STATE_LEFT_PRESS, x, y); + _cursorState.left = CURSOR_PRESSED; + _cursorState.old = 1; + break; + case SDL_BUTTON_MIDDLE: + _cursorState.middle = CURSOR_PRESSED; + break; + case SDL_BUTTON_RIGHT: + store_mouse_input(MOUSE_STATE_RIGHT_PRESS, x, y); + _cursorState.right = CURSOR_PRESSED; + _cursorState.old = 2; + break; + } + break; + } + case SDL_MOUSEBUTTONUP: + { + sint32 x = (sint32)(e.button.x / gConfigGeneral.window_scale); + sint32 y = (sint32)(e.button.y / gConfigGeneral.window_scale); + switch (e.button.button) { + case SDL_BUTTON_LEFT: + store_mouse_input(MOUSE_STATE_LEFT_RELEASE, x, y); + _cursorState.left = CURSOR_RELEASED; + _cursorState.old = 3; + break; + case SDL_BUTTON_MIDDLE: + _cursorState.middle = CURSOR_RELEASED; + break; + case SDL_BUTTON_RIGHT: + store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, x, y); + _cursorState.right = CURSOR_RELEASED; + _cursorState.old = 4; + break; + } + break; + } + // Apple sends touchscreen events for trackpads, so ignore these events on macOS +#ifndef __MACOSX__ + case SDL_FINGERMOTION: + _cursorState.x = (sint32)(e.tfinger.x * gScreenWidth); + _cursorState.y = (sint32)(e.tfinger.y * gScreenHeight); + break; + case SDL_FINGERDOWN: + { + sint32 x = (sint32)(e.tfinger.x * gScreenWidth); + sint32 y = (sint32)(e.tfinger.y * gScreenHeight); + + _cursorState.touchIsDouble = (!_cursorState.touchIsDouble && + e.tfinger.timestamp - _cursorState.touchDownTimestamp < TOUCH_DOUBLE_TIMEOUT); + + if (_cursorState.touchIsDouble) + { + store_mouse_input(MOUSE_STATE_RIGHT_PRESS, x, y); + _cursorState.right = CURSOR_PRESSED; + _cursorState.old = 2; + } + else + { + store_mouse_input(MOUSE_STATE_LEFT_PRESS, x, y); + _cursorState.left = CURSOR_PRESSED; + _cursorState.old = 1; + } + _cursorState.touch = true; + _cursorState.touchDownTimestamp = e.tfinger.timestamp; + break; + } + case SDL_FINGERUP: + { + sint32 x = (sint32)(e.tfinger.x * gScreenWidth); + sint32 y = (sint32)(e.tfinger.y * gScreenHeight); + + if (_cursorState.touchIsDouble) + { + store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, x, y); + _cursorState.left = CURSOR_RELEASED; + _cursorState.old = 4; + } + else { + store_mouse_input(MOUSE_STATE_LEFT_RELEASE, x, y); + _cursorState.left = CURSOR_RELEASED; + _cursorState.old = 3; + } + _cursorState.touch = true; + break; + } +#endif + case SDL_KEYDOWN: + _textComposition.HandleMessage(&e); + break; + case SDL_MULTIGESTURE: + if (e.mgesture.numFingers == 2) + { + if (e.mgesture.timestamp > _lastGestureTimestamp + 1000) + { + _gestureRadius = 0; + } + _lastGestureTimestamp = e.mgesture.timestamp; + _gestureRadius += e.mgesture.dDist; + + // Zoom gesture + constexpr sint32 tolerance = 128; + sint32 gesturePixels = (sint32)(_gestureRadius * gScreenWidth); + if (abs(gesturePixels) > tolerance) + { + _gestureRadius = 0; + main_window_zoom(gesturePixels > 0, true); + } + } + break; + case SDL_TEXTEDITING: + _textComposition.HandleMessage(&e); + break; + case SDL_TEXTINPUT: + _textComposition.HandleMessage(&e); + break; + default: + break; + } + } + + _cursorState.any = _cursorState.left | _cursorState.middle | _cursorState.right; + + // Updates the state of the keys + sint32 numKeys = 256; + _keysState = SDL_GetKeyboardState(&numKeys); + } private: void CreateWindow() diff --git a/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp index 5a044158c7..641994f6b4 100644 --- a/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp @@ -15,6 +15,8 @@ #pragma endregion #include +#include +#include #include #include #include @@ -37,6 +39,7 @@ extern "C" #include } +using namespace OpenRCT2; using namespace OpenRCT2::Drawing; class SoftwareDrawingEngine; @@ -730,14 +733,15 @@ private: } SDL_RenderCopy(_sdlRenderer, _screenTexture, nullptr, nullptr); - if (gSteamOverlayActive && gConfigGeneral.steam_overlay_pause) + bool isSteamOverlayActive = GetContext()->GetUiContext()->IsSteamOverlayActive(); + if (isSteamOverlayActive && gConfigGeneral.steam_overlay_pause) { OverlayPreRenderCheck(); } SDL_RenderPresent(_sdlRenderer); - if (gSteamOverlayActive && gConfigGeneral.steam_overlay_pause) + if (isSteamOverlayActive && gConfigGeneral.steam_overlay_pause) { OverlayPostRenderCheck(); } diff --git a/src/openrct2-ui/libopenrct2ui.vcxproj b/src/openrct2-ui/libopenrct2ui.vcxproj index 8424c2c760..15bdf61d77 100644 --- a/src/openrct2-ui/libopenrct2ui.vcxproj +++ b/src/openrct2-ui/libopenrct2ui.vcxproj @@ -35,6 +35,7 @@ + @@ -53,6 +54,7 @@ + diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 8a2b30421f..ed94d722df 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -15,6 +15,7 @@ #pragma endregion #include +#include "config/Config.h" #include "Context.h" #include "OpenRCT2.h" #include "ui/UiContext.h" @@ -88,4 +89,63 @@ extern "C" { GetContext()->GetUiContext()->SetCursor((CURSOR_ID)cursor); } + + void context_hide_cursor() + { + GetContext()->GetUiContext()->SetCursorVisible(false); + } + + void context_show_cursor() + { + GetContext()->GetUiContext()->SetCursorVisible(true); + } + + void context_get_cursor_position(sint32 * x, sint32 * y) + { + GetContext()->GetUiContext()->GetCursorPosition(x, y); + } + + void context_get_cursor_position_scaled(sint32 * x, sint32 * y) + { + context_get_cursor_position(x, y); + + // Compensate for window scaling. + *x = (sint32)ceilf(*x / gConfigGeneral.window_scale); + *y = (sint32)ceilf(*y / gConfigGeneral.window_scale); + } + + void context_set_cursor_position(sint32 x, sint32 y) + { + GetContext()->GetUiContext()->SetCursorPosition(x, y); + } + + const CursorState * context_get_cursor_state() + { + return GetContext()->GetUiContext()->GetCursorState(); + } + + const uint8 * context_get_keys_state() + { + return GetContext()->GetUiContext()->GetKeysState(); + } + + const uint8 * context_get_keys_pressed() + { + return GetContext()->GetUiContext()->GetKeysPressed(); + } + + void context_start_text_input(utf8 * buffer, size_t maxLength) + { + GetContext()->GetUiContext()->StartTextInput(buffer, maxLength); + } + + void context_stop_text_input() + { + GetContext()->GetUiContext()->StopTextInput(); + } + + bool context_is_input_active() + { + return GetContext()->GetUiContext()->IsTextInputActive(); + } } diff --git a/src/openrct2/Context.h b/src/openrct2/Context.h index 7d82241922..321c378d79 100644 --- a/src/openrct2/Context.h +++ b/src/openrct2/Context.h @@ -18,6 +18,25 @@ #include "common.h" +typedef struct CursorState +{ + sint32 x, y; + uint8 left, middle, right, any; + sint32 wheel; + sint32 old; + bool touch, touchIsDouble; + uint32 touchDownTimestamp; +} CursorState; + +enum +{ + CURSOR_UP = 0, + CURSOR_DOWN = 1, + CURSOR_CHANGED = 2, + CURSOR_RELEASED = CURSOR_UP | CURSOR_CHANGED, + CURSOR_PRESSED = CURSOR_DOWN | CURSOR_CHANGED, +}; + #ifdef __cplusplus namespace OpenRCT2 @@ -45,11 +64,23 @@ namespace OpenRCT2 #endif // __cplusplus -#if __cplusplus +#ifdef __cplusplus extern "C" { #endif void context_setcurrentcursor(sint32 cursor); -#if __cplusplus + void context_hide_cursor(); + void context_show_cursor(); + void context_get_cursor_position(sint32 * x, sint32 * y); + void context_get_cursor_position_scaled(sint32 * x, sint32 * y); + void context_set_cursor_position(sint32 x, sint32 y); + const CursorState * context_get_cursor_state(); + const uint8 * context_get_keys_state(); + const uint8 * context_get_keys_pressed(); + void context_start_text_input(utf8 * buffer, size_t maxLength); + void context_stop_text_input(); + bool context_is_input_active(); + +#ifdef __cplusplus } #endif diff --git a/src/openrct2/OpenRCT2.cpp b/src/openrct2/OpenRCT2.cpp index db5a198f96..56657be733 100644 --- a/src/openrct2/OpenRCT2.cpp +++ b/src/openrct2/OpenRCT2.cpp @@ -16,6 +16,8 @@ #include #include +#include "Context.h" +#include "ui/UiContext.h" #include "core/Console.hpp" #include "core/File.h" #include "core/FileStream.hpp" @@ -463,7 +465,7 @@ namespace OpenRCT2 { _lastTick = currentTick; } - platform_process_messages(); + GetContext()->GetUiContext()->ProcessMessages(); rct2_update(); if (!_isWindowMinimised) { @@ -486,7 +488,7 @@ namespace OpenRCT2 _uncapTick = currentTick - UPDATE_TIME_MS - 1; } - platform_process_messages(); + GetContext()->GetUiContext()->ProcessMessages(); while (_uncapTick <= currentTick && currentTick - _uncapTick > UPDATE_TIME_MS) { diff --git a/src/openrct2/audio/AudioMixer.cpp b/src/openrct2/audio/AudioMixer.cpp index af0a2e6370..72453547ed 100644 --- a/src/openrct2/audio/AudioMixer.cpp +++ b/src/openrct2/audio/AudioMixer.cpp @@ -690,3 +690,19 @@ void Mixer_SetVolume(float volume) gMixer->SetVolume(volume); } } + +sint32 DStoMixerVolume(sint32 volume) +{ + return (sint32)(SDL_MIX_MAXVOLUME * (SDL_pow(10, (float)volume / 2000))); +} + +float DStoMixerPan(sint32 pan) +{ + return (((float)pan + -DSBPAN_LEFT) / DSBPAN_RIGHT) / 2; +} + +double DStoMixerRate(sint32 frequency) +{ + return (double)frequency / 22050; +} + diff --git a/src/openrct2/audio/AudioMixer.h b/src/openrct2/audio/AudioMixer.h index c27e17de22..085e911795 100644 --- a/src/openrct2/audio/AudioMixer.h +++ b/src/openrct2/audio/AudioMixer.h @@ -19,17 +19,6 @@ #include "../common.h" -#ifdef __cplusplus -extern "C" -{ -#endif // __cplusplus - - #include "../platform/platform.h" - -#ifdef __cplusplus -} -#endif // __cplusplus - #define MIXER_LOOP_NONE 0 #define MIXER_LOOP_INFINITE -1 @@ -89,9 +78,9 @@ void Mixer_Channel_SetGroup(void* channel, sint32 group); void* Mixer_Play_Music(sint32 pathId, sint32 loop, sint32 streaming); void Mixer_SetVolume(float volume); -static inline sint32 DStoMixerVolume(sint32 volume) { return (sint32)(SDL_MIX_MAXVOLUME * (SDL_pow(10, (float)volume / 2000))); } -static inline float DStoMixerPan(sint32 pan) { return (((float)pan + -DSBPAN_LEFT) / DSBPAN_RIGHT) / 2; } -static inline double DStoMixerRate(sint32 frequency) { return (double)frequency / 22050; } +sint32 DStoMixerVolume(sint32 volume); +float DStoMixerPan(sint32 pan); +double DStoMixerRate(sint32 frequency); #ifdef __cplusplus } diff --git a/src/openrct2/input.c b/src/openrct2/input.c index 5170516f60..de806bf396 100644 --- a/src/openrct2/input.c +++ b/src/openrct2/input.c @@ -159,8 +159,9 @@ static sint32 game_get_next_input(sint32 *x, sint32 *y) { rct_mouse_data *input = get_mouse_input(); if (input == NULL) { - *x = gCursorState.x; - *y = gCursorState.y; + const CursorState * cursorState = context_get_cursor_state(); + *x = cursorState->x; + *y = cursorState->y; return 0; } else { *x = input->x; @@ -200,7 +201,7 @@ static void input_scroll_drag_begin(sint32 x, sint32 y, rct_window* w, rct_widge _ticksSinceDragStart = 0; _dragScrollIndex = window_get_scroll_data_index(w, widgetIndex); - platform_hide_cursor(); + context_hide_cursor(); } /** @@ -241,7 +242,7 @@ static void input_scroll_drag_continue(sint32 x, sint32 y, rct_window* w) sint32 fixedCursorPositionX = (sint32) ceilf(gInputDragLastX * gConfigGeneral.window_scale); sint32 fixedCursorPositionY = (sint32) ceilf(gInputDragLastY * gConfigGeneral.window_scale); - platform_set_cursor_position(fixedCursorPositionX, fixedCursorPositionY); + context_set_cursor_position(fixedCursorPositionX, fixedCursorPositionY); } /** @@ -252,7 +253,7 @@ static void input_scroll_right(sint32 x, sint32 y, sint32 state) { rct_window* w = window_find_by_number(_dragWidget.window_classification, _dragWidget.window_number); if (w == NULL) { - platform_show_cursor(); + context_show_cursor(); _inputState = INPUT_STATE_RESET; return; } @@ -267,7 +268,7 @@ static void input_scroll_right(sint32 x, sint32 y, sint32 state) break; case MOUSE_STATE_RIGHT_RELEASE: _inputState = INPUT_STATE_RESET; - platform_show_cursor(); + context_show_cursor(); break; } } @@ -502,8 +503,8 @@ static void input_viewport_drag_begin(rct_window *w, sint32 x, sint32 y) _dragWidget.window_classification = w->classification; _dragWidget.window_number = w->number; _ticksSinceDragStart = 0; - platform_get_cursor_position(&gInputDragLastX, &gInputDragLastY); - platform_hide_cursor(); + context_get_cursor_position(&gInputDragLastX, &gInputDragLastY); + context_hide_cursor(); // gInputFlags |= INPUT_FLAG_5; } @@ -514,7 +515,7 @@ static void input_viewport_drag_continue() rct_window *w; rct_viewport *viewport; - platform_get_cursor_position(&newDragX, &newDragY); + context_get_cursor_position(&newDragX, &newDragY); dx = newDragX - gInputDragLastX; dy = newDragY - gInputDragLastY; @@ -530,7 +531,7 @@ static void input_viewport_drag_continue() viewport = w->viewport; _ticksSinceDragStart += gTicksSinceLastUpdate; if (viewport == NULL) { - platform_show_cursor(); + context_show_cursor(); _inputState = INPUT_STATE_RESET; } else if (dx != 0 || dy != 0) { if (!(w->flags & WF_NO_SCROLLING)) { @@ -552,13 +553,13 @@ static void input_viewport_drag_continue() } } - platform_set_cursor_position(gInputDragLastX, gInputDragLastY); + context_set_cursor_position(gInputDragLastX, gInputDragLastY); } static void input_viewport_drag_end() { _inputState = INPUT_STATE_RESET; - platform_show_cursor(); + context_show_cursor(); } #pragma endregion @@ -1411,14 +1412,15 @@ void title_handle_keyboard_input() if (!gConsoleOpen) { // Handle modifier keys and key scrolling gInputPlaceObjectModifier = PLACE_OBJECT_MODIFIER_NONE; - if (gKeysState[SDL_SCANCODE_LSHIFT] || gKeysState[SDL_SCANCODE_RSHIFT]) + const uint8 * keysState = context_get_keys_state(); + if (keysState[SDL_SCANCODE_LSHIFT] || keysState[SDL_SCANCODE_RSHIFT]) gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_SHIFT_Z; - if (gKeysState[SDL_SCANCODE_LCTRL] || gKeysState[SDL_SCANCODE_RCTRL]) + if (keysState[SDL_SCANCODE_LCTRL] || keysState[SDL_SCANCODE_RCTRL]) gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_COPY_Z; - if (gKeysState[SDL_SCANCODE_LALT] || gKeysState[SDL_SCANCODE_RALT]) + if (keysState[SDL_SCANCODE_LALT] || keysState[SDL_SCANCODE_RALT]) gInputPlaceObjectModifier |= 4; #ifdef __MACOSX__ - if (gKeysState[SDL_SCANCODE_LGUI] || gKeysState[SDL_SCANCODE_RGUI]) { + if (keysState[SDL_SCANCODE_LGUI] || keysState[SDL_SCANCODE_RGUI]) { gInputPlaceObjectModifier |= 8; } #endif @@ -1430,7 +1432,7 @@ void title_handle_keyboard_input() // Reserve backtick for console if (key == SDL_SCANCODE_GRAVE) { - if ((gConfigGeneral.debugging_tools && !platform_is_input_active()) || gConsoleOpen) { + if ((gConfigGeneral.debugging_tools && !context_is_input_active()) || gConsoleOpen) { window_cancel_textbox(); console_toggle(); } @@ -1476,17 +1478,18 @@ void game_handle_keyboard_input() // Handle modifier keys and key scrolling gInputPlaceObjectModifier = PLACE_OBJECT_MODIFIER_NONE; - if (gKeysState[SDL_SCANCODE_LSHIFT] || gKeysState[SDL_SCANCODE_RSHIFT]) { + const uint8 * keysState = context_get_keys_state(); + if (keysState[SDL_SCANCODE_LSHIFT] || keysState[SDL_SCANCODE_RSHIFT]) { gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_SHIFT_Z; } - if (gKeysState[SDL_SCANCODE_LCTRL] || gKeysState[SDL_SCANCODE_RCTRL]) { + if (keysState[SDL_SCANCODE_LCTRL] || keysState[SDL_SCANCODE_RCTRL]) { gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_COPY_Z; } - if (gKeysState[SDL_SCANCODE_LALT] || gKeysState[SDL_SCANCODE_RALT]) { + if (keysState[SDL_SCANCODE_LALT] || keysState[SDL_SCANCODE_RALT]) { gInputPlaceObjectModifier |= 4; } #ifdef __MACOSX__ - if (gKeysState[SDL_SCANCODE_LGUI] || gKeysState[SDL_SCANCODE_RGUI]) { + if (keysState[SDL_SCANCODE_LGUI] || keysState[SDL_SCANCODE_RGUI]) { gInputPlaceObjectModifier |= 8; } #endif @@ -1501,7 +1504,7 @@ void game_handle_keyboard_input() // Reserve backtick for console if (key == SDL_SCANCODE_GRAVE) { - if ((gConfigGeneral.debugging_tools && !platform_is_input_active()) || gConsoleOpen) { + if ((gConfigGeneral.debugging_tools && !context_is_input_active()) || gConsoleOpen) { window_cancel_textbox(); console_toggle(); } @@ -1536,10 +1539,10 @@ void game_handle_keyboard_input() */ sint32 get_next_key() { - sint32 i; - for (i = 0; i < 221; i++) { - if (gKeysPressed[i]) { - gKeysPressed[i] = 0; + uint8 * keysPressed = (uint8 *)context_get_keys_pressed(); + for (sint32 i = 0; i < 221; i++) { + if (keysPressed[i]) { + keysPressed[i] = 0; return i; } } @@ -1617,15 +1620,16 @@ void game_handle_edge_scroll() scrollY = 0; // Scroll left / right - if (gCursorState.x == 0) + const CursorState * cursorState = context_get_cursor_state(); + if (cursorState->x == 0) scrollX = -1; - else if (gCursorState.x >= gScreenWidth - 1) + else if (cursorState->x >= gScreenWidth - 1) scrollX = 1; // Scroll up / down - if (gCursorState.y == 0) + if (cursorState->y == 0) scrollY = -1; - else if (gCursorState.y >= gScreenHeight - 1) + else if (cursorState->y >= gScreenHeight - 1) scrollY = 1; // Scroll viewport @@ -1661,25 +1665,26 @@ void game_handle_key_scroll() scrollX = 0; scrollY = 0; + const uint8 * keysState = context_get_keys_state(); for (sint32 shortcutId = SHORTCUT_SCROLL_MAP_UP; shortcutId <= SHORTCUT_SCROLL_MAP_RIGHT; shortcutId++) { uint16 shortcutKey = gShortcutKeys[shortcutId]; uint8 scancode = shortcutKey & 0xFF; if (shortcutKey == 0xFFFF) continue; - if (!gKeysState[scancode]) continue; + if (!keysState[scancode]) continue; if (shortcutKey & SHIFT) { - if (!gKeysState[SDL_SCANCODE_LSHIFT] && !gKeysState[SDL_SCANCODE_RSHIFT]) continue; + if (!keysState[SDL_SCANCODE_LSHIFT] && !keysState[SDL_SCANCODE_RSHIFT]) continue; } if (shortcutKey & CTRL) { - if (!gKeysState[SDL_SCANCODE_LCTRL] && !gKeysState[SDL_SCANCODE_RCTRL]) continue; + if (!keysState[SDL_SCANCODE_LCTRL] && !keysState[SDL_SCANCODE_RCTRL]) continue; } if (shortcutKey & ALT) { - if (!gKeysState[SDL_SCANCODE_LALT] && !gKeysState[SDL_SCANCODE_RALT]) continue; + if (!keysState[SDL_SCANCODE_LALT] && !keysState[SDL_SCANCODE_RALT]) continue; } #ifdef __MACOSX__ if (shortcutKey & CMD) { - if (!gKeysState[SDL_SCANCODE_LGUI] && !gKeysState[SDL_SCANCODE_RGUI]) continue; + if (!keysState[SDL_SCANCODE_LGUI] && !keysState[SDL_SCANCODE_RGUI]) continue; } #endif diff --git a/src/openrct2/interface/chat.c b/src/openrct2/interface/chat.c index 5c5c755011..ec76f4f873 100644 --- a/src/openrct2/interface/chat.c +++ b/src/openrct2/interface/chat.c @@ -16,6 +16,7 @@ #include "../audio/audio.h" #include "../audio/AudioMixer.h" +#include "../Context.h" #include "../interface/themes.h" #include "../localisation/localisation.h" #include "../network/network.h" @@ -44,13 +45,13 @@ static void chat_clear_input(); void chat_open() { gChatOpen = true; - platform_start_text_input(_chatCurrentLine, sizeof(_chatCurrentLine)); + context_start_text_input(_chatCurrentLine, sizeof(_chatCurrentLine)); } void chat_close() { gChatOpen = false; - platform_stop_text_input(); + context_stop_text_input(); } void chat_toggle() diff --git a/src/openrct2/interface/console.c b/src/openrct2/interface/console.c index 6a296a9d4d..65a7f86617 100644 --- a/src/openrct2/interface/console.c +++ b/src/openrct2/interface/console.c @@ -18,6 +18,7 @@ #include #include "../config/Config.h" +#include "../Context.h" #include "../drawing/drawing.h" #include "../game.h" #include "../input.h" @@ -91,14 +92,14 @@ void console_open() _consoleScrollPos = 0; console_refresh_caret(); console_update_scroll(); - platform_start_text_input(_consoleCurrentLine, sizeof(_consoleCurrentLine)); + context_start_text_input(_consoleCurrentLine, sizeof(_consoleCurrentLine)); } void console_close() { gConsoleOpen = false; console_invalidate(); - platform_stop_text_input(); + context_stop_text_input(); } void console_toggle() @@ -412,7 +413,7 @@ static void console_clear_input() { _consoleCurrentLine[0] = 0; if (gConsoleOpen) { - platform_start_text_input(_consoleCurrentLine, sizeof(_consoleCurrentLine)); + context_start_text_input(_consoleCurrentLine, sizeof(_consoleCurrentLine)); } } diff --git a/src/openrct2/interface/window.c b/src/openrct2/interface/window.c index a675cc5398..08a1adbdfb 100644 --- a/src/openrct2/interface/window.c +++ b/src/openrct2/interface/window.c @@ -15,6 +15,7 @@ #pragma endregion #include "../audio/audio.h" +#include "../Context.h" #include "../core/Guard.hpp" #include "../drawing/drawing.h" #include "../editor.h" @@ -295,7 +296,7 @@ static bool window_other_wheel_input(rct_window *w, rct_widgetindex widgetIndex, static void window_all_wheel_input() { // Get wheel value - sint32 raw = gCursorState.wheel; + sint32 raw = context_get_cursor_state()->wheel; sint32 wheel = 0; while (1) { raw -= 120; @@ -311,14 +312,17 @@ static void window_all_wheel_input() wheel += 17; } raw -= 120; - gCursorState.wheel = raw; + + // TODO do something about this hack + CursorState * cursorState = (CursorState *)context_get_cursor_state(); + cursorState->wheel = raw; if (wheel == 0) return; // Check window cursor is over if (!(input_test_flag(INPUT_FLAG_5))) { - rct_window *w = window_find_from_point(gCursorState.x, gCursorState.y); + rct_window *w = window_find_from_point(cursorState->x, cursorState->y); if (w != NULL) { // Check if main window if (w->classification == WC_MAIN_WINDOW || w->classification == WC_VIEWPORT) { @@ -327,7 +331,7 @@ static void window_all_wheel_input() } // Check scroll view, cursor is over - rct_widgetindex widgetIndex = window_find_widget_from_point(w, gCursorState.x, gCursorState.y); + rct_widgetindex widgetIndex = window_find_widget_from_point(w, cursorState->x, cursorState->y); if (widgetIndex != -1) { rct_widget *widget = &w->widgets[widgetIndex]; if (widget->type == WWT_SCROLL) { @@ -1471,7 +1475,7 @@ void window_viewport_get_map_coords_by_cursor(rct_window *w, sint16 *map_x, sint { // Get mouse position to offset against. sint32 mouse_x, mouse_y; - platform_get_cursor_position_scaled(&mouse_x, &mouse_y); + context_get_cursor_position_scaled(&mouse_x, &mouse_y); // Compute map coordinate by mouse position. get_map_coordinates_from_pos(mouse_x, mouse_y, VIEWPORT_INTERACTION_MASK_NONE, map_x, map_y, NULL, NULL, NULL); @@ -1499,7 +1503,7 @@ void window_viewport_centre_tile_around_cursor(rct_window *w, sint16 map_x, sint // Get mouse position to offset against. sint32 mouse_x, mouse_y; - platform_get_cursor_position_scaled(&mouse_x, &mouse_y); + context_get_cursor_position_scaled(&mouse_x, &mouse_y); // Rebase mouse position onto centre of window, and compensate for zoom level. sint32 rebased_x = ((w->width >> 1) - mouse_x) * (1 << w->viewport->zoom), @@ -2507,7 +2511,7 @@ void window_start_textbox(rct_window *call_w, rct_widgetindex call_widget, rct_s // from crashing the game. gTextBoxInput[maxLength - 1] = '\0'; - platform_start_text_input(gTextBoxInput, maxLength); + context_start_text_input(gTextBoxInput, maxLength); } void window_cancel_textbox() @@ -2520,7 +2524,7 @@ void window_cancel_textbox() window_event_textinput_call(w, gCurrentTextBox.widget_index, NULL); gCurrentTextBox.window.classification = WC_NULL; gCurrentTextBox.window.number = 0; - platform_stop_text_input(); + context_stop_text_input(); gUsingWidgetTextBox = false; widget_invalidate(w, gCurrentTextBox.widget_index); gCurrentTextBox.widget_index = WWT_LAST; diff --git a/src/openrct2/intro.c b/src/openrct2/intro.c index c2740dc8a9..5b3b7fcd07 100644 --- a/src/openrct2/intro.c +++ b/src/openrct2/intro.c @@ -16,6 +16,7 @@ #include "audio/audio.h" #include "audio/AudioMixer.h" +#include "Context.h" #include "drawing/drawing.h" #include "intro.h" #include "rct2.h" @@ -221,7 +222,7 @@ void intro_draw(rct_drawpixelinfo *dpi) static void screen_intro_process_mouse_input() { - if (gCursorState.any == CURSOR_PRESSED) { + if (context_get_cursor_state()->any == CURSOR_PRESSED) { screen_intro_skip_part(); } } @@ -232,8 +233,12 @@ static void screen_intro_process_mouse_input() */ static void screen_intro_process_keyboard_input() { - if (gLastKeyPressed != 0) { - screen_intro_skip_part(); + const uint8 * keys = context_get_keys_state(); + for (int i = 0; i < 256; i++) { + if (keys[i] != 0) { + screen_intro_skip_part(); + break; + } } } diff --git a/src/openrct2/platform/platform.h b/src/openrct2/platform/platform.h index ed48c36a68..3c5516b6e2 100644 --- a/src/openrct2/platform/platform.h +++ b/src/openrct2/platform/platform.h @@ -86,23 +86,6 @@ typedef struct rct2_time { uint8 second; } rct2_time; -typedef struct openrct2_cursor { - sint32 x, y; - uint8 left, middle, right, any; - sint32 wheel; - sint32 old; - bool touch, touchIsDouble; - uint32 touchDownTimestamp; -} openrct2_cursor; - -enum { - CURSOR_UP = 0, - CURSOR_DOWN = 1, - CURSOR_CHANGED = 2, - CURSOR_RELEASED = CURSOR_UP | CURSOR_CHANGED, - CURSOR_PRESSED = CURSOR_DOWN | CURSOR_CHANGED, -}; - typedef enum {FD_OPEN, FD_SAVE} filedialog_type; typedef struct file_dialog_desc { @@ -116,44 +99,18 @@ typedef struct file_dialog_desc { } filters[8]; } file_dialog_desc; -extern openrct2_cursor gCursorState; -extern const uint8 *gKeysState; -extern uint8 *gKeysPressed; -extern uint32 gLastKeyPressed; - -extern textinputbuffer gTextInput; -extern bool gTextInputCompositionActive; -extern utf8 gTextInputComposition[32]; -extern sint32 gTextInputCompositionStart; -extern sint32 gTextInputCompositionLength; - -extern sint32 gResolutionsAllowAnyAspectRatio; -extern sint32 gNumResolutions; -extern resolution_t *gResolutions; extern SDL_Window *gWindow; - extern SDL_Color gPalette[256]; -extern bool gSteamOverlayActive; - // Platform shared definitions -void platform_update_fullscreen_resolutions(); -void platform_get_closest_resolution(sint32 inWidth, sint32 inHeight, sint32 *outWidth, sint32 *outHeight); void platform_init(); void platform_draw(); void platform_draw_require_end(); void platform_free(); -void platform_trigger_resize(); void platform_update_palette(const uint8 *colours, sint32 start_index, sint32 num_colours); -void platform_set_fullscreen_mode(sint32 mode); void platform_toggle_windowed_mode(); -void platform_set_cursor(uint8 cursor); void platform_refresh_video(); -void platform_process_messages(); sint32 platform_scancode_to_rct_keycode(sint32 sdl_key); -void platform_start_text_input(utf8 *buffer, sint32 max_length); -void platform_stop_text_input(); -bool platform_is_input_active(); void platform_get_date_utc(rct2_date *out_date); void platform_get_time_utc(rct2_time *out_time); void platform_get_date_local(rct2_date *out_date); @@ -183,11 +140,6 @@ sint32 platform_get_drives(); bool platform_file_copy(const utf8 *srcPath, const utf8 *dstPath, bool overwrite); bool platform_file_move(const utf8 *srcPath, const utf8 *dstPath); bool platform_file_delete(const utf8 *path); -void platform_hide_cursor(); -void platform_show_cursor(); -void platform_get_cursor_position(sint32 *x, sint32 *y); -void platform_get_cursor_position_scaled(sint32 *x, sint32 *y); -void platform_set_cursor_position(sint32 x, sint32 y); uint32 platform_get_ticks(); void platform_sleep(uint32 ms); void platform_resolve_user_data_path(); diff --git a/src/openrct2/platform/shared.c b/src/openrct2/platform/shared.c index 80aa3fb641..277c911ed8 100644 --- a/src/openrct2/platform/shared.c +++ b/src/openrct2/platform/shared.c @@ -49,21 +49,6 @@ typedef void(*update_palette_func)(const uint8*, sint32, sint32); -openrct2_cursor gCursorState; -const uint8 *gKeysState; -uint8 *gKeysPressed; -uint32 gLastKeyPressed; -textinputbuffer gTextInput; - -bool gTextInputCompositionActive; -utf8 gTextInputComposition[32]; -sint32 gTextInputCompositionStart; -sint32 gTextInputCompositionLength; - -sint32 gNumResolutions = 0; -resolution_t *gResolutions = NULL; -sint32 gResolutionsAllowAnyAspectRatio = 0; - SDL_Window *gWindow = NULL; SDL_Renderer *gRenderer = NULL; SDL_Texture *gBufferTexture = NULL; @@ -147,277 +132,6 @@ void platform_update_palette(const uint8* colours, sint32 start_index, sint32 nu } } -void platform_process_messages() -{ - SDL_Event e; - - gLastKeyPressed = 0; - // gCursorState.wheel = 0; - gCursorState.left &= ~CURSOR_CHANGED; - gCursorState.middle &= ~CURSOR_CHANGED; - gCursorState.right &= ~CURSOR_CHANGED; - gCursorState.old = 0; - gCursorState.touch = false; - - while (SDL_PollEvent(&e)) { - switch (e.type) { - case SDL_QUIT: -// rct2_finish(); - rct2_quit(); - break; - case SDL_WINDOWEVENT: - // HACK: Fix #2158, OpenRCT2 does not draw if it does not think that the window is - // visible - due a bug in SDL2.0.3 this hack is required if the - // window is maximised, minimised and then restored again. - if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { - if (SDL_GetWindowFlags(gWindow) & SDL_WINDOW_MAXIMIZED) { - SDL_RestoreWindow(gWindow); - SDL_MaximizeWindow(gWindow); - } - if ((SDL_GetWindowFlags(gWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { - SDL_RestoreWindow(gWindow); - SDL_SetWindowFullscreen(gWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); - } - } - - if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) - platform_resize(e.window.data1, e.window.data2); - if (gConfigSound.audio_focus && gConfigSound.sound_enabled) { - if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { - Mixer_SetVolume(1); - } - if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { - Mixer_SetVolume(0); - } - } - break; - case SDL_MOUSEMOTION: - gCursorState.x = (sint32)(e.motion.x / gConfigGeneral.window_scale); - gCursorState.y = (sint32)(e.motion.y / gConfigGeneral.window_scale); - break; - case SDL_MOUSEWHEEL: - if (gConsoleOpen) { - console_scroll(e.wheel.y); - break; - } - gCursorState.wheel += e.wheel.y * 128; - break; - case SDL_MOUSEBUTTONDOWN: - { - sint32 x = (sint32)(e.button.x / gConfigGeneral.window_scale); - sint32 y = (sint32)(e.button.y / gConfigGeneral.window_scale); - switch (e.button.button) { - case SDL_BUTTON_LEFT: - store_mouse_input(MOUSE_STATE_LEFT_PRESS, x, y); - gCursorState.left = CURSOR_PRESSED; - gCursorState.old = 1; - break; - case SDL_BUTTON_MIDDLE: - gCursorState.middle = CURSOR_PRESSED; - break; - case SDL_BUTTON_RIGHT: - store_mouse_input(MOUSE_STATE_RIGHT_PRESS, x, y); - gCursorState.right = CURSOR_PRESSED; - gCursorState.old = 2; - break; - } - break; - } - case SDL_MOUSEBUTTONUP: - { - sint32 x = (sint32)(e.button.x / gConfigGeneral.window_scale); - sint32 y = (sint32)(e.button.y / gConfigGeneral.window_scale); - switch (e.button.button) { - case SDL_BUTTON_LEFT: - store_mouse_input(MOUSE_STATE_LEFT_RELEASE, x, y); - gCursorState.left = CURSOR_RELEASED; - gCursorState.old = 3; - break; - case SDL_BUTTON_MIDDLE: - gCursorState.middle = CURSOR_RELEASED; - break; - case SDL_BUTTON_RIGHT: - store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, x, y); - gCursorState.right = CURSOR_RELEASED; - gCursorState.old = 4; - break; - } - break; - } -// Apple sends touchscreen events for trackpads, so ignore these events on macOS -#ifndef __MACOSX__ - case SDL_FINGERMOTION: - gCursorState.x = (sint32)(e.tfinger.x * gScreenWidth); - gCursorState.y = (sint32)(e.tfinger.y * gScreenHeight); - break; - case SDL_FINGERDOWN: - { - sint32 x = (sint32)(e.tfinger.x * gScreenWidth); - sint32 y = (sint32)(e.tfinger.y * gScreenHeight); - - gCursorState.touchIsDouble = (!gCursorState.touchIsDouble - && e.tfinger.timestamp - gCursorState.touchDownTimestamp < TOUCH_DOUBLE_TIMEOUT); - - if (gCursorState.touchIsDouble) { - store_mouse_input(MOUSE_STATE_RIGHT_PRESS, x, y); - gCursorState.right = CURSOR_PRESSED; - gCursorState.old = 2; - } else { - store_mouse_input(MOUSE_STATE_LEFT_PRESS, x, y); - gCursorState.left = CURSOR_PRESSED; - gCursorState.old = 1; - } - gCursorState.touch = true; - gCursorState.touchDownTimestamp = e.tfinger.timestamp; - break; - } - case SDL_FINGERUP: - { - sint32 x = (sint32)(e.tfinger.x * gScreenWidth); - sint32 y = (sint32)(e.tfinger.y * gScreenHeight); - - if (gCursorState.touchIsDouble) { - store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, x, y); - gCursorState.left = CURSOR_RELEASED; - gCursorState.old = 4; - } else { - store_mouse_input(MOUSE_STATE_LEFT_RELEASE, x, y); - gCursorState.left = CURSOR_RELEASED; - gCursorState.old = 3; - } - gCursorState.touch = true; - break; - } -#endif - case SDL_KEYDOWN: - if (gTextInputCompositionActive) break; - - if (e.key.keysym.sym == SDLK_KP_ENTER){ - // Map Keypad enter to regular enter. - e.key.keysym.scancode = SDL_SCANCODE_RETURN; - } - - gLastKeyPressed = e.key.keysym.sym; - gKeysPressed[e.key.keysym.scancode] = 1; - - // Text input - if (gTextInput.buffer == NULL) break; - - // Clear the input on Backspace (Windows/Linux) or Backspace (macOS) - if (e.key.keysym.sym == SDLK_BACKSPACE && (e.key.keysym.mod & KEYBOARD_PRIMARY_MODIFIER)) { - textinputbuffer_clear(&gTextInput); - console_refresh_caret(); - window_update_textbox(); - } - - // If backspace and we have input text with a cursor position none zero - if (e.key.keysym.sym == SDLK_BACKSPACE) { - if (gTextInput.selection_offset > 0) { - size_t endOffset = gTextInput.selection_offset; - textinputbuffer_cursor_left(&gTextInput); - gTextInput.selection_size = endOffset - gTextInput.selection_offset; - textinputbuffer_remove_selected(&gTextInput); - - console_refresh_caret(); - window_update_textbox(); - } - } - if (e.key.keysym.sym == SDLK_HOME) { - textinputbuffer_cursor_home(&gTextInput); - console_refresh_caret(); - } - if (e.key.keysym.sym == SDLK_END) { - textinputbuffer_cursor_end(&gTextInput); - console_refresh_caret(); - } - if (e.key.keysym.sym == SDLK_DELETE) { - size_t startOffset = gTextInput.selection_offset; - textinputbuffer_cursor_right(&gTextInput); - gTextInput.selection_size = gTextInput.selection_offset - startOffset; - gTextInput.selection_offset = startOffset; - textinputbuffer_remove_selected(&gTextInput); - console_refresh_caret(); - window_update_textbox(); - } - if (e.key.keysym.sym == SDLK_RETURN) { - window_cancel_textbox(); - } - if (e.key.keysym.sym == SDLK_LEFT) { - textinputbuffer_cursor_left(&gTextInput); - console_refresh_caret(); - } - else if (e.key.keysym.sym == SDLK_RIGHT) { - textinputbuffer_cursor_right(&gTextInput); - console_refresh_caret(); - } - else if (e.key.keysym.sym == SDLK_v && (SDL_GetModState() & KEYBOARD_PRIMARY_MODIFIER)) { - if (SDL_HasClipboardText()) { - utf8* text = SDL_GetClipboardText(); - - utf8_remove_formatting(text, false); - textinputbuffer_insert(&gTextInput, text); - - SDL_free(text); - - window_update_textbox(); - } - } - break; - case SDL_MULTIGESTURE: - if (e.mgesture.numFingers == 2) { - if (e.mgesture.timestamp > _lastGestureTimestamp + 1000) - _gestureRadius = 0; - _lastGestureTimestamp = e.mgesture.timestamp; - _gestureRadius += e.mgesture.dDist; - - // Zoom gesture - const sint32 tolerance = 128; - sint32 gesturePixels = (sint32)(_gestureRadius * gScreenWidth); - if (abs(gesturePixels) > tolerance) { - _gestureRadius = 0; - main_window_zoom(gesturePixels > 0, true); - } - } - break; - case SDL_TEXTEDITING: - // When inputting Korean characters, `e.edit.length` is always Zero. - safe_strcpy(gTextInputComposition, e.edit.text, sizeof(gTextInputComposition)); - gTextInputCompositionStart = e.edit.start; - gTextInputCompositionLength = e.edit.length; - gTextInputCompositionActive = ((e.edit.length != 0 || strlen(e.edit.text) != 0) && gTextInputComposition[0] != 0); - break; - case SDL_TEXTINPUT: - // will receive an `SDL_TEXTINPUT` event when a composition is committed. - // so, set gTextInputCompositionActive to false. - gTextInputCompositionActive = false; - - if (gTextInput.buffer == NULL) break; - - // HACK ` will close console, so don't input any text - if (e.text.text[0] == '`' && gConsoleOpen) { - break; - } - - utf8* newText = e.text.text; - - utf8_remove_formatting(newText, false); - textinputbuffer_insert(&gTextInput, newText); - - console_refresh_caret(); - window_update_textbox(); - break; - default: - break; - } - } - - gCursorState.any = gCursorState.left | gCursorState.middle | gCursorState.right; - - // Updates the state of the keys - sint32 numKeys = 256; - gKeysState = SDL_GetKeyboardState(&numKeys); -} - void platform_init() { gKeysPressed = malloc(sizeof(uint8) * 256); @@ -450,56 +164,6 @@ void platform_free() free(gKeysPressed); } -void platform_start_text_input(utf8* buffer, sint32 max_length) -{ - // TODO This doesn't work, and position could be improved to where text entry is - SDL_Rect rect = { 10, 10, 100, 100 }; - SDL_SetTextInputRect(&rect); - - SDL_StartTextInput(); - - textinputbuffer_init(&gTextInput, buffer, max_length); -} - -bool platform_is_input_active() -{ - return SDL_IsTextInputActive() && gTextInput.buffer != NULL; -} - -void platform_stop_text_input() -{ - SDL_StopTextInput(); - gTextInput.buffer = NULL; - gTextInputCompositionActive = false; -} - -void platform_set_fullscreen_mode(sint32 mode) -{ - sint32 width, height; - - mode = _fullscreen_modes[mode]; - - // HACK Changing window size when in fullscreen usually has no effect - if (mode == SDL_WINDOW_FULLSCREEN) - SDL_SetWindowFullscreen(gWindow, 0); - - // Set window size - if (mode == SDL_WINDOW_FULLSCREEN) { - platform_update_fullscreen_resolutions(); - platform_get_closest_resolution(gConfigGeneral.fullscreen_width, gConfigGeneral.fullscreen_height, &width, &height); - SDL_SetWindowSize(gWindow, width, height); - } else if (mode == 0) { - SDL_SetWindowSize(gWindow, gConfigGeneral.window_width, gConfigGeneral.window_height); - } - - if (SDL_SetWindowFullscreen(gWindow, mode)) { - log_fatal("SDL_SetWindowFullscreen %s", SDL_GetError()); - exit(1); - - // TODO try another display mode rather than just exiting the game - } -} - void platform_toggle_windowed_mode() { sint32 targetMode = gConfigGeneral.fullscreen_mode == 0 ? 2 : 0; @@ -519,35 +183,6 @@ void platform_refresh_video() gfx_invalidate_screen(); } -void platform_hide_cursor() -{ - SDL_ShowCursor(SDL_DISABLE); -} - -void platform_show_cursor() -{ - SDL_ShowCursor(SDL_ENABLE); -} - -void platform_get_cursor_position(sint32 *x, sint32 *y) -{ - SDL_GetMouseState(x, y); -} - -void platform_get_cursor_position_scaled(sint32 *x, sint32 *y) -{ - platform_get_cursor_position(x, y); - - // Compensate for window scaling. - *x = (sint32) ceilf(*x / gConfigGeneral.window_scale); - *y = (sint32) ceilf(*y / gConfigGeneral.window_scale); -} - -void platform_set_cursor_position(sint32 x, sint32 y) -{ - SDL_WarpMouseInWindow(NULL, x, y); -} - uint32 platform_get_ticks() { #ifdef _WIN32 diff --git a/src/openrct2/ui/UiContext.h b/src/openrct2/ui/UiContext.h index c03ead8342..2f84db1f00 100644 --- a/src/openrct2/ui/UiContext.h +++ b/src/openrct2/ui/UiContext.h @@ -20,6 +20,8 @@ #include "../common.h" #include "../interface/Cursors.h" +struct CursorState; + namespace OpenRCT2 { namespace Drawing @@ -42,7 +44,7 @@ namespace OpenRCT2 const utf8 * Buffer; // UTF-8 stream size_t BufferSize; // Maximum number of bytes (excluding null terminator) size_t Size; // Number of bytes (excluding null terminator) - uint32 Length; // Number of codepoints + size_t Length; // Number of codepoints size_t SelectionStart; // Selection start, in bytes size_t SelectionSize; // Selection length in bytes }; @@ -83,21 +85,30 @@ namespace OpenRCT2 virtual ~IUiContext() = default; // Window - virtual CURSOR_ID GetCursor() abstract; - virtual void SetCursor(CURSOR_ID cursor) abstract; virtual void * GetWindow() abstract; virtual sint32 GetWidth() abstract; virtual sint32 GetHeight() abstract; virtual void SetFullscreenMode(FULLSCREEN_MODE mode) abstract; - virtual std::vector GetFullscreenResolutions() abstract; + virtual bool IsSteamOverlayActive() abstract; + virtual void ProcessMessages() abstract; + + // Input + virtual const CursorState * GetCursorState() abstract; + virtual CURSOR_ID GetCursor() abstract; + virtual void SetCursor(CURSOR_ID cursor) abstract; + virtual void SetCursorVisible(bool value) abstract; + virtual void GetCursorPosition(sint32 * x, sint32 * y) abstract; + virtual void SetCursorPosition(sint32 x, sint32 y) abstract; + virtual const uint8 * GetKeysState() abstract; + virtual const uint8 * GetKeysPressed() abstract; // Drawing virtual Drawing::IDrawingEngine * CreateDrawingEngine(Drawing::DRAWING_ENGINE_TYPE type) abstract; // Text input virtual bool IsTextInputActive() abstract; - virtual const TextInputSession * StartTextInput(utf8 * buffer, sint32 bufferSize) abstract; + virtual const TextInputSession * StartTextInput(utf8 * buffer, size_t bufferSize) abstract; virtual void StopTextInput() abstract; }; }