From f6a87f064f13bd5448dfda741b19f12d3d39ac7a Mon Sep 17 00:00:00 2001 From: Oskari Alaranta Date: Sat, 30 May 2026 04:21:41 +0300 Subject: [PATCH] Add support for system cursors When a client creates a cursor from the default cursor font, we try to detect which cursor it is and create a native system cursor based on that. --- xbanan/Base.cpp | 89 +++++++++++++++-------- xbanan/Base.h | 2 + xbanan/Definitions.h | 13 +--- xbanan/Events.cpp | 36 +++------- xbanan/Font.cpp | 66 ++++++++++++++--- xbanan/Font.h | 2 + xbanan/Platform.h | 32 ++++++++- xbanan/SDL2/sdl2.cpp | 135 ++++++++++++++++++++++++++++++----- xbanan/SafeGetters.cpp | 9 ++- xbanan/SafeGetters.h | 2 +- xbanan/banan-os/banan-os.cpp | 60 +++++++++++++--- 11 files changed, 336 insertions(+), 110 deletions(-) diff --git a/xbanan/Base.cpp b/xbanan/Base.cpp index 671cf88..b867f20 100644 --- a/xbanan/Base.cpp +++ b/xbanan/Base.cpp @@ -711,6 +711,46 @@ WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y) return wid; } +static PlatformWindow* get_platform_window(const Object::Window& window) +{ + if (window.platform_window) + return const_cast(window.platform_window.ptr()); + if (window.parent == None) + return nullptr; + + auto& object = *g_objects[window.parent]; + ASSERT(object.type == Object::Type::Window); + return get_platform_window(object.object.get()); +} + +void update_cursor(WINDOW wid, int32_t x, int32_t y) +{ + if (g_platform_ops.set_cursor == nullptr) + return; + + wid = find_child_window(wid, x, y); + if (wid == None) + return; + + const CURSOR cid = [&wid]() -> CURSOR { + for (;;) + { + const auto& window = g_objects[wid]->object.get(); + if (window.cursor != None || window.parent == None) + return window.cursor; + wid = window.parent; + } + }(); + + static CURSOR active_cid = None; + if (cid == active_cid) + return; + + const auto& window = g_objects[wid]->object.get(); + g_platform_ops.set_cursor(get_platform_window(window), get_cursor_safe(cid)); + + active_cid = cid; +} static void on_root_client_message(const xEvent& event) { @@ -930,7 +970,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) auto& window = TRY_REF(get_window(client_info, request.window, opcode)); - CURSOR cursor_id = None; + BAN::Optional cursor_id = None; BAN::Optional background; for (size_t i = 0; i < 32; i++) @@ -967,25 +1007,11 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) } } - if (window.cursor != cursor_id) + if (cursor_id.has_value()) { - // FIXME: show cursor if wid is hovered - - if (window.platform_window) - { - if (g_platform_ops.set_cursor) - { - const auto& cursor = get_cursor_safe(cursor_id); - g_platform_ops.set_cursor( - window.platform_window.ptr(), - cursor.pixels.data(), - cursor.width, cursor.height, - cursor.origin_x, cursor.origin_y - ); - } - } - - window.cursor = cursor_id; + window.cursor = cursor_id.value(); + if (window.hovered) + update_cursor(request.window, window.cursor_x, window.cursor_y); } if (background.has_value()) @@ -2613,17 +2639,12 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) static_cast(request.backBlue >> 8) << 8 | static_cast(request.backGreen >> 8) << 0; - Object::Cursor cursor { - .width = source.width, - .height = source.height, - .origin_x = request.x, - .origin_y = request.y, - }; - TRY(cursor.pixels.resize(cursor.width * cursor.height)); + BAN::Vector pixels; + TRY(pixels.resize(source.width * source.height)); auto* source_data_u32 = reinterpret_cast(source.data.data()); - for (size_t i = 0; i < cursor.width * cursor.height; i++) - cursor.pixels[i] = 0xFF000000 | (source_data_u32[i] ? foreground : background); + for (size_t i = 0; i < source.width * source.height; i++) + pixels[i] = 0xFF000000 | (source_data_u32[i] ? foreground : background); if (request.mask != None) { @@ -2633,9 +2654,17 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) ASSERT(mask.height == source.height); auto* mask_data_u32 = reinterpret_cast(mask.data.data()); - for (size_t i = 0; i < cursor.width * cursor.height; i++) + for (size_t i = 0; i < source.width * source.height; i++) if (!mask_data_u32[i]) - cursor.pixels[i] = 0; + pixels[i] = 0; + } + + BAN::UniqPtr cursor; + if (g_platform_ops.create_bitmap_cursor) + { + auto cursor_or_error = g_platform_ops.create_bitmap_cursor(pixels.data(), source.width, source.height, request.x, request.y); + if (!cursor_or_error.is_error()) + cursor = cursor_or_error.release_value(); } TRY(client_info.objects.insert(request.cid)); diff --git a/xbanan/Base.h b/xbanan/Base.h index b6dc190..10d060d 100644 --- a/xbanan/Base.h +++ b/xbanan/Base.h @@ -8,6 +8,8 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h); void send_exposure_recursive(WINDOW wid); +void update_cursor(WINDOW wid, int32_t x, int32_t y); + BAN::ErrorOr destroy_window(Client& client_info, WINDOW wid); WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y); diff --git a/xbanan/Definitions.h b/xbanan/Definitions.h index c0613c5..6af8b3a 100644 --- a/xbanan/Definitions.h +++ b/xbanan/Definitions.h @@ -41,15 +41,6 @@ struct Object Type type; - struct Cursor - { - uint32_t width; - uint32_t height; - int32_t origin_x; - int32_t origin_y; - BAN::Vector pixels; - }; - struct Window { Client& owner; @@ -57,6 +48,8 @@ struct Object bool mapped { false }; bool focused { false }; bool fullscreen { false }; + bool hovered { false }; + uint8_t depth { 0 }; int32_t x { 0 }; int32_t y { 0 }; @@ -136,7 +129,7 @@ struct Object void (*destructor)(Extension&); }; - BAN::Variant, Extension> object; + BAN::Variant, BAN::UniqPtr, Extension> object; }; struct Client diff --git a/xbanan/Events.cpp b/xbanan/Events.cpp index a124277..62686dc 100644 --- a/xbanan/Events.cpp +++ b/xbanan/Events.cpp @@ -294,32 +294,6 @@ static void update_cursor_position_recursive(WINDOW wid, int32_t new_x, int32_t } } -static void update_cursor(WINDOW wid, int32_t old_x, int32_t old_y, int32_t new_x, int32_t new_y) -{ - const auto old_wid = find_child_window(wid, old_x, old_y); - const auto new_wid = find_child_window(wid, new_x, new_y); - if (old_wid == new_wid || old_wid == None || new_wid == None) - return; - - const auto& old_window = g_objects[old_wid]->object.get(); - const auto& new_window = g_objects[new_wid]->object.get(); - if (old_window.cursor == new_window.cursor) - return; - - auto& window = g_objects[wid]->object.get(); - const auto& cursor = get_cursor_safe(window.cursor); - - if (g_platform_ops.set_cursor) - { - g_platform_ops.set_cursor( - window.platform_window.ptr(), - cursor.pixels.data(), - cursor.width, cursor.height, - cursor.origin_x, cursor.origin_y - ); - } -} - static BAN::Vector get_path_to_child(WINDOW wid, int32_t x, int32_t y) { BAN::Vector result; @@ -474,6 +448,12 @@ static void send_enter_and_leave_events(WINDOW old_wid, int32_t old_x, int32_t o event.u.u.detail = detail; MUST(window.send_event(event, EnterWindowMask)); } + + for (const auto wid : old_child_path) + g_objects[wid]->object.get().hovered = false; + for (const auto wid : new_child_path) + g_objects[wid]->object.get().hovered = true; + } void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y) @@ -482,10 +462,11 @@ void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y) ASSERT(object.type == Object::Type::Window); auto& window = object.object.get(); - update_cursor(wid, window.cursor_x, window.cursor_y, x, y); + update_cursor(wid, x, y); { static WINDOW old_wid = g_root.windowId; + auto it = g_objects.find(old_wid); if (it == g_objects.end()) { @@ -497,6 +478,7 @@ void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y) auto& old_window = it->value->object.get(); send_enter_and_leave_events(old_wid, old_window.cursor_x, old_window.cursor_y, wid, x, y); + old_wid = wid; } diff --git a/xbanan/Font.cpp b/xbanan/Font.cpp index e37d5a9..846a62e 100644 --- a/xbanan/Font.cpp +++ b/xbanan/Font.cpp @@ -385,6 +385,8 @@ static BAN::ErrorOr> parse_font(const BAN::String& path) font->font_ascent = font->max_bounds.ascent; font->font_descent = font->max_bounds.descent; + font->is_cursor_font = (path == "fonts/misc/cursor.pcf.gz"_sv); + return font; } @@ -938,6 +940,49 @@ BAN::ErrorOr create_glyph_cursor(Client& client_info, BAN::ConstByteSpan p const auto& source_font = TRY(get_fontable(client_info, request.source, X_CreateGlyphCursor)); + // Try to use system cursor for known cursors + if (source_font->is_cursor_font && request.mask == request.source && request.sourceChar + 1 == request.maskChar && g_platform_ops.create_system_cursor) + { + BAN::Optional type; + switch (request.sourceChar) + { + case 68: type = SystemCursorType::Pointer; break; + case 152: type = SystemCursorType::Text; break; + case 150: type = SystemCursorType::Wait; break; + case 60: type = SystemCursorType::Hand; break; + case 92: type = SystemCursorType::Help; break; + case 52: type = SystemCursorType::Move; break; + case 88: type = SystemCursorType::Forbidden; break; + case 138: type = SystemCursorType::ResizeN; break; + case 96: type = SystemCursorType::ResizeE; break; + case 16: type = SystemCursorType::ResizeS; break; + case 70: type = SystemCursorType::ResizeW; break; + case 134: type = SystemCursorType::ResizeNW; break; + case 136: type = SystemCursorType::ResizeNE; break; + case 12: type = SystemCursorType::ResizeSW; break; + case 14: type = SystemCursorType::ResizeSE; break; + case 108: type = SystemCursorType::ResizeHorizontal; break; + case 116: type = SystemCursorType::ResizeVertical; break; + } + + if (type.has_value()) + { + auto cursor_or_error = g_platform_ops.create_system_cursor(type.value()); + if (!cursor_or_error.is_error()) + { + TRY(client_info.objects.insert(request.cid)); + TRY(g_objects.insert( + request.cid, + TRY(BAN::UniqPtr::create(Object { + .type = Object::Type::Cursor, + .object = BAN::move(cursor_or_error.value()), + })) + )); + return {}; + } + } + } + auto source_glyph_index = source_font->find_glyph(request.sourceChar); if (!source_glyph_index.has_value()) { @@ -958,19 +1003,14 @@ BAN::ErrorOr create_glyph_cursor(Client& client_info, BAN::ConstByteSpan p const uint32_t source_width = source_ci.rightSideBearing - source_ci.leftSideBearing; const uint32_t source_height = source_ci.ascent + source_ci.descent; - Object::Cursor cursor { - .width = source_width, - .height = source_height, - .origin_x = -source_ci.leftSideBearing, - .origin_y = source_ci.ascent, - }; - TRY(cursor.pixels.resize(cursor.width * cursor.height)); + BAN::Vector pixels; + TRY(pixels.resize(source_width * source_height)); for (uint32_t y = 0; y < source_height; y++) { const uint8_t* row_base = source_font->bitmap.data() + source_glyph.bitmap_offset + (source_width + 7) / 8 * y; for (uint32_t x = 0; x < source_width; x++) - cursor.pixels[y * source_width + x] = 0xFF000000 | ((row_base[x / 8] & (1 << (x % 8))) ? foreground : background); + pixels[y * source_width + x] = 0xFF000000 | ((row_base[x / 8] & (1 << (x % 8))) ? foreground : background); } if (request.mask != None) @@ -1008,11 +1048,19 @@ BAN::ErrorOr create_glyph_cursor(Client& client_info, BAN::ConstByteSpan p const uint8_t* row_base = mask_font->bitmap.data() + mask_glyph.bitmap_offset + (mask_width + 7) / 8 * mask_y; if (!(row_base[mask_x / 8] & (1 << (mask_x % 8)))) - cursor.pixels[src_y * source_width + src_x] = 0; + pixels[src_y * source_width + src_x] = 0; } } } + BAN::UniqPtr cursor; + if (g_platform_ops.create_bitmap_cursor) + { + auto cursor_or_error = g_platform_ops.create_bitmap_cursor(pixels.data(), source_width, source_height, -source_ci.leftSideBearing, source_ci.ascent); + if (!cursor_or_error.is_error()) + cursor = cursor_or_error.release_value(); + } + TRY(client_info.objects.insert(request.cid)); TRY(g_objects.insert( request.cid, diff --git a/xbanan/Font.h b/xbanan/Font.h index a952114..04ba18b 100644 --- a/xbanan/Font.h +++ b/xbanan/Font.h @@ -40,6 +40,8 @@ struct PCFFont : public BAN::RefCounted, public BAN::Weakable BAN::Vector map; BAN::Vector bitmap; + bool is_cursor_font; + BAN::Optional find_glyph(uint16_t codepoint) const { size_t l = 0; diff --git a/xbanan/Platform.h b/xbanan/Platform.h index fe7cb88..5042db4 100644 --- a/xbanan/Platform.h +++ b/xbanan/Platform.h @@ -9,6 +9,11 @@ struct PlatformWindow virtual ~PlatformWindow() = default; }; +struct PlatformCursor +{ + virtual ~PlatformCursor() = default; +}; + enum class WindowType { Popup, @@ -16,6 +21,27 @@ enum class WindowType Utility, }; +enum class SystemCursorType +{ + Pointer, + Text, + Wait, + Hand, + Help, + Move, + Forbidden, + ResizeN, + ResizeE, + ResizeS, + ResizeW, + ResizeNW, + ResizeNE, + ResizeSW, + ResizeSE, + ResizeVertical, + ResizeHorizontal, +}; + // initialize, poll_events, create_window and invalidate are required struct PlatformOps { @@ -31,7 +57,11 @@ struct PlatformOps void (*request_resize)(PlatformWindow*, uint32_t width, uint32_t height); /* Request new fullscreen state, can be async */ void (*request_fullscreen)(PlatformWindow*, bool fullscreen); + /* Create a system cursor */ + BAN::ErrorOr> (*create_system_cursor)(SystemCursorType); + /* Create cursor from custom bitmap */ + BAN::ErrorOr> (*create_bitmap_cursor)(const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y); /* Set custom cursor */ - void (*set_cursor)(PlatformWindow*, const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y); + void (*set_cursor)(PlatformWindow*, PlatformCursor*); }; extern PlatformOps g_platform_ops; diff --git a/xbanan/SDL2/sdl2.cpp b/xbanan/SDL2/sdl2.cpp index 50eaa1b..c756ede 100644 --- a/xbanan/SDL2/sdl2.cpp +++ b/xbanan/SDL2/sdl2.cpp @@ -39,6 +39,17 @@ struct SDLWindow final : public PlatformWindow SDL_Texture* texture { nullptr }; }; +struct SDLCursor final : public PlatformCursor +{ + ~SDLCursor() + { + if (cursor != nullptr) + SDL_FreeCursor(cursor); + } + + SDL_Cursor* cursor { nullptr }; +}; + static int s_eventfd { -1 }; struct Keymap @@ -48,6 +59,8 @@ struct Keymap }; static Keymap s_sdl_keymap; +static SDL_Cursor* s_default_cursor { nullptr }; + static void* sdl2_thread(void*) { for (;;) @@ -77,6 +90,8 @@ static bool sdl2_initialize(uint32_t* display_w, uint32_t* display_h) *display_w = DM.w; *display_h = DM.h; + s_default_cursor = SDL_GetCursor(); + s_eventfd = eventfd(0, 0); if (s_eventfd == -1) { @@ -307,31 +322,115 @@ static void sdl2_invalidate(PlatformWindow* window, const uint32_t* src_pixels, SDL_RenderPresent(sdl_window.renderer); } -static void sdl2_set_cursor(PlatformWindow* window, const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y) -{ - auto& sdl_window = *static_cast(window); - (void)sdl_window; - (void)pixels; - (void)width; - (void)height; - (void)origin_x; - (void)origin_y; -} - static void sdl2_request_fullscreen(PlatformWindow* window, bool fullscreen) { auto& sdl_window = *static_cast(window); SDL_SetWindowFullscreen(sdl_window.window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); } +static BAN::ErrorOr> sdl2_create_system_cursor(SystemCursorType type) +{ + SDL_SystemCursor sdl_type; + switch (type) + { + case SystemCursorType::Help: + case SystemCursorType::Pointer: + sdl_type = SDL_SYSTEM_CURSOR_ARROW; + break; + case SystemCursorType::Text: + sdl_type = SDL_SYSTEM_CURSOR_IBEAM; + break; + case SystemCursorType::Wait: + sdl_type = SDL_SYSTEM_CURSOR_WAIT; + break; + case SystemCursorType::Hand: + sdl_type = SDL_SYSTEM_CURSOR_HAND; + break; + case SystemCursorType::Move: + sdl_type = SDL_SYSTEM_CURSOR_SIZEALL; + break; + case SystemCursorType::Forbidden: + sdl_type = SDL_SYSTEM_CURSOR_NO; + break; + case SystemCursorType::ResizeN: + case SystemCursorType::ResizeS: + case SystemCursorType::ResizeVertical: + sdl_type = SDL_SYSTEM_CURSOR_SIZENS; + break; + case SystemCursorType::ResizeE: + case SystemCursorType::ResizeW: + case SystemCursorType::ResizeHorizontal: + sdl_type = SDL_SYSTEM_CURSOR_SIZEWE; + break; + case SystemCursorType::ResizeNW: + case SystemCursorType::ResizeSE: + sdl_type = SDL_SYSTEM_CURSOR_SIZENWSE; + break; + case SystemCursorType::ResizeNE: + case SystemCursorType::ResizeSW: + sdl_type = SDL_SYSTEM_CURSOR_SIZENESW; + break; + } + + auto cursor = TRY(BAN::UniqPtr::create()); + + cursor->cursor = SDL_CreateSystemCursor(sdl_type); + if (cursor->cursor == nullptr) + { + dwarnln("Could not create SDL system cursor: {}", SDL_GetError()); + return BAN::Error::from_errno(EFAULT); + } + + return BAN::UniqPtr(BAN::move(cursor)); +} + +static BAN::ErrorOr> sdl2_create_bitmap_cursor(const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y) +{ + auto cursor = TRY(BAN::UniqPtr::create()); + + SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormatFrom(const_cast(pixels), width, height, 32, width * 4, SDL_PIXELFORMAT_ARGB8888); + if (surface == nullptr) + { + dwarnln("Could not create SDL surface for cursor: {}", SDL_GetError()); + return BAN::Error::from_errno(EFAULT); + } + + origin_x = BAN::Math::clamp(origin_x, 0, width - 1); + origin_y = BAN::Math::clamp(origin_y, 0, height - 1); + cursor->cursor = SDL_CreateColorCursor(surface, origin_x, origin_y); + + SDL_FreeSurface(surface); + + if (cursor->cursor == nullptr) + { + dwarnln("Could not create SDL color cursor: {}", SDL_GetError()); + return BAN::Error::from_errno(EFAULT); + } + + return BAN::UniqPtr(BAN::move(cursor)); +} + +static void sdl2_set_cursor(PlatformWindow*, PlatformCursor* cursor) +{ + if (cursor == nullptr) + SDL_SetCursor(s_default_cursor); + else + { + auto& sdl_cursor = *static_cast(cursor); + SDL_SetCursor(sdl_cursor.cursor); + } +} + PlatformOps g_platform_ops = { - .initialize = sdl2_initialize, - .poll_events = sdl2_poll_events, - .create_window = sdl2_create_window, - .invalidate = sdl2_invalidate, - .request_resize = sdl2_request_resize, - .request_fullscreen = sdl2_request_fullscreen, - .set_cursor = sdl2_set_cursor, + .initialize = sdl2_initialize, + .poll_events = sdl2_poll_events, + .create_window = sdl2_create_window, + .invalidate = sdl2_invalidate, + .request_resize = sdl2_request_resize, + .request_fullscreen = sdl2_request_fullscreen, + .create_system_cursor = sdl2_create_system_cursor, + .create_bitmap_cursor = sdl2_create_bitmap_cursor, + .set_cursor = sdl2_set_cursor, }; #include diff --git a/xbanan/SafeGetters.cpp b/xbanan/SafeGetters.cpp index 0ec6d0e..719594b 100644 --- a/xbanan/SafeGetters.cpp +++ b/xbanan/SafeGetters.cpp @@ -109,13 +109,12 @@ BAN::ErrorOr get_drawable_info(Client& client_info, CARD32 drawabl return info; } -Object::Cursor& get_cursor_safe(CURSOR cid) +PlatformCursor* get_cursor_safe(CURSOR cid) { - static Object::Cursor dummy {}; auto it = g_objects.find(cid); if (it == g_objects.end()) - return dummy; + return nullptr; if (it->value->type != Object::Type::Cursor) - return dummy; - return it->value->object.get(); + return nullptr; + return it->value->object.get>().ptr(); } diff --git a/xbanan/SafeGetters.h b/xbanan/SafeGetters.h index 9553a36..c1839a5 100644 --- a/xbanan/SafeGetters.h +++ b/xbanan/SafeGetters.h @@ -14,4 +14,4 @@ BAN::ErrorOr get_pixmap(Client& client_info, CARD32 pid, BYTE o BAN::ErrorOr get_gc(Client& client_info, CARD32 gc, BYTE op_major, BYTE op_minor = 0); BAN::ErrorOr get_drawable_info(Client& client_info, CARD32 drawable, BYTE op_major, BYTE op_minor = 0); -Object::Cursor& get_cursor_safe(CURSOR cid); +PlatformCursor* get_cursor_safe(CURSOR cid); diff --git a/xbanan/banan-os/banan-os.cpp b/xbanan/banan-os/banan-os.cpp index 5fa0c4b..0afc004 100644 --- a/xbanan/banan-os/banan-os.cpp +++ b/xbanan/banan-os/banan-os.cpp @@ -1,6 +1,8 @@ #include "../Events.h" #include "../Platform.h" +#include + #include struct BananWindow final : public PlatformWindow @@ -14,6 +16,15 @@ struct BananWindow final : public PlatformWindow BAN::UniqPtr window; }; +struct BananCursor final : public PlatformCursor +{ + BAN::Vector pixels; + uint32_t width; + uint32_t height; + int32_t origin_x; + int32_t origin_y; +}; + static bool bananos_initialize(uint32_t* display_w, uint32_t* display_h) { auto attributes = LibGUI::Window::default_attributes; @@ -123,10 +134,39 @@ static void bananos_invalidate(PlatformWindow* window, const uint32_t* pixels, u banan_window.window->invalidate(x, y, width, height); } -static void bananos_set_cursor(PlatformWindow* window, const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y) +static BAN::ErrorOr> bananos_create_system_cursor(SystemCursorType type) { + (void)type; + return BAN::Error::from_errno(ENOTSUP); +} + +static BAN::ErrorOr> bananos_create_bitmap_cursor(const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y) +{ + auto cursor = TRY(BAN::UniqPtr::create()); + cursor->width = width; + cursor->height = height; + cursor->origin_x = origin_x; + cursor->origin_y = origin_y; + + TRY(cursor->pixels.resize(width * height)); + memcpy(cursor->pixels.data(), pixels, cursor->pixels.size() * 4); + + return BAN::UniqPtr(BAN::move(cursor)); +} + +static void bananos_set_cursor(PlatformWindow* window, PlatformCursor* cursor) +{ + if (window == nullptr) + return; + auto& banan_window = *static_cast(window); - banan_window.window->set_cursor(width, height, { pixels, width * height }, origin_x, origin_y); + if (cursor == nullptr) + banan_window.window->set_cursor(0, 0, {}, 0, 0); + else + { + auto& banan_cursor = *static_cast(cursor); + banan_window.window->set_cursor(banan_cursor.width, banan_cursor.height, banan_cursor.pixels.span(), banan_cursor.origin_x, banan_cursor.origin_y); + } } static void bananos_request_fullscreen(PlatformWindow* window, bool fullscreen) @@ -136,11 +176,13 @@ static void bananos_request_fullscreen(PlatformWindow* window, bool fullscreen) } PlatformOps g_platform_ops = { - .initialize = bananos_initialize, - .poll_events = bananos_poll_events, - .create_window = bananos_create_window, - .invalidate = bananos_invalidate, - .request_resize = bananos_request_resize, - .request_fullscreen = bananos_request_fullscreen, - .set_cursor = bananos_set_cursor, + .initialize = bananos_initialize, + .poll_events = bananos_poll_events, + .create_window = bananos_create_window, + .invalidate = bananos_invalidate, + .request_resize = bananos_request_resize, + .request_fullscreen = bananos_request_fullscreen, + .create_system_cursor = bananos_create_system_cursor, + .create_bitmap_cursor = bananos_create_bitmap_cursor, + .set_cursor = bananos_set_cursor, };