From eada8e137941e8b3c61893301c9f53360d1701dd Mon Sep 17 00:00:00 2001 From: Jin Liu Date: Mon, 4 Nov 2024 11:13:01 +0800 Subject: [PATCH] cursor: scale cursors to the requested size on env var "XCURSOR_RESIZED=true" This is to sync a recent change from libXcursor: https://gitlab.freedesktop.org/xorg/lib/libxcursor/-/merge_requests/22 The motivation is to unify the cursor scaling behavior in various X11 and Wayland apps / toolkits. Currently, when the cursor theme doesn't have the requested size, not all clients scale the cursor by themselves, resulting in inconsistent cursor size across clients. This problem is especially evident in Wayland, as a HiDPI-enabled clients will request cursors in the size: "XCURSOR_SIZE * display scale" which greatly increases the chance that the cursor theme doesn't provide the requested size. Tested with Weston. With an updated libwayland-cursor.so, and env "XCURSOR_RESIZED=true", Weston renders the cursor scaled to any XCURSOR_SIZE. Signed-off-by: Jin Liu --- cursor/xcursor.c | 89 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/cursor/xcursor.c b/cursor/xcursor.c index 0d5761f2..4d388e8e 100644 --- a/cursor/xcursor.c +++ b/cursor/xcursor.c @@ -29,6 +29,7 @@ #include #include #include +#include #include /* @@ -436,8 +437,45 @@ xcursor_read_image(FILE *file, return image; } +static struct xcursor_image * +xcursor_resize_image (struct xcursor_image *src, int size) +{ + uint32_t dest_y, dest_x; + double scale = (double) size / src->size; + struct xcursor_image *dest; + + if (size < 0) + return NULL; + if (size > XCURSOR_IMAGE_MAX_SIZE) + return NULL; + + dest = xcursor_image_create((int) (src->width * scale), + (int) (src->height * scale)); + if (!dest) + return NULL; + + dest->size = (uint32_t) size; + dest->xhot = (uint32_t) (src->xhot * scale); + dest->yhot = (uint32_t) (src->yhot * scale); + dest->delay = src->delay; + + for (dest_y = 0; dest_y < dest->height; dest_y++) + { + uint32_t src_y = (uint32_t) (dest_y / scale); + uint32_t *src_row = src->pixels + (src_y * src->width); + uint32_t *dest_row = dest->pixels + (dest_y * dest->width); + for (dest_x = 0; dest_x < dest->width; dest_x++) + { + uint32_t src_x = (uint32_t) (dest_x / scale); + dest_row[dest_x] = src_row[src_x]; + } + } + + return dest; +} + static struct xcursor_images * -xcursor_xc_file_load_images(FILE *file, int size) +xcursor_xc_file_load_images(FILE *file, int size, bool resize) { struct xcursor_file_header *file_header; uint32_t best_size; @@ -465,8 +503,15 @@ xcursor_xc_file_load_images(FILE *file, int size) toc = xcursor_find_image_toc(file_header, best_size, n); if (toc < 0) break; - images->images[images->nimage] = xcursor_read_image(file, file_header, - toc); + struct xcursor_image *image = xcursor_read_image(file, file_header, toc); + if (!image) + break; + if (resize && image->size != (uint32_t) size) { + struct xcursor_image *resized_image = xcursor_resize_image(image, size); + xcursor_image_destroy(image); + image = resized_image; + } + images->images[images->nimage] = image; if (!images->images[images->nimage]) break; images->nimage++; @@ -680,6 +725,40 @@ xcursor_theme_inherits(const char *full) return result; } +static int +xcursor_default_parse_bool(const char *v) +{ + char c0; + + c0 = *v; + if (isupper ((int)c0)) + c0 = (char) tolower (c0); + if (c0 == 't' || c0 == 'y' || c0 == '1') + return 1; + if (c0 == 'f' || c0 == 'n' || c0 == '0') + return 0; + if (c0 == 'o') + { + char c1 = v[1]; + if (isupper ((int)c1)) + c1 = (char) tolower (c1); + if (c1 == 'n') + return 1; + if (c1 == 'f') + return 0; + } + return -1; +} + +static bool +xcursor_get_resizable(void) +{ + const char *v = getenv("XCURSOR_RESIZED"); + if (!v) + return false; + return xcursor_default_parse_bool(v) > 0; +} + static void load_all_cursors_from_dir(const char *path, int size, void (*load_callback)(struct xcursor_images *, void *), @@ -694,6 +773,8 @@ load_all_cursors_from_dir(const char *path, int size, if (!dir) return; + const bool resize = xcursor_get_resizable(); + for (ent = readdir(dir); ent; ent = readdir(dir)) { #ifdef _DIRENT_HAVE_D_TYPE if (ent->d_type != DT_UNKNOWN && @@ -712,7 +793,7 @@ load_all_cursors_from_dir(const char *path, int size, continue; } - images = xcursor_xc_file_load_images(f, size); + images = xcursor_xc_file_load_images(f, size, resize); if (images) { images->name = strdup(ent->d_name);