#include #include #include #include #include #include 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; using cursor_ptr = std::unique_ptr; } struct cursor { std::unique_ptr 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(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_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(type); } private: cursor arrow_; cursor beam_; cursor hand_; cursor size_; }; } std::unique_ptr system_cursor_provider() { return std::make_unique(); } std::unique_ptr image_cursor_provider(std::function image_provider) { return std::make_unique(std::move(image_provider)); } static std::unique_ptr current_provider; std::unique_ptr set_cursor_provider(std::unique_ptr new_provider) { std::swap(current_provider, new_provider); return new_provider; } cursor_provider const * get_cursor_provider() { static std::unique_ptr 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()); } }