diff --git a/src/openrct2/interface/CursorData.cpp b/src/openrct2-ui/CursorData.cpp similarity index 99% rename from src/openrct2/interface/CursorData.cpp rename to src/openrct2-ui/CursorData.cpp index 4777b161eb..2015f5d88b 100644 --- a/src/openrct2/interface/CursorData.cpp +++ b/src/openrct2-ui/CursorData.cpp @@ -14,9 +14,10 @@ *****************************************************************************/ #pragma endregion -#include "Cursors.h" +#include +#include "CursorRepository.h" -namespace Cursors +namespace OpenRCT2 { namespace Ui { static const CursorData BlankCursorData = { @@ -654,7 +655,7 @@ namespace Cursors &HandClosedDownCursorData, // CURSOR_HAND_CLOSED }; - const CursorData * GetCursorData(CURSOR_ID cursorId) + const CursorData * CursorRepository::GetCursorData(CURSOR_ID cursorId) { const CursorData * result = nullptr; if (cursorId >= 0 && cursorId < CURSOR_COUNT) @@ -663,4 +664,4 @@ namespace Cursors } return result; } -} +} } diff --git a/src/openrct2-ui/CursorRepository.cpp b/src/openrct2-ui/CursorRepository.cpp new file mode 100644 index 0000000000..61b027c2b2 --- /dev/null +++ b/src/openrct2-ui/CursorRepository.cpp @@ -0,0 +1,80 @@ +#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 "CursorRepository.h" + +using namespace OpenRCT2::Ui; + +CursorRepository::CursorRepository() +{ + // Using system cursors + _loadedCursors[CURSOR_ARROW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + _loadedCursors[CURSOR_HAND_POINT] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); + + // Using custom cursors + for (size_t i = 0; i < CURSOR_COUNT; i++) + { + const CursorData * cursorData = GetCursorData((CURSOR_ID)i); + if (cursorData != nullptr) + { + _loadedCursors[i] = Create(cursorData); + } + } + + _currentCursor = CURSOR_UNDEFINED; + SetCurrentCursor(CURSOR_ARROW); +} + +CursorRepository::~CursorRepository() +{ + for (size_t i = 0; i < CURSOR_COUNT; i++) + { + SDL_FreeCursor(_loadedCursors[i]); + _loadedCursors[i] = nullptr; + } + _currentCursor = CURSOR_UNDEFINED; +} + +CURSOR_ID CursorRepository::GetCurrentCursor() +{ + return _currentCursor; +} + +void CursorRepository::SetCurrentCursor(CURSOR_ID cursorId) +{ + if (_currentCursor != cursorId) + { + SDL_Cursor * cursor = _loadedCursors[cursorId]; + SDL_SetCursor(cursor); + _currentCursor = cursorId; + } +} + +SDL_Cursor * CursorRepository::Create(const CursorData * cursorInfo) +{ + SDL_Cursor * cursor = SDL_CreateCursor( + cursorInfo->Data, + cursorInfo->Mask, + CURSOR_WIDTH, + CURSOR_HEIGHT, + cursorInfo->HotSpot.X, + cursorInfo->HotSpot.Y); + return cursor; +} diff --git a/src/openrct2-ui/CursorRepository.h b/src/openrct2-ui/CursorRepository.h new file mode 100644 index 0000000000..c15faf4a95 --- /dev/null +++ b/src/openrct2-ui/CursorRepository.h @@ -0,0 +1,45 @@ +#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 + +struct SDL_Cursor; + +namespace OpenRCT2 +{ + namespace Ui + { + class CursorRepository + { + private: + constexpr static sint32 CURSOR_WIDTH = 32; + constexpr static sint32 CURSOR_HEIGHT = 32; + + SDL_Cursor * _loadedCursors[CURSOR_COUNT]; + CURSOR_ID _currentCursor = CURSOR_UNDEFINED; + + public: + CursorRepository(); + ~CursorRepository(); + CURSOR_ID GetCurrentCursor(); + void SetCurrentCursor(CURSOR_ID cursorId); + + private: + SDL_Cursor * Create(const CursorData * cursorInfo); + static const CursorData * GetCursorData(CURSOR_ID cursorId); + }; + } +} diff --git a/src/openrct2-ui/UiContext.Win32.cpp b/src/openrct2-ui/UiContext.Win32.cpp new file mode 100644 index 0000000000..55d0a6efd7 --- /dev/null +++ b/src/openrct2-ui/UiContext.Win32.cpp @@ -0,0 +1,99 @@ +#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 + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include "UiContext.h" + +// Native resource IDs +#include "../../../resources/resource.h" + +#ifdef __WINDOWS__ + +namespace OpenRCT2 { namespace Ui +{ + class Win32Context : public IPlatformUiContext + { + private: + HMODULE _win32module; + + public: + Win32Context() + { + _win32module = GetModuleHandleA(nullptr); + } + + void SetWindowIcon(SDL_Window * window) override + { + if (_win32module != nullptr) + { + HICON icon = LoadIconA(_win32module, MAKEINTRESOURCEA(IDI_ICON)); + if (icon != nullptr) + { + HWND hwnd = GetHWND(window); + if (hwnd != nullptr) + { + SendMessageA(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)icon); + } + } + } + } + + bool IsSteamOverlayAttached() override + { + return (GetModuleHandleA("GameOverlayRenderer.dll") != nullptr); + } + + private: + HWND GetHWND(SDL_Window * window) + { + HWND result = nullptr; + if (window != nullptr) + { + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + if (SDL_GetWindowWMInfo(window, &wmInfo) != SDL_TRUE) + { + log_error("SDL_GetWindowWMInfo failed %s", SDL_GetError()); + exit(-1); + } + + result = wmInfo.info.win.window; +#ifdef __MINGW32__ + assert(sizeof(HWND) == sizeof(uint32)); + uint8 A = (uint32)result & 0xff000000 >> 24; + uint8 B = (uint32)result & 0xff0000 >> 16; + uint8 C = (uint32)result & 0xff00 >> 8; + uint8 D = (uint32)handle & 0xff; + result = (HWND)(D << 24 | A << 16 | B << 8 | C); + log_warning("twisting bits of handle, a workaround for mingw/sdl bug"); +#endif // __MINGW32__ + } + return result; + } + }; + + IPlatformUiContext * CreatePlatformUiContext() + { + return new Win32Context(); + } +} } + +#endif // __WINDOWS__ diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index 397a78b9ac..711c495f7f 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -14,11 +14,17 @@ *****************************************************************************/ #pragma endregion +#include #include #include +#include +#include #include #include +#include #include +#include +#include "CursorRepository.h" #include "drawing/engines/DrawingEngines.h" #include "UiContext.h" @@ -28,20 +34,93 @@ using namespace OpenRCT2::Ui; class UiContext : public IUiContext { +private: + IPlatformUiContext * const _platformUiContext; + + CursorRepository _cursorRepository; + + SDL_Window * _window = nullptr; + sint32 _width = 0; + sint32 _height = 0; + + bool _resolutionsAllowAnyAspectRatio = false; + std::vector _fsResolutions; + + bool _steamOverlayActive = false; + public: - UiContext() + UiContext(IPlatformUiContext * platformUiContext) + : _platformUiContext(platformUiContext) { } ~UiContext() override { + CloseWindow(); } // Window - void * GetWindow() override { return nullptr; } - sint32 GetWidth() override { return 0; } - sint32 GetHeight() override { return 0; } - void SetFullscreenMode(FULLSCREEN_MODE mode) override { } + CURSOR_ID GetCursor() override + { + return _cursorRepository.GetCurrentCursor(); + } + + void SetCursor(CURSOR_ID cursor) override + { + _cursorRepository.SetCurrentCursor(cursor); + } + + void * GetWindow() override + { + return _window; + } + + sint32 GetWidth() override + { + return _width; + } + + sint32 GetHeight() override + { + return _height; + } + + void SetFullscreenMode(FULLSCREEN_MODE mode) override + { + static const sint32 SDLFSFlags[] = { 0, SDL_WINDOW_FULLSCREEN, SDL_WINDOW_FULLSCREEN_DESKTOP }; + uint32 windowFlags = SDLFSFlags[(sint32)mode]; + + // HACK Changing window size when in fullscreen usually has no effect + if (mode == FULLSCREEN_MODE::FULLSCREEN) + { + SDL_SetWindowFullscreen(_window, 0); + } + + // Set window size + if (mode == FULLSCREEN_MODE::FULLSCREEN) + { + UpdateFullscreenResolutions(); + Resolution resolution = GetClosestResolution(gConfigGeneral.fullscreen_width, gConfigGeneral.fullscreen_height); + SDL_SetWindowSize(_window, resolution.Width, resolution.Height); + } + else if (mode == FULLSCREEN_MODE::WINDOWED) + { + SDL_SetWindowSize(_window, gConfigGeneral.window_width, gConfigGeneral.window_height); + } + + if (SDL_SetWindowFullscreen(_window, windowFlags)) + { + log_fatal("SDL_SetWindowFullscreen %s", SDL_GetError()); + exit(1); + + // TODO try another display mode rather than just exiting the game + } + } + + std::vector GetFullscreenResolutions() override + { + return _fsResolutions; + } // Drawing IDrawingEngine * CreateDrawingEngine(DRAWING_ENGINE_TYPE type) override @@ -64,9 +143,199 @@ public: bool IsTextInputActive() override { return false; } const TextInputSession * StartTextInput(utf8 * buffer, sint32 bufferSize) override { return nullptr; } void StopTextInput() override { } + +private: + void CreateWindow() + { + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + log_fatal("SDL_Init %s", SDL_GetError()); + exit(-1); + } + + SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, gConfigGeneral.minimize_fullscreen_focus_loss ? "1" : "0"); + + // TODO This should probably be called somewhere else. It has nothing to do with window creation and can be done as soon as + // g1.dat is loaded. + sub_68371D(); + + // Get saved window size + sint32 width = gConfigGeneral.window_width; + sint32 height = gConfigGeneral.window_height; + if (width == -1) width = 640; + if (height == -1) height = 480; + + // Create window in window first rather than fullscreen so we have the display the window is on first + uint32 flags = SDL_WINDOW_RESIZABLE; + if (gConfigGeneral.drawing_engine == DRAWING_ENGINE_OPENGL) + { + flags |= SDL_WINDOW_OPENGL; + } + + _window = SDL_CreateWindow(OPENRCT2_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags); + if (_window == nullptr) + { + log_fatal("SDL_CreateWindow failed %s", SDL_GetError()); + exit(-1); + } + + SDL_SetWindowGrab(_window, gConfigGeneral.trap_cursor ? SDL_TRUE : SDL_FALSE); + SDL_SetWindowMinimumSize(_window, 720, 480); + _platformUiContext->SetWindowIcon(_window); +#ifdef __MACOSX__ + macos_disallow_automatic_window_tabbing(); +#endif + + // Initialise the surface, palette and draw buffer + OnResize(width, height); + + UpdateFullscreenResolutions(); + SetFullscreenMode((FULLSCREEN_MODE)gConfigGeneral.fullscreen_mode); + + // Check if steam overlay renderer is loaded into the process + _steamOverlayActive = _platformUiContext->IsSteamOverlayAttached(); + TriggerResize(); + } + + void CloseWindow() + { + drawing_engine_dispose(); + SDL_DestroyWindow(_window); + SDL_Quit(); + } + + void OnResize(sint32 width, sint32 height) + { + // Scale the native window size to the game's canvas size + _width = (sint32)(width / gConfigGeneral.window_scale); + _height = (sint32)(height / gConfigGeneral.window_scale); + + drawing_engine_resize(); + + uint32 flags = SDL_GetWindowFlags(_window); + if ((flags & SDL_WINDOW_MINIMIZED) == 0) + { + window_resize_gui(_width, _height); + window_relocate_windows(_width, _height); + } + + gfx_invalidate_screen(); + + // Check if the window has been resized in windowed mode and update the config file accordingly + // This is called in rct2_update and is only called after resizing a window has finished + sint32 nonWindowFlags = SDL_WINDOW_MAXIMIZED | + SDL_WINDOW_MINIMIZED | + SDL_WINDOW_FULLSCREEN | + SDL_WINDOW_FULLSCREEN_DESKTOP; + if (!(flags & nonWindowFlags)) + { + if (width != gConfigGeneral.window_width || height != gConfigGeneral.window_height) + { + gConfigGeneral.window_width = width; + gConfigGeneral.window_height = height; + config_save_default(); + } + } + } + + /** + * Helper function to set various render target features. + * Does not get triggered on resize, but rather manually on config changes. + */ + void TriggerResize() + { + char scaleQualityBuffer[4]; + uint8 scaleQuality = gConfigGeneral.scale_quality; + if (gConfigGeneral.use_nn_at_integer_scales && + gConfigGeneral.window_scale == std::floor(gConfigGeneral.window_scale)) + { + scaleQuality = 0; + } + snprintf(scaleQualityBuffer, sizeof(scaleQualityBuffer), "%u", scaleQuality); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, scaleQualityBuffer); + + sint32 width, height; + SDL_GetWindowSize(_window, &width, &height); + OnResize(width, height); + } + + void UpdateFullscreenResolutions() + { + // Query number of display modes + sint32 displayIndex = SDL_GetWindowDisplayIndex(_window); + sint32 numDisplayModes = SDL_GetNumDisplayModes(displayIndex); + + // Get desktop aspect ratio + SDL_DisplayMode mode; + SDL_GetDesktopDisplayMode(displayIndex, &mode); + + // Get resolutions + auto resolutions = std::vector(numDisplayModes); + float desktopAspectRatio = (float)mode.w / mode.h; + for (sint32 i = 0; i < numDisplayModes; i++) + { + SDL_GetDisplayMode(displayIndex, i, &mode); + + float aspectRatio = (float)mode.w / mode.h; + if (_resolutionsAllowAnyAspectRatio || std::fabs(desktopAspectRatio - aspectRatio) < 0.0001f) + { + resolutions.push_back({ mode.w, mode.h }); + } + } + + // Sort by area + std::sort(resolutions.begin(), resolutions.end()); + // [](const Resolution &a, const Resolution &b) -> bool + // { + // sint32 areaA = a.Width * a.Height; + // sint32 areaB = b.Width * b.Height; + // return areaA < areaB; + // }); + + // Remove duplicates + auto last = std::unique(resolutions.begin(), resolutions.end()); + // [](const Resolution &a, const Resolution &b) -> bool + // { + // return (a.Width == b.Width && a.Height == b.Height); + // }); + resolutions.erase(last, resolutions.end()); + + // Update config fullscreen resolution if not set + if (gConfigGeneral.fullscreen_width == -1 || gConfigGeneral.fullscreen_height == -1) + { + gConfigGeneral.fullscreen_width = resolutions.back().Width; + gConfigGeneral.fullscreen_height = resolutions.back().Height; + } + } + + Resolution GetClosestResolution(sint32 inWidth, sint32 inHeight) + { + Resolution result = { 640, 480 }; + sint32 closestAreaDiff = -1; + sint32 destinationArea = inWidth * inHeight; + for (const Resolution &resolution : _fsResolutions) + { + // Check if exact match + if (resolution.Width == inWidth && resolution.Height == inHeight) + { + result = resolution; + break; + } + + // Check if area is closer to best match + sint32 areaDiff = std::abs((resolution.Width * resolution.Height) - destinationArea); + if (closestAreaDiff == -1 || areaDiff < closestAreaDiff) + { + closestAreaDiff = areaDiff; + result = resolution; + } + } + return result; + } }; IUiContext * OpenRCT2::Ui::CreateContext() { - return new UiContext(); + auto platformUiContext = std::unique_ptr(CreatePlatformUiContext()); + return new UiContext(platformUiContext.get()); } diff --git a/src/openrct2-ui/UiContext.h b/src/openrct2-ui/UiContext.h index cfff4c2439..7712bdf935 100644 --- a/src/openrct2-ui/UiContext.h +++ b/src/openrct2-ui/UiContext.h @@ -18,6 +18,8 @@ #include +struct SDL_Window; + namespace OpenRCT2 { interface IContext; @@ -26,6 +28,14 @@ namespace OpenRCT2 { interface IUiContext; + interface IPlatformUiContext + { + virtual ~IPlatformUiContext() = default; + virtual void SetWindowIcon(SDL_Window * window) abstract; + virtual bool IsSteamOverlayAttached() abstract; + }; + IUiContext * CreateContext(); + IPlatformUiContext * CreatePlatformUiContext(); } } diff --git a/src/openrct2-ui/libopenrct2ui.vcxproj b/src/openrct2-ui/libopenrct2ui.vcxproj index 3e86ba50d6..8424c2c760 100644 --- a/src/openrct2-ui/libopenrct2ui.vcxproj +++ b/src/openrct2-ui/libopenrct2ui.vcxproj @@ -22,6 +22,8 @@ + + @@ -35,8 +37,10 @@ + + diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 0e659cdb20..8a2b30421f 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -17,6 +17,7 @@ #include #include "Context.h" #include "OpenRCT2.h" +#include "ui/UiContext.h" using namespace OpenRCT2; using namespace OpenRCT2::Ui; @@ -80,3 +81,11 @@ namespace OpenRCT2 return Context::Instance; } } + +extern "C" +{ + void context_setcurrentcursor(sint32 cursor) + { + GetContext()->GetUiContext()->SetCursor((CURSOR_ID)cursor); + } +} diff --git a/src/openrct2/Context.h b/src/openrct2/Context.h index b73841d29b..7d82241922 100644 --- a/src/openrct2/Context.h +++ b/src/openrct2/Context.h @@ -18,6 +18,8 @@ #include "common.h" +#ifdef __cplusplus + namespace OpenRCT2 { namespace Ui @@ -40,3 +42,14 @@ namespace OpenRCT2 IContext * CreateContext(Ui::IUiContext * uiContext); IContext * GetContext(); } + +#endif // __cplusplus + +#if __cplusplus +extern "C" +{ +#endif + void context_setcurrentcursor(sint32 cursor); +#if __cplusplus +} +#endif diff --git a/src/openrct2/input.c b/src/openrct2/input.c index ab3579e7e3..5170516f60 100644 --- a/src/openrct2/input.c +++ b/src/openrct2/input.c @@ -17,6 +17,7 @@ #include #include "audio/audio.h" #include "config/Config.h" +#include "Context.h" #include "game.h" #include "input.h" #include "interface/chat.h" @@ -1557,7 +1558,7 @@ void sub_6ED990(uint8 cursor_id) if (_inputState == INPUT_STATE_RESIZING) { cursor_id = CURSOR_DIAGONAL_ARROWS; } - cursors_setcurrentcursor(cursor_id); + context_setcurrentcursor(cursor_id); } diff --git a/src/openrct2/interface/Cursors.cpp b/src/openrct2/interface/Cursors.cpp deleted file mode 100644 index d2e915cd6e..0000000000 --- a/src/openrct2/interface/Cursors.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#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 "../core/Guard.hpp" -#include "Cursors.h" - -namespace Cursors -{ - constexpr sint32 CURSOR_WIDTH = 32; - constexpr sint32 CURSOR_HEIGHT = 32; - - static SDL_Cursor * _loadedCursors[CURSOR_COUNT]; - static bool _initialised = false; - static CURSOR_ID _currentCursor = CURSOR_UNDEFINED; - - static SDL_Cursor * Create(const CursorData * cursorInfo) - { - SDL_Cursor * cursor = SDL_CreateCursor( - cursorInfo->Data, - cursorInfo->Mask, - CURSOR_WIDTH, - CURSOR_HEIGHT, - cursorInfo->HotSpot.X, - cursorInfo->HotSpot.Y); - return cursor; - } - - void Initialise() - { - Guard::Assert(!_initialised, "Cursors have already been initialised."); - _initialised = true; - - // Using system cursors - _loadedCursors[CURSOR_ARROW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); - _loadedCursors[CURSOR_HAND_POINT] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); - - // Using custom cursors - for (size_t i = 0; i < CURSOR_COUNT; i++) - { - const CursorData * cursorData = GetCursorData((CURSOR_ID)i); - if (cursorData != nullptr) - { - _loadedCursors[i] = Create(cursorData); - } - } - - _currentCursor = CURSOR_UNDEFINED; - SetCurrentCursor(CURSOR_ARROW); - } - - void Dispose() - { - if (_initialised) - { - for (size_t i = 0; i < CURSOR_COUNT; i++) - { - SDL_FreeCursor(_loadedCursors[i]); - _loadedCursors[i] = nullptr; - } - _currentCursor = CURSOR_UNDEFINED; - _initialised = false; - } - } - - CURSOR_ID GetCurrentCursor() - { - return _currentCursor; - } - - void SetCurrentCursor(CURSOR_ID cursorId) - { - if (_currentCursor != cursorId) - { - SDL_Cursor * cursor = _loadedCursors[cursorId]; - SDL_SetCursor(cursor); - _currentCursor = cursorId; - } - } -} - -extern "C" -{ - void cursors_initialise() - { - Cursors::Initialise(); - } - - void cursors_dispose() - { - Cursors::Dispose(); - } - - sint32 cursors_getcurrentcursor() - { - return Cursors::GetCurrentCursor(); - } - - void cursors_setcurrentcursor(sint32 cursorId) - { - Cursors::SetCurrentCursor((CURSOR_ID)cursorId); - } -} diff --git a/src/openrct2/interface/Cursors.h b/src/openrct2/interface/Cursors.h index 87ae8b1a4a..125d1d9921 100644 --- a/src/openrct2/interface/Cursors.h +++ b/src/openrct2/interface/Cursors.h @@ -53,7 +53,7 @@ enum CURSOR_ID #ifdef __cplusplus -namespace Cursors +namespace OpenRCT2 { namespace Ui { struct CursorData { @@ -65,27 +65,6 @@ namespace Cursors uint8 Data[32 * 4]; uint8 Mask[32 * 4]; }; - - const CursorData * GetCursorData(CURSOR_ID cursorId); - - void Initialise(); - void Dispose(); - CURSOR_ID GetCurrentCursor(); - void SetCurrentCursor(CURSOR_ID cursorId); -} +} } #endif - -#ifdef __cplusplus -extern "C" -{ -#endif - - void cursors_initialise(); - void cursors_dispose(); - sint32 cursors_getcurrentcursor(); - void cursors_setcurrentcursor(sint32 cursorId); - -#ifdef __cplusplus -} -#endif diff --git a/src/openrct2/platform/shared.c b/src/openrct2/platform/shared.c index 2f3ee893a9..80aa3fb641 100644 --- a/src/openrct2/platform/shared.c +++ b/src/openrct2/platform/shared.c @@ -77,108 +77,6 @@ static const sint32 _fullscreen_modes[] = { 0, SDL_WINDOW_FULLSCREEN, SDL_WINDOW static uint32 _lastGestureTimestamp; static float _gestureRadius; -static void platform_create_window(); - -#if defined(__APPLE__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101200) - mach_timebase_info_data_t _mach_base_info = { 0 }; -#endif - -static sint32 resolution_sort_func(const void *pa, const void *pb) -{ - const resolution_t *a = (resolution_t*)pa; - const resolution_t *b = (resolution_t*)pb; - - sint32 areaA = a->width * a->height; - sint32 areaB = b->width * b->height; - - if (areaA == areaB) return 0; - if (areaA < areaB) return -1; - return 1; -} - -void platform_update_fullscreen_resolutions() -{ - // Query number of display modes - sint32 displayIndex = SDL_GetWindowDisplayIndex(gWindow); - sint32 numDisplayModes = SDL_GetNumDisplayModes(displayIndex); - - // Get desktop aspect ratio - SDL_DisplayMode mode; - SDL_GetDesktopDisplayMode(displayIndex, &mode); - - if (gResolutions != NULL) - free(gResolutions); - - // Get resolutions - gNumResolutions = numDisplayModes; - gResolutions = malloc(gNumResolutions * sizeof(resolution_t)); - gNumResolutions = 0; - - float desktopAspectRatio = (float)mode.w / mode.h; - for (sint32 i = 0; i < numDisplayModes; i++) { - SDL_GetDisplayMode(displayIndex, i, &mode); - - float aspectRatio = (float)mode.w / mode.h; - if (gResolutionsAllowAnyAspectRatio || fabs(desktopAspectRatio - aspectRatio) < 0.0001f) { - gResolutions[gNumResolutions].width = mode.w; - gResolutions[gNumResolutions].height = mode.h; - gNumResolutions++; - } - } - - // Sort by area - qsort(gResolutions, gNumResolutions, sizeof(resolution_t), resolution_sort_func); - - // Remove duplicates - resolution_t *resPlace = &gResolutions[0]; - for (sint32 i = 1; i < gNumResolutions; i++) { - resolution_t *resLook = &gResolutions[i]; - if (resLook->width != resPlace->width || resLook->height != resPlace->height) - *++resPlace = *resLook; - } - - gNumResolutions = (sint32)(resPlace - &gResolutions[0]) + 1; - - // Update config fullscreen resolution if not set - if (gConfigGeneral.fullscreen_width == -1 || gConfigGeneral.fullscreen_height == -1) { - gConfigGeneral.fullscreen_width = gResolutions[gNumResolutions - 1].width; - gConfigGeneral.fullscreen_height = gResolutions[gNumResolutions - 1].height; - } -} - -void platform_get_closest_resolution(sint32 inWidth, sint32 inHeight, sint32 *outWidth, sint32 *outHeight) -{ - sint32 closestWidth = 640, closestHeight = 480; - - sint32 closestAreaDiff = -1; - sint32 destinationArea = inWidth * inHeight; - for (sint32 i = 0; i < gNumResolutions; i++) { - // Check if exact match - if (gResolutions[i].width == inWidth && gResolutions[i].height == inHeight) { - closestWidth = gResolutions[i].width; - closestHeight = gResolutions[i].height; - closestAreaDiff = 0; - break; - } - - // Check if area is closer to best match - sint32 areaDiff = abs((gResolutions[i].width * gResolutions[i].height) - destinationArea); - if (closestAreaDiff == -1 || areaDiff < closestAreaDiff) { - closestAreaDiff = areaDiff; - closestWidth = gResolutions[i].width; - closestHeight = gResolutions[i].height; - } - } - - if (closestAreaDiff != -1) { - *outWidth = closestWidth; - *outHeight = closestHeight; - } else { - *outWidth = 640; - *outHeight = 480; - } -} - void platform_draw() { if (!gOpenRCT2Headless) { @@ -186,58 +84,6 @@ void platform_draw() } } -static void platform_resize(sint32 width, sint32 height) -{ - uint32 flags; - sint32 dst_w = (sint32)(width / gConfigGeneral.window_scale); - sint32 dst_h = (sint32)(height / gConfigGeneral.window_scale); - - gScreenWidth = dst_w; - gScreenHeight = dst_h; - - drawing_engine_resize(); - - flags = SDL_GetWindowFlags(gWindow); - - if ((flags & SDL_WINDOW_MINIMIZED) == 0) { - window_resize_gui(dst_w, dst_h); - window_relocate_windows(dst_w, dst_h); - } - - gfx_invalidate_screen(); - - // Check if the window has been resized in windowed mode and update the config file accordingly - // This is called in rct2_update and is only called after resizing a window has finished - if (!(flags & platform_get_non_window_flags())) { - if (width != gConfigGeneral.window_width || height != gConfigGeneral.window_height) { - gConfigGeneral.window_width = width; - gConfigGeneral.window_height = height; - config_save_default(); - } - } -} - -/** - * @brief platform_trigger_resize - * Helper function to set various render target features. - * - * Does not get triggered on resize, but rather manually on config changes. - */ -void platform_trigger_resize() -{ - char scale_quality_buffer[4]; // just to make sure we can hold whole uint8 - uint8 scale_quality = gConfigGeneral.scale_quality; - if (gConfigGeneral.use_nn_at_integer_scales && gConfigGeneral.window_scale == floor(gConfigGeneral.window_scale)) { - scale_quality = 0; - } - snprintf(scale_quality_buffer, sizeof(scale_quality_buffer), "%u", scale_quality); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, scale_quality_buffer); - - sint32 w, h; - SDL_GetWindowSize(gWindow, &w, &h); - platform_resize(w, h); -} - static uint8 soft_light(uint8 a, uint8 b) { float fa = a / 255.0f; @@ -572,15 +418,8 @@ void platform_process_messages() gKeysState = SDL_GetKeyboardState(&numKeys); } -static void platform_close_window() -{ - drawing_engine_dispose(); - cursors_dispose(); -} - void platform_init() { - platform_create_window(); gKeysPressed = malloc(sizeof(uint8) * 256); memset(gKeysPressed, 0, sizeof(uint8) * 256); @@ -593,57 +432,6 @@ void platform_init() gPalette[255].b = 255; } -static void platform_create_window() -{ - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - log_fatal("SDL_Init %s", SDL_GetError()); - exit(-1); - } - - SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, gConfigGeneral.minimize_fullscreen_focus_loss ? "1" : "0"); - - cursors_initialise(); - - // TODO This should probably be called somewhere else. It has nothing to do with window creation and can be done as soon as - // g1.dat is loaded. - sub_68371D(); - - // Get window size - sint32 width = gConfigGeneral.window_width; - sint32 height = gConfigGeneral.window_height; - if (width == -1) width = 640; - if (height == -1) height = 480; - - // Create window in window first rather than fullscreen so we have the display the window is on first - uint32 flags = SDL_WINDOW_RESIZABLE; - if (gConfigGeneral.drawing_engine == DRAWING_ENGINE_OPENGL) { - flags |= SDL_WINDOW_OPENGL; - } - - gWindow = SDL_CreateWindow(OPENRCT2_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags); - if (!gWindow) { - log_fatal("SDL_CreateWindow failed %s", SDL_GetError()); - exit(-1); - } - - SDL_SetWindowGrab(gWindow, gConfigGeneral.trap_cursor ? SDL_TRUE : SDL_FALSE); - SDL_SetWindowMinimumSize(gWindow, 720, 480); - platform_init_window_icon(); -#ifdef __MACOSX__ - macos_disallow_automatic_window_tabbing(); -#endif - - // Initialise the surface, palette and draw buffer - platform_resize(width, height); - - platform_update_fullscreen_resolutions(); - platform_set_fullscreen_mode(gConfigGeneral.fullscreen_mode); - - // Check if steam overlay renderer is loaded into the process - gSteamOverlayActive = platform_check_steam_overlay_attached(); - platform_trigger_resize(); -} - sint32 platform_scancode_to_rct_keycode(sint32 sdl_key) { char keycode = (char)SDL_GetKeyFromScancode((SDL_Scancode)sdl_key); @@ -660,9 +448,6 @@ sint32 platform_scancode_to_rct_keycode(sint32 sdl_key) void platform_free() { free(gKeysPressed); - - platform_close_window(); - SDL_Quit(); } void platform_start_text_input(utf8* buffer, sint32 max_length) @@ -723,16 +508,6 @@ void platform_toggle_windowed_mode() config_save_default(); } -/** - * This is not quite the same as the below function as we don't want to - * dereference the cursor before the function. - * rct2: 0x0407956 - */ -void platform_set_cursor(uint8 cursor) -{ - cursors_setcurrentcursor(cursor); -} - void platform_refresh_video() { SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, gConfigGeneral.minimize_fullscreen_focus_loss ? "1" : "0"); diff --git a/src/openrct2/platform/windows.c b/src/openrct2/platform/windows.c index 0d8878860b..d139fe59d8 100644 --- a/src/openrct2/platform/windows.c +++ b/src/openrct2/platform/windows.c @@ -804,18 +804,6 @@ HWND windows_get_window_handle() return result; } -void platform_init_window_icon() -{ - HMODULE module = plaform_get_dll_module(); - if (module != NULL) { - HICON icon = LoadIcon(module, MAKEINTRESOURCE(IDI_ICON)); - if (icon != NULL) { - HWND hwnd = windows_get_window_handle(); - SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)icon); - } - } -} - uint16 platform_get_locale_language() { CHAR langCode[4]; diff --git a/src/openrct2/ui/UiContext.h b/src/openrct2/ui/UiContext.h index 0cbe4be69d..c03ead8342 100644 --- a/src/openrct2/ui/UiContext.h +++ b/src/openrct2/ui/UiContext.h @@ -16,7 +16,9 @@ #pragma once +#include #include "../common.h" +#include "../interface/Cursors.h" namespace OpenRCT2 { @@ -45,6 +47,34 @@ namespace OpenRCT2 size_t SelectionSize; // Selection length in bytes }; + struct Resolution + { + sint32 Width; + sint32 Height; + }; + + inline bool operator <(const Resolution& lhs, const Resolution& rhs) + { + sint32 areaA = lhs.Width * lhs.Height; + sint32 areaB = rhs.Width * rhs.Height; + if (areaA == areaB) + { + return lhs.Width < rhs.Width; + } + return areaA < areaB; + } + + inline bool operator ==(const Resolution& lhs, const Resolution& rhs) + { + return lhs.Width == rhs.Width && + lhs.Height == rhs.Height; + } + + inline bool operator !=(const Resolution& lhs, const Resolution& rhs) + { + return !(lhs == rhs); + } + /** * Represents the window or screen that OpenRCT2 is presented on. */ @@ -53,11 +83,15 @@ 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; + // Drawing virtual Drawing::IDrawingEngine * CreateDrawingEngine(Drawing::DRAWING_ENGINE_TYPE type) abstract; diff --git a/src/openrct2/world/map.c b/src/openrct2/world/map.c index c102c98bbb..b84478c3ac 100644 --- a/src/openrct2/world/map.c +++ b/src/openrct2/world/map.c @@ -18,6 +18,7 @@ #include "../audio/audio.h" #include "../cheats.h" #include "../config/Config.h" +#include "../Context.h" #include "../game.h" #include "../interface/Cursors.h" #include "../interface/window.h" @@ -3319,7 +3320,7 @@ void map_invalidate_selection_rect() */ void map_reorganise_elements() { - platform_set_cursor(CURSOR_ZZZ); + context_setcurrentcursor(CURSOR_ZZZ); rct_map_element* new_map_elements = malloc(0x30000 * sizeof(rct_map_element)); rct_map_element* new_elements_pointer = new_map_elements;