Merge branch 'cursor-shape'

Closes #1379
This commit is contained in:
Daniel Eklöf 2023-07-03 15:01:49 +02:00
commit e00a20465b
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
16 changed files with 366 additions and 82 deletions

View file

@ -53,6 +53,10 @@
* Support for the new fractional-scaling-v1 Wayland protocol. This
brings true fractional scaling to Wayland in general, and with this
release, foot.
* Support for the new `cursor-shape-v1` Wayland protocol, i.e. server
side cursor shapes ([#1379][1379]).
[1379]: https://codeberg.org/dnkl/foot/issues/1379
### Changed

View file

@ -67,12 +67,13 @@ version_and_features(void)
{
static char buf[256];
snprintf(buf, sizeof(buf),
"version: %s %cpgo %cime %cgraphemes %cfractional-scaling %cassertions",
"version: %s %cpgo %cime %cgraphemes %cfractional-scaling %ccursor-shape %cassertions",
FOOT_VERSION,
feature_pgo() ? '+' : '-',
feature_ime() ? '+' : '-',
feature_graphemes() ? '+' : '-',
feature_fractional_scaling() ? '+' : ':',
feature_cursor_shape() ? '+' : '-',
feature_assertions() ? '+' : '-');
return buf;
}

115
cursor-shape.c Normal file
View file

@ -0,0 +1,115 @@
#include <stdlib.h>
#include <string.h>
#define LOG_MODULE "cursor-shape"
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "cursor-shape.h"
#include "debug.h"
#include "util.h"
const char *
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",
};
xassert(shape <= ALEN(table));
xassert(table[shape] != NULL);
return table[shape];
}
#if defined(HAVE_CURSOR_SHAPE)
enum wp_cursor_shape_device_v1_shape
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,
[CURSOR_SHAPE_BOTTOM_RIGHT_CORNER] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE,
[CURSOR_SHAPE_LEFT_SIDE] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE,
[CURSOR_SHAPE_RIGHT_SIDE] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE,
[CURSOR_SHAPE_TOP_SIDE] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE,
[CURSOR_SHAPE_BOTTOM_SIDE] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE,
};
xassert(shape <= ALEN(table));
xassert(table[shape] != 0);
return table[shape];
}
enum wp_cursor_shape_device_v1_shape
cursor_string_to_server_shape(const char *xcursor)
{
if (xcursor == NULL)
return 0;
static const char *const table[][2] = {
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT] = {"default", "left_ptr"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CONTEXT_MENU] = {"context-menu"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP] = {"help", "question_arrow"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER] = {"pointer", "hand"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS] = {"progress", "left_ptr_watch"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT] = {"wait", "watch"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CELL] = {"cell"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR] = {"crosshair", "cross"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT] = {"text", "xterm"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_VERTICAL_TEXT] = {"vertical-text"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALIAS] = {"alias", "dnd-link"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY] = {"copy", "dnd-copy"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE] = {"move"}, /* dnd-move? */
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP] = {"no-drop", "dnd-no-drop"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NOT_ALLOWED] = {"not-allowed", "crossed_circle"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB] = {"grab", "hand1"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRABBING] = {"grabbing"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE] = {"e-resize", "right_side"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE] = {"n-resize", "top_side"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE] = {"ne-resize", "top_right_corner"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE] = {"nw-resize", "top_left_corner"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE] = {"s-resize", "bottom_side"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE] = {"se-resize", "bottom_right_corner"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE] = {"sw-resize", "bottom_left_corner"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE] = {"w-resize", "left_side"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE] = {"ew-resize", "sb_h_double_arrow"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE] = {"ns-resize", "sb_v_double_arrow"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE] = {"nesw-resize", "fd_double_arrow"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE] = {"nwse-resize", "bd_double_arrow"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COL_RESIZE] = {"col-resize", "sb_h_double_arrow"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ROW_RESIZE] = {"row-resize", "sb_v_double_arrow"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_SCROLL] = {"all-scroll", "fleur"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_IN] = {"zoom-in"},
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT] = {"zoom-out"},
};
for (size_t i = 0; i < ALEN(table); i++) {
for (size_t j = 0; j < ALEN(table[i]); j++) {
if (table[i][j] != NULL && strcmp(xcursor, table[i][j]) == 0) {
return i;
}
}
}
return 0;
}
#endif /* HAVE_CURSOR_SHAPE */

34
cursor-shape.h Normal file
View file

