summaryrefslogtreecommitdiff
path: root/src/wayland
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-12 22:12:02 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-12 22:12:02 +0300
commit94c5e361fbb229b0cd6853e2102a7d95f1f358b3 (patch)
tree3c0f11af9dcb84dac8067a31a6979acd6dd56fbf /src/wayland
parent08dc9047098d6d96791024ac7d8f5837633673ef (diff)
better public interface and doxygen
Diffstat (limited to 'src/wayland')
-rw-r--r--src/wayland/clipboard.c8
-rw-r--r--src/wayland/ime.c2
-rw-r--r--src/wayland/internal.h19
-rw-r--r--src/wayland/keyboard.c94
-rw-r--r--src/wayland/pointer.c126
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)