diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-12 22:12:02 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-12 22:12:02 +0300 |
| commit | 94c5e361fbb229b0cd6853e2102a7d95f1f358b3 (patch) | |
| tree | 3c0f11af9dcb84dac8067a31a6979acd6dd56fbf /src/wayland | |
| parent | 08dc9047098d6d96791024ac7d8f5837633673ef (diff) | |
better public interface and doxygen
Diffstat (limited to 'src/wayland')
| -rw-r--r-- | src/wayland/clipboard.c | 8 | ||||
| -rw-r--r-- | src/wayland/ime.c | 2 | ||||
| -rw-r--r-- | src/wayland/internal.h | 19 | ||||
| -rw-r--r-- | src/wayland/keyboard.c | 94 | ||||
| -rw-r--r-- | src/wayland/pointer.c | 126 |
5 files changed, 134 insertions, 115 deletions
diff --git a/src/wayland/clipboard.c b/src/wayland/clipboard.c index e3eec0f..a6922c4 100644 --- a/src/wayland/clipboard.c +++ b/src/wayland/clipboard.c @@ -131,9 +131,11 @@ static int transfer_io(int fd, short events, void *ud) if (n > 0) { t->len += (size_t)n; } else if (n == 0) { - t->owner->receive(t->text ? t->text : "", t->len, t->uri_list, - t->drop_surface, t->owner->ud); - transfer_end(t, true); + bool valid = yasl_utf8_validate(t->text, t->len); + if (valid) + t->owner->receive(t->text ? t->text : "", t->len, t->uri_list, + t->drop_surface, t->owner->ud); + transfer_end(t, valid); } else if (errno != EAGAIN && errno != EINTR) transfer_destroy(t); } return 0; diff --git a/src/wayland/ime.c b/src/wayland/ime.c index 74db53f..bd9375b 100644 --- a/src/wayland/ime.c +++ b/src/wayland/ime.c @@ -98,6 +98,7 @@ static void text_input_preedit_string(void *data, (void)text_input; struct wayland_surface *w = ((struct seat *)data)->ime_focus; if (!w) return; + keyboard_text_reset((struct seat *)data); free(w->ime_preedit); w->ime_preedit = text ? strndup(text, IME_TEXT_MAX) : NULL; int len = w->ime_preedit ? (int)strlen(w->ime_preedit) : 0; @@ -113,6 +114,7 @@ static void text_input_commit_string(void *data, (void)text_input; struct wayland_surface *w = ((struct seat *)data)->ime_focus; if (!w) return; + keyboard_text_reset((struct seat *)data); free(w->ime_commit); w->ime_commit = (text && text[0]) ? strndup(text, IME_TEXT_MAX) : NULL; } diff --git a/src/wayland/internal.h b/src/wayland/internal.h index ad6531b..b687a61 100644 --- a/src/wayland/internal.h +++ b/src/wayland/internal.h @@ -5,6 +5,7 @@ #include "api.h" #include "common/timing.h" +#include "common/utf8.h" #include "clipboard.h" #include <wayland-client.h> @@ -38,6 +39,7 @@ #include <unistd.h> #include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon-keysyms.h> +#include <xkbcommon/xkbcommon-compose.h> #define NBUF 3 @@ -140,6 +142,8 @@ struct seat { struct clipboard *clipboard; struct xkb_keymap *xkb_keymap; struct xkb_state *xkb_state; + struct xkb_compose_state *compose; + bool compose_attempted; uint64_t repeat_started_ns; struct wayland_surface *pointer_focus; struct wayland_surface *keyboard_focus; @@ -152,8 +156,10 @@ struct seat { struct touch_point *touch_primary; uint64_t pointer_buttons; uint64_t tool_buttons; - double axis_v, axis_h, frame_v; - double touch_scroll_y, touch_scroll_rest; + double axis[2], axis_value120[2]; + int64_t axis_discrete[2]; + bool axis_has_value120[2], axis_has_discrete[2]; + double touch_scroll_y; /* Embedded structs */ xkb_mod_mask_t input_mod_masks[8]; @@ -174,18 +180,12 @@ struct seat { uint32_t press_serial; uint32_t pointer_serial; int cursor_shape; - int frame_v120; - int axis_discrete_v, axis_discrete_h; - int axis_value120_v, axis_value120_h; struct yasl_pointer_pen pen; int tool_wheel; /* 1-byte booleans grouped tightly */ bool input_mods_ready; bool repeat_held; - bool axis_has_v120_v; - bool axis_has_v120_h; - bool axis_smooth; bool motion_pending; bool ptr_left_motion; bool tool_motion; @@ -325,8 +325,6 @@ struct wayland_surface { bool want_draw; }; -char *yasl_utf8_sanitize_dup(const char *str); -bool yasl_utf8_validate(const char *str, size_t len); int device_px(int logical, uint32_t scale_120); bool buffer_size(int width, int height, size_t *bytes); @@ -415,6 +413,7 @@ void seat_pointer_reset(struct seat *s); extern const struct zwp_text_input_v3_listener text_input_listener; void ime_reset(struct wayland_surface *w); +void keyboard_text_reset(struct seat *s); void ime_send_caret(struct wayland_surface *w); int surface_init_shell(struct wayland_surface *w, const char *title); diff --git a/src/wayland/keyboard.c b/src/wayland/keyboard.c index 11c7f3f..f1170c6 100644 --- a/src/wayland/keyboard.c +++ b/src/wayland/keyboard.c @@ -1,8 +1,14 @@ #include "internal.h" +#include "common/keyboard.h" #define XKB_EVDEV_OFFSET 8 #define KEYMAP_MAX_BYTES (8u * 1024 * 1024) +void keyboard_text_reset(struct seat *s) +{ + if (s->compose) xkb_compose_state_reset(s->compose); +} + void keyboard_repeat_stop(struct seat *s) { if (!s->repeat_held) return; @@ -28,11 +34,10 @@ void keyboard_repeat_sync(struct seat *s) timerfd_settime(s->repeat_fd, 0, &timer, NULL); } -static const char *const input_mod_names[8] = { +static const char *const input_mod_names[5] = { XKB_MOD_NAME_SHIFT, XKB_MOD_NAME_ALT, XKB_MOD_NAME_CTRL, XKB_MOD_NAME_LOGO, - "Hyper", "Meta", - XKB_MOD_NAME_CAPS, XKB_MOD_NAME_NUM + XKB_MOD_NAME_CAPS }; static bool mod_active(const struct seat *s, const char *name) @@ -48,7 +53,7 @@ static void resolve_input_mod_masks(struct seat *s) if (!probe) return; for (unsigned real = 0; real < 8; real++) { xkb_state_update_mask(probe, 1u << real, 0, 0, 0, 0, 0); - for (unsigned i = 0; i < 8; i++) { + for (unsigned i = 0; i < 5; i++) { if (xkb_state_mod_name_is_active(probe, input_mod_names[i], XKB_STATE_MODS_EFFECTIVE) > 0) s->input_mod_masks[i] |= 1u << real; @@ -64,6 +69,7 @@ static void keyboard_keymap(void *data, struct wl_keyboard *keyboard, (void)keyboard; struct seat *s = data; keyboard_repeat_stop(s); + keyboard_text_reset(s); s->input_mods_ready = false; if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { close(fd); @@ -116,6 +122,7 @@ static void keyboard_reset_keys(struct seat *s, struct wayland_surface *w) { (void)w; keyboard_repeat_stop(s); + keyboard_text_reset(s); } bool conn_keyboard_on(const struct wayland_display *c, const struct wayland_surface *w, @@ -194,7 +201,7 @@ static unsigned input_modifiers(struct seat *s) unsigned mods = 0; xkb_mod_mask_t reported = 0; - for (unsigned i = 0; i < 8; i++) { + for (unsigned i = 0; i < 5; i++) { xkb_mod_mask_t mask = s->input_mod_masks[i]; if (mask && (mask & ~reported) && mod_active(s, input_mod_names[i])) { mods |= 1u << i; @@ -215,23 +222,75 @@ static struct yasl_key_event xkb_key_event(struct seat *s, uint32_t key, enum yasl_key_action action) { uint32_t code = key + XKB_EVDEV_OFFSET; - xkb_keysym_t sym = xkb_state_key_get_one_sym(s->xkb_state, code); xkb_layout_index_t layout = xkb_state_key_get_layout(s->xkb_state, code); struct yasl_key_event k = { - .key = sym, - .keypad = key >= KEY_KP0 && key <= KEY_KPCOMMA, + .key = yasl_key_from_evdev(key), .action = action, .mods = input_modifiers(s), - .codepoint = key_codepoint_at_level(s, key, layout, 0), - .shifted = key_codepoint_at_level(s, key, layout, 1), - .base = key, - .text = xkb_state_key_get_utf32(s->xkb_state, code), + .codepoint = yasl_key_label(key_codepoint_at_level(s, key, layout, 0)), + .shifted = yasl_key_label(key_codepoint_at_level(s, key, layout, 1)), + .native_code = key, }; - if (!k.codepoint) k.codepoint = xkb_keysym_to_utf32(sym); return k; } +static void keyboard_text(struct seat *s, struct wayland_surface *w, uint32_t key) +{ + uint32_t code = key + XKB_EVDEV_OFFSET; + unsigned mods = input_modifiers(s); + if ((mods & (YASL_MOD_CTRL | YASL_MOD_SUPER)) || + ((mods & YASL_MOD_ALT) && + !xkb_state_mod_index_is_consumed(s->xkb_state, code, s->mod_alt))) { + keyboard_text_reset(s); + return; + } + if (!s->compose_attempted) { + s->compose_attempted = true; + const char *locale = getenv("LC_ALL"); + if (!locale || !*locale) locale = getenv("LC_CTYPE"); + if (!locale || !*locale) locale = getenv("LANG"); + if (!locale || !*locale) locale = "C.UTF-8"; + struct xkb_compose_table *table = xkb_compose_table_new_from_locale( + s->conn->xkb_ctx, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); + if (table) { + s->compose = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(table); + } + } + bool composed = false; + if (s->compose) { + xkb_compose_state_feed(s->compose, xkb_state_key_get_one_sym(s->xkb_state, code)); + switch (xkb_compose_state_get_status(s->compose)) { + case XKB_COMPOSE_COMPOSING: return; + case XKB_COMPOSE_CANCELLED: keyboard_text_reset(s); return; + case XKB_COMPOSE_COMPOSED: composed = true; break; + default: break; + } + } + int len = composed ? xkb_compose_state_get_utf8(s->compose, NULL, 0) : + xkb_state_key_get_utf8(s->xkb_state, code, NULL, 0); + if (len > 0) { + char local[128]; + char *text = (size_t)len < sizeof(local) ? local : malloc((size_t)len + 1); + if (text) { + if (composed) xkb_compose_state_get_utf8(s->compose, text, (size_t)len + 1); + else xkb_state_key_get_utf8(s->xkb_state, code, text, (size_t)len + 1); + /* Editing commands belong to key events, not text insertion. */ + if ((unsigned char)text[0] >= 0x20 && text[0] != 0x7f) { + struct yasl_input_event ev = {.kind = YASL_INPUT_TEXT, + .surface = &w->base, .text = {text, (size_t)len}}; + input_queue_push(&w->input, &ev); + } + if (text != local) free(text); + } else { + struct yasl_input_event ev = {.kind = YASL_INPUT_RESET, .surface = &w->base}; + input_queue_push(&w->input, &ev); + } + } + if (composed) keyboard_text_reset(s); +} + static bool keyboard_dispatch_key(struct seat *s, struct wayland_surface *w, uint32_t key, enum yasl_key_action action) { @@ -243,7 +302,11 @@ static bool keyboard_dispatch_key(struct seat *s, struct wayland_surface *w, uin .surface = &w->base, .key = k, }; - input_queue_push(&w->input, &ev); + if (!input_queue_push(&w->input, &ev)) { + keyboard_text_reset(s); + return false; + } + if (action != YASL_KEY_RELEASE) keyboard_text(s, w, key); return action != YASL_KEY_RELEASE; } @@ -351,7 +414,8 @@ int seat_modifiers(const struct seat *s) return (mod_index_active(s, s->mod_shift) * YASL_MOD_SHIFT) | (mod_index_active(s, s->mod_alt) * YASL_MOD_ALT) | (mod_index_active(s, s->mod_ctrl) * YASL_MOD_CTRL) | - (mod_index_active(s, s->mod_super) * YASL_MOD_SUPER); + (mod_index_active(s, s->mod_super) * YASL_MOD_SUPER) | + (mod_active(s, XKB_MOD_NAME_CAPS) * YASL_MOD_CAPS); } int wayland_display_modifiers(const struct wayland_display *c) diff --git a/src/wayland/pointer.c b/src/wayland/pointer.c index 065669f..90b7ecc 100644 --- a/src/wayland/pointer.c +++ b/src/wayland/pointer.c @@ -109,12 +109,12 @@ static void report_mouse(struct seat *s, struct wayland_surface *w, struct yasl_ }); } -static void report_wheel(struct seat *s, struct wayland_surface *w, struct yasl_vector2i pos, - int button, int lines, float delta) +static void report_wheel(struct seat *s, struct wayland_surface *w, + struct yasl_vector2i pos, double x, double y) { + if ((x == 0 && y == 0) || !isfinite(x) || !isfinite(y)) return; push_mouse(s, w, (struct yasl_mouse_event){ - .pos = pos, .button = button, .kind = YASL_PTR_SCROLL, - .scroll_lines = lines, .scroll_delta = delta, + .pos = pos, .kind = YASL_PTR_SCROLL, .scroll = {x, y}, }); } @@ -295,12 +295,11 @@ 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; + memset(s->axis, 0, sizeof(s->axis)); + memset(s->axis_value120, 0, sizeof(s->axis_value120)); + memset(s->axis_discrete, 0, sizeof(s->axis_discrete)); + memset(s->axis_has_value120, 0, sizeof(s->axis_has_value120)); + memset(s->axis_has_discrete, 0, sizeof(s->axis_has_discrete)); s->cursor_shape = -1; } @@ -405,8 +404,7 @@ static void pointer_axis(void *data, struct wl_pointer *pointer, 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); + s->axis[axis] += v; } pointer_event_done(s); } @@ -414,81 +412,49 @@ static void pointer_axis(void *data, struct wl_pointer *pointer, 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); + (void)data; (void)axis_source; } 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; - } + /* A stop ends a gesture, not the offsets already received in this frame. */ + (void)data; (void)pointer; (void)time; (void)axis; } 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; + if (axis < 2) { + s->axis_discrete[axis] += discrete; + s->axis_has_discrete[axis] = true; + } } 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); + if (axis < 2) { + s->axis_value120[axis] += value120; + s->axis_has_value120[axis] = true; } } -static void pointer_frame_vertical(struct seat *s, struct wayland_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 wayland_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_scroll(struct seat *s, struct wayland_surface *w) +{ + double lines[2]; + for (int axis = 0; axis < 2; axis++) { + /* A wheel may provide all three representations. Pick one, never sum + * them. value120 preserves high-resolution detents on both axes. */ + lines[axis] = s->axis_has_value120[axis] ? s->axis_value120[axis] / 120.0 : + s->axis_has_discrete[axis] ? (double)s->axis_discrete[axis] : + s->axis[axis] / SCROLL_PX_PER_LINE; + s->axis[axis] = s->axis_value120[axis] = 0; + s->axis_discrete[axis] = 0; + s->axis_has_value120[axis] = s->axis_has_discrete[axis] = false; } + report_wheel(s, w, s->pointer_pos, lines[1], lines[0]); } static void pointer_frame(void *data, struct wl_pointer *pointer) @@ -521,18 +487,7 @@ static void pointer_frame(void *data, struct wl_pointer *pointer) 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; + pointer_frame_scroll(s, w); } static void pointer_axis_relative_direction(void *data, struct wl_pointer *pointer, uint32_t axis, uint32_t direction) @@ -651,20 +606,15 @@ static void touch_frame(void *data, struct wl_touch *touch) s->touch_pressed = false; s->touch_scrolling = true; s->touch_scroll_y = centroid_n ? centroid_sum / centroid_n : 0; - s->touch_scroll_rest = 0; } else if (s->touch_scrolling && primary && active_n >= 2) { double y = centroid_n ? centroid_sum / centroid_n : 0; 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, + .pos = touch_pos(primary), .kind = YASL_PTR_SCROLL, + .scroll = {0, -lines}, .device = YASL_PTR_DEV_TOUCH, }); } } else if (primary && primary->moved && s->touch_pressed) { @@ -870,7 +820,7 @@ static void tool_frame(void *d, struct zwp_tablet_tool_v2 *t, uint32_t time) struct yasl_mouse_event ev = like; ev.pos = s->tool_pos; ev.kind = YASL_PTR_SCROLL; - ev.scroll_lines = -s->tool_wheel; + ev.scroll.y = s->tool_wheel; push_mouse(s, w, ev); s->tool_wheel = 0; } @@ -1105,6 +1055,7 @@ static void seat_drop_pointer(struct seat *s) static void seat_drop_keyboard(struct seat *s) { keyboard_repeat_stop(s); + keyboard_text_reset(s); if (s->keyboard) { if (wl_keyboard_get_version(s->keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) wl_keyboard_release(s->keyboard); @@ -1281,6 +1232,7 @@ void seat_destroy(struct seat *s) 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->compose) xkb_compose_state_unref(s->compose); 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) |