@ -0,0 +1,34 @@
#pragma once
#if defined(HAVE_CURSOR_SHAPE)
#include <cursor-shape-v1.h>
#endif
enum cursor_shape {
CURSOR_SHAPE_NONE,
CURSOR_SHAPE_CUSTOM,
CURSOR_SHAPE_HIDDEN,
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,
CURSOR_SHAPE_BOTTOM_RIGHT_CORNER,
CURSOR_SHAPE_LEFT_SIDE,
CURSOR_SHAPE_RIGHT_SIDE,
CURSOR_SHAPE_TOP_SIDE,
CURSOR_SHAPE_BOTTOM_SIDE,
CURSOR_SHAPE_COUNT,
};
const char *cursor_shape_to_string(enum cursor_shape shape);
#if defined(HAVE_CURSOR_SHAPE)
enum wp_cursor_shape_device_v1_shape cursor_shape_to_server_shape(
enum cursor_shape shape);
enum wp_cursor_shape_device_v1_shape cursor_string_to_server_shape(
const char *xcursor);
#endif

View file

@ -46,3 +46,12 @@ static inline bool feature_fractional_scaling(void)
return false;
#endif
}
static inline bool feature_cursor_shape(void)
{
#if defined(HAVE_CURSOR_SHAPE)
return true;
#else
return false;
#endif
}

22
input.c
View file

@ -1704,20 +1704,20 @@ is_bottom_right(const struct terminal *term, int x, int y)
(term->active_surface == TERM_SURF_BORDER_BOTTOM && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale)));
}
const char *
enum cursor_shape
xcursor_for_csd_border(struct terminal *term, int x, int y)
{
if (is_top_left(term, x, y)) return XCURSOR_TOP_LEFT_CORNER;
else if (is_top_right(term, x, y)) return XCURSOR_TOP_RIGHT_CORNER;
else if (is_bottom_left(term, x, y)) return XCURSOR_BOTTOM_LEFT_CORNER;
else if (is_bottom_right(term, x, y)) return XCURSOR_BOTTOM_RIGHT_CORNER;
else if (term->active_surface == TERM_SURF_BORDER_LEFT) return XCURSOR_LEFT_SIDE;
else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return XCURSOR_RIGHT_SIDE;
else if (term->active_surface == TERM_SURF_BORDER_TOP) return XCURSOR_TOP_SIDE;
else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return XCURSOR_BOTTOM_SIDE;
if (is_top_left(term, x, y)) return CURSOR_SHAPE_TOP_LEFT_CORNER;
else if (is_top_right(term, x, y)) return CURSOR_SHAPE_TOP_RIGHT_CORNER;
else if (is_bottom_left(term, x, y)) return CURSOR_SHAPE_BOTTOM_LEFT_CORNER;
else if (is_bottom_right(term, x, y)) return CURSOR_SHAPE_BOTTOM_RIGHT_CORNER;
else if (term->active_surface == TERM_SURF_BORDER_LEFT) return CURSOR_SHAPE_LEFT_SIDE;
else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return CURSOR_SHAPE_RIGHT_SIDE;
else if (term->active_surface == TERM_SURF_BORDER_TOP) return CURSOR_SHAPE_TOP_SIDE;
else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return CURSOR_SHAPE_BOTTOM_SIDE;
else {
BUG("Unreachable");
return NULL;
return CURSOR_SHAPE_NONE;
}
}
@ -1819,7 +1819,7 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
}
/* Reset last-set-xcursor, to ensure we update it on a pointer-enter event */
seat->pointer.xcursor = NULL;
seat->pointer.shape = CURSOR_SHAPE_NONE;
/* Reset mouse state */
seat->mouse.x = seat->mouse.y = 0;

View file

