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:
2026-05-30 04:21:41 +03:00
parent feb194f416
commit df2aa925cb
11 changed files with 339 additions and 112 deletions

View File

@@ -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,