diff --git a/CMakeLists.txt b/CMakeLists.txt index ab62796d..3db421fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -336,6 +336,7 @@ set(libdevilutionx_SRCS Source/gendung.cpp Source/gmenu.cpp Source/help.cpp + Source/hwcursor.cpp Source/init.cpp Source/interfac.cpp Source/inv.cpp diff --git a/Source/DiabloUI/credits.cpp b/Source/DiabloUI/credits.cpp index 4a264b4b..4d5dd2df 100644 --- a/Source/DiabloUI/credits.cpp +++ b/Source/DiabloUI/credits.cpp @@ -10,6 +10,7 @@ #include "DiabloUI/support_lines.h" #include "control.h" #include "controls/menu_controls.h" +#include "hwcursor.hpp" #include "utils/display.h" #include "utils/sdl_compat.h" #include "utils/sdl_ptrs.h" @@ -199,6 +200,9 @@ bool TextDialog() CreditsRenderer creditsRenderer; bool endMenu = false; + if (IsHardwareCursorEnabled()) + SetHardwareCursorVisible(false); + SDL_Event event; do { creditsRenderer.Render(); diff --git a/Source/DiabloUI/diabloui.cpp b/Source/DiabloUI/diabloui.cpp index 02b939c5..052f5d43 100644 --- a/Source/DiabloUI/diabloui.cpp +++ b/Source/DiabloUI/diabloui.cpp @@ -12,6 +12,7 @@ #include "controls/controller.h" #include "controls/menu_controls.h" #include "dx.h" +#include "hwcursor.hpp" #include "palette.h" #include "storm/storm.h" #include "utils/display.h" @@ -391,6 +392,8 @@ void UiHandleEvents(SDL_Event *event) gbActive = true; else if (event->window.event == SDL_WINDOWEVENT_HIDDEN) gbActive = false; + else if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + ReinitializeHardwareCursor(); } #endif } @@ -598,7 +601,17 @@ void LoadBackgroundArt(const char *pszFile, int frames) fadeTc = 0; fadeValue = 0; + + if (IsHardwareCursorEnabled() && ArtCursor.surface != nullptr && GetCurrentCursorInfo().type() != CursorType::UserInterface) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_SetSurfacePalette(ArtCursor.surface.get(), palette); + SDL_SetColorKey(ArtCursor.surface.get(), 1, 0); +#endif + SetHardwareCursor(CursorInfo::UserInterfaceCursor()); + } + BlackPalette(); + SDL_FillRect(DiabloUiSurface(), nullptr, 0x000000); if (DiabloUiSurface() == pal_surface) BltFast(nullptr, nullptr); @@ -634,6 +647,7 @@ void UiFadeIn() } SetFadeLevel(fadeValue); } + if (DiabloUiSurface() == pal_surface) BltFast(nullptr, nullptr); RenderPresent(); @@ -672,6 +686,10 @@ void UiPollAndRender() UiRenderItems(gUiItems); DrawMouse(); UiFadeIn(); + + // Must happen after the very first UiFadeIn, which sets the cursor. + if (IsHardwareCursorEnabled()) + SetHardwareCursorVisible(!sgbControllerActive); } namespace { @@ -933,7 +951,7 @@ bool UiItemMouseEvents(SDL_Event *event, const std::vector &items) void DrawMouse() { - if (sgbControllerActive) + if (IsHardwareCursorEnabled() || sgbControllerActive) return; DrawArt(MouseX, MouseY, &ArtCursor); diff --git a/Source/DiabloUI/dialogs.cpp b/Source/DiabloUI/dialogs.cpp index f83774f3..422047e8 100644 --- a/Source/DiabloUI/dialogs.cpp +++ b/Source/DiabloUI/dialogs.cpp @@ -9,6 +9,7 @@ #include "control.h" #include "controls/menu_controls.h" #include "dx.h" +#include "hwcursor.hpp" #include "palette.h" #include "utils/display.h" #include "utils/log.hpp" @@ -263,8 +264,10 @@ void UiOkDialog(const char *text, const char *caption, bool error, const std::ve static bool inDialog = false; if (!gbActive || inDialog) { - if (SDL_ShowCursor(SDL_ENABLE) <= -1) { - Log("{}", SDL_GetError()); + if (!IsHardwareCursorEnabled()) { + if (SDL_ShowCursor(SDL_ENABLE) <= -1) { + Log("{}", SDL_GetError()); + } } if (!gbQuietMode) { if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, text, caption, nullptr) <= -1) { diff --git a/Source/DiabloUI/progress.cpp b/Source/DiabloUI/progress.cpp index a2dd7200..983ee350 100644 --- a/Source/DiabloUI/progress.cpp +++ b/Source/DiabloUI/progress.cpp @@ -5,6 +5,7 @@ #include "control.h" #include "controls/menu_controls.h" #include "dx.h" +#include "hwcursor.hpp" #include "palette.h" #include "utils/display.h" #include "utils/language.h" diff --git a/Source/cursor.cpp b/Source/cursor.cpp index fd467022..f6a0dc67 100644 --- a/Source/cursor.cpp +++ b/Source/cursor.cpp @@ -10,13 +10,15 @@ #include "control.h" #include "doom.h" #include "engine.h" +#include "engine/render/cel_render.hpp" +#include "hwcursor.hpp" #include "inv.h" #include "missiles.h" +#include "qol/itemlabels.h" #include "towners.h" #include "track.h" #include "trigs.h" #include "utils/language.h" -#include "qol/itemlabels.h" namespace devilution { namespace { @@ -174,6 +176,22 @@ void NewCursor(int i) pcurs = i; std::tie(cursW, cursH) = GetInvItemSize(i); SetICursor(i); + if (IsHardwareCursorEnabled() && GetCurrentCursorInfo() != CursorInfo::GameCursor(pcurs) && pcurs != CURSOR_NONE) { + SetHardwareCursor(CursorInfo::GameCursor(pcurs)); + } +} + +void CelDrawCursor(const CelOutputBuffer &out, Point position, int pcurs) +{ + const auto &sprite = GetInvItemSprite(pcurs); + const int frame = GetInvItemFrame(pcurs); + if (IsItemSprite(pcurs)) { + const auto &heldItem = plr[myplr].HoldItem; + CelBlitOutlineTo(out, GetOutlineColor(heldItem, true), position, sprite, frame, false); + CelDrawItem(heldItem._iStatFlag, out, position, sprite, frame); + } else { + CelClippedDrawTo(out, position, sprite, frame); + } } void InitLevelCursor() diff --git a/Source/cursor.h b/Source/cursor.h index 91bf368b..a5153647 100644 --- a/Source/cursor.h +++ b/Source/cursor.h @@ -54,6 +54,13 @@ void CheckRportal(); void CheckTown(); void CheckCursMove(); +inline bool IsItemSprite(int pcurs) +{ + return pcurs >= CURSOR_FIRSTITEM; +} + +void CelDrawCursor(const CelOutputBuffer &out, Point position, int pcurs); + /** Returns the sprite for the given inventory index. */ const CelSprite &GetInvItemSprite(int i); diff --git a/Source/diablo.cpp b/Source/diablo.cpp index 1888c5de..0bacc6fa 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -437,7 +437,7 @@ static void run_game_loop(interface_mode uMsg) NewCursor(CURSOR_NONE); ClearScreenBuffer(); force_redraw = 255; - scrollrt_draw_game_screen(true); + scrollrt_draw_game_screen(); saveProc = SetWindowProc(saveProc); assert(saveProc == GM_Game); free_game(); @@ -522,6 +522,9 @@ static void SaveOptions() setIniInt("Graphics", "Blended Transparency", sgOptions.Graphics.bBlendedTransparancy); setIniInt("Graphics", "Gamma Correction", sgOptions.Graphics.nGammaCorrection); setIniInt("Graphics", "Color Cycling", sgOptions.Graphics.bColorCycling); +#ifndef USE_SDL1 + setIniInt("Graphics", "Hardware Cursor", sgOptions.Graphics.bHardwareCursor); +#endif setIniInt("Graphics", "FPS Limiter", sgOptions.Graphics.bFPSLimit); setIniInt("Graphics", "Show FPS", sgOptions.Graphics.bShowFPS); @@ -606,6 +609,11 @@ static void LoadOptions() sgOptions.Graphics.bBlendedTransparancy = getIniBool("Graphics", "Blended Transparency", true); sgOptions.Graphics.nGammaCorrection = getIniInt("Graphics", "Gamma Correction", 100); sgOptions.Graphics.bColorCycling = getIniBool("Graphics", "Color Cycling", true); +#ifndef USE_SDL1 + sgOptions.Graphics.bHardwareCursor = getIniBool("Graphics", "Hardware Cursor", false); +#else + sgOptions.Graphics.bHardwareCursor = false; +#endif sgOptions.Graphics.bFPSLimit = getIniBool("Graphics", "FPS Limiter", true); sgOptions.Graphics.bShowFPS = getIniInt("Graphics", "Show FPS", false); @@ -1940,7 +1948,7 @@ static void timeout_cursor(bool bTimeout) NewCursor(CURSOR_HOURGLASS); force_redraw = 255; } - scrollrt_draw_game_screen(true); + scrollrt_draw_game_screen(); } else if (sgnTimeoutCurs != CURSOR_NONE) { NewCursor(sgnTimeoutCurs); sgnTimeoutCurs = CURSOR_NONE; diff --git a/Source/engine.cpp b/Source/engine.cpp index bf4e8211..54b3dff6 100644 --- a/Source/engine.cpp +++ b/Source/engine.cpp @@ -334,7 +334,7 @@ void PlayInGameMovie(const char *pszMovie) play_movie(pszMovie, false); ClearScreenBuffer(); force_redraw = 255; - scrollrt_draw_game_screen(true); + scrollrt_draw_game_screen(); PaletteFadeIn(8); force_redraw = 255; } diff --git a/Source/gamemenu.cpp b/Source/gamemenu.cpp index f6729201..b24a83fc 100644 --- a/Source/gamemenu.cpp +++ b/Source/gamemenu.cpp @@ -104,7 +104,7 @@ void gamemenu_new_game(bool bActivate) deathflag = false; force_redraw = 255; - scrollrt_draw_game_screen(true); + scrollrt_draw_game_screen(); CornerStone.activated = false; gbRunGame = false; gamemenu_off(); diff --git a/Source/hwcursor.cpp b/Source/hwcursor.cpp new file mode 100644 index 00000000..69641b24 --- /dev/null +++ b/Source/hwcursor.cpp @@ -0,0 +1,103 @@ +#include "hwcursor.hpp" + +#include +#include + +#if SDL_VERSION_ATLEAST(2, 0, 0) +#include +#include +#include +#endif + +#include "DiabloUI/diabloui.h" + +#include "cursor.h" +#include "engine.h" +#include "utils/display.h" +#include "utils/sdl_ptrs.h" + +namespace devilution { +namespace { +CursorInfo CurrentCursorInfo; + +#if SDL_VERSION_ATLEAST(2, 0, 0) +SDLCursorUniquePtr CurrentCursor; + +void SetHardwareCursor(SDL_Surface *surface) +{ + float scaleX; + float scaleY; + if (renderer != nullptr) { + SDL_RenderGetScale(renderer, &scaleX, &scaleY); + } + + SDLCursorUniquePtr newCursor; + if (renderer == nullptr || (scaleX == 1.0F && scaleY == 1.0F)) { + newCursor = SDLCursorUniquePtr { SDL_CreateColorCursor(surface, 0, 0) }; + } else { + // SDL does not support BlitScaled from 8-bit to RGBA. + SDLSurfaceUniquePtr converted { SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0) }; + + const int scaledW = surface->w * scaleX; // NOLINT(bugprone-narrowing-conversions) + const int scaledH = surface->h * scaleY; // NOLINT(bugprone-narrowing-conversions) + SDLSurfaceUniquePtr scaledSurface { SDL_CreateRGBSurfaceWithFormat(0, scaledW, scaledH, 32, SDL_PIXELFORMAT_ARGB8888) }; + SDL_BlitScaled(converted.get(), nullptr, scaledSurface.get(), nullptr); + newCursor = SDLCursorUniquePtr { SDL_CreateColorCursor(scaledSurface.get(), 0, 0) }; + } + SDL_SetCursor(newCursor.get()); + CurrentCursor = std::move(newCursor); +} + +void SetHardwareCursorFromSprite(int pcurs) +{ + const int outlineWidth = IsItemSprite(pcurs) ? 1 : 0; + + int width; + int height; + std::tie(width, height) = GetInvItemSize(pcurs); + width += 2 * outlineWidth; + height += 2 * outlineWidth; + + auto out = CelOutputBuffer::Alloc(width, height); + SDL_SetSurfacePalette(out.surface, palette); + + // Transparent color must not be used in the sprite itself. + // Colors 1-127 are outside of the UI palette so are safe to use. + constexpr std::uint8_t TransparentColor = 1; + SDL_FillRect(out.surface, nullptr, TransparentColor); + SDL_SetColorKey(out.surface, 1, TransparentColor); + CelDrawCursor(out, { outlineWidth, height - outlineWidth }, pcurs); + + SetHardwareCursor(out.surface); + out.Free(); +} +#endif + +} // namespace + +CursorInfo GetCurrentCursorInfo() +{ + return CurrentCursorInfo; +} + +void SetHardwareCursor(CursorInfo cursorInfo) +{ +#if SDL_VERSION_ATLEAST(2, 0, 0) + CurrentCursorInfo = cursorInfo; + switch (cursorInfo.type()) { + case CursorType::Game: + SetHardwareCursorFromSprite(cursorInfo.id()); + break; + case CursorType::UserInterface: + // ArtCursor is null while loading the game on the progress screen, + // called via palette fade from ShowProgress. + if (ArtCursor.surface != nullptr) + SetHardwareCursor(ArtCursor.surface.get()); + break; + case CursorType::Unknown: + break; + } +#endif +} + +} // namespace devilution diff --git a/Source/hwcursor.hpp b/Source/hwcursor.hpp new file mode 100644 index 00000000..0daeb8d7 --- /dev/null +++ b/Source/hwcursor.hpp @@ -0,0 +1,94 @@ +/** + * @file hwcursor.hpp + * + * Hardware cursor (SDL2 only). + */ +#include + +#include "options.h" + +namespace devilution { + +inline bool IsHardwareCursorEnabled() +{ +#if SDL_VERSION_ATLEAST(2, 0, 0) + return sgOptions.Graphics.bHardwareCursor; +#else + return false; +#endif +} + +/** + * @return Whether the cursor was previously visible. + */ +inline bool SetHardwareCursorVisible(bool visible) +{ +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE) == 1; +#else + return false; +#endif +} + +enum class CursorType { + Unknown, + UserInterface, + Game, +}; + +class CursorInfo { +public: + CursorInfo() = default; + + static CursorInfo UserInterfaceCursor() + { + return CursorInfo { CursorType::UserInterface }; + } + + static CursorInfo GameCursor(int gameSpriteId) + { + return CursorInfo { CursorType::Game, gameSpriteId }; + } + + [[nodiscard]] CursorType type() const + { + return type_; + } + + [[nodiscard]] int id() const + { + return id_; + } + + bool operator==(const CursorInfo &other) const + { + return type_ == other.type_ && (type_ != CursorType::Game || id_ == other.id_); + } + bool operator!=(const CursorInfo &other) const + { + return !(*this == other); + } + +private: + explicit CursorInfo(CursorType type, int id = 0) + : type_(type) + , id_(id) + { + } + + CursorType type_ = CursorType::Unknown; + + // ID for CursorType::Game + int id_; +}; + +CursorInfo GetCurrentCursorInfo(); + +void SetHardwareCursor(CursorInfo cursorInfo); + +inline void ReinitializeHardwareCursor() +{ + SetHardwareCursor(GetCurrentCursorInfo()); +} + +} // namespace devilution diff --git a/Source/interfac.cpp b/Source/interfac.cpp index ab0c97da..174eeac8 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -11,6 +11,7 @@ #include "dx.h" #include "engine.h" #include "engine/render/cel_render.hpp" +#include "hwcursor.hpp" #include "init.h" #include "loadsave.h" #include "palette.h" @@ -179,8 +180,9 @@ static void DrawCutscene() SDL_FillRect(out.surface, &rect, BarColor[progress_id]); unlock_buf(1); - force_redraw = 255; - scrollrt_draw_game_screen(false); + + BltFast(&rect, &rect); + RenderPresent(); } void interface_msg_pump() @@ -218,10 +220,14 @@ void ShowProgress(interface_mode uMsg) interface_msg_pump(); ClearScreenBuffer(); - scrollrt_draw_game_screen(true); + scrollrt_draw_game_screen(); InitCutscene(uMsg); BlackPalette(); DrawCutscene(); + + if (IsHardwareCursorEnabled()) + SetHardwareCursorVisible(false); + PaletteFadeIn(8); IncProgress(); sound_init(); diff --git a/Source/miniwin/misc_msg.cpp b/Source/miniwin/misc_msg.cpp index 73db3e54..b7651653 100644 --- a/Source/miniwin/misc_msg.cpp +++ b/Source/miniwin/misc_msg.cpp @@ -10,6 +10,7 @@ #include "controls/remap_keyboard.h" #include "controls/touch.h" #include "cursor.h" +#include "hwcursor.hpp" #include "inv.h" #include "movie.h" #include "utils/display.h" @@ -539,9 +540,11 @@ bool FetchMessage(tagMSG *lpMsg) case SDL_WINDOWEVENT_LEAVE: lpMsg->message = DVL_WM_CAPTURECHANGED; break; + case SDL_WINDOWEVENT_SIZE_CHANGED: + ReinitializeHardwareCursor(); + break; case SDL_WINDOWEVENT_MOVED: case SDL_WINDOWEVENT_RESIZED: - case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_MINIMIZED: case SDL_WINDOWEVENT_MAXIMIZED: case SDL_WINDOWEVENT_RESTORED: diff --git a/Source/movie.cpp b/Source/movie.cpp index c146fc05..fcbdf8a4 100644 --- a/Source/movie.cpp +++ b/Source/movie.cpp @@ -5,6 +5,7 @@ */ #include "diablo.h" +#include "hwcursor.hpp" #include "effects.h" #include "storm/storm_svid.h" #include "utils/display.h" @@ -65,6 +66,7 @@ void play_movie(const char *pszMovie, bool user_can_close) #endif movie_playing = false; + SDL_GetMouseState(&MouseX, &MouseY); OutputToLogical(&MouseX, &MouseY); } diff --git a/Source/options.h b/Source/options.h index 4c9216ee..d2d51bd5 100644 --- a/Source/options.h +++ b/Source/options.h @@ -61,6 +61,8 @@ struct GraphicsOptions { int nGammaCorrection; /** @brief Enable color cycling animations. */ bool bColorCycling; + /** @brief Use a hardware cursor (SDL2 only). */ + bool bHardwareCursor; /** @brief Enable FPS Limit. */ bool bFPSLimit; /** @brief Show FPS, even without the -f command line flag. */ diff --git a/Source/palette.cpp b/Source/palette.cpp index 72f5a5ef..cc570a96 100644 --- a/Source/palette.cpp +++ b/Source/palette.cpp @@ -5,6 +5,7 @@ */ #include "dx.h" +#include "hwcursor.hpp" #include "options.h" #include "storm/storm.h" #include "utils/display.h" @@ -227,6 +228,9 @@ void SetFadeLevel(int fadeval) system_palette[i].b = (fadeval * logical_palette[i].b) / 256; } palette_update(); + if (IsHardwareCursorEnabled()) { + ReinitializeHardwareCursor(); + } } void BlackPalette() diff --git a/Source/scrollrt.cpp b/Source/scrollrt.cpp index 06dc9a62..2013b262 100644 --- a/Source/scrollrt.cpp +++ b/Source/scrollrt.cpp @@ -16,6 +16,7 @@ #include "error.h" #include "gmenu.h" #include "help.h" +#include "hwcursor.hpp" #include "init.h" #include "inv.h" #include "lighting.h" @@ -189,22 +190,22 @@ static void UndrawCursor(const CelOutputBuffer &out) sgdwCursWdt = 0; } +static bool ShouldShowCursor() +{ + return !(sgbControllerActive && !IsMovingMouseCursorWithController() && pcurs != CURSOR_TELEPORT && !invflag && (!chrflag || plr[myplr]._pStatPts <= 0)); +} + /** * @brief Save the content behind the cursor to a temporary buffer, then draw the cursor. */ static void DrawCursor(const CelOutputBuffer &out) { - if (pcurs <= CURSOR_NONE || cursW == 0 || cursH == 0) { - return; - } - - if (sgbControllerActive && !IsMovingMouseCursorWithController() && pcurs != CURSOR_TELEPORT && !invflag && (!chrflag || plr[myplr]._pStatPts <= 0)) { + if (pcurs <= CURSOR_NONE || cursW == 0 || cursH == 0 || !ShouldShowCursor()) { return; } // Copy the buffer before the item cursor and its 1px outline are drawn to a temporary buffer. - const bool isItem = pcurs >= CURSOR_FIRSTITEM; - const int outlineWidth = isItem ? 1 : 0; + const int outlineWidth = IsItemSprite(pcurs) ? 1 : 0; if (MouseX < -cursW - outlineWidth || MouseX - outlineWidth >= out.w() || MouseY < -cursH - outlineWidth || MouseY - outlineWidth >= out.h()) return; @@ -227,17 +228,7 @@ static void DrawCursor(const CelOutputBuffer &out) Clip(sgdwCursY, sgdwCursHgt, out.h()); BlitCursor(sgSaveBack, sgdwCursWdt, out.at(sgdwCursX, sgdwCursY), out.pitch()); - - const auto &sprite = GetInvItemSprite(pcurs); - const int frame = GetInvItemFrame(pcurs); - const Point mousePosition { MouseX, MouseY + cursH - 1 }; - if (isItem) { - const auto &heldItem = plr[myplr].HoldItem; - CelBlitOutlineTo(out, GetOutlineColor(heldItem, true), mousePosition, sprite, frame, false); - CelDrawItem(heldItem._iStatFlag, out, mousePosition, sprite, frame); - } else { - CelClippedDrawTo(out, mousePosition, sprite, frame); - } + CelDrawCursor(out, Point { MouseX, MouseY + cursH - 1 }, pcurs); } /** @@ -1527,9 +1518,8 @@ static void DrawMain(int dwHgt, bool draw_desc, bool draw_hp, bool draw_mana, bo /** * @brief Redraw screen - * @param draw_cursor */ -void scrollrt_draw_game_screen(bool draw_cursor) +void scrollrt_draw_game_screen() { int hgt = 0; @@ -1538,7 +1528,9 @@ void scrollrt_draw_game_screen(bool draw_cursor) hgt = gnScreenHeight; } - if (draw_cursor) { + if (IsHardwareCursorEnabled()) { + SetHardwareCursorVisible(ShouldShowCursor()); + } else { lock_buf(0); DrawCursor(GlobalBackBuffer()); unlock_buf(0); @@ -1548,7 +1540,7 @@ void scrollrt_draw_game_screen(bool draw_cursor) RenderPresent(); - if (draw_cursor) { + if (!IsHardwareCursorEnabled()) { lock_buf(0); UndrawCursor(GlobalBackBuffer()); unlock_buf(0); @@ -1611,7 +1603,12 @@ void DrawAndBlit() hgt = gnScreenHeight; } DrawXPBar(out); - DrawCursor(out); + + if (IsHardwareCursorEnabled()) { + SetHardwareCursorVisible(ShouldShowCursor()); + } else { + DrawCursor(out); + } DrawFPS(out); diff --git a/Source/scrollrt.h b/Source/scrollrt.h index 31c3e315..1becf0b5 100644 --- a/Source/scrollrt.h +++ b/Source/scrollrt.h @@ -64,7 +64,7 @@ void ClearScreenBuffer(); void ScrollView(); #endif void EnableFrameCount(); -void scrollrt_draw_game_screen(bool draw_cursor); +void scrollrt_draw_game_screen(); void DrawAndBlit(); } // namespace devilution diff --git a/Source/utils/sdl_ptrs.h b/Source/utils/sdl_ptrs.h index 1f68f841..18cc1a53 100644 --- a/Source/utils/sdl_ptrs.h +++ b/Source/utils/sdl_ptrs.h @@ -22,6 +22,17 @@ struct SDLSurfaceDeleter { using SDLSurfaceUniquePtr = std::unique_ptr; +#if SDL_VERSION_ATLEAST(2, 0, 0) +struct SDLCursorDeleter { + void operator()(SDL_Cursor *cursor) const + { + SDL_FreeCursor(cursor); + } +}; + +using SDLCursorUniquePtr = std::unique_ptr; +#endif + /** * @brief Deletes the object using `SDL_free`. */