@ -3,8 +3,9 @@
#include <stdint.h>
#include <wayland-client.h>
#include "wayland.h"
#include "cursor-shape.h"
#include "misc.h"
#include "wayland.h"
/*
* Custom defines for mouse wheel left/right buttons.
@ -33,4 +34,4 @@ void get_current_modifiers(const struct seat *seat,
xkb_mod_mask_t *consumed,
uint32_t key);
const char *xcursor_for_csd_border(struct terminal *term, int x, int y);
enum cursor_shape xcursor_for_csd_border(struct terminal *term, int x, int y);

3
main.c
View file

@ -53,12 +53,13 @@ version_and_features(void)
{
static char buf[256];
snprintf(buf, sizeof(buf),
"version: %s %cpgo %cime %cgraphemes %cfractional-scaling %cassertions",
"version: %s %cpgo %cime %cgraphemes %cfractional-scaling %ccursor-shape %cassertions",
FOOT_VERSION,
feature_pgo() ? '+' : '-',
feature_ime() ? '+' : '-',
feature_graphemes() ? '+' : '-',
feature_fractional_scaling() ? '+' : '-',
feature_cursor_shape() ? '+' : '-',
feature_assertions() ? '+' : '-');
return buf;
}

View file

@ -169,6 +169,16 @@ if wayland_protocols.version().version_compare('>=1.31')
else
fractional_scale = false
endif
if wayland_protocols.version().version_compare('>=1.32')
wl_proto_xml += [
wayland_protocols_datadir + '/unstable/tablet/tablet-unstable-v2.xml', # required by cursor-shape-v1
wayland_protocols_datadir + '/staging/cursor-shape/cursor-shape-v1.xml',
]
add_project_arguments('-DHAVE_CURSOR_SHAPE', language: 'c')
cursor_shape = true
else
cursor_shape = false
endif
foreach prot : wl_proto_xml
wl_proto_headers += custom_target(
@ -223,6 +233,7 @@ vtlib = static_library(
'vtlib',
'base64.c', 'base64.h',
'composed.c', 'composed.h',
'cursor-shape.c', 'cursor-shape.h',
'csi.c', 'csi.h',
'dcs.c', 'dcs.h',
'macros.h',
@ -380,6 +391,7 @@ summary(
'Grapheme clustering': utf8proc.found(),
'Wayland: xdg-activation-v1': xdg_activation,
'Wayland: fractional-scale-v1': fractional_scale,
'Wayland: cursor-shape-v1': cursor_shape,
'utmp backend': utmp_backend,
'utmp helper default path': utmp_default_helper_path,
'Build terminfo': tic.found(),

View file

@ -76,15 +76,15 @@ render_xcursor_is_valid(const struct seat *seat, const char *cursor)
}
bool
render_xcursor_set(struct seat *seat, struct terminal *term, const char *xcursor)
render_xcursor_set(struct seat *seat, struct terminal *term, enum cursor_shape shape)
{
return true;
}
const char *
enum cursor_shape
xcursor_for_csd_border(struct terminal *term, int x, int y)
{
return XCURSOR_LEFT_PTR;
return CURSOR_SHAPE_LEFT_PTR;
}
struct wl_window *

View file

@ -31,6 +31,7 @@
#include "box-drawing.h"
#include "char32.h"
#include "config.h"
#include "cursor-shape.h"
#include "grid.h"
#include "hsl.h"
#include "ime.h"
@ -4254,26 +4255,61 @@ render_xcursor_update(struct seat *seat)
if (!seat->mouse_focus)
return;
xassert(seat->pointer.xcursor != NULL);
xassert(seat->pointer.shape != CURSOR_SHAPE_NONE);
if (seat->pointer.xcursor == XCURSOR_HIDDEN) {
if (seat->pointer.shape == CURSOR_SHAPE_HIDDEN) {
/* Hide cursor */
LOG_DBG("hiding cursor using client-side NULL-surface");
wl_surface_attach(seat->pointer.surface.surf, NULL, 0, 0);
wl_pointer_set_cursor(
seat->wl_pointer, seat->pointer.serial, seat->pointer.surface.surf,
0, 0);
wl_surface_commit(seat->pointer.surface.surf);
return;
}
xassert(seat->pointer.cursor != NULL);
const float scale = seat->pointer.scale;
#if defined(HAVE_CURSOR_SHAPE)
const enum cursor_shape shape = seat->pointer.shape;
const char *const xcursor = seat->pointer.last_custom_xcursor;
if (seat->pointer.shape_device != NULL) {
xassert(shape != CURSOR_SHAPE_CUSTOM || xcursor != NULL);
const enum wp_cursor_shape_device_v1_shape custom_shape =
(shape == CURSOR_SHAPE_CUSTOM && xcursor != NULL
? cursor_string_to_server_shape(xcursor)
: 0);
if (shape != CURSOR_SHAPE_CUSTOM || custom_shape != 0) {
xassert(custom_shape == 0 || shape == CURSOR_SHAPE_CUSTOM);
const enum wp_cursor_shape_device_v1_shape wp_shape = custom_shape != 0
? custom_shape
: cursor_shape_to_server_shape(shape);
LOG_DBG("setting %scursor shape using cursor-shape-v1",
custom_shape != 0 ? "custom " : "");
wp_cursor_shape_device_v1_set_shape(
seat->pointer.shape_device,
seat->pointer.serial,
wp_shape);
return;
}
}
#endif
LOG_DBG("setting %scursor shape using a client-side cursor surface",
shape == CURSOR_SHAPE_CUSTOM ? "custom " : "");
const int scale = seat->pointer.scale;
struct wl_cursor_image *image = seat->pointer.cursor->images[0];
struct wl_buffer *buf = wl_cursor_image_get_buffer(image);
wayl_surface_scale_explicit_width_height(
seat->mouse_focus->window,
&seat->pointer.surface, image->width, image->height, scale);
wl_surface_attach(seat->pointer.surface.surf, buf, 0, 0);
wl_surface_attach(
seat->pointer.surface.surf, wl_cursor_image_get_buffer(image), 0, 0);
wl_pointer_set_cursor(
seat->wl_pointer, seat->pointer.serial,
@ -4283,6 +4319,8 @@ render_xcursor_update(struct seat *seat)
wl_surface_damage_buffer(
seat->pointer.surface.surf, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_set_buffer_scale(seat->pointer.surface.surf, scale);
xassert(seat->pointer.xcursor_callback == NULL);
seat->pointer.xcursor_callback = wl_surface_frame(seat->pointer.surface.surf);
wl_callback_add_listener(seat->pointer.xcursor_callback, &xcursor_listener, seat);
@ -4434,13 +4472,14 @@ render_refresh_urls(struct terminal *term)
}
bool
render_xcursor_set(struct seat *seat, struct terminal *term, const char *xcursor)
render_xcursor_set(struct seat *seat, struct terminal *term,
enum cursor_shape shape)
{
if (seat->pointer.theme == NULL)
return false;
if (seat->mouse_focus == NULL) {
seat->pointer.xcursor = NULL;
seat->pointer.shape = CURSOR_SHAPE_NONE;
return true;
}
@ -4449,26 +4488,48 @@ render_xcursor_set(struct seat *seat, struct terminal *term, const char *xcursor
return true;
}
if (seat->pointer.xcursor == xcursor)
if (seat->pointer.shape == shape &&
!(shape == CURSOR_SHAPE_CUSTOM &&
strcmp(seat->pointer.last_custom_xcursor,
term->mouse_user_cursor) != 0))
{
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);
if (xcursor != XCURSOR_HIDDEN) {
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, XCURSOR_TEXT_FALLBACK );
seat->pointer.theme, fallback);
if (seat->pointer.cursor == NULL) {
LOG_ERR("failed to load xcursor pointer '%s', and fallback '%s'", xcursor, XCURSOR_TEXT_FALLBACK);
LOG_ERR("failed to load xcursor pointer "
"'%s', and fallback '%s'", xcursor, fallback);
return false;
}
}
} else
if (shape == CURSOR_SHAPE_CUSTOM) {
free(seat->pointer.last_custom_xcursor);
seat->pointer.last_custom_xcursor = xstrdup(term->mouse_user_cursor);
}
} else {
seat->pointer.cursor = NULL;
free(seat->pointer.last_custom_xcursor);
seat->pointer.last_custom_xcursor = NULL;
}
/* FDM hook takes care of actual rendering */
seat->pointer.xcursor = xcursor;
seat->pointer.shape = shape;
seat->pointer.xcursor_pending = true;
return true;
}

