psemek/libs/sdl2/source/cursor.cpp
2022-12-17 15:18:32 +03:00

167 lines
3.9 KiB
C++

#include <psemek/sdl2/cursor.hpp>
#include <psemek/sdl2/init.hpp>
#include <psemek/log/log.hpp>
#include <psemek/util/enum.hpp>
#include <SDL2/SDL_mouse.h>
#include <memory>
namespace psemek::sdl2
{
namespace
{
struct surface_deleter
{
void operator() (SDL_Surface * s) const
{
SDL_FreeSurface(s);
}
};
struct cursor_deleter
{
void operator() (SDL_Cursor * c) const
{
SDL_FreeCursor(c);
}
};
using surface_ptr = std::unique_ptr<SDL_Surface, surface_deleter>;
using cursor_ptr = std::unique_ptr<SDL_Cursor, cursor_deleter>;
}
struct cursor
{
std::unique_ptr<SDL_Cursor, cursor_deleter> cursor;
};
namespace
{
SDL_Cursor * create_system_cursor(SDL_SystemCursor id)
{
auto cursor = SDL_CreateSystemCursor(id);
if (!cursor)
log::warning() << "Failed to create system cursor: " << SDL_GetError();
return cursor;
}
struct system_cursor_provider_impl
: cursor_provider
{
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;
}
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_;
};
}
std::unique_ptr<cursor_provider> system_cursor_provider()
{
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());
}
}