#include "internal.h" #include #include #include #define SCROLL_PX_PER_LINE 10.0 struct tablet_tool { struct tablet_tool *next; struct seat *seat; struct zwp_tablet_tool_v2 *proxy; struct wp_cursor_shape_device_v1 *shape_dev; uint32_t proximity_serial; bool eraser; }; struct tablet_device { struct tablet_device *next; struct zwp_tablet_v2 *proxy; }; struct tablet_pad { struct tablet_pad *next; struct zwp_tablet_pad_v2 *proxy; }; static inline int device_px_round(double v) { return (int)lrint(v); } static struct yasl_vector2i device_from_fixed(const struct yasl_surface *w, wl_fixed_t x, wl_fixed_t y) { return (struct yasl_vector2i){ .x = device_px_round(wl_fixed_to_double(x) * w->scale_factor), .y = device_px_round(wl_fixed_to_double(y) * w->scale_factor), }; } static inline int motion_button(uint64_t buttons) { static const uint8_t motion_btn_lut[256] = { [0] = 3, [1] = 0, [2] = 1, [3] = 0, [4] = 2, [5] = 0, [6] = 1, [7] = 0, [8] = 3, [9] = 0, [10] = 1, [11] = 0, [12] = 2, [13] = 0, [14] = 1, [15] = 0, [16] = 4, [17] = 0, [18] = 1, [19] = 0, [20] = 2, [21] = 0, [22] = 1, [23] = 0, [24] = 3, [25] = 0, [26] = 1, [27] = 0, [28] = 2, [29] = 0, [30] = 1, [31] = 0, [32] = 5, [33] = 0, [34] = 1, [35] = 0, [36] = 2, [37] = 0, [38] = 1, [39] = 0, [40] = 3, [41] = 0, [42] = 1, [43] = 0, [44] = 2, [45] = 0, [46] = 1, [47] = 0, [48] = 4, [49] = 0, [50] = 1, [51] = 0, [52] = 2, [53] = 0, [54] = 1, [55] = 0, [56] = 3, [57] = 0, [58] = 1, [59] = 0, [60] = 2, [61] = 0, [62] = 1, [63] = 0, [64] = 6, [65] = 0, [66] = 1, [67] = 0, [68] = 2, [69] = 0, [70] = 1, [71] = 0, [72] = 3, [73] = 0, [74] = 1, [75] = 0, [76] = 2, [77] = 0, [78] = 1, [79] = 0, [80] = 4, [81] = 0, [82] = 1, [83] = 0, [84] = 2, [85] = 0, [86] = 1, [87] = 0, [88] = 3, [89] = 0, [90] = 1, [91] = 0, [92] = 2, [93] = 0, [94] = 1, [95] = 0, [96] = 5, [97] = 0, [98] = 1, [99] = 0, [100] = 2, [101] = 0, [102] = 1, [103] = 0, [104] = 3, [105] = 0, [106] = 1, [107] = 0, [108] = 2, [109] = 0, [110] = 1, [111] = 0, [112] = 4, [113] = 0, [114] = 1, [115] = 0, [116] = 2, [117] = 0, [118] = 1, [119] = 0, [120] = 3, [121] = 0, [122] = 1, [123] = 0, [124] = 2, [125] = 0, [126] = 1, [127] = 0, [128] = 7, [129] = 0, [130] = 1, [131] = 0, [132] = 2, [133] = 0, [134] = 1, [135] = 0, [136] = 3, [137] = 0, [138] = 1, [139] = 0, [140] = 2, [141] = 0, [142] = 1, [143] = 0, [144] = 4, [145] = 0, [146] = 1, [147] = 0, [148] = 2, [149] = 0, [150] = 1, [151] = 0, [152] = 3, [153] = 0, [154] = 1, [155] = 0, [156] = 2, [157] = 0, [158] = 1, [159] = 0, [160] = 5, [161] = 0, [162] = 1, [163] = 0, [164] = 2, [165] = 0, [166] = 1, [167] = 0, [168] = 3, [169] = 0, [170] = 1, [171] = 0, [172] = 2, [173] = 0, [174] = 1, [175] = 0, [176] = 4, [177] = 0, [178] = 1, [179] = 0, [180] = 2, [181] = 0, [182] = 1, [183] = 0, [184] = 3, [185] = 0, [186] = 1, [187] = 0, [188] = 2, [189] = 0, [190] = 1, [191] = 0, [192] = 6, [193] = 0, [194] = 1, [195] = 0, [196] = 2, [197] = 0, [198] = 1, [199] = 0, [200] = 3, [201] = 0, [202] = 1, [203] = 0, [204] = 2, [205] = 0, [206] = 1, [207] = 0, [208] = 4, [209] = 0, [210] = 1, [211] = 0, [212] = 2, [213] = 0, [214] = 1, [215] = 0, [216] = 3, [217] = 0, [218] = 1, [219] = 0, [220] = 2, [221] = 0, [222] = 1, [223] = 0, [224] = 5, [225] = 0, [226] = 1, [227] = 0, [228] = 2, [229] = 0, [230] = 1, [231] = 0, [232] = 3, [233] = 0, [234] = 1, [235] = 0, [236] = 2, [237] = 0, [238] = 1, [239] = 0, [240] = 4, [241] = 0, [242] = 1, [243] = 0, [244] = 2, [245] = 0, [246] = 1, [247] = 0, [248] = 3, [249] = 0, [250] = 1, [251] = 0, [252] = 2, [253] = 0, [254] = 1, [255] = 0, }; return motion_btn_lut[(uint8_t)buttons]; } void seat_note_input(struct seat *s, uint32_t serial) { s->input_serial = serial; s->conn->active_seat = s; } struct seat *conn_active_seat(const struct yasl_display *c) { return c->active_seat ? c->active_seat : c->seats; } static void push_mouse(struct seat *s, struct yasl_surface *w, struct yasl_mouse_event ev) { ev.mods = (unsigned)seat_modifiers(s); struct yasl_input_event input_ev = { .kind = YASL_INPUT_MOUSE, .surface = w, .mouse = ev, }; input_queue_push(&surface_root(w)->input, &input_ev); } static void report_mouse(struct seat *s, struct yasl_surface *w, struct yasl_vector2i pos, int button, enum yasl_pointer_kind kind) { push_mouse(s, w, (struct yasl_mouse_event){ .pos = pos, .button = button, .kind = kind, }); } static void report_wheel(struct seat *s, struct yasl_surface *w, struct yasl_vector2i pos, int button, int lines, float delta) { push_mouse(s, w, (struct yasl_mouse_event){ .pos = pos, .button = button, .kind = YASL_PTR_SCROLL, .scroll_lines = lines, .scroll_delta = delta, }); } static bool cursor_theme_for(struct yasl_display *c, int scale) { if (c->cursor_theme && c->cursor_theme_scale == scale) return true; struct wl_cursor_theme *theme = wl_cursor_theme_load(c->cursor_theme_name, c->cursor_size * scale, c->shm); if (!theme) return c->cursor_theme != NULL; if (c->cursor_theme) wl_cursor_theme_destroy(c->cursor_theme); c->cursor_theme = theme; c->cursor_theme_scale = scale; return true; } static bool seat_cursor_surface(struct seat *s) { struct yasl_display *c = s->conn; if (s->cursor_surface) return true; s->cursor_surface = wl_compositor_create_surface(c->compositor); if (!s->cursor_surface) return false; if (c->viewporter) s->cursor_viewport = wp_viewporter_get_viewport(c->viewporter, s->cursor_surface); return true; } static void set_cursor_theme_fallback(struct seat *s, struct yasl_surface *w, enum yasl_cursor shape) { struct yasl_display *c = s->conn; int scale = (int)((w->scale_120 + YASL_SCALE_UNIT - 1) / YASL_SCALE_UNIT); if (scale < 1) scale = 1; if (!seat_cursor_surface(s) || !cursor_theme_for(c, scale)) return; static const char *names[][2] = { {"default", "left_ptr"}, {"text", "xterm"}, {"pointer", "hand2"}, {"crosshair", "crosshair"}, {"move", "move"}, {"nwse-resize", "nw-resize"}, {"nesw-resize", "ne-resize"}, {"ew-resize", "ew-resize"}, {"ns-resize", "ns-resize"}, }; int idx = shape < YASL_CURSOR_NONE ? (int)shape : 0; struct wl_cursor *cursor = wl_cursor_theme_get_cursor(c->cursor_theme, names[idx][0]); if (!cursor) cursor = wl_cursor_theme_get_cursor(c->cursor_theme, names[idx][1]); if (!cursor || !cursor->image_count) return; struct wl_cursor_image *img = cursor->images[0]; struct wl_buffer *buffer = wl_cursor_image_get_buffer(img); if (!buffer) return; int theme_scale = c->cursor_theme_scale; if (s->cursor_viewport) { wp_viewport_set_destination(s->cursor_viewport, logical_px_ceil((int)img->width, (uint32_t)theme_scale * YASL_SCALE_UNIT), logical_px_ceil((int)img->height, (uint32_t)theme_scale * YASL_SCALE_UNIT)); } else { if (img->width % (uint32_t)theme_scale || img->height % (uint32_t)theme_scale) theme_scale = 1; wl_surface_set_buffer_scale(s->cursor_surface, theme_scale); } int shown = s->cursor_viewport ? c->cursor_theme_scale : theme_scale; wl_pointer_set_cursor(s->pointer, s->pointer_serial, s->cursor_surface, (int32_t)img->hotspot_x / shown, (int32_t)img->hotspot_y / shown); wl_surface_attach(s->cursor_surface, buffer, 0, 0); wl_surface_damage_buffer(s->cursor_surface, 0, 0, (int32_t)img->width, (int32_t)img->height); wl_surface_commit(s->cursor_surface); } static uint32_t cursor_shape_of(enum yasl_cursor shape) { switch (shape) { case YASL_CURSOR_TEXT: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT; case YASL_CURSOR_LINK: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER; case YASL_CURSOR_CROSSHAIR: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR; case YASL_CURSOR_MOVE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE; case YASL_CURSOR_RESIZE_EW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE; case YASL_CURSOR_RESIZE_NS: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE; case YASL_CURSOR_RESIZE_NWSE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE; case YASL_CURSOR_RESIZE_NESW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE; default: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; } } static void seat_set_cursor(struct seat *s, struct yasl_surface *w, enum yasl_cursor shape) { if (!s->pointer || !s->pointer_serial || s->pointer_focus != w || s->cursor_shape == (int)shape) return; if (shape == YASL_CURSOR_NONE) wl_pointer_set_cursor(s->pointer, s->pointer_serial, NULL, 0, 0); else if (s->cursor_shape_dev) wp_cursor_shape_device_v1_set_shape(s->cursor_shape_dev, s->pointer_serial, cursor_shape_of(shape)); else set_cursor_theme_fallback(s, w, shape); s->cursor_shape = shape; } static void tool_set_cursor(struct tablet_tool *t, enum yasl_cursor shape) { if (!t->proximity_serial) return; if (shape == YASL_CURSOR_NONE) zwp_tablet_tool_v2_set_cursor(t->proxy, t->proximity_serial, NULL, 0, 0); else if (t->shape_dev) wp_cursor_shape_device_v1_set_shape(t->shape_dev, t->proximity_serial, cursor_shape_of(shape)); } void yasl_surface_set_cursor(struct yasl_surface *w, enum yasl_cursor shape) { if (!w || !w->conn) return; for (struct seat *s = w->conn->seats; s; s = s->next) { seat_set_cursor(s, w, shape); if (s->tool && s->tablet_focus == w) tool_set_cursor(s->tool, shape); } } static void press_queue_flush(struct seat *s, struct press_queue *q, struct yasl_surface *only, uint64_t *buttons, struct yasl_mouse_event like) { int kept = 0; for (int i = 0; i < q->n; i++) { struct pending_press p = q->ev[i]; if (only && p.surface != only) { q->ev[kept++] = p; continue; } if (!p.surface) continue; uint64_t mask = 1ull << p.button; *buttons = (*buttons & ~mask) | ((uint64_t)(!p.release) << p.button); struct yasl_mouse_event ev = like; ev.pos = p.pos; ev.button = p.button; ev.kind = (enum yasl_pointer_kind)p.release; push_mouse(s, p.surface, ev); } q->n = kept; } static void press_queue_push(struct press_queue *q, struct yasl_surface *w, struct yasl_vector2i pos, int button, bool release) { if (q->n == (int)(sizeof(q->ev) / sizeof(q->ev[0]))) { memmove(q->ev, q->ev + 1, sizeof(q->ev[0]) * (size_t)(q->n - 1)); q->n--; } q->ev[q->n++] = (struct pending_press){ .surface = w, .pos = pos, .button = button, .release = release, }; } static void press_queue_forget(struct press_queue *q, struct yasl_surface *w) { for (int i = 0; i < q->n; i++) if (q->ev[i].surface == w) q->ev[i].surface = NULL; } void seat_pointer_reset(struct seat *s) { s->pointer_buttons = 0; s->pointer_serial = 0; s->motion_pending = false; s->axis_v = s->axis_h = s->frame_v = 0; s->frame_v120 = 0; s->axis_discrete_v = s->axis_discrete_h = 0; s->axis_value120_v = s->axis_value120_h = 0; s->axis_has_v120_v = s->axis_has_v120_h = false; s->axis_smooth = false; s->cursor_shape = -1; } static void pointer_frame(void *data, struct wl_pointer *pointer); static void pointer_event_done(struct seat *s) { if (s->pointer && wl_pointer_get_version(s->pointer) < WL_POINTER_FRAME_SINCE_VERSION) pointer_frame(s, s->pointer); } static void flush_pending_motion(struct seat *s, struct yasl_surface *w) { if (!s->motion_pending) return; s->motion_pending = false; report_mouse(s, w, s->pointer_pos, motion_button(s->pointer_buttons), YASL_PTR_MOTION); } static void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { (void)pointer; struct seat *s = data; struct yasl_surface *w = conn_surface_for(s->conn, surface); s->pointer_focus = w; if (!w) return; s->pointer_pos = device_from_fixed(w, surface_x, surface_y); s->pointer_serial = serial; s->cursor_shape = -1; seat_set_cursor(s, w, w->parent ? YASL_CURSOR_ARROW : YASL_CURSOR_TEXT); s->motion_pending = true; pointer_event_done(s); } static void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { (void)pointer; (void)serial; (void)surface; struct seat *s = data; struct yasl_surface *w = s->pointer_focus; s->pointer_focus = NULL; if (!w) return; if (s->ptr_left) pointer_frame(s, s->pointer); s->ptr_left = w; s->ptr_left_pos = s->pointer_pos; s->ptr_left_motion = s->motion_pending; s->motion_pending = false; pointer_event_done(s); } static void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { (void)pointer; (void)time; struct seat *s = data; struct yasl_surface *w = s->pointer_focus; if (!w) return; s->pointer_pos = device_from_fixed(w, surface_x, surface_y); s->motion_pending = true; pointer_event_done(s); } static void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { (void)pointer; (void)time; struct seat *s = data; struct yasl_surface *w = s->pointer_focus; if (unlikely(!w)) return; static const int8_t btn_map[8] = { [0] = 0, /* BTN_LEFT (0x110) */ [1] = 2, /* BTN_RIGHT (0x111) */ [2] = 1, /* BTN_MIDDLE (0x112) */ [3] = 3, /* BTN_SIDE (0x113) */ [4] = 4, /* BTN_EXTRA (0x114) */ [5] = 4, /* BTN_FORWARD (0x115) */ [6] = 3, /* BTN_BACK (0x116) */ [7] = -1, }; unsigned idx = button - 0x110; if (unlikely(idx >= 7)) return; int b = btn_map[idx]; if (unlikely(b < 0)) return; seat_note_input(s, serial); bool release = (state == WL_POINTER_BUTTON_STATE_RELEASED); if (!release) { s->press_serial = serial; s->press_surface = w; } press_queue_push(&s->ptr_presses, w, s->pointer_pos, b, release); pointer_event_done(s); } static void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { (void)pointer; (void)time; struct seat *s = data; if (unlikely(!s->pointer_focus)) return; double v = wl_fixed_to_double(value); if (likely(axis < 2)) { (&s->axis_v)[axis] += v; s->frame_v += v * (!axis); } pointer_event_done(s); } static void pointer_axis_source(void *data, struct wl_pointer *pointer, uint32_t axis_source) { (void)pointer; struct seat *s = data; s->axis_smooth = (axis_source == WL_POINTER_AXIS_SOURCE_FINGER || axis_source == WL_POINTER_AXIS_SOURCE_CONTINUOUS); } static void pointer_axis_stop(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis) { (void)pointer; (void)time; struct seat *s = data; if (likely(axis < 2)) { (&s->axis_value120_v)[axis] = 0; (&s->axis_has_v120_v)[axis] = false; } } static void pointer_axis_discrete(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t discrete) { (void)pointer; struct seat *s = data; if (likely(axis < 2)) (&s->axis_discrete_v)[axis] += discrete; } static void pointer_axis_value120(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t value120) { (void)pointer; struct seat *s = data; if (likely(axis < 2)) { (&s->axis_value120_v)[axis] += value120; (&s->axis_has_v120_v)[axis] = true; s->frame_v120 += value120 * (!axis); } } static void pointer_frame_vertical(struct seat *s, struct yasl_surface *w) { if (s->axis_has_v120_v) { int discrete = s->axis_value120_v / 120; s->axis_value120_v -= discrete * 120; float delta = -(float)s->frame_v120 / 120.0f; int btn = discrete ? (discrete < 0 ? 4 : 5) : (delta > 0 ? 4 : 5); if (discrete || delta != 0.0f) report_wheel(s, w, s->pointer_pos, btn, -discrete, delta); s->axis_v = 0; } else if (s->axis_discrete_v) { report_wheel(s, w, s->pointer_pos, s->axis_discrete_v > 0 ? 5 : 4, -s->axis_discrete_v, 0.0f); s->axis_v = 0; } else if (s->frame_v != 0.0) { int lines = -(int)(s->axis_v / SCROLL_PX_PER_LINE); s->axis_v += (double)lines * SCROLL_PX_PER_LINE; float delta = (float)(-s->frame_v / SCROLL_PX_PER_LINE); report_wheel(s, w, s->pointer_pos, delta > 0 ? 4 : 5, lines, delta); } s->frame_v = 0; s->frame_v120 = 0; } static void pointer_frame_horizontal(struct seat *s, struct yasl_surface *w) { if (s->axis_has_v120_h) { int discrete = s->axis_value120_h / 120; s->axis_value120_h -= discrete * 120; if (discrete) report_wheel(s, w, s->pointer_pos, discrete > 0 ? 7 : 6, discrete, 0.0f); s->axis_h = 0; } else if (s->axis_discrete_h) { report_wheel(s, w, s->pointer_pos, s->axis_discrete_h > 0 ? 7 : 6, s->axis_discrete_h, 0.0f); s->axis_h = 0; } else if (s->axis_h) { int steps = (int)(s->axis_h / SCROLL_PX_PER_LINE); if (steps != 0) { s->axis_h -= (double)steps * SCROLL_PX_PER_LINE; report_wheel(s, w, s->pointer_pos, steps > 0 ? 7 : 6, steps, 0.0f); } } } static void pointer_frame(void *data, struct wl_pointer *pointer) { (void)pointer; struct seat *s = data; struct yasl_surface *left = s->ptr_left; if (left) { s->ptr_left = NULL; if (s->ptr_left_motion) report_mouse(s, left, s->ptr_left_pos, motion_button(s->pointer_buttons), YASL_PTR_MOTION); press_queue_flush(s, &s->ptr_presses, left, &s->pointer_buttons, (struct yasl_mouse_event){0}); report_mouse(s, left, s->ptr_left_pos, 3, YASL_PTR_LEAVE); struct yasl_vector2i pos = s->pointer_pos; bool motion = s->motion_pending; uint32_t serial = s->pointer_focus ? s->pointer_serial : 0; int shape = s->pointer_focus ? s->cursor_shape : -1; seat_pointer_reset(s); s->pointer_pos = pos; s->motion_pending = motion; s->pointer_serial = serial; s->cursor_shape = shape; } struct yasl_surface *w = s->pointer_focus; if (!w) { s->ptr_presses.n = 0; return; } flush_pending_motion(s, w); press_queue_flush(s, &s->ptr_presses, NULL, &s->pointer_buttons, (struct yasl_mouse_event){0}); if (s->axis_smooth) { if (!s->axis_has_v120_v) s->axis_value120_v = 0; if (!s->axis_has_v120_h) s->axis_value120_h = 0; s->axis_smooth = false; } pointer_frame_vertical(s, w); pointer_frame_horizontal(s, w); s->axis_discrete_v = 0; s->axis_discrete_h = 0; s->axis_has_v120_v = false; s->axis_has_v120_h = false; } static void pointer_axis_relative_direction(void *data, struct wl_pointer *pointer, uint32_t axis, uint32_t direction) { (void)data; (void)pointer; (void)axis; (void)direction; } static void pointer_warp(void *data, struct wl_pointer *pointer, wl_fixed_t x, wl_fixed_t y) { (void)data; (void)pointer; (void)x; (void)y; } static const struct wl_pointer_listener pointer_listener = { .axis_relative_direction = pointer_axis_relative_direction, .warp = pointer_warp, .enter = pointer_enter, .leave = pointer_leave, .motion = pointer_motion, .button = pointer_button, .axis = pointer_axis, .frame = pointer_frame, .axis_source = pointer_axis_source, .axis_stop = pointer_axis_stop, .axis_discrete = pointer_axis_discrete, .axis_value120 = pointer_axis_value120, }; static struct touch_point *touch_find(struct seat *s, int32_t id) { for (int i = 0; i < SEAT_TOUCH_POINTS; i++) if (s->touch_points[i].active && s->touch_points[i].id == id) return &s->touch_points[i]; return NULL; } static struct yasl_vector2i touch_pos(const struct touch_point *p) { double scale = p->surface->scale_factor; return (struct yasl_vector2i){ .x = device_px_round(p->x * scale), .y = device_px_round(p->y * scale), }; } static int touch_count(const struct seat *s) { int n = 0; for (int i = 0; i < SEAT_TOUCH_POINTS; i++) if (s->touch_points[i].active && !s->touch_points[i].up) n++; return n; } static double touch_centroid_y(const struct seat *s, const struct yasl_surface *on) { double sum = 0; int n = 0; for (int i = 0; i < SEAT_TOUCH_POINTS; i++) { const struct touch_point *p = &s->touch_points[i]; if (p->active && !p->up && p->surface == on) { sum += p->y; n++; } } return n ? sum / n : 0; } static void touch_report(struct seat *s, struct touch_point *p, int button, enum yasl_pointer_kind kind) { push_mouse(s, p->surface, (struct yasl_mouse_event){ .pos = touch_pos(p), .button = button, .kind = kind, .device = YASL_PTR_DEV_TOUCH, }); } static void touch_down(void *data, struct wl_touch *touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y) { (void)touch; (void)time; struct seat *s = data; struct yasl_surface *w = conn_surface_for(s->conn, surface); if (!w || touch_find(s, id)) return; struct touch_point *p = NULL; for (int i = 0; i < SEAT_TOUCH_POINTS && !p; i++) if (!s->touch_points[i].active) p = &s->touch_points[i]; if (!p) return; seat_note_input(s, serial); s->press_serial = serial; s->press_surface = w; *p = (struct touch_point){ .surface = w, .id = id, .active = true, .down = true, .x = wl_fixed_to_double(x), .y = wl_fixed_to_double(y), }; } static void touch_up(void *data, struct wl_touch *touch, uint32_t serial, uint32_t time, int32_t id) { (void)touch; (void)serial; (void)time; struct touch_point *p = touch_find(data, id); if (p) p->up = true; } static void touch_motion(void *data, struct wl_touch *touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { (void)touch; (void)time; struct touch_point *p = touch_find(data, id); if (!p) return; p->x = wl_fixed_to_double(x); p->y = wl_fixed_to_double(y); p->moved = true; } static void touch_frame(void *data, struct wl_touch *touch) { (void)touch; struct seat *s = data; for (int i = 0; i < SEAT_TOUCH_POINTS; i++) { struct touch_point *p = &s->touch_points[i]; if (p->active && p->down && !s->touch_primary && !s->touch_scrolling) { s->touch_primary = p; s->touch_pressed = true; touch_report(s, p, 0, YASL_PTR_PRESS); } } struct touch_point *primary = s->touch_primary; if (primary && !s->touch_scrolling && touch_count(s) >= 2) { if (s->touch_pressed) touch_report(s, primary, 0, YASL_PTR_RELEASE); s->touch_pressed = false; s->touch_scrolling = true; s->touch_scroll_y = touch_centroid_y(s, primary->surface); s->touch_scroll_rest = 0; } else if (s->touch_scrolling && primary && touch_count(s) >= 2) { double y = touch_centroid_y(s, primary->surface); double dy = y - s->touch_scroll_y; s->touch_scroll_y = y; if (dy != 0.0) { double lines = dy / SCROLL_PX_PER_LINE; s->touch_scroll_rest += lines; int whole = (int)s->touch_scroll_rest; s->touch_scroll_rest -= whole; push_mouse(s, primary->surface, (struct yasl_mouse_event){ .pos = touch_pos(primary), .button = lines > 0 ? 4 : 5, .kind = YASL_PTR_SCROLL, .scroll_lines = whole, .scroll_delta = (float)lines, .device = YASL_PTR_DEV_TOUCH, }); } } else if (primary && primary->moved && s->touch_pressed) { touch_report(s, primary, 0, YASL_PTR_MOTION); } for (int i = 0; i < SEAT_TOUCH_POINTS; i++) { struct touch_point *p = &s->touch_points[i]; if (!p->active) continue; if (p->up) { if (p == s->touch_primary) { if (s->touch_pressed) touch_report(s, p, 0, YASL_PTR_RELEASE); s->touch_pressed = false; s->touch_primary = NULL; } *p = (struct touch_point){0}; } else { p->down = p->moved = false; } } if (!touch_count(s)) s->touch_scrolling = false; } static void touch_cancel(void *data, struct wl_touch *touch) { (void)touch; struct seat *s = data; if (s->touch_primary && s->touch_pressed) touch_report(s, s->touch_primary, 0, YASL_PTR_RELEASE); memset(s->touch_points, 0, sizeof(s->touch_points)); s->touch_primary = NULL; s->touch_pressed = false; s->touch_scrolling = false; } static void touch_shape(void *data, struct wl_touch *touch, int32_t id, wl_fixed_t major, wl_fixed_t minor) { (void)data; (void)touch; (void)id; (void)major; (void)minor; } static void touch_orientation(void *data, struct wl_touch *touch, int32_t id, wl_fixed_t orientation) { (void)data; (void)touch; (void)id; (void)orientation; } static const struct wl_touch_listener touch_listener = { .shape = touch_shape, .orientation = touch_orientation, .down = touch_down, .up = touch_up, .motion = touch_motion, .frame = touch_frame, .cancel = touch_cancel, }; static struct yasl_mouse_event tool_event(const struct seat *s) { return (struct yasl_mouse_event){ .device = s->tool && s->tool->eraser ? YASL_PTR_DEV_ERASER : YASL_PTR_DEV_PEN, .pen = s->pen, }; } static void tablet_tool_proximity_in(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) { (void)tool; (void)tablet; struct tablet_tool *t = data; struct seat *s = t->seat; struct yasl_surface *w = conn_surface_for(s->conn, surface); s->tablet_focus = w; s->tool = t; s->tool_buttons = 0; s->pen = (struct yasl_pointer_pen){0}; t->proximity_serial = serial; if (!w) return; if (s->conn->cursor_shape_mgr && !t->shape_dev) t->shape_dev = wp_cursor_shape_manager_v1_get_tablet_tool_v2( s->conn->cursor_shape_mgr, t->proxy); tool_set_cursor(t, w->parent ? YASL_CURSOR_ARROW : YASL_CURSOR_TEXT); s->tool_motion = true; } static void tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *tool) { (void)tool; struct tablet_tool *t = data; struct seat *s = t->seat; struct yasl_surface *w = s->tablet_focus; if (w) { press_queue_flush(s, &s->tool_presses, NULL, &s->tool_buttons, tool_event(s)); struct yasl_mouse_event ev = tool_event(s); ev.pos = s->tool_pos; ev.button = 3; ev.kind = YASL_PTR_LEAVE; push_mouse(s, w, ev); } s->tool_presses.n = 0; s->tablet_focus = NULL; s->tool = NULL; s->tool_motion = false; t->proximity_serial = 0; } static void tablet_tool_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial) { (void)tool; struct seat *s = ((struct tablet_tool *)data)->seat; struct yasl_surface *w = s->tablet_focus; if (!w) return; seat_note_input(s, serial); s->press_serial = serial; s->press_surface = w; press_queue_push(&s->tool_presses, w, s->tool_pos, 0, false); } static void tablet_tool_up(void *data, struct zwp_tablet_tool_v2 *tool) { (void)tool; struct seat *s = ((struct tablet_tool *)data)->seat; struct yasl_surface *w = s->tablet_focus; if (!w) return; press_queue_push(&s->tool_presses, w, s->tool_pos, 0, true); } static void tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t x, wl_fixed_t y) { (void)tool; struct seat *s = ((struct tablet_tool *)data)->seat; struct yasl_surface *w = s->tablet_focus; if (!w) return; s->tool_pos = device_from_fixed(w, x, y); s->tool_motion = true; } static void tablet_tool_button(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, uint32_t button, uint32_t state) { (void)tool; struct seat *s = ((struct tablet_tool *)data)->seat; struct yasl_surface *w = s->tablet_focus; if (!w) return; seat_note_input(s, serial); int b; switch (button) { case BTN_STYLUS: b = 2; break; case BTN_STYLUS2: b = 1; break; case BTN_STYLUS3: b = 3; break; default: return; } bool release = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED; press_queue_push(&s->tool_presses, w, s->tool_pos, b, release); } static void tool_pressure(void *d, struct zwp_tablet_tool_v2 *t, uint32_t v) { (void)t; struct seat *s = ((struct tablet_tool *)d)->seat; s->pen.pressure = v > 65535 ? 65535 : (uint16_t)v; s->tool_motion = true; } static int8_t tilt_degrees(wl_fixed_t v) { double d = wl_fixed_to_double(v); return (int8_t)(d < -90 ? -90 : d > 90 ? 90 : lround(d)); } static void tool_tilt(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t x, wl_fixed_t y) { (void)t; struct seat *s = ((struct tablet_tool *)d)->seat; s->pen.tilt_x = tilt_degrees(x); s->pen.tilt_y = tilt_degrees(y); s->tool_motion = true; } static void tool_wheel(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t deg, int32_t clicks) { (void)t; (void)deg; ((struct tablet_tool *)d)->seat->tool_wheel += clicks; } static void tool_frame(void *d, struct zwp_tablet_tool_v2 *t, uint32_t time) { (void)t; (void)time; struct seat *s = ((struct tablet_tool *)d)->seat; struct yasl_surface *w = s->tablet_focus; if (!w) { s->tool_presses.n = 0; s->tool_wheel = 0; return; } struct yasl_mouse_event like = tool_event(s); if (s->tool_motion) { struct yasl_mouse_event ev = like; ev.pos = s->tool_pos; ev.button = motion_button(s->tool_buttons); ev.kind = YASL_PTR_MOTION; push_mouse(s, w, ev); s->tool_motion = false; } press_queue_flush(s, &s->tool_presses, NULL, &s->tool_buttons, like); if (s->tool_wheel) { report_wheel(s, w, s->tool_pos, s->tool_wheel > 0 ? 5 : 4, -s->tool_wheel, 0.0f); s->tool_wheel = 0; } } static void tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *tool) { struct tablet_tool *t = data; struct seat *s = t->seat; if (s->tool == t) { s->tool = NULL; s->tablet_focus = NULL; s->tool_presses.n = 0; } struct tablet_tool **link = &s->tablet_tools; while (*link && *link != t) link = &(*link)->next; if (*link) *link = t->next; if (t->shape_dev) wp_cursor_shape_device_v1_destroy(t->shape_dev); zwp_tablet_tool_v2_destroy(tool); free(t); } static void tool_type(void *d, struct zwp_tablet_tool_v2 *t, uint32_t type) { (void)t; ((struct tablet_tool *)d)->eraser = type == ZWP_TABLET_TOOL_V2_TYPE_ERASER; } static void tool_hardware_serial(void *d, struct zwp_tablet_tool_v2 *t, uint32_t hi, uint32_t lo) { (void)d; (void)t; (void)hi; (void)lo; } static void tool_hardware_id_wacom(void *d, struct zwp_tablet_tool_v2 *t, uint32_t hi, uint32_t lo) { (void)d; (void)t; (void)hi; (void)lo; } static void tool_capability(void *d, struct zwp_tablet_tool_v2 *t, uint32_t cap) { (void)d; (void)t; (void)cap; } static void tool_done(void *d, struct zwp_tablet_tool_v2 *t) { (void)d; (void)t; } static void tool_distance(void *d, struct zwp_tablet_tool_v2 *t, uint32_t v) { (void)d; (void)t; (void)v; } static void tool_rotation(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t deg) { (void)d; (void)t; (void)deg; } static void tool_slider(void *d, struct zwp_tablet_tool_v2 *t, int32_t pos) { (void)d; (void)t; (void)pos; } static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = { .type = tool_type, .hardware_serial = tool_hardware_serial, .hardware_id_wacom = tool_hardware_id_wacom, .capability = tool_capability, .done = tool_done, .removed = tablet_tool_removed, .proximity_in = tablet_tool_proximity_in, .proximity_out = tablet_tool_proximity_out, .down = tablet_tool_down, .up = tablet_tool_up, .motion = tablet_tool_motion, .pressure = tool_pressure, .distance = tool_distance, .tilt = tool_tilt, .rotation = tool_rotation, .slider = tool_slider, .wheel = tool_wheel, .button = tablet_tool_button, .frame = tool_frame, }; static void tablet_name(void *data, struct zwp_tablet_v2 *tablet, const char *name) { (void)data; (void)tablet; (void)name; } static void tablet_id(void *data, struct zwp_tablet_v2 *tablet, uint32_t vid, uint32_t pid) { (void)data; (void)tablet; (void)vid; (void)pid; } static void tablet_path(void *data, struct zwp_tablet_v2 *tablet, const char *path) { (void)data; (void)tablet; (void)path; } static void tablet_done(void *data, struct zwp_tablet_v2 *tablet) { (void)data; (void)tablet; } static void tablet_bustype(void *data, struct zwp_tablet_v2 *tablet, uint32_t bustype) { (void)data; (void)tablet; (void)bustype; } static void tablet_removed(void *data, struct zwp_tablet_v2 *tablet) { struct seat *s = data; struct tablet_device **link = &s->tablet_devices; while (*link && (*link)->proxy != tablet) link = &(*link)->next; if (*link) { struct tablet_device *removed = *link; *link = removed->next; free(removed); } zwp_tablet_v2_destroy(tablet); } static const struct zwp_tablet_v2_listener tablet_listener = { .name = tablet_name, .id = tablet_id, .path = tablet_path, .done = tablet_done, .removed = tablet_removed, .bustype = tablet_bustype, }; static void tablet_pad_group(void *data, struct zwp_tablet_pad_v2 *pad, struct zwp_tablet_pad_group_v2 *group) { (void)data; (void)pad; (void)group; } static void tablet_pad_path(void *data, struct zwp_tablet_pad_v2 *pad, const char *path) { (void)data; (void)pad; (void)path; } static void tablet_pad_buttons(void *data, struct zwp_tablet_pad_v2 *pad, uint32_t buttons) { (void)data; (void)pad; (void)buttons; } static void tablet_pad_done(void *data, struct zwp_tablet_pad_v2 *pad) { (void)data; (void)pad; } static void tablet_pad_button(void *data, struct zwp_tablet_pad_v2 *pad, uint32_t time, uint32_t button, uint32_t state) { (void)data; (void)pad; (void)time; (void)button; (void)state; } static void tablet_pad_enter(void *data, struct zwp_tablet_pad_v2 *pad, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) { (void)data; (void)pad; (void)serial; (void)tablet; (void)surface; } static void tablet_pad_leave(void *data, struct zwp_tablet_pad_v2 *pad, uint32_t serial, struct wl_surface *surface) { (void)data; (void)pad; (void)serial; (void)surface; } static void tablet_pad_removed(void *data, struct zwp_tablet_pad_v2 *pad) { struct seat *s = data; struct tablet_pad **link = &s->tablet_pads; while (*link && (*link)->proxy != pad) link = &(*link)->next; if (*link) { struct tablet_pad *removed = *link; *link = removed->next; free(removed); } zwp_tablet_pad_v2_destroy(pad); } static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = { .group = tablet_pad_group, .path = tablet_pad_path, .buttons = tablet_pad_buttons, .done = tablet_pad_done, .button = tablet_pad_button, .enter = tablet_pad_enter, .leave = tablet_pad_leave, .removed = tablet_pad_removed, }; void seat_destroy_tablet(struct seat *s) { while (s->tablet_tools) tablet_tool_removed(s->tablet_tools, s->tablet_tools->proxy); while (s->tablet_pads) tablet_pad_removed(s, s->tablet_pads->proxy); while (s->tablet_devices) tablet_removed(s, s->tablet_devices->proxy); if (s->tablet_seat) { zwp_tablet_seat_v2_destroy(s->tablet_seat); s->tablet_seat = NULL; } } static void tablet_seat_tablet_added(void *data, struct zwp_tablet_seat_v2 *tablet_seat, struct zwp_tablet_v2 *tablet) { (void)tablet_seat; struct seat *s = data; struct tablet_device *owned = calloc(1, sizeof(*owned)); if (!owned) { zwp_tablet_v2_destroy(tablet); return; } owned->proxy = tablet; owned->next = s->tablet_devices; s->tablet_devices = owned; zwp_tablet_v2_add_listener(tablet, &tablet_listener, s); } static void tablet_seat_tool_added(void *data, struct zwp_tablet_seat_v2 *tablet_seat, struct zwp_tablet_tool_v2 *tool) { (void)tablet_seat; struct seat *s = data; struct tablet_tool *owned = calloc(1, sizeof(*owned)); if (!owned) { zwp_tablet_tool_v2_destroy(tool); return; } owned->proxy = tool; owned->seat = s; owned->next = s->tablet_tools; s->tablet_tools = owned; zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, owned); } static void tablet_seat_pad_added(void *data, struct zwp_tablet_seat_v2 *tablet_seat, struct zwp_tablet_pad_v2 *pad) { (void)tablet_seat; struct seat *s = data; struct tablet_pad *owned = calloc(1, sizeof(*owned)); if (!owned) { zwp_tablet_pad_v2_destroy(pad); return; } owned->proxy = pad; owned->next = s->tablet_pads; s->tablet_pads = owned; zwp_tablet_pad_v2_add_listener(pad, &tablet_pad_listener, s); } static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { .tablet_added = tablet_seat_tablet_added, .tool_added = tablet_seat_tool_added, .pad_added = tablet_seat_pad_added, }; static void clipboard_received(const char *text, size_t len, bool uri_list, struct wl_surface *on, void *ud) { struct seat *s = ud; struct yasl_display *c = s->conn; struct yasl_surface *w = on ? surface_root(conn_surface_for(c, on)) : NULL; if (!w) w = c->paste_target ? c->paste_target : s->keyboard_focus; if (!len || !w) return; struct yasl_input_event ev = { .surface = w, .kind = uri_list ? YASL_INPUT_DROP_FILES : YASL_INPUT_PASTE, }; if (uri_list) { ev.drop_files.files = text; ev.drop_files.len = len; } else { ev.paste.text = text; ev.paste.len = len; } input_queue_push(&w->input, &ev); } static void clipboard_drag(enum yasl_drag_event ev, struct wl_surface *on, double x, double y, void *ud) { struct seat *s = ud; conn_drag_target(s->conn, ev, on, x, y); } static void clipboard_drop_hover(enum yasl_drop_event ev, struct wl_surface *on, double x, double y, void *ud) { struct seat *s = ud; conn_drop_target(s->conn, ev, on, x, y); } void seat_clipboard_ensure(struct seat *s) { struct yasl_display *c = s->conn; if (!c->data_manager || !s->proxy) return; if (!s->clipboard) { s->clipboard = clipboard_create(c->wl, c->data_manager, s->proxy, c->loop, clipboard_received, s); if (!s->clipboard) return; clipboard_set_drag_handler(s->clipboard, clipboard_drag, s); clipboard_set_drop_handler(s->clipboard, clipboard_drop_hover, s); } if (c->primary_selection_mgr) clipboard_set_primary_manager(s->clipboard, c->primary_selection_mgr); } static void seat_drop_pointer(struct seat *s) { if (s->cursor_shape_dev) { wp_cursor_shape_device_v1_destroy(s->cursor_shape_dev); s->cursor_shape_dev = NULL; } if (s->pointer) { if (wl_pointer_get_version(s->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) wl_pointer_release(s->pointer); else wl_pointer_destroy(s->pointer); s->pointer = NULL; } seat_pointer_reset(s); s->pointer_focus = s->ptr_left = NULL; s->ptr_presses.n = 0; } static void seat_drop_keyboard(struct seat *s) { keyboard_repeat_stop(s); if (s->keyboard) { if (wl_keyboard_get_version(s->keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) wl_keyboard_release(s->keyboard); else wl_keyboard_destroy(s->keyboard); s->keyboard = NULL; } s->repeat_rate = 0; struct yasl_surface *w = s->keyboard_focus; s->keyboard_focus = NULL; if (w && !s->focus_leaving) s->focus_leaving = w; } static void seat_drop_touch(struct seat *s) { if (s->touch) { touch_cancel(s, s->touch); if (wl_touch_get_version(s->touch) >= WL_TOUCH_RELEASE_SINCE_VERSION) wl_touch_release(s->touch); else wl_touch_destroy(s->touch); s->touch = NULL; } } static void seat_capabilities(void *data, struct wl_seat *proxy, uint32_t caps) { struct seat *s = data; struct yasl_display *c = s->conn; if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { if (!s->keyboard) { s->keyboard = wl_seat_get_keyboard(proxy); if (s->keyboard) wl_keyboard_add_listener(s->keyboard, &keyboard_listener, s); if (s->keyboard && wl_keyboard_get_version(s->keyboard) < WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) { s->repeat_rate = 25; s->repeat_delay = 600; } } } else if (s->keyboard) { seat_drop_keyboard(s); } if (caps & WL_SEAT_CAPABILITY_POINTER) { if (!s->pointer) { s->pointer = wl_seat_get_pointer(proxy); if (s->pointer) wl_pointer_add_listener(s->pointer, &pointer_listener, s); if (s->pointer && c->cursor_shape_mgr) s->cursor_shape_dev = wp_cursor_shape_manager_v1_get_pointer( c->cursor_shape_mgr, s->pointer); } } else if (s->pointer) { seat_drop_pointer(s); } if (caps & WL_SEAT_CAPABILITY_TOUCH) { if (!s->touch) { s->touch = wl_seat_get_touch(proxy); if (s->touch) wl_touch_add_listener(s->touch, &touch_listener, s); } } else if (s->touch) { seat_drop_touch(s); } if (c->tablet_mgr && !s->tablet_seat) { s->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(c->tablet_mgr, proxy); if (s->tablet_seat) zwp_tablet_seat_v2_add_listener(s->tablet_seat, &tablet_seat_listener, s); } if (c->text_input_mgr && !s->text_input) { s->text_input = zwp_text_input_manager_v3_get_text_input(c->text_input_mgr, proxy); if (s->text_input) zwp_text_input_v3_add_listener(s->text_input, &text_input_listener, s); } seat_clipboard_ensure(s); } static void seat_name(void *data, struct wl_seat *seat, const char *name) { (void)data; (void)seat; (void)name; } const struct wl_seat_listener seat_listener = { .capabilities = seat_capabilities, .name = seat_name, }; void seat_bind(struct yasl_display *c, struct wl_registry *registry, uint32_t name, uint32_t version) { struct seat *s = calloc(1, sizeof(*s)); if (!s) return; s->conn = c; s->name = name; s->repeat_fd = -1; s->cursor_shape = -1; s->proxy = wl_registry_bind(registry, name, &wl_seat_interface, version < 8 ? version : 8); if (!s->proxy) { free(s); return; } s->next = c->seats; c->seats = s; wl_seat_add_listener(s->proxy, &seat_listener, s); keyboard_repeat_init(s); } void seat_forget_surface(struct seat *s, struct yasl_surface *w) { struct yasl_surface **refs[] = { &s->pointer_focus, &s->keyboard_focus, &s->tablet_focus, &s->ime_focus, &s->focus_leaving, &s->ptr_left, &s->press_surface, }; for (size_t i = 0; i < sizeof(refs) / sizeof(refs[0]); i++) if (*refs[i] == w) *refs[i] = NULL; press_queue_forget(&s->ptr_presses, w); press_queue_forget(&s->tool_presses, w); for (int i = 0; i < SEAT_TOUCH_POINTS; i++) { if (s->touch_points[i].active && s->touch_points[i].surface == w) { if (s->touch_primary == &s->touch_points[i]) { s->touch_primary = NULL; s->touch_pressed = false; } s->touch_points[i] = (struct touch_point){0}; } } } void seat_destroy(struct seat *s) { struct yasl_display *c = s->conn; for (struct seat **link = &c->seats; *link; link = &(*link)->next) { if (*link == s) { *link = s->next; break; } } if (c->active_seat == s) c->active_seat = NULL; for (struct yasl_surface *w = c->surfaces; w; w = w->conn_next) { if (w->ime_seat == s) { w->ime_seat = NULL; ime_reset(w); } } if (c->drag.seat == s) conn_drag_end(c); if (s->text_input) zwp_text_input_v3_destroy(s->text_input); seat_destroy_tablet(s); seat_drop_pointer(s); seat_drop_keyboard(s); struct yasl_surface *left = s->focus_leaving; if (left && !conn_keyboard_on(c, left, NULL)) { struct yasl_input_event ev = { .kind = YASL_INPUT_FOCUS, .surface = left, .focus = { .focused = false }, }; input_queue_push(&left->input, &ev); } seat_drop_touch(s); clipboard_destroy(s->clipboard); if (s->repeat_watch > 0) yasl_loop_del_fd(c->loop, s->repeat_watch); if (s->repeat_fd >= 0) close(s->repeat_fd); if (s->cursor_viewport) wp_viewport_destroy(s->cursor_viewport); if (s->cursor_surface) wl_surface_destroy(s->cursor_surface); if (s->xkb_state) xkb_state_unref(s->xkb_state); if (s->xkb_keymap) xkb_keymap_unref(s->xkb_keymap); if (s->proxy) { if (wl_seat_get_version(s->proxy) >= WL_SEAT_RELEASE_SINCE_VERSION) wl_seat_release(s->proxy); else wl_seat_destroy(s->proxy); } free(s); } bool seat_remove(struct yasl_display *c, uint32_t name) { for (struct seat *s = c->seats; s; s = s->next) { if (s->name == name) { seat_destroy(s); return true; } } return false; } int display_input_init(struct yasl_display *c) { for (struct seat *s = c->seats; s; s = s->next) { keyboard_repeat_init(s); seat_clipboard_ensure(s); } return 0; } void surface_init_cursor(struct yasl_display *c) { if (c->cursor_shape_mgr) return; const char *env_size = getenv("XCURSOR_SIZE"); int size = env_size ? atoi(env_size) : 0; c->cursor_theme_name = getenv("XCURSOR_THEME"); c->cursor_size = size > 0 && size <= 512 ? size : 24; cursor_theme_for(c, 1); }