View file

@ -19,7 +19,7 @@ void render_refresh_search(struct terminal *term);
void render_refresh_title(struct terminal *term);
void render_refresh_urls(struct terminal *term);
bool render_xcursor_set(
struct seat *seat, struct terminal *term, const char *xcursor);
struct seat *seat, struct terminal *term, enum cursor_shape shape);
bool render_xcursor_is_valid(const struct seat *seat, const char *cursor);
struct render_worker_context {

View file

@ -47,20 +47,6 @@
#define PTMX_TIMING 0
const char *const XCURSOR_HIDDEN = "hidden";
const char *const XCURSOR_LEFT_PTR = "left_ptr";
const char *const XCURSOR_TEXT = "text";
const char *const XCURSOR_TEXT_FALLBACK = "xterm";
//const char *const XCURSOR_HAND2 = "hand2";
const char *const XCURSOR_TOP_LEFT_CORNER = "top_left_corner";
const char *const XCURSOR_TOP_RIGHT_CORNER = "top_right_corner";
const char *const XCURSOR_BOTTOM_LEFT_CORNER = "bottom_left_corner";
const char *const XCURSOR_BOTTOM_RIGHT_CORNER = "bottom_right_corner";
const char *const XCURSOR_LEFT_SIDE = "left_side";
const char *const XCURSOR_RIGHT_SIDE = "right_side";
const char *const XCURSOR_TOP_SIDE = "top_side";
const char *const XCURSOR_BOTTOM_SIDE = "bottom_side";
static void
enqueue_data_for_slave(const void *data, size_t len, size_t offset,
ptmx_buffer_list_t *buffer_list)
@ -3137,44 +3123,56 @@ term_mouse_motion(struct terminal *term, int button, int row, int col,
void
term_xcursor_update_for_seat(struct terminal *term, struct seat *seat)
{
const char *xcursor = NULL;
enum cursor_shape shape = CURSOR_SHAPE_NONE;
switch (term->active_surface) {
case TERM_SURF_GRID: {
bool have_custom_cursor =
render_xcursor_is_valid(seat, term->mouse_user_cursor);
case TERM_SURF_GRID:
if (seat->pointer.hidden)
shape = CURSOR_SHAPE_HIDDEN;
xcursor = seat->pointer.hidden ? XCURSOR_HIDDEN
: have_custom_cursor ? term->mouse_user_cursor
: term->is_searching ? XCURSOR_LEFT_PTR
: (seat->mouse.col >= 0 &&
seat->mouse.row >= 0 &&
term_mouse_grabbed(term, seat)) ? XCURSOR_TEXT
: XCURSOR_LEFT_PTR;
#if defined(HAVE_CURSOR_SHAPE)
else if (cursor_string_to_server_shape(term->mouse_user_cursor) != 0
#else
else if (false
#endif
|| render_xcursor_is_valid(seat, term->mouse_user_cursor))
{
shape = CURSOR_SHAPE_CUSTOM;
}
else if (seat->mouse.col >= 0 &&
seat->mouse.row >= 0 &&
term_mouse_grabbed(term, seat))
{
shape = CURSOR_SHAPE_TEXT;
}
else
shape = CURSOR_SHAPE_LEFT_PTR;
break;
}
case TERM_SURF_TITLE:
case TERM_SURF_BUTTON_MINIMIZE:
case TERM_SURF_BUTTON_MAXIMIZE:
case TERM_SURF_BUTTON_CLOSE:
xcursor = XCURSOR_LEFT_PTR;
shape = CURSOR_SHAPE_LEFT_PTR;
break;
case TERM_SURF_BORDER_LEFT:
case TERM_SURF_BORDER_RIGHT:
case TERM_SURF_BORDER_TOP:
case TERM_SURF_BORDER_BOTTOM:
xcursor = xcursor_for_csd_border(term, seat->mouse.x, seat->mouse.y);
shape = xcursor_for_csd_border(term, seat->mouse.x, seat->mouse.y);
break;
case TERM_SURF_NONE:
return;
}
if (xcursor == NULL)
if (shape == CURSOR_SHAPE_NONE)
BUG("xcursor not set");
render_xcursor_set(seat, term, xcursor);
render_xcursor_set(seat, term, shape);
}
void
@ -3725,6 +3723,8 @@ void
term_set_user_mouse_cursor(struct terminal *term, const char *cursor)
{
free(term->mouse_user_cursor);
term->mouse_user_cursor = cursor != NULL ? xstrdup(cursor) : NULL;
term->mouse_user_cursor = cursor != NULL && strlen(cursor) > 0
? xstrdup(cursor)
: NULL;
term_xcursor_update(term);
}

View file

@ -718,20 +718,6 @@ struct terminal {
char *cwd;
};
extern const char *const XCURSOR_HIDDEN;
extern const char *const XCURSOR_LEFT_PTR;
extern const char *const XCURSOR_TEXT;
extern const char *const XCURSOR_TEXT_FALLBACK;
//extern const char *const XCURSOR_HAND2;
extern const char *const XCURSOR_TOP_LEFT_CORNER;
extern const char *const XCURSOR_TOP_RIGHT_CORNER;
extern const char *const XCURSOR_BOTTOM_LEFT_CORNER;
extern const char *const XCURSOR_BOTTOM_RIGHT_CORNER;
extern const char *const XCURSOR_LEFT_SIDE;
extern const char *const XCURSOR_RIGHT_SIDE;
extern const char *const XCURSOR_TOP_SIDE;
extern const char *const XCURSOR_BOTTOM_SIDE;
struct config;
struct terminal *term_init(
const struct config *conf, struct fdm *fdm, struct reaper *reaper,

View file

@ -14,6 +14,10 @@
#include <wayland-cursor.h>
#include <xkbcommon/xkbcommon-compose.h>
#if defined(HAVE_CURSOR_SHAPE)
#include <cursor-shape-v1.h>
#endif
#include <tllist.h>
#define LOG_MODULE "wayland"
@ -209,6 +213,11 @@ seat_destroy(struct seat *seat)
if (seat->data_device != NULL)
wl_data_device_release(seat->data_device);
#if defined(HAVE_CURSOR_SHAPE)
if (seat->pointer.shape_device != NULL)
wp_cursor_shape_device_v1_destroy(seat->pointer.shape_device);
#endif
if (seat->wl_keyboard != NULL)
wl_keyboard_release(seat->wl_keyboard);
if (seat->wl_pointer != NULL)
@ -225,6 +234,7 @@ seat_destroy(struct seat *seat)
ime_reset_pending(seat);
free(seat->clipboard.text);
free(seat->primary.text);
free(seat->pointer.last_custom_xcursor);
free(seat->name);
}
@ -316,9 +326,22 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
seat->wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
#if defined(HAVE_CURSOR_SHAPE)
if (seat->wayl->cursor_shape_manager != NULL) {
xassert(seat->pointer.shape_device == NULL);
seat->pointer.shape_device = wp_cursor_shape_manager_v1_get_pointer(
seat->wayl->cursor_shape_manager, seat->wl_pointer);
}
#endif
}
} else {
if (seat->wl_pointer != NULL) {
#if defined(HAVE_CURSOR_SHAPE)
wp_cursor_shape_device_v1_destroy(seat->pointer.shape_device);
seat->pointer.shape_device = NULL;
#endif
wl_pointer_release(seat->wl_pointer);
wl_surface_destroy(seat->pointer.surface.surf);
@ -1167,6 +1190,17 @@ handle_global(void *data, struct wl_registry *registry,
}
#endif
#if defined(HAVE_CURSOR_SHAPE)
else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
const uint32_t required = 1;
if (!verify_iface_version(interface, version, required))
return;
wayl->cursor_shape_manager = wl_registry_bind(
wayl->registry, name, &wp_cursor_shape_manager_v1_interface, required);
}
#endif
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
const uint32_t required = 1;
@ -1401,6 +1435,15 @@ wayl_init(struct fdm *fdm, struct key_binding_manager *key_binding_manager,
LOG_WARN("fractional scaling not available");
}
#if defined(HAVE_CURSOR_SHAPE)
if (wayl->cursor_shape_manager == NULL) {
#else
if (true) {
#endif
LOG_WARN("no server-side cursors available, "
"falling back to client-side cursors");
}
if (presentation_timings && wayl->presentation == NULL) {
LOG_ERR("presentation time interface not implemented by compositor");
goto out;
@ -1495,6 +1538,10 @@ wayl_destroy(struct wayland *wayl)
if (wayl->viewporter != NULL)
wp_viewporter_destroy(wayl->viewporter);
#endif
#if defined(HAVE_CURSOR_SHAPE)
if (wayl->cursor_shape_manager != NULL)
wp_cursor_shape_manager_v1_destroy(wayl->cursor_shape_manager);
#endif
#if defined(HAVE_XDG_ACTIVATION)
if (wayl->xdg_activation != NULL)
xdg_activation_v1_destroy(wayl->xdg_activation);

View file

@ -28,6 +28,7 @@
#include <fcft/fcft.h>
#include <tllist.h>
#include "cursor-shape.h"
#include "fdm.h"
/* Forward declarations */
@ -145,13 +146,21 @@ struct seat {
struct {
uint32_t serial;
/* Client-side cursor */
struct wayl_surface surface;
struct wl_cursor_theme *theme;
struct wl_cursor *cursor;
/* Server-side cursor */
#if defined(HAVE_CURSOR_SHAPE)
struct wp_cursor_shape_device_v1 *shape_device;
#endif
float scale;
bool hidden;
enum cursor_shape shape;
char *last_custom_xcursor;
const char *xcursor;
struct wl_callback *xcursor_callback;
bool xcursor_pending;
} pointer;
@ -425,6 +434,10 @@ struct wayland {
struct xdg_activation_v1 *xdg_activation;
#endif
#if defined(HAVE_CURSOR_SHAPE)
struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
#endif
bool presentation_timings;
struct wp_presentation *presentation;
uint32_t presentation_clock_id;