Redesign psemek::sdl2 cursor api

This commit is contained in:
Nikita Lisitsa 2024-07-30 23:51:49 +03:00
parent 13862d8b47
commit 7151fef7a8
2 changed files with 62 additions and 139 deletions

View file

@ -4,40 +4,31 @@
#include <psemek/geom/point.hpp>
#include <memory>
#include <functional>
namespace psemek::sdl2
{
enum class cursor_type
enum class default_cursor_type
{
arrow,
beam,
wait,
crosshair,
waitarrow,
sizenwse,
sizenesw,
sizewe,
sizens,
sizeall,
no,
hand,
size,
};
struct cursor;
struct cursor_provider
{
virtual cursor const & get(cursor_type type) const = 0;
std::shared_ptr<cursor> get_default_cursor(default_cursor_type type);
std::shared_ptr<cursor> make_cursor(gfx::pixmap_rgba const & image, geom::point<int, 2> const & pivot);
virtual ~cursor_provider() {}
};
struct image_cursor_description
{
gfx::pixmap_rgba image;
geom::point<int, 2> pivot;
};
std::unique_ptr<cursor_provider> system_cursor_provider();
std::unique_ptr<cursor_provider> image_cursor_provider(std::function<image_cursor_description(cursor_type)> image_provider);
std::unique_ptr<cursor_provider> set_cursor_provider(std::unique_ptr<cursor_provider> new_provider);
cursor_provider const * get_cursor_provider();
void set_cursor(cursor_type type);
void set_cursor(cursor const & cursor);
}

View file

