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.
This commit is contained in:
@@ -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<PlatformWindow*>(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<Object::Window>());
|
||||
}
|
||||
|
||||
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<Object::Window>();
|
||||
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<Object::Window>();
|
||||
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<void> 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> cursor_id = None;
|
||||
|
||||
BAN::Optional<uint32_t> background;
|
||||
for (size_t i = 0; i < 32; i++)
|
||||
@@ -967,25 +1007,11 @@ BAN::ErrorOr<void> 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<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
|
||||
static_cast<uint32_t>(request.backBlue >> 8) << 8 |
|
||||
static_cast<uint32_t>(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<uint32_t> pixels;
|
||||
TRY(pixels.resize(source.width * source.height));
|
||||
|
||||
auto* source_data_u32 = reinterpret_cast<const uint32_t*>(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<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
|
||||
ASSERT(mask.height == source.height);
|
||||
|
||||
auto* mask_data_u32 = reinterpret_cast<const uint32_t*>(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<PlatformCursor> 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));
|
||||
|
||||
@@ -8,6 +8,8 @@ BAN::ErrorOr<void> 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<void> destroy_window(Client& client_info, WINDOW wid);
|
||||
|
||||
WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y);
|
||||
|
||||
@@ -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<uint32_t> 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<Cursor, Window, Pixmap, GraphicsContext, BAN::RefPtr<PCFFont>, Extension> object;
|
||||
BAN::Variant<Window, Pixmap, GraphicsContext, BAN::RefPtr<PCFFont>, BAN::UniqPtr<PlatformCursor>, Extension> object;
|
||||
};
|
||||
|
||||
struct Client
|
||||
|
||||
@@ -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<Object::Window>();
|
||||
const auto& new_window = g_objects[new_wid]->object.get<Object::Window>();
|
||||
if (old_window.cursor == new_window.cursor)
|
||||
return;
|
||||
|
||||
auto& window = g_objects[wid]->object.get<Object::Window>();
|
||||
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<WINDOW> get_path_to_child(WINDOW wid, int32_t x, int32_t y)
|
||||
{
|
||||
BAN::Vector<WINDOW> 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<Object::Window>().hovered = false;
|
||||
for (const auto wid : new_child_path)
|
||||
g_objects[wid]->object.get<Object::Window>().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<Object::Window>();
|
||||
|
||||
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<Object::Window>();
|
||||
|
||||
send_enter_and_leave_events(old_wid, old_window.cursor_x, old_window.cursor_y, wid, x, y);
|
||||
|
||||
old_wid = wid;
|
||||
}
|
||||
|
||||
|
||||
@@ -385,6 +385,8 @@ static BAN::ErrorOr<BAN::RefPtr<PCFFont>> 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<void> 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<SystemCursorType> 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<Object>::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<void> 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<uint32_t> 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<void> 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<PlatformCursor> 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,
|
||||
|
||||
@@ -40,6 +40,8 @@ struct PCFFont : public BAN::RefCounted<PCFFont>, public BAN::Weakable<PCFFont>
|
||||
BAN::Vector<MapEntry> map;
|
||||
BAN::Vector<uint8_t> bitmap;
|
||||
|
||||
bool is_cursor_font;
|
||||
|
||||
BAN::Optional<uint16_t> find_glyph(uint16_t codepoint) const
|
||||
{
|
||||
size_t l = 0;
|
||||
|
||||
@@ -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<BAN::UniqPtr<PlatformCursor>> (*create_system_cursor)(SystemCursorType);
|
||||
/* Create cursor from custom bitmap */
|
||||
BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> (*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;
|
||||
|
||||
@@ -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<SDLWindow*>(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<SDLWindow*>(window);
|
||||
SDL_SetWindowFullscreen(sdl_window.window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> 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<SDLCursor>::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<PlatformCursor>(BAN::move(cursor));
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> 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<SDLCursor>::create());
|
||||
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormatFrom(const_cast<uint32_t*>(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<int32_t>(origin_x, 0, width - 1);
|
||||
origin_y = BAN::Math::clamp<int32_t>(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<PlatformCursor>(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<SDLCursor*>(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 <LibInput/KeyEvent.h>
|
||||
|
||||
@@ -109,13 +109,12 @@ BAN::ErrorOr<DrawableInfo> 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<Object::Cursor>();
|
||||
return nullptr;
|
||||
return it->value->object.get<BAN::UniqPtr<PlatformCursor>>().ptr();
|
||||
}
|
||||
|
||||
@@ -14,4 +14,4 @@ BAN::ErrorOr<Object::Pixmap&> get_pixmap(Client& client_info, CARD32 pid, BYTE o
|
||||
BAN::ErrorOr<Object::GraphicsContext&> get_gc(Client& client_info, CARD32 gc, BYTE op_major, BYTE op_minor = 0);
|
||||
BAN::ErrorOr<DrawableInfo> 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);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "../Events.h"
|
||||
#include "../Platform.h"
|
||||
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
struct BananWindow final : public PlatformWindow
|
||||
@@ -14,6 +16,15 @@ struct BananWindow final : public PlatformWindow
|
||||
BAN::UniqPtr<LibGUI::Window> window;
|
||||
};
|
||||
|
||||
struct BananCursor final : public PlatformCursor
|
||||
{
|
||||
BAN::Vector<uint32_t> 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<BAN::UniqPtr<PlatformCursor>> bananos_create_system_cursor(SystemCursorType type)
|
||||
{
|
||||
(void)type;
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> 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<BananCursor>::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<PlatformCursor>(BAN::move(cursor));
|
||||
}
|
||||
|
||||
static void bananos_set_cursor(PlatformWindow* window, PlatformCursor* cursor)
|
||||
{
|
||||
if (window == nullptr)
|
||||
return;
|
||||
|
||||
auto& banan_window = *static_cast<BananWindow*>(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<BananCursor*>(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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user