From d3b348a5b183b0e6295dc985b1d5dcd939cb5c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 29 Feb 2024 07:54:27 +0100 Subject: [PATCH] cursor-shape: improve xcursor fallback support, and prefer CSS names Before this patch, we used legacy X11 xcursor names, and didn't really have any fallback handling in place (we only tried to fallback to "xterm", regardless of which cursor shape we were trying to load). This patch changes two things: 1. Improved fallback support. cursor_shape_to_string() now returns a list of strings. This allows us to have per-shape fallbacks, and any number of fallbacks. 2. We prefer CSS xcursor names over legacy X11 names. --- CHANGELOG.md | 1 + cursor-shape.c | 31 +++++++++++------------- cursor-shape.h | 3 +-- render.c | 64 +++++++++++++++++++++++++++++--------------------- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b06bf41..e6b6bcd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,6 +90,7 @@ bindings `ctrl-+` and `ctrl+-`). * Use XRGB pixel format (instead of ARGB) when there is no transparency. +* Prefer CSS xcursor names, and fallback to legacy X11 names. [1526]: https://codeberg.org/dnkl/foot/issues/1526 [1528]: https://codeberg.org/dnkl/foot/issues/1528 diff --git a/cursor-shape.c b/cursor-shape.c index 131e6f1a..bbf75ab8 100644 --- a/cursor-shape.c +++ b/cursor-shape.c @@ -9,28 +9,26 @@ #include "debug.h" #include "util.h" -const char * +const char *const * cursor_shape_to_string(enum cursor_shape shape) { - static const char *const table[CURSOR_SHAPE_COUNT] = { - [CURSOR_SHAPE_NONE] = NULL, - [CURSOR_SHAPE_HIDDEN] = "hidden", - [CURSOR_SHAPE_LEFT_PTR] = "left_ptr", - [CURSOR_SHAPE_TEXT] = "text", - [CURSOR_SHAPE_TEXT_FALLBACK] = "xterm", - [CURSOR_SHAPE_TOP_LEFT_CORNER] = "top_left_corner", - [CURSOR_SHAPE_TOP_RIGHT_CORNER] = "top_right_corner", - [CURSOR_SHAPE_BOTTOM_LEFT_CORNER] = "bottom_left_corner", - [CURSOR_SHAPE_BOTTOM_RIGHT_CORNER] = "bottom_right_corner", - [CURSOR_SHAPE_LEFT_SIDE] = "left_side", - [CURSOR_SHAPE_RIGHT_SIDE] = "right_side", - [CURSOR_SHAPE_TOP_SIDE] = "top_side", - [CURSOR_SHAPE_BOTTOM_SIDE] = "bottom_side", + static const char *const table[][CURSOR_SHAPE_COUNT]= { + [CURSOR_SHAPE_NONE] = {NULL}, + [CURSOR_SHAPE_HIDDEN] = {"hidden", NULL}, + [CURSOR_SHAPE_LEFT_PTR] = {"default", "left_ptr", NULL}, + [CURSOR_SHAPE_TEXT] = {"text", "xterm", NULL}, + [CURSOR_SHAPE_TOP_LEFT_CORNER] = {"nw-resize", "top_left_corner", NULL}, + [CURSOR_SHAPE_TOP_RIGHT_CORNER] = {"ne-resize", "top_right_corner", NULL}, + [CURSOR_SHAPE_BOTTOM_LEFT_CORNER] = {"sw-resize", "bottom_left_corner", NULL}, + [CURSOR_SHAPE_BOTTOM_RIGHT_CORNER] = {"se-resize", "bottom_right_corner", NULL}, + [CURSOR_SHAPE_LEFT_SIDE] = {"w-resize", "left_side", NULL}, + [CURSOR_SHAPE_RIGHT_SIDE] = {"e-resize", "right_side", NULL}, + [CURSOR_SHAPE_TOP_SIDE] = {"n-resize", "top_side", NULL}, + [CURSOR_SHAPE_BOTTOM_SIDE] = {"s-resize", "bottom_side", NULL}, }; xassert(shape <= ALEN(table)); - xassert(table[shape] != NULL); return table[shape]; } @@ -40,7 +38,6 @@ cursor_shape_to_server_shape(enum cursor_shape shape) static const enum wp_cursor_shape_device_v1_shape table[CURSOR_SHAPE_COUNT] = { [CURSOR_SHAPE_LEFT_PTR] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT, [CURSOR_SHAPE_TEXT] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT, - [CURSOR_SHAPE_TEXT_FALLBACK] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT, [CURSOR_SHAPE_TOP_LEFT_CORNER] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE, [CURSOR_SHAPE_TOP_RIGHT_CORNER] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE, [CURSOR_SHAPE_BOTTOM_LEFT_CORNER] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE, diff --git a/cursor-shape.h b/cursor-shape.h index 58755382..110dbd2e 100644 --- a/cursor-shape.h +++ b/cursor-shape.h @@ -9,7 +9,6 @@ enum cursor_shape { CURSOR_SHAPE_LEFT_PTR, CURSOR_SHAPE_TEXT, - CURSOR_SHAPE_TEXT_FALLBACK, CURSOR_SHAPE_TOP_LEFT_CORNER, CURSOR_SHAPE_TOP_RIGHT_CORNER, CURSOR_SHAPE_BOTTOM_LEFT_CORNER, @@ -22,7 +21,7 @@ enum cursor_shape { CURSOR_SHAPE_COUNT, }; -const char *cursor_shape_to_string(enum cursor_shape shape); +const char *const *cursor_shape_to_string(enum cursor_shape shape); enum wp_cursor_shape_device_v1_shape cursor_shape_to_server_shape( enum cursor_shape shape); diff --git a/render.c b/render.c index 3ce8a226..91472027 100644 --- a/render.c +++ b/render.c @@ -4625,38 +4625,48 @@ render_xcursor_set(struct seat *seat, struct terminal *term, return true; } - /* TODO: skip this when using server-side cursors */ - if (shape != CURSOR_SHAPE_HIDDEN) { - const char *const xcursor = shape == CURSOR_SHAPE_CUSTOM - ? term->mouse_user_cursor - : cursor_shape_to_string(shape); - const char *const fallback = - cursor_shape_to_string(CURSOR_SHAPE_TEXT_FALLBACK); - - seat->pointer.cursor = wl_cursor_theme_get_cursor( - seat->pointer.theme, xcursor); - - if (seat->pointer.cursor == NULL) { - seat->pointer.cursor = wl_cursor_theme_get_cursor( - seat->pointer.theme, fallback); - - if (seat->pointer.cursor == NULL) { - LOG_ERR("failed to load xcursor pointer " - "'%s', and fallback '%s'", xcursor, fallback); - return false; - } - } - - if (shape == CURSOR_SHAPE_CUSTOM) { - free(seat->pointer.last_custom_xcursor); - seat->pointer.last_custom_xcursor = xstrdup(term->mouse_user_cursor); - } - } else { + if (shape == CURSOR_SHAPE_HIDDEN) { seat->pointer.cursor = NULL; free(seat->pointer.last_custom_xcursor); seat->pointer.last_custom_xcursor = NULL; } + else if (seat->pointer.shape_device == NULL) { + const char *const custom_xcursors[] = {term->mouse_user_cursor, NULL}; + const char *const *xcursors = shape == CURSOR_SHAPE_CUSTOM + ? custom_xcursors + : cursor_shape_to_string(shape); + + xassert(xcursors[0] != NULL); + + seat->pointer.cursor = NULL; + + for (size_t i = 0; xcursors[i] != NULL; i++) { + seat->pointer.cursor = + wl_cursor_theme_get_cursor(seat->pointer.theme, xcursors[i]); + + if (seat->pointer.cursor != NULL) { + LOG_DBG("loaded xcursor %s", xcursors[i]); + break; + } + } + + if (seat->pointer.cursor == NULL) { + LOG_ERR( + "failed to load xcursor pointer '%s', and all of its fallbacks", + xcursors[0]); + return false; + } + } else { + /* Server-side cursors - no need to load anything */ + } + + if (shape == CURSOR_SHAPE_CUSTOM) { + free(seat->pointer.last_custom_xcursor); + seat->pointer.last_custom_xcursor = + xstrdup(term->mouse_user_cursor); + } + /* FDM hook takes care of actual rendering */ seat->pointer.shape = shape; seat->pointer.xcursor_pending = true;