@ -13,7 +13,7 @@ namespace psemek::sdl2
namespace
{
struct surface_deleter
struct sdl_surface_deleter
{
void operator() (SDL_Surface * s) const
{
@ -21,7 +21,7 @@ namespace psemek::sdl2
}
};
struct cursor_deleter
struct sdl_cursor_deleter
{
void operator() (SDL_Cursor * c) const
{
@ -29,138 +29,70 @@ namespace psemek::sdl2
}
};
using surface_ptr = std::unique_ptr<SDL_Surface, surface_deleter>;
using cursor_ptr = std::unique_ptr<SDL_Cursor, cursor_deleter>;
using sdl_surface_ptr = std::unique_ptr<SDL_Surface, sdl_surface_deleter>;
using sdl_cursor_ptr = std::unique_ptr<SDL_Cursor, sdl_cursor_deleter>;
SDL_SystemCursor to_sdl(default_cursor_type type)
{
switch (type)
{
case default_cursor_type::arrow: return SDL_SYSTEM_CURSOR_ARROW;
case default_cursor_type::beam: return SDL_SYSTEM_CURSOR_IBEAM;
case default_cursor_type::wait: return SDL_SYSTEM_CURSOR_WAIT;
case default_cursor_type::crosshair: return SDL_SYSTEM_CURSOR_CROSSHAIR;
case default_cursor_type::waitarrow: return SDL_SYSTEM_CURSOR_WAITARROW;
case default_cursor_type::sizenwse: return SDL_SYSTEM_CURSOR_SIZENWSE;
case default_cursor_type::sizenesw: return SDL_SYSTEM_CURSOR_SIZENESW;
case default_cursor_type::sizewe: return SDL_SYSTEM_CURSOR_SIZEWE;
case default_cursor_type::sizens: return SDL_SYSTEM_CURSOR_SIZENS;
case default_cursor_type::sizeall: return SDL_SYSTEM_CURSOR_SIZEALL;
case default_cursor_type::no: return SDL_SYSTEM_CURSOR_NO;
case default_cursor_type::hand: return SDL_SYSTEM_CURSOR_HAND;
}
return SDL_SYSTEM_CURSOR_ARROW;
}
}
struct cursor
{
std::unique_ptr<SDL_Cursor, cursor_deleter> cursor;
sdl_cursor_ptr cursor_ptr;
};
namespace
std::shared_ptr<cursor> get_default_cursor(default_cursor_type type)
{
SDL_Cursor * create_system_cursor(SDL_SystemCursor id)
sdl_cursor_ptr cursor_ptr{SDL_CreateSystemCursor(to_sdl(type))};
if (!cursor_ptr)
{
auto cursor = SDL_CreateSystemCursor(id);
if (!cursor)
log::warning() << "Failed to create system cursor: " << SDL_GetError();
return cursor;
log::warning() << "Failed to create system cursor: " << SDL_GetError();
return nullptr;
}
return std::make_shared<cursor>(std::move(cursor_ptr));
}
std::shared_ptr<cursor> make_cursor(gfx::pixmap_rgba const & image, geom::point<int, 2> const & pivot)
{
sdl_surface_ptr surface_ptr{SDL_CreateRGBSurfaceFrom((void *)image.data(), image.width(), image.height(), 32, 4 * image.width(), 0x000000ffu, 0x0000ff00u, 0x00ff0000u, 0xff000000u)};
if (!surface_ptr)
{
log::warning() << "Failed to create cursor SDL surface: " << SDL_GetError();
return nullptr;
}
struct system_cursor_provider_impl
: cursor_provider
sdl_cursor_ptr cursor_ptr{SDL_CreateColorCursor(surface_ptr.get(), pivot[0], pivot[1])};
if (!cursor_ptr)
{
system_cursor_provider_impl()
{
arrow_.cursor.reset(create_system_cursor(SDL_SYSTEM_CURSOR_ARROW));
beam_.cursor.reset(create_system_cursor(SDL_SYSTEM_CURSOR_IBEAM));
hand_.cursor.reset(create_system_cursor(SDL_SYSTEM_CURSOR_HAND));
size_.cursor.reset(create_system_cursor(SDL_SYSTEM_CURSOR_SIZEALL));
}
cursor const & get(cursor_type type) const override
{
switch (type)
{
case cursor_type::arrow: return arrow_;
case cursor_type::beam: return beam_;
case cursor_type::hand: return hand_;
case cursor_type::size: return size_;
}
throw util::unknown_enum_value_exception<cursor_type>(type);
}
private:
cursor arrow_;
cursor beam_;
cursor hand_;
cursor size_;
};
cursor_ptr create_image_cursor(image_cursor_description desc)
{
surface_ptr surface{SDL_CreateRGBSurfaceFrom(desc.image.data(), desc.image.width(), desc.image.height(), 32, 4 * desc.image.width(), 0x000000ffu, 0x0000ff00u, 0x00ff0000u, 0xff000000u)};
if (!surface)
fail("failed to create cursor surface: ");
cursor_ptr cursor{SDL_CreateColorCursor(surface.get(), desc.pivot[0], desc.pivot[1])};
if (!cursor)
fail("failed to create cursor: ");
return cursor;
log::warning() << "Failed to create cursor from SDL surface: " << SDL_GetError();
return nullptr;
}
struct image_cursor_provider_impl
: cursor_provider
{
image_cursor_provider_impl(std::function<image_cursor_description(cursor_type)> image_provider)
{
arrow_.cursor = create_image_cursor(image_provider(cursor_type::arrow));
beam_.cursor = create_image_cursor(image_provider(cursor_type::beam));
hand_.cursor = create_image_cursor(image_provider(cursor_type::hand));
size_.cursor = create_image_cursor(image_provider(cursor_type::size));
}
cursor const & get(cursor_type type) const override
{
switch (type)
{
case cursor_type::arrow: return arrow_;
case cursor_type::beam: return beam_;
case cursor_type::hand: return hand_;
case cursor_type::size: return size_;
}
throw util::unknown_enum_value_exception<cursor_type>(type);
}
private:
cursor arrow_;
cursor beam_;
cursor hand_;
cursor size_;
};
return std::make_shared<cursor>(std::move(cursor_ptr));
}
std::unique_ptr<cursor_provider> system_cursor_provider()
void set_cursor(cursor const & cursor)
{
return std::make_unique<system_cursor_provider_impl>();
}
std::unique_ptr<cursor_provider> image_cursor_provider(std::function<image_cursor_description(cursor_type)> image_provider)
{
return std::make_unique<image_cursor_provider_impl>(std::move(image_provider));
}
static std::unique_ptr<cursor_provider> current_provider;
std::unique_ptr<cursor_provider> set_cursor_provider(std::unique_ptr<cursor_provider> new_provider)
{
std::swap(current_provider, new_provider);
return new_provider;
}
cursor_provider const * get_cursor_provider()
{
static std::unique_ptr<cursor_provider> fallback;
if (current_provider)
return current_provider.get();
if (!fallback)
fallback = system_cursor_provider();
return fallback.get();
}
void set_cursor(cursor_type type)
{
SDL_SetCursor(get_cursor_provider()->get(type).cursor.get());
SDL_SetCursor(cursor.cursor_ptr.get());
}