Compare commits

...

2 commits

Author SHA1 Message Date
Jin Liu
a50f4a1864 Merge branch 'work/jinliu/cursor-scaling' into 'main'
cursor: scale cursors to the requested size on env var "XCURSOR_RESIZED=true"

See merge request wayland/wayland!440
2025-07-08 15:37:21 +00:00
Jin Liu
eada8e1379 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 <m.liu.jin@gmail.com>
2024-11-04 11:28:58 +08:00

View file

@ -29,6 +29,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
/*
@ -438,8 +439,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;
@ -467,8 +505,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++;
@ -682,6 +727,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 *),
@ -700,6 +779,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 &&
@ -718,7 +799,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);