2017-03-24 03:47:43 +01:00
|
|
|
/*****************************************************************************
|
2024-01-01 12:52:28 +01:00
|
|
|
* Copyright (c) 2014-2024 OpenRCT2 developers
|
2018-06-15 14:07:34 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*****************************************************************************/
|
2017-03-24 03:47:43 +01:00
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
#include "UiContext.h"
|
|
|
|
|
|
|
|
#include "CursorRepository.h"
|
|
|
|
#include "SDLException.h"
|
|
|
|
#include "TextComposition.h"
|
|
|
|
#include "WindowManager.h"
|
|
|
|
#include "drawing/engines/DrawingEngineFactory.hpp"
|
2021-01-12 23:34:30 +01:00
|
|
|
#include "input/ShortcutManager.h"
|
2018-06-22 23:22:29 +02:00
|
|
|
#include "interface/InGameConsole.h"
|
|
|
|
#include "interface/Theme.h"
|
2018-03-24 20:30:10 +01:00
|
|
|
#include "scripting/UiExtensions.h"
|
2018-06-22 23:22:29 +02:00
|
|
|
#include "title/TitleSequencePlayer.h"
|
|
|
|
|
2019-01-02 21:23:58 +01:00
|
|
|
#include <SDL.h>
|
2017-03-25 15:57:02 +01:00
|
|
|
#include <algorithm>
|
2017-04-01 14:38:52 +02:00
|
|
|
#include <chrono>
|
2017-05-12 01:37:00 +02:00
|
|
|
#include <cmath>
|
2018-06-22 23:22:29 +02:00
|
|
|
#include <cstdlib>
|
2017-03-24 17:26:43 +01:00
|
|
|
#include <memory>
|
2020-12-13 04:40:10 +01:00
|
|
|
#include <openrct2-ui/input/InputManager.h>
|
2018-06-22 23:22:29 +02:00
|
|
|
#include <openrct2-ui/interface/Window.h>
|
|
|
|
#include <openrct2/Context.h>
|
|
|
|
#include <openrct2/Input.h>
|
|
|
|
#include <openrct2/Version.h>
|
2022-06-27 18:41:53 +02:00
|
|
|
#include <openrct2/audio/AudioContext.h>
|
2017-03-25 18:42:14 +01:00
|
|
|
#include <openrct2/audio/AudioMixer.h>
|
2017-03-25 15:57:02 +01:00
|
|
|
#include <openrct2/config/Config.h>
|
2017-04-01 14:38:52 +02:00
|
|
|
#include <openrct2/core/String.hpp>
|
2018-03-19 23:28:40 +01:00
|
|
|
#include <openrct2/drawing/Drawing.h>
|
2018-06-22 23:22:29 +02:00
|
|
|
#include <openrct2/drawing/IDrawingEngine.h>
|
2018-06-02 01:31:06 +02:00
|
|
|
#include <openrct2/interface/Chat.h>
|
2018-06-22 23:22:29 +02:00
|
|
|
#include <openrct2/interface/InteractiveConsole.h>
|
|
|
|
#include <openrct2/localisation/StringIds.h>
|
2022-02-18 21:57:00 +01:00
|
|
|
#include <openrct2/platform/Platform.h>
|
2024-04-23 21:31:05 +02:00
|
|
|
#include <openrct2/scenes/title/TitleSequencePlayer.h>
|
2018-03-24 20:30:10 +01:00
|
|
|
#include <openrct2/scripting/ScriptEngine.h>
|
2017-03-25 04:16:40 +01:00
|
|
|
#include <openrct2/ui/UiContext.h>
|
2017-07-30 17:33:42 +02:00
|
|
|
#include <openrct2/ui/WindowManager.h>
|
2020-07-06 15:10:39 +02:00
|
|
|
#include <openrct2/world/Location.hpp>
|
2018-06-22 23:22:29 +02:00
|
|
|
#include <vector>
|
2018-03-11 23:25:34 +01:00
|
|
|
|
2017-03-24 03:47:43 +01:00
|
|
|
using namespace OpenRCT2;
|
2017-03-25 04:16:40 +01:00
|
|
|
using namespace OpenRCT2::Drawing;
|
2018-03-24 20:30:10 +01:00
|
|
|
using namespace OpenRCT2::Scripting;
|
2017-03-24 03:47:43 +01:00
|
|
|
using namespace OpenRCT2::Ui;
|
|
|
|
|
2017-03-25 18:42:14 +01:00
|
|
|
#ifdef __MACOSX__
|
2018-06-22 23:22:29 +02:00
|
|
|
// macOS uses COMMAND rather than CTRL for many keyboard shortcuts
|
2018-07-21 16:17:06 +02:00
|
|
|
# define KEYBOARD_PRIMARY_MODIFIER KMOD_GUI
|
2017-03-25 18:42:14 +01:00
|
|
|
#else
|
2018-07-21 16:17:06 +02:00
|
|
|
# define KEYBOARD_PRIMARY_MODIFIER KMOD_CTRL
|
2017-03-25 18:42:14 +01:00
|
|
|
#endif
|
|
|
|
|
2017-05-08 19:11:42 +02:00
|
|
|
class UiContext final : public IUiContext
|
2017-03-24 03:47:43 +01:00
|
|
|
{
|
2017-03-25 15:57:02 +01:00
|
|
|
private:
|
2018-06-20 17:28:51 +02:00
|
|
|
constexpr static uint32_t TOUCH_DOUBLE_TIMEOUT = 300;
|
2017-03-25 18:42:14 +01:00
|
|
|
|
2022-07-28 19:19:38 +02:00
|
|
|
const std::unique_ptr<IPlatformUiContext> _platformUiContext;
|
|
|
|
const std::unique_ptr<IWindowManager> _windowManager;
|
2017-03-25 15:57:02 +01:00
|
|
|
|
|
|
|
CursorRepository _cursorRepository;
|
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
SDL_Window* _window = nullptr;
|
|
|
|
int32_t _width = 0;
|
|
|
|
int32_t _height = 0;
|
2020-08-21 04:32:56 +02:00
|
|
|
ScaleQuality _scaleQuality = ScaleQuality::NearestNeighbour;
|
2017-03-25 15:57:02 +01:00
|
|
|
|
|
|
|
std::vector<Resolution> _fsResolutions;
|
|
|
|
|
|
|
|
bool _steamOverlayActive = false;
|
|
|
|
|
2017-03-25 18:42:14 +01:00
|
|
|
// Input
|
2021-01-20 23:49:11 +01:00
|
|
|
InputManager _inputManager;
|
2021-01-12 23:34:30 +01:00
|
|
|
ShortcutManager _shortcutManager;
|
2018-06-22 23:22:29 +02:00
|
|
|
TextComposition _textComposition;
|
|
|
|
CursorState _cursorState = {};
|
|
|
|
uint32_t _lastKeyPressed = 0;
|
|
|
|
const uint8_t* _keysState = nullptr;
|
|
|
|
uint8_t _keysPressed[256] = {};
|
|
|
|
uint32_t _lastGestureTimestamp = 0;
|
|
|
|
float _gestureRadius = 0;
|
|
|
|
|
|
|
|
InGameConsole _inGameConsole;
|
2018-06-02 13:33:20 +02:00
|
|
|
std::unique_ptr<ITitleSequencePlayer> _titleSequencePlayer;
|
2018-03-11 23:25:34 +01:00
|
|
|
|
2017-03-24 03:47:43 +01:00
|
|
|
public:
|
2018-06-22 23:22:29 +02:00
|
|
|
InGameConsole& GetInGameConsole()
|
|
|
|
{
|
|
|
|
return _inGameConsole;
|
|
|
|
}
|
2018-03-11 23:25:34 +01:00
|
|
|
|
2021-01-20 23:49:11 +01:00
|
|
|
InputManager& GetInputManager()
|
|
|
|
{
|
|
|
|
return _inputManager;
|
|
|
|
}
|
|
|
|
|
2021-01-12 23:34:30 +01:00
|
|
|
ShortcutManager& GetShortcutManager()
|
|
|
|
{
|
|
|
|
return _shortcutManager;
|
|
|
|
}
|
|
|
|
|
2018-04-27 19:47:57 +02:00
|
|
|
explicit UiContext(const std::shared_ptr<IPlatformEnvironment>& env)
|
2018-06-22 23:22:29 +02:00
|
|
|
: _platformUiContext(CreatePlatformUiContext())
|
|
|
|
, _windowManager(CreateWindowManager())
|
2021-01-12 23:34:30 +01:00
|
|
|
, _shortcutManager(env)
|
2017-03-24 03:47:43 +01:00
|
|
|
{
|
2022-05-10 21:54:04 +02:00
|
|
|
LogSDLVersion();
|
2020-12-16 00:14:40 +01:00
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
2020-12-16 00:14:40 +01:00
|
|
|
SDLException::Throw("SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)");
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
2017-03-26 22:42:07 +02:00
|
|
|
_cursorRepository.LoadCursors();
|
2021-01-12 23:34:30 +01:00
|
|
|
_shortcutManager.LoadUserBindings();
|
2017-03-24 03:47:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
~UiContext() override
|
|
|
|
{
|
2021-07-24 23:41:50 +02:00
|
|
|
UiContext::CloseWindow();
|
2017-03-29 19:44:19 +02:00
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
2017-03-24 03:47:43 +01:00
|
|
|
}
|
2017-03-25 04:16:40 +01:00
|
|
|
|
2018-03-24 20:30:10 +01:00
|
|
|
void Initialise() override
|
|
|
|
{
|
2020-04-24 16:58:01 +02:00
|
|
|
#ifdef ENABLE_SCRIPTING
|
2018-03-24 20:30:10 +01:00
|
|
|
auto& scriptEngine = GetContext()->GetScriptEngine();
|
|
|
|
UiScriptExtensions::Extend(scriptEngine);
|
2020-02-23 13:55:48 +01:00
|
|
|
#endif
|
2018-03-24 20:30:10 +01:00
|
|
|
}
|
|
|
|
|
2021-12-02 16:35:31 +01:00
|
|
|
void Tick() override
|
2018-03-11 23:25:34 +01:00
|
|
|
{
|
|
|
|
_inGameConsole.Update();
|
|
|
|
}
|
|
|
|
|
2023-04-03 22:18:42 +02:00
|
|
|
void Draw(DrawPixelInfo& dpi) override
|
2018-03-11 23:25:34 +01:00
|
|
|
{
|
2022-08-21 18:38:25 +02:00
|
|
|
auto bgColour = ThemeGetColour(WindowClass::Chat, 0);
|
2023-04-03 22:18:42 +02:00
|
|
|
ChatDraw(dpi, bgColour);
|
|
|
|
_inGameConsole.Draw(dpi);
|
2018-03-11 23:25:34 +01:00
|
|
|
}
|
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
// Window
|
2018-06-22 23:22:29 +02:00
|
|
|
void* GetWindow() override
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
|
|
|
return _window;
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t GetWidth() override
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
|
|
|
return _width;
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t GetHeight() override
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
|
|
|
return _height;
|
|
|
|
}
|
|
|
|
|
2020-08-21 04:32:56 +02:00
|
|
|
ScaleQuality GetScaleQuality() override
|
2017-10-25 03:17:40 +02:00
|
|
|
{
|
|
|
|
return _scaleQuality;
|
|
|
|
}
|
|
|
|
|
2017-03-25 15:57:02 +01:00
|
|
|
void SetFullscreenMode(FULLSCREEN_MODE mode) override
|
|
|
|
{
|
2023-06-24 11:45:26 +02:00
|
|
|
static constexpr int32_t _sdlFullscreenFlags[] = {
|
2021-10-17 15:31:56 +02:00
|
|
|
0,
|
|
|
|
SDL_WINDOW_FULLSCREEN,
|
|
|
|
SDL_WINDOW_FULLSCREEN_DESKTOP,
|
|
|
|
};
|
2024-03-04 16:37:20 +01:00
|
|
|
uint32_t windowFlags = _sdlFullscreenFlags[EnumValue(mode)];
|
2017-03-25 15:57:02 +01:00
|
|
|
|
|
|
|
// HACK Changing window size when in fullscreen usually has no effect
|
|
|
|
if (mode == FULLSCREEN_MODE::FULLSCREEN)
|
|
|
|
{
|
|
|
|
SDL_SetWindowFullscreen(_window, 0);
|
|
|
|
|
2021-07-24 23:41:50 +02:00
|
|
|
// Set window size
|
2017-03-25 15:57:02 +01:00
|
|
|
UpdateFullscreenResolutions();
|
2022-10-16 17:27:01 +02:00
|
|
|
Resolution resolution = GetClosestResolution(gConfigGeneral.FullscreenWidth, gConfigGeneral.FullscreenHeight);
|
2017-03-25 15:57:02 +01:00
|
|
|
SDL_SetWindowSize(_window, resolution.Width, resolution.Height);
|
|
|
|
}
|
|
|
|
else if (mode == FULLSCREEN_MODE::WINDOWED)
|
|
|
|
{
|
2022-10-16 17:27:01 +02:00
|
|
|
SDL_SetWindowSize(_window, gConfigGeneral.WindowWidth, gConfigGeneral.WindowHeight);
|
2017-03-25 15:57:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_SetWindowFullscreen(_window, windowFlags))
|
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_FATAL("SDL_SetWindowFullscreen %s", SDL_GetError());
|
2017-03-25 15:57:02 +01:00
|
|
|
exit(1);
|
|
|
|
|
|
|
|
// TODO try another display mode rather than just exiting the game
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-06 07:20:57 +01:00
|
|
|
const std::vector<Resolution>& GetFullscreenResolutions() override
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
2017-03-26 04:36:22 +02:00
|
|
|
UpdateFullscreenResolutions();
|
2017-03-25 15:57:02 +01:00
|
|
|
return _fsResolutions;
|
|
|
|
}
|
2017-03-25 04:16:40 +01:00
|
|
|
|
2017-04-01 14:38:52 +02:00
|
|
|
bool HasFocus() override
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t windowFlags = GetWindowFlags();
|
2017-05-06 22:33:23 +02:00
|
|
|
return (windowFlags & SDL_WINDOW_INPUT_FOCUS) != 0;
|
2017-04-01 14:38:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsMinimised() override
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t windowFlags = GetWindowFlags();
|
2018-06-22 23:22:29 +02:00
|
|
|
return (windowFlags & SDL_WINDOW_MINIMIZED) || (windowFlags & SDL_WINDOW_HIDDEN);
|
2017-04-01 14:38:52 +02:00
|
|
|
}
|
|
|
|
|
2017-03-25 18:42:14 +01:00
|
|
|
bool IsSteamOverlayActive() override
|
|
|
|
{
|
|
|
|
return _steamOverlayActive;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Input
|
2018-06-22 23:22:29 +02:00
|
|
|
const CursorState* GetCursorState() override
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
|
|
|
return &_cursorState;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
const uint8_t* GetKeysState() override
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
|
|
|
return _keysState;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
const uint8_t* GetKeysPressed() override
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
|
|
|
return _keysPressed;
|
|
|
|
}
|
|
|
|
|
2020-10-11 18:01:14 +02:00
|
|
|
CursorID GetCursor() override
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
|
|
|
return _cursorRepository.GetCurrentCursor();
|
|
|
|
}
|
|
|
|
|
2020-10-11 18:01:14 +02:00
|
|
|
void SetCursor(CursorID cursor) override
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
|
|
|
_cursorRepository.SetCurrentCursor(cursor);
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
void SetCursorScale(uint8_t scale) override
|
2017-12-09 17:08:38 +01:00
|
|
|
{
|
|
|
|
_cursorRepository.SetCursorScale(scale);
|
|
|
|
}
|
|
|
|
|
2017-03-25 18:42:14 +01:00
|
|
|
void SetCursorVisible(bool value) override
|
|
|
|
{
|
|
|
|
SDL_ShowCursor(value ? SDL_ENABLE : SDL_DISABLE);
|
|
|
|
}
|
|
|
|
|
2019-12-14 22:52:18 +01:00
|
|
|
ScreenCoordsXY GetCursorPosition() override
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
2019-12-14 22:52:18 +01:00
|
|
|
ScreenCoordsXY cursorPosition;
|
|
|
|
SDL_GetMouseState(&cursorPosition.x, &cursorPosition.y);
|
|
|
|
return cursorPosition;
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-29 03:43:29 +01:00
|
|
|
void SetCursorPosition(const ScreenCoordsXY& cursorPosition) override
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
2019-12-14 22:52:18 +01:00
|
|
|
SDL_WarpMouseInWindow(nullptr, cursorPosition.x, cursorPosition.y);
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
|
|
|
|
2017-04-01 14:38:52 +02:00
|
|
|
void SetCursorTrap(bool value) override
|
|
|
|
{
|
|
|
|
SDL_SetWindowGrab(_window, value ? SDL_TRUE : SDL_FALSE);
|
|
|
|
}
|
2017-05-27 20:59:59 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
void SetKeysPressed(uint32_t keysym, uint8_t scancode) override
|
2017-05-17 17:36:53 +02:00
|
|
|
{
|
|
|
|
_lastKeyPressed = keysym;
|
|
|
|
_keysPressed[scancode] = 1;
|
|
|
|
}
|
2017-04-01 14:38:52 +02:00
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
// Drawing
|
2018-04-21 01:29:12 +02:00
|
|
|
std::shared_ptr<Drawing::IDrawingEngineFactory> GetDrawingEngineFactory() override
|
|
|
|
{
|
|
|
|
return std::make_shared<DrawingEngineFactory>();
|
2017-03-25 04:16:40 +01:00
|
|
|
}
|
|
|
|
|
2023-04-03 22:18:42 +02:00
|
|
|
void DrawWeatherAnimation(IWeatherDrawer* weatherDrawer, DrawPixelInfo& dpi, DrawWeatherFunc drawFunc) override
|
2018-06-15 08:46:04 +02:00
|
|
|
{
|
2023-04-03 22:18:42 +02:00
|
|
|
int32_t left = dpi.x;
|
|
|
|
int32_t right = left + dpi.width;
|
|
|
|
int32_t top = dpi.y;
|
|
|
|
int32_t bottom = top + dpi.height;
|
2018-06-15 08:46:04 +02:00
|
|
|
|
2018-06-16 03:14:59 +02:00
|
|
|
for (auto& w : g_window_list)
|
2018-06-15 08:46:04 +02:00
|
|
|
{
|
2021-10-06 22:37:02 +02:00
|
|
|
DrawWeatherWindow(dpi, weatherDrawer, w.get(), left, right, top, bottom, drawFunc);
|
2018-06-15 08:46:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
// Text input
|
2017-03-25 18:42:14 +01:00
|
|
|
bool IsTextInputActive() override
|
|
|
|
{
|
|
|
|
return _textComposition.IsActive();
|
|
|
|
}
|
|
|
|
|
2023-02-20 19:23:16 +01:00
|
|
|
TextInputSession* StartTextInput(u8string& buffer, size_t maxLength) override
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
2023-02-20 19:23:16 +01:00
|
|
|
return _textComposition.Start(buffer, maxLength);
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void StopTextInput() override
|
|
|
|
{
|
|
|
|
_textComposition.Stop();
|
|
|
|
}
|
|
|
|
|
2017-03-26 04:36:22 +02:00
|
|
|
void ProcessMessages() override
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
|
|
|
_lastKeyPressed = 0;
|
|
|
|
_cursorState.left &= ~CURSOR_CHANGED;
|
|
|
|
_cursorState.middle &= ~CURSOR_CHANGED;
|
|
|
|
_cursorState.right &= ~CURSOR_CHANGED;
|
|
|
|
_cursorState.old = 0;
|
|
|
|
|
|
|
|
SDL_Event e;
|
|
|
|
while (SDL_PollEvent(&e))
|
|
|
|
{
|
2018-06-22 23:22:29 +02:00
|
|
|
switch (e.type)
|
|
|
|
{
|
|
|
|
case SDL_QUIT:
|
2022-11-06 21:49:07 +01:00
|
|
|
ContextQuit();
|
2018-06-22 23:22:29 +02:00
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT:
|
2023-04-03 16:09:50 +02:00
|
|
|
if (e.window.event == SDL_WINDOWEVENT_RESIZED)
|
2017-05-28 00:28:54 +02:00
|
|
|
{
|
2023-04-03 16:09:50 +02:00
|
|
|
LOG_VERBOSE("New Window size: %ux%u\n", e.window.data1, e.window.data2);
|
2018-06-22 23:22:29 +02:00
|
|
|
OnResize(e.window.data1, e.window.data2);
|
2017-05-28 00:28:54 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
switch (e.window.event)
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
2023-04-03 16:09:50 +02:00
|
|
|
case SDL_WINDOWEVENT_RESIZED:
|
2018-06-22 23:22:29 +02:00
|
|
|
case SDL_WINDOWEVENT_MOVED:
|
|
|
|
case SDL_WINDOWEVENT_MAXIMIZED:
|
|
|
|
case SDL_WINDOWEVENT_RESTORED:
|
|
|
|
{
|
|
|
|
// Update default display index
|
|
|
|
int32_t displayIndex = SDL_GetWindowDisplayIndex(_window);
|
2022-10-16 17:27:01 +02:00
|
|
|
if (displayIndex != gConfigGeneral.DefaultDisplay)
|
2018-06-22 23:22:29 +02:00
|
|
|
{
|
2022-10-16 17:27:01 +02:00
|
|
|
gConfigGeneral.DefaultDisplay = displayIndex;
|
2022-10-16 18:00:03 +02:00
|
|
|
ConfigSaveDefault();
|
2018-06-22 23:22:29 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
2018-06-22 23:22:29 +02:00
|
|
|
|
2018-10-16 23:07:41 +02:00
|
|
|
if (gConfigSound.audio_focus)
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
2018-06-22 23:22:29 +02:00
|
|
|
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
|
|
|
|
{
|
2022-06-27 18:41:53 +02:00
|
|
|
SetAudioVolume(1);
|
2018-06-22 23:22:29 +02:00
|
|
|
}
|
|
|
|
if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
|
|
|
|
{
|
2022-06-27 18:41:53 +02:00
|
|
|
SetAudioVolume(0);
|
2018-06-22 23:22:29 +02:00
|
|
|
}
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
|
|
|
break;
|
2018-06-22 23:22:29 +02:00
|
|
|
case SDL_MOUSEMOTION:
|
2022-10-16 17:27:01 +02:00
|
|
|
_cursorState.position = { static_cast<int32_t>(e.motion.x / gConfigGeneral.WindowScale),
|
|
|
|
static_cast<int32_t>(e.motion.y / gConfigGeneral.WindowScale) };
|
2017-03-25 18:42:14 +01:00
|
|
|
break;
|
2018-06-22 23:22:29 +02:00
|
|
|
case SDL_MOUSEWHEEL:
|
|
|
|
if (_inGameConsole.IsOpen())
|
|
|
|
{
|
|
|
|
_inGameConsole.Scroll(e.wheel.y * 3); // Scroll 3 lines at a time
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_cursorState.wheel -= e.wheel.y;
|
2017-03-25 18:42:14 +01:00
|
|
|
break;
|
2018-06-22 23:22:29 +02:00
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
{
|
2018-10-06 17:43:39 +02:00
|
|
|
if (e.button.which == SDL_TOUCH_MOUSEID)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2022-10-16 17:27:01 +02:00
|
|
|
ScreenCoordsXY mousePos = { static_cast<int32_t>(e.button.x / gConfigGeneral.WindowScale),
|
|
|
|
static_cast<int32_t>(e.button.y / gConfigGeneral.WindowScale) };
|
2018-06-22 23:22:29 +02:00
|
|
|
switch (e.button.button)
|
|
|
|
{
|
|
|
|
case SDL_BUTTON_LEFT:
|
2020-11-03 22:41:19 +01:00
|
|
|
StoreMouseInput(MouseState::LeftPress, mousePos);
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.left = CURSOR_PRESSED;
|
|
|
|
_cursorState.old = 1;
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_MIDDLE:
|
|
|
|
_cursorState.middle = CURSOR_PRESSED;
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_RIGHT:
|
2020-11-03 22:41:19 +01:00
|
|
|
StoreMouseInput(MouseState::RightPress, mousePos);
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.right = CURSOR_PRESSED;
|
|
|
|
_cursorState.old = 2;
|
|
|
|
break;
|
|
|
|
}
|
2018-10-09 20:50:17 +02:00
|
|
|
_cursorState.touch = false;
|
2020-12-13 04:40:10 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
InputEvent ie;
|
|
|
|
ie.DeviceKind = InputDeviceKind::Mouse;
|
2020-12-13 23:51:50 +01:00
|
|
|
ie.Modifiers = SDL_GetModState();
|
2020-12-13 04:40:10 +01:00
|
|
|
ie.Button = e.button.button;
|
|
|
|
ie.State = InputEventState::Down;
|
2021-01-20 23:49:11 +01:00
|
|
|
_inputManager.QueueInputEvent(std::move(ie));
|
2020-12-13 04:40:10 +01:00
|
|
|
}
|
2017-03-25 18:42:14 +01:00
|
|
|
break;
|
|
|
|
}
|
2018-06-22 23:22:29 +02:00
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
|
|
{
|
2018-10-06 17:43:39 +02:00
|
|
|
if (e.button.which == SDL_TOUCH_MOUSEID)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2022-10-16 17:27:01 +02:00
|
|
|
ScreenCoordsXY mousePos = { static_cast<int32_t>(e.button.x / gConfigGeneral.WindowScale),
|
|
|
|
static_cast<int32_t>(e.button.y / gConfigGeneral.WindowScale) };
|
2018-06-22 23:22:29 +02:00
|
|
|
switch (e.button.button)
|
|
|
|
{
|
|
|
|
case SDL_BUTTON_LEFT:
|
2020-11-03 22:41:19 +01:00
|
|
|
StoreMouseInput(MouseState::LeftRelease, mousePos);
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.left = CURSOR_RELEASED;
|
|
|
|
_cursorState.old = 3;
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_MIDDLE:
|
|
|
|
_cursorState.middle = CURSOR_RELEASED;
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_RIGHT:
|
2020-11-03 22:41:19 +01:00
|
|
|
StoreMouseInput(MouseState::RightRelease, mousePos);
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.right = CURSOR_RELEASED;
|
|
|
|
_cursorState.old = 4;
|
|
|
|
break;
|
|
|
|
}
|
2018-10-09 20:50:17 +02:00
|
|
|
_cursorState.touch = false;
|
2020-12-13 04:40:10 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
InputEvent ie;
|
|
|
|
ie.DeviceKind = InputDeviceKind::Mouse;
|
2020-12-13 23:51:50 +01:00
|
|
|
ie.Modifiers = SDL_GetModState();
|
2020-12-13 04:40:10 +01:00
|
|
|
ie.Button = e.button.button;
|
|
|
|
ie.State = InputEventState::Release;
|
2021-01-20 23:49:11 +01:00
|
|
|
_inputManager.QueueInputEvent(std::move(ie));
|
2020-12-13 04:40:10 +01:00
|
|
|
}
|
2017-03-25 18:42:14 +01:00
|
|
|
break;
|
|
|
|
}
|
2018-06-22 23:22:29 +02:00
|
|
|
// Apple sends touchscreen events for trackpads, so ignore these events on macOS
|
2017-03-25 18:42:14 +01:00
|
|
|
#ifndef __MACOSX__
|
2018-06-22 23:22:29 +02:00
|
|
|
case SDL_FINGERMOTION:
|
2019-12-15 03:30:55 +01:00
|
|
|
_cursorState.position = { static_cast<int32_t>(e.tfinger.x * _width),
|
|
|
|
static_cast<int32_t>(e.tfinger.y * _height) };
|
2018-06-22 23:22:29 +02:00
|
|
|
break;
|
|
|
|
case SDL_FINGERDOWN:
|
|
|
|
{
|
2019-12-17 18:46:28 +01:00
|
|
|
ScreenCoordsXY fingerPos = { static_cast<int32_t>(e.tfinger.x * _width),
|
|
|
|
static_cast<int32_t>(e.tfinger.y * _height) };
|
2017-03-25 18:42:14 +01:00
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.touchIsDouble
|
|
|
|
= (!_cursorState.touchIsDouble
|
|
|
|
&& e.tfinger.timestamp - _cursorState.touchDownTimestamp < TOUCH_DOUBLE_TIMEOUT);
|
2017-03-25 18:42:14 +01:00
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
if (_cursorState.touchIsDouble)
|
|
|
|
{
|
2020-11-03 22:41:19 +01:00
|
|
|
StoreMouseInput(MouseState::RightPress, fingerPos);
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.right = CURSOR_PRESSED;
|
|
|
|
_cursorState.old = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-11-03 22:41:19 +01:00
|
|
|
StoreMouseInput(MouseState::LeftPress, fingerPos);
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.left = CURSOR_PRESSED;
|
|
|
|
_cursorState.old = 1;
|
|
|
|
}
|
|
|
|
_cursorState.touch = true;
|
|
|
|
_cursorState.touchDownTimestamp = e.tfinger.timestamp;
|
|
|
|
break;
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
2018-06-22 23:22:29 +02:00
|
|
|
case SDL_FINGERUP:
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
2019-12-17 18:46:28 +01:00
|
|
|
ScreenCoordsXY fingerPos = { static_cast<int32_t>(e.tfinger.x * _width),
|
|
|
|
static_cast<int32_t>(e.tfinger.y * _height) };
|
2017-03-25 18:42:14 +01:00
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
if (_cursorState.touchIsDouble)
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
2020-11-03 22:41:19 +01:00
|
|
|
StoreMouseInput(MouseState::RightRelease, fingerPos);
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.right = CURSOR_RELEASED;
|
|
|
|
_cursorState.old = 4;
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
2018-06-22 23:22:29 +02:00
|
|
|
else
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
2020-11-03 22:41:19 +01:00
|
|
|
StoreMouseInput(MouseState::LeftRelease, fingerPos);
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.left = CURSOR_RELEASED;
|
|
|
|
_cursorState.old = 3;
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
2018-06-22 23:22:29 +02:00
|
|
|
_cursorState.touch = true;
|
|
|
|
break;
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
2018-06-22 23:22:29 +02:00
|
|
|
#endif
|
|
|
|
case SDL_KEYDOWN:
|
2021-02-17 23:53:13 +01:00
|
|
|
{
|
2021-04-04 19:00:18 +02:00
|
|
|
#ifndef __MACOSX__
|
|
|
|
// Ignore winkey keydowns. Handles edge case where tiling
|
|
|
|
// window managers don't eat the keypresses when changing
|
|
|
|
// workspaces.
|
Handle mod key edge-cases in tiling WMs
This commit ignores keypresses when the mod key is held.
The reasoning is that an odd interaction happens between SDL applications and
tiling window managers. Tiling window managers like Xmonad and i3 usually use
the mod ("windows") key and a number to change workspaces. When changing
workspaces, however, the WMs still send the number key through instead of
"eating" it. It's not clear why, exactly, but it seems universal.
Mod+1 -> Goes to workspace #1
Mod+2 -> Goes to workspace #2
...
Mod+9 -> Goes to workspace #9
Most applications don't even see the number key being sent, so if you move to
workspace 1, Firefox won't type "1" into the browser bar, Vim won't type "1"
into your file, etc. But SDL applications, for whatever reason, DO see this
keydown. Of course, they'll handle it like a regular key press. So if you move
to workspace 1, which contains OpenRCT, it inadvertently toggles x-ray mode.
I first found this bug in another SDL game, The Powder Toy. After some
discussion with the devs, they fixed it like this, by ignoring keydown events
when the mod key is pressed, since the mod key is reserved for the window
manager anyway. It works well and should be in the next release.
https://github.com/The-Powder-Toy/The-Powder-Toy/compare/c7619387ace6a24c4bd31e6899073ef3a91bcf7a...93b920a57f4d3843a8e6234e360fcf5e919181a6
I did the same thing here.
2021-04-02 19:40:01 +02:00
|
|
|
if (SDL_GetModState() & KMOD_GUI)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2021-04-03 02:14:56 +02:00
|
|
|
#endif
|
2018-06-22 23:22:29 +02:00
|
|
|
_textComposition.HandleMessage(&e);
|
2021-02-17 23:53:13 +01:00
|
|
|
auto ie = GetInputEventFromSDLEvent(e);
|
|
|
|
ie.State = InputEventState::Down;
|
|
|
|
_inputManager.QueueInputEvent(std::move(ie));
|
2018-06-22 23:22:29 +02:00
|
|
|
break;
|
2021-02-17 23:53:13 +01:00
|
|
|
}
|
2020-12-13 23:51:50 +01:00
|
|
|
case SDL_KEYUP:
|
|
|
|
{
|
2021-02-17 23:53:13 +01:00
|
|
|
auto ie = GetInputEventFromSDLEvent(e);
|
2020-12-13 23:51:50 +01:00
|
|
|
ie.State = InputEventState::Release;
|
2021-01-20 23:49:11 +01:00
|
|
|
_inputManager.QueueInputEvent(std::move(ie));
|
2021-02-17 23:53:13 +01:00
|
|
|
break;
|
2020-12-13 23:51:50 +01:00
|
|
|
}
|
2018-06-22 23:22:29 +02:00
|
|
|
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 int32_t tolerance = 128;
|
2020-04-18 12:15:07 +02:00
|
|
|
int32_t gesturePixels = static_cast<int32_t>(_gestureRadius * _width);
|
2018-06-22 23:22:29 +02:00
|
|
|
if (abs(gesturePixels) > tolerance)
|
|
|
|
{
|
|
|
|
_gestureRadius = 0;
|
2023-01-16 21:13:42 +01:00
|
|
|
MainWindowZoom(gesturePixels > 0, true);
|
2018-06-22 23:22:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_TEXTEDITING:
|
|
|
|
_textComposition.HandleMessage(&e);
|
|
|
|
break;
|
|
|
|
case SDL_TEXTINPUT:
|
|
|
|
_textComposition.HandleMessage(&e);
|
|
|
|
break;
|
|
|
|
default:
|
2020-12-16 00:14:40 +01:00
|
|
|
{
|
2021-01-20 23:49:11 +01:00
|
|
|
_inputManager.QueueInputEvent(e);
|
2018-06-22 23:22:29 +02:00
|
|
|
break;
|
2020-12-16 00:14:40 +01:00
|
|
|
}
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_cursorState.any = _cursorState.left | _cursorState.middle | _cursorState.right;
|
|
|
|
|
|
|
|
// Updates the state of the keys
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t numKeys = 256;
|
2017-03-25 18:42:14 +01:00
|
|
|
_keysState = SDL_GetKeyboardState(&numKeys);
|
|
|
|
}
|
2017-03-25 15:57:02 +01:00
|
|
|
|
2017-03-26 04:36:22 +02:00
|
|
|
/**
|
|
|
|
* Helper function to set various render target features.
|
|
|
|
* Does not get triggered on resize, but rather manually on config changes.
|
|
|
|
*/
|
|
|
|
void TriggerResize() override
|
|
|
|
{
|
|
|
|
char scaleQualityBuffer[4];
|
2022-01-24 17:46:22 +01:00
|
|
|
_scaleQuality = ScaleQuality::SmoothNearestNeighbour;
|
2022-10-16 17:27:01 +02:00
|
|
|
if (gConfigGeneral.WindowScale == std::floor(gConfigGeneral.WindowScale))
|
2017-03-26 04:36:22 +02:00
|
|
|
{
|
2020-08-21 04:32:56 +02:00
|
|
|
_scaleQuality = ScaleQuality::NearestNeighbour;
|
2017-03-26 04:36:22 +02:00
|
|
|
}
|
2017-12-03 22:35:16 +01:00
|
|
|
|
2020-08-21 04:32:56 +02:00
|
|
|
ScaleQuality scaleQuality = _scaleQuality;
|
|
|
|
if (_scaleQuality == ScaleQuality::SmoothNearestNeighbour)
|
2017-10-30 19:11:57 +01:00
|
|
|
{
|
2020-08-21 04:32:56 +02:00
|
|
|
scaleQuality = ScaleQuality::Linear;
|
2017-10-30 19:11:57 +01:00
|
|
|
}
|
2021-07-24 23:41:50 +02:00
|
|
|
snprintf(scaleQualityBuffer, sizeof(scaleQualityBuffer), "%d", static_cast<int32_t>(scaleQuality));
|
2017-03-26 04:36:22 +02:00
|
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, scaleQualityBuffer);
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t width, height;
|
2017-03-26 04:36:22 +02:00
|
|
|
SDL_GetWindowSize(_window, &width, &height);
|
|
|
|
OnResize(width, height);
|
|
|
|
}
|
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
void CreateWindow() override
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
2022-10-16 17:27:01 +02:00
|
|
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, gConfigGeneral.MinimizeFullscreenFocusLoss ? "1" : "0");
|
2017-03-25 15:57:02 +01:00
|
|
|
|
2017-05-28 00:28:54 +02:00
|
|
|
// Set window position to default display
|
2022-10-16 17:27:01 +02:00
|
|
|
int32_t defaultDisplay = std::clamp(gConfigGeneral.DefaultDisplay, 0, 0xFFFF);
|
2020-02-22 00:27:09 +01:00
|
|
|
auto windowPos = ScreenCoordsXY{ static_cast<int32_t>(SDL_WINDOWPOS_UNDEFINED_DISPLAY(defaultDisplay)),
|
|
|
|
static_cast<int32_t>(SDL_WINDOWPOS_UNDEFINED_DISPLAY(defaultDisplay)) };
|
2017-05-28 00:28:54 +02:00
|
|
|
|
2020-02-22 00:27:09 +01:00
|
|
|
CreateWindow(windowPos);
|
2017-03-25 15:57:02 +01:00
|
|
|
|
|
|
|
// Check if steam overlay renderer is loaded into the process
|
|
|
|
_steamOverlayActive = _platformUiContext->IsSteamOverlayAttached();
|
|
|
|
}
|
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
void CloseWindow() override
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
2023-01-16 23:31:34 +01:00
|
|
|
DrawingEngineDispose();
|
2018-05-04 21:24:35 +02:00
|
|
|
if (_window != nullptr)
|
|
|
|
{
|
|
|
|
SDL_DestroyWindow(_window);
|
|
|
|
_window = nullptr;
|
|
|
|
}
|
2017-07-11 15:56:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void RecreateWindow() override
|
|
|
|
{
|
|
|
|
// Use the position of the current window for the new window
|
2020-02-22 00:27:09 +01:00
|
|
|
ScreenCoordsXY windowPos;
|
2017-07-11 15:56:17 +02:00
|
|
|
SDL_SetWindowFullscreen(_window, 0);
|
2020-02-22 00:27:09 +01:00
|
|
|
SDL_GetWindowPosition(_window, &windowPos.x, &windowPos.y);
|
2017-07-11 15:56:17 +02:00
|
|
|
|
|
|
|
CloseWindow();
|
2020-02-22 00:27:09 +01:00
|
|
|
CreateWindow(windowPos);
|
2017-03-25 15:57:02 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
void ShowMessageBox(const std::string& message) override
|
2017-04-01 14:38:52 +02:00
|
|
|
{
|
|
|
|
_platformUiContext->ShowMessageBox(_window, message);
|
|
|
|
}
|
|
|
|
|
2022-08-04 18:38:51 +02:00
|
|
|
int32_t ShowMessageBox(
|
|
|
|
const std::string& title, const std::string& message, const std::vector<std::string>& options) override
|
2022-08-02 08:28:20 +02:00
|
|
|
{
|
|
|
|
auto message_box_button_data = std::make_unique<SDL_MessageBoxButtonData[]>(options.size());
|
|
|
|
for (size_t i = 0; i < options.size(); i++)
|
|
|
|
{
|
|
|
|
message_box_button_data[i].buttonid = static_cast<int>(i);
|
|
|
|
message_box_button_data[i].text = options[i].c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_MessageBoxData message_box_data{};
|
|
|
|
message_box_data.window = _window;
|
|
|
|
message_box_data.title = title.c_str();
|
|
|
|
message_box_data.message = message.c_str();
|
|
|
|
message_box_data.numbuttons = static_cast<int>(options.size());
|
|
|
|
message_box_data.buttons = message_box_button_data.get();
|
|
|
|
|
|
|
|
int buttonid{};
|
|
|
|
|
|
|
|
SDL_ShowMessageBox(&message_box_data, &buttonid);
|
|
|
|
|
|
|
|
return buttonid;
|
|
|
|
}
|
|
|
|
|
2020-10-21 19:53:22 +02:00
|
|
|
bool HasMenuSupport() override
|
|
|
|
{
|
|
|
|
return _platformUiContext->HasMenuSupport();
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t ShowMenuDialog(const std::vector<std::string>& options, const std::string& title, const std::string& text) override
|
|
|
|
{
|
|
|
|
return _platformUiContext->ShowMenuDialog(options, title, text);
|
|
|
|
}
|
|
|
|
|
2018-09-04 11:40:51 +02:00
|
|
|
void OpenFolder(const std::string& path) override
|
|
|
|
{
|
|
|
|
_platformUiContext->OpenFolder(path);
|
|
|
|
}
|
|
|
|
|
2020-07-26 21:57:56 +02:00
|
|
|
void OpenURL(const std::string& url) override
|
|
|
|
{
|
|
|
|
_platformUiContext->OpenURL(url);
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
std::string ShowFileDialog(const FileDialogDesc& desc) override
|
2017-04-01 18:27:39 +02:00
|
|
|
{
|
|
|
|
return _platformUiContext->ShowFileDialog(_window, desc);
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
std::string ShowDirectoryDialog(const std::string& title) override
|
2017-04-01 18:27:39 +02:00
|
|
|
{
|
|
|
|
return _platformUiContext->ShowDirectoryDialog(_window, title);
|
|
|
|
}
|
|
|
|
|
2020-11-08 13:29:44 +01:00
|
|
|
bool HasFilePicker() const override
|
|
|
|
{
|
|
|
|
return _platformUiContext->HasFilePicker();
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
IWindowManager* GetWindowManager() override
|
2017-06-11 16:16:47 +02:00
|
|
|
{
|
2022-07-28 19:19:38 +02:00
|
|
|
return _windowManager.get();
|
2017-06-11 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
2017-06-06 15:34:30 +02:00
|
|
|
bool SetClipboardText(const utf8* target) override
|
|
|
|
{
|
|
|
|
return (SDL_SetClipboardText(target) == 0);
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:22:29 +02:00
|
|
|
ITitleSequencePlayer* GetTitleSequencePlayer() override
|
2018-06-02 13:33:20 +02:00
|
|
|
{
|
|
|
|
if (_titleSequencePlayer == nullptr)
|
|
|
|
{
|
2024-03-26 15:08:17 +01:00
|
|
|
_titleSequencePlayer = OpenRCT2::Title::CreateTitleSequencePlayer();
|
2018-06-02 13:33:20 +02:00
|
|
|
}
|
|
|
|
return _titleSequencePlayer.get();
|
|
|
|
}
|
2017-06-14 20:47:22 +02:00
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
private:
|
2022-05-10 21:54:04 +02:00
|
|
|
void LogSDLVersion()
|
|
|
|
{
|
|
|
|
SDL_version version{};
|
|
|
|
SDL_GetVersion(&version);
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_VERBOSE("SDL2 version: %d.%d.%d", version.major, version.minor, version.patch);
|
2022-05-10 21:54:04 +02:00
|
|
|
}
|
|
|
|
|
2020-02-22 00:27:09 +01:00
|
|
|
void CreateWindow(const ScreenCoordsXY& windowPos)
|
2017-07-11 15:56:17 +02:00
|
|
|
{
|
|
|
|
// Get saved window size
|
2022-10-16 17:27:01 +02:00
|
|
|
int32_t width = gConfigGeneral.WindowWidth;
|
|
|
|
int32_t height = gConfigGeneral.WindowHeight;
|
2018-06-22 23:22:29 +02:00
|
|
|
if (width <= 0)
|
|
|
|
width = 640;
|
|
|
|
if (height <= 0)
|
|
|
|
height = 480;
|
2017-07-11 15:56:17 +02:00
|
|
|
|
|
|
|
// Create window in window first rather than fullscreen so we have the display the window is on first
|
2019-10-21 22:36:05 +02:00
|
|
|
uint32_t flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
2022-10-16 17:27:01 +02:00
|
|
|
if (gConfigGeneral.DrawingEngine == DrawingEngine::OpenGL)
|
2017-07-11 15:56:17 +02:00
|
|
|
{
|
|
|
|
flags |= SDL_WINDOW_OPENGL;
|
|
|
|
}
|
|
|
|
|
2020-02-22 00:27:09 +01:00
|
|
|
_window = SDL_CreateWindow(OPENRCT2_NAME, windowPos.x, windowPos.y, width, height, flags);
|
2017-07-11 15:56:17 +02:00
|
|
|
if (_window == nullptr)
|
|
|
|
{
|
|
|
|
SDLException::Throw("SDL_CreateWindow(...)");
|
|
|
|
}
|
|
|
|
|
2020-10-20 22:02:44 +02:00
|
|
|
ApplyScreenSaverLockSetting();
|
|
|
|
|
2017-07-11 15:56:17 +02:00
|
|
|
SDL_SetWindowMinimumSize(_window, 720, 480);
|
2022-10-16 17:27:01 +02:00
|
|
|
SetCursorTrap(gConfigGeneral.TrapCursor);
|
2017-07-11 15:56:17 +02:00
|
|
|
_platformUiContext->SetWindowIcon(_window);
|
|
|
|
|
|
|
|
// Initialise the surface, palette and draw buffer
|
2023-01-16 23:31:34 +01:00
|
|
|
DrawingEngineInit();
|
2017-07-11 15:56:17 +02:00
|
|
|
OnResize(width, height);
|
|
|
|
|
|
|
|
UpdateFullscreenResolutions();
|
|
|
|
|
2022-10-16 17:27:01 +02:00
|
|
|
SetFullscreenMode(static_cast<FULLSCREEN_MODE>(gConfigGeneral.FullscreenMode));
|
2017-07-11 15:56:17 +02:00
|
|
|
TriggerResize();
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
void OnResize(int32_t width, int32_t height)
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
|
|
|
// Scale the native window size to the game's canvas size
|
2022-10-16 17:27:01 +02:00
|
|
|
_width = static_cast<int32_t>(width / gConfigGeneral.WindowScale);
|
|
|
|
_height = static_cast<int32_t>(height / gConfigGeneral.WindowScale);
|
2017-03-25 15:57:02 +01:00
|
|
|
|
2023-01-16 23:31:34 +01:00
|
|
|
DrawingEngineResize();
|
2017-03-25 15:57:02 +01:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t flags = SDL_GetWindowFlags(_window);
|
2017-03-25 15:57:02 +01:00
|
|
|
if ((flags & SDL_WINDOW_MINIMIZED) == 0)
|
|
|
|
{
|
2023-01-16 21:13:42 +01:00
|
|
|
WindowResizeGui(_width, _height);
|
|
|
|
WindowRelocateWindows(_width, _height);
|
2017-03-25 15:57:02 +01:00
|
|
|
}
|
|
|
|
|
2023-01-16 14:50:43 +01:00
|
|
|
GfxInvalidateScreen();
|
2017-03-25 15:57:02 +01:00
|
|
|
|
|
|
|
// Check if the window has been resized in windowed mode and update the config file accordingly
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t nonWindowFlags =
|
2017-06-10 17:33:00 +02:00
|
|
|
#ifndef __MACOSX__
|
|
|
|
SDL_WINDOW_MAXIMIZED |
|
|
|
|
#endif
|
2018-06-22 23:22:29 +02:00
|
|
|
SDL_WINDOW_MINIMIZED | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP;
|
2017-06-10 17:33:00 +02:00
|
|
|
|
2017-03-25 15:57:02 +01:00
|
|
|
if (!(flags & nonWindowFlags))
|
|
|
|
{
|
2022-10-16 17:27:01 +02:00
|
|
|
if (width != gConfigGeneral.WindowWidth || height != gConfigGeneral.WindowHeight)
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
2022-10-16 17:27:01 +02:00
|
|
|
gConfigGeneral.WindowWidth = width;
|
|
|
|
gConfigGeneral.WindowHeight = height;
|
2022-10-16 18:00:03 +02:00
|
|
|
ConfigSaveDefault();
|
2017-03-25 15:57:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void UpdateFullscreenResolutions()
|
|
|
|
{
|
|
|
|
// Query number of display modes
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t displayIndex = SDL_GetWindowDisplayIndex(_window);
|
|
|
|
int32_t numDisplayModes = SDL_GetNumDisplayModes(displayIndex);
|
2017-03-25 15:57:02 +01:00
|
|
|
|
|
|
|
// Get desktop aspect ratio
|
|
|
|
SDL_DisplayMode mode;
|
|
|
|
SDL_GetDesktopDisplayMode(displayIndex, &mode);
|
|
|
|
|
|
|
|
// Get resolutions
|
2017-06-02 19:36:21 +02:00
|
|
|
auto resolutions = std::vector<Resolution>();
|
2020-04-18 12:15:07 +02:00
|
|
|
float desktopAspectRatio = static_cast<float>(mode.w) / mode.h;
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t i = 0; i < numDisplayModes; i++)
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
|
|
|
SDL_GetDisplayMode(displayIndex, i, &mode);
|
2017-06-02 19:36:21 +02:00
|
|
|
if (mode.w > 0 && mode.h > 0)
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
2020-04-18 12:15:07 +02:00
|
|
|
float aspectRatio = static_cast<float>(mode.w) / mode.h;
|
2018-07-18 23:33:20 +02:00
|
|
|
if (std::fabs(desktopAspectRatio - aspectRatio) < 0.1f)
|
2017-06-02 19:36:21 +02:00
|
|
|
{
|
|
|
|
resolutions.push_back({ mode.w, mode.h });
|
|
|
|
}
|
2017-03-25 15:57:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort by area
|
2018-06-22 23:22:29 +02:00
|
|
|
std::sort(resolutions.begin(), resolutions.end(), [](const Resolution& a, const Resolution& b) -> bool {
|
|
|
|
int32_t areaA = a.Width * a.Height;
|
|
|
|
int32_t areaB = b.Width * b.Height;
|
|
|
|
return areaA < areaB;
|
|
|
|
});
|
2017-03-25 15:57:02 +01:00
|
|
|
|
|
|
|
// Remove duplicates
|
2018-06-22 23:22:29 +02:00
|
|
|
auto last = std::unique(resolutions.begin(), resolutions.end(), [](const Resolution& a, const Resolution& b) -> bool {
|
|
|
|
return (a.Width == b.Width && a.Height == b.Height);
|
|
|
|
});
|
2017-03-25 15:57:02 +01:00
|
|
|
resolutions.erase(last, resolutions.end());
|
|
|
|
|
|
|
|
// Update config fullscreen resolution if not set
|
2022-10-16 17:27:01 +02:00
|
|
|
if (!resolutions.empty() && (gConfigGeneral.FullscreenWidth == -1 || gConfigGeneral.FullscreenHeight == -1))
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
2022-10-16 17:27:01 +02:00
|
|
|
gConfigGeneral.FullscreenWidth = resolutions.back().Width;
|
|
|
|
gConfigGeneral.FullscreenHeight = resolutions.back().Height;
|
2017-03-25 15:57:02 +01:00
|
|
|
}
|
2017-06-02 19:36:21 +02:00
|
|
|
|
|
|
|
_fsResolutions = resolutions;
|
2017-03-25 15:57:02 +01:00
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
Resolution GetClosestResolution(int32_t inWidth, int32_t inHeight)
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
|
|
|
Resolution result = { 640, 480 };
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t closestAreaDiff = -1;
|
|
|
|
int32_t destinationArea = inWidth * inHeight;
|
2018-06-22 23:22:29 +02:00
|
|
|
for (const Resolution& resolution : _fsResolutions)
|
2017-03-25 15:57:02 +01:00
|
|
|
{
|
|
|
|
// Check if exact match
|
|
|
|
if (resolution.Width == inWidth && resolution.Height == inHeight)
|
|
|
|
{
|
|
|
|
result = resolution;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if area is closer to best match
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t areaDiff = std::abs((resolution.Width * resolution.Height) - destinationArea);
|
2017-03-25 15:57:02 +01:00
|
|
|
if (closestAreaDiff == -1 || areaDiff < closestAreaDiff)
|
|
|
|
{
|
|
|
|
closestAreaDiff = areaDiff;
|
|
|
|
result = resolution;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2017-04-01 14:38:52 +02:00
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t GetWindowFlags()
|
2017-04-01 14:38:52 +02:00
|
|
|
{
|
2017-07-24 22:33:40 +02:00
|
|
|
return SDL_GetWindowFlags(_window);
|
2017-04-01 14:38:52 +02:00
|
|
|
}
|
2018-06-15 08:46:04 +02:00
|
|
|
|
2020-09-14 21:36:30 +02:00
|
|
|
static void DrawWeatherWindow(
|
2023-04-03 22:18:42 +02:00
|
|
|
DrawPixelInfo& dpi, IWeatherDrawer* weatherDrawer, WindowBase* original_w, int16_t left, int16_t right, int16_t top,
|
2021-10-06 22:37:02 +02:00
|
|
|
int16_t bottom, DrawWeatherFunc drawFunc)
|
2018-06-15 08:46:04 +02:00
|
|
|
{
|
2023-01-21 20:57:17 +01:00
|
|
|
WindowBase* w{};
|
2023-01-16 21:13:42 +01:00
|
|
|
auto itStart = WindowGetIterator(original_w);
|
2019-05-29 19:15:50 +02:00
|
|
|
for (auto it = std::next(itStart);; it++)
|
2018-06-15 08:46:04 +02:00
|
|
|
{
|
2019-05-29 19:15:50 +02:00
|
|
|
if (it == g_window_list.end())
|
2018-06-15 08:46:04 +02:00
|
|
|
{
|
2020-09-14 21:36:30 +02:00
|
|
|
// Loop ended, draw weather for original_w
|
2018-06-15 08:46:04 +02:00
|
|
|
auto vp = original_w->viewport;
|
|
|
|
if (vp != nullptr)
|
|
|
|
{
|
2020-03-01 13:56:14 +01:00
|
|
|
left = std::max<int16_t>(left, vp->pos.x);
|
|
|
|
right = std::min<int16_t>(right, vp->pos.x + vp->width);
|
|
|
|
top = std::max<int16_t>(top, vp->pos.y);
|
|
|
|
bottom = std::min<int16_t>(bottom, vp->pos.y + vp->height);
|
2018-06-15 08:46:04 +02:00
|
|
|
if (left < right && top < bottom)
|
|
|
|
{
|
|
|
|
auto width = right - left;
|
|
|
|
auto height = bottom - top;
|
2021-10-06 22:37:02 +02:00
|
|
|
drawFunc(dpi, weatherDrawer, left, top, width, height);
|
2018-06-15 08:46:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-29 19:15:50 +02:00
|
|
|
w = it->get();
|
2020-03-01 20:32:35 +01:00
|
|
|
if (right <= w->windowPos.x || bottom <= w->windowPos.y)
|
2018-06-15 08:46:04 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (RCT_WINDOW_RIGHT(w) <= left || RCT_WINDOW_BOTTOM(w) <= top)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-03-01 20:32:35 +01:00
|
|
|
if (left >= w->windowPos.x)
|
2018-06-15 08:46:04 +02:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-10-06 22:37:02 +02:00
|
|
|
DrawWeatherWindow(dpi, weatherDrawer, original_w, left, w->windowPos.x, top, bottom, drawFunc);
|
2018-06-15 08:46:04 +02:00
|
|
|
|
2020-03-01 20:32:35 +01:00
|
|
|
left = w->windowPos.x;
|
2021-10-06 22:37:02 +02:00
|
|
|
DrawWeatherWindow(dpi, weatherDrawer, original_w, left, right, top, bottom, drawFunc);
|
2018-06-15 08:46:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t w_right = RCT_WINDOW_RIGHT(w);
|
2018-06-22 23:22:29 +02:00
|
|
|
if (right > w_right)
|
|
|
|
{
|
2021-10-06 22:37:02 +02:00
|
|
|
DrawWeatherWindow(dpi, weatherDrawer, original_w, left, w_right, top, bottom, drawFunc);
|
2018-06-15 08:46:04 +02:00
|
|
|
|
|
|
|
left = w_right;
|
2021-10-06 22:37:02 +02:00
|
|
|
DrawWeatherWindow(dpi, weatherDrawer, original_w, left, right, top, bottom, drawFunc);
|
2018-06-15 08:46:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-01 20:32:35 +01:00
|
|
|
if (top < w->windowPos.y)
|
2018-06-22 23:22:29 +02:00
|
|
|
{
|
2021-10-06 22:37:02 +02:00
|
|
|
DrawWeatherWindow(dpi, weatherDrawer, original_w, left, right, top, w->windowPos.y, drawFunc);
|
2018-06-15 08:46:04 +02:00
|
|
|
|
2020-03-01 20:32:35 +01:00
|
|
|
top = w->windowPos.y;
|
2021-10-06 22:37:02 +02:00
|
|
|
DrawWeatherWindow(dpi, weatherDrawer, original_w, left, right, top, bottom, drawFunc);
|
2018-06-15 08:46:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t w_bottom = RCT_WINDOW_BOTTOM(w);
|
2018-06-15 08:46:04 +02:00
|
|
|
if (bottom > w_bottom)
|
|
|
|
{
|
2021-10-06 22:37:02 +02:00
|
|
|
DrawWeatherWindow(dpi, weatherDrawer, original_w, left, right, top, w_bottom, drawFunc);
|
2018-06-15 08:46:04 +02:00
|
|
|
|
|
|
|
top = w_bottom;
|
2021-10-06 22:37:02 +02:00
|
|
|
DrawWeatherWindow(dpi, weatherDrawer, original_w, left, right, top, bottom, drawFunc);
|
2018-06-15 08:46:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-02-17 23:53:13 +01:00
|
|
|
|
2021-02-18 14:35:49 +01:00
|
|
|
InputEvent GetInputEventFromSDLEvent(const SDL_Event& e)
|
2021-02-17 23:53:13 +01:00
|
|
|
{
|
|
|
|
InputEvent ie;
|
|
|
|
ie.DeviceKind = InputDeviceKind::Keyboard;
|
|
|
|
ie.Modifiers = e.key.keysym.mod;
|
|
|
|
ie.Button = e.key.keysym.sym;
|
|
|
|
|
|
|
|
// Handle dead keys
|
2021-02-18 14:35:49 +01:00
|
|
|
if (ie.Button == (SDLK_SCANCODE_MASK | 0))
|
2021-02-17 23:53:13 +01:00
|
|
|
{
|
|
|
|
switch (e.key.keysym.scancode)
|
|
|
|
{
|
|
|
|
case SDL_SCANCODE_APOSTROPHE:
|
|
|
|
ie.Button = '\'';
|
|
|
|
break;
|
|
|
|
case SDL_SCANCODE_GRAVE:
|
|
|
|
ie.Button = '`';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ie;
|
|
|
|
}
|
2022-06-27 18:41:53 +02:00
|
|
|
|
|
|
|
void SetAudioVolume(float value)
|
|
|
|
{
|
|
|
|
auto audioContext = GetContext()->GetAudioContext();
|
|
|
|
auto mixer = audioContext->GetMixer();
|
|
|
|
if (mixer != nullptr)
|
|
|
|
{
|
|
|
|
mixer->SetVolume(value);
|
|
|
|
}
|
|
|
|
}
|
2017-03-24 03:47:43 +01:00
|
|
|
};
|
|
|
|
|
2018-04-27 19:47:57 +02:00
|
|
|
std::unique_ptr<IUiContext> OpenRCT2::Ui::CreateUiContext(const std::shared_ptr<IPlatformEnvironment>& env)
|
2017-03-24 03:47:43 +01:00
|
|
|
{
|
2018-04-20 20:58:59 +02:00
|
|
|
return std::make_unique<UiContext>(env);
|
2017-03-24 03:47:43 +01:00
|
|
|
}
|
2018-03-11 23:25:34 +01:00
|
|
|
|
|
|
|
InGameConsole& OpenRCT2::Ui::GetInGameConsole()
|
|
|
|
{
|
2018-04-21 01:29:12 +02:00
|
|
|
auto uiContext = std::static_pointer_cast<UiContext>(GetContext()->GetUiContext());
|
2018-03-11 23:25:34 +01:00
|
|
|
return uiContext->GetInGameConsole();
|
|
|
|
}
|
2021-01-12 23:34:30 +01:00
|
|
|
|
2021-01-20 23:49:11 +01:00
|
|
|
InputManager& OpenRCT2::Ui::GetInputManager()
|
|
|
|
{
|
|
|
|
auto uiContext = std::static_pointer_cast<UiContext>(GetContext()->GetUiContext());
|
|
|
|
return uiContext->GetInputManager();
|
|
|
|
}
|
|
|
|
|
2021-01-12 23:34:30 +01:00
|
|
|
ShortcutManager& OpenRCT2::Ui::GetShortcutManager()
|
|
|
|
{
|
|
|
|
auto uiContext = std::static_pointer_cast<UiContext>(GetContext()->GetUiContext());
|
|
|
|
return uiContext->GetShortcutManager();
|
|
|
|
}
|