From 76073fe5fad8799e5850805f2f090af2cb62c741 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Tue, 13 Oct 2026 23:41:31 +0300 Subject: Simplify the public event and CPU API Applications now need far less ceremony: hello.c is a 20-line loop that handles REFRESH, CLOSE and QUIT and draws into the surface's own pixels. - Fold GEOMETRY, STATE and FOCUS into one WINDOW event carrying size, scale and state; unchanged values send nothing. - Guarantee REFRESH once a window has a size and after every resize, never at size zero. - Merge DROP_HOVER into DROP with over/leave/data actions and give drag and drop actions enums. - Keep event strings valid until the next wait and drop yasl_event_release; empty or failed waits report NONE. - Replace copy-in presentation with a lent pixel buffer: yasl_cpu_pixels and yasl_cpu_present. - Remove yasl_poll_event, yasl_cpu_read_pixels, unused event fields and comments that restated names. - Wayland: resize floating windows in request_size instead of reporting it unsupported, and stop marking new GPU surfaces as Vulkan so yasl_vulkan_create_surface no longer fails with NATIVE_WINDOW_IN_USE. Also includes the in-progress public API refactor that was uncommitted in the tree (removal of the app/loop layer, new surface, GL and Vulkan entry points). Co-Authored-By: Claude Opus 5.5 --- src/platform/wayland/ime.c | 147 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 122 insertions(+), 25 deletions(-) (limited to 'src/platform/wayland/ime.c') diff --git a/src/platform/wayland/ime.c b/src/platform/wayland/ime.c index 4a091d8..62e5936 100644 --- a/src/platform/wayland/ime.c +++ b/src/platform/wayland/ime.c @@ -1,5 +1,7 @@ #include "internal.h" +#include + #define IME_TEXT_MAX (256u * 1024) static void show_preedit(struct wayland_surface *w, char *text, int begin, int end) @@ -12,7 +14,7 @@ static void show_preedit(struct wayland_surface *w, char *text, int begin, int e w->ime_shown_begin = begin; w->ime_shown_end = end; if (changed) { - yasl_surface_notify_composition(&w->base, w->ime_shown, + yasl_private_surface_notify_composition(&w->base, w->ime_shown, w->ime_shown_begin, w->ime_shown_end); wayland_surface_schedule_redraw(w); } @@ -24,21 +26,63 @@ void ime_reset(struct wayland_surface *w) w->ime_preedit = NULL; free(w->ime_commit); w->ime_commit = NULL; + w->ime_delete_pending = false; show_preedit(w, NULL, -1, -1); } void ime_send_caret(struct wayland_surface *w) { + wayland_surface_update_text_input(w); +} + +bool wayland_surface_update_text_input(struct wayland_surface *w) +{ + if (!w || !w->conn) { + errno = EINVAL; + return false; + } + if (!w->conn->text_input_mgr) { + errno = ENOTSUP; + return false; + } struct seat *seat = w->ime_seat; - if (!seat || !seat->text_input) - return; - uint32_t scale = w->scale_120 ? w->scale_120 : YASL_SCALE_UNIT; + if (!seat || !seat->text_input || seat->ime_focus != w) + return true; + if (!w->base.text_input_active) { + if (seat->ime_enabled) { + zwp_text_input_v3_disable(seat->text_input); + zwp_text_input_v3_commit(seat->text_input); + seat->ime_enabled = false; + } + ime_reset(w); + return true; + } + if (!seat->ime_enabled) { + zwp_text_input_v3_enable(seat->text_input); + seat->ime_enabled = true; + } + unsigned purpose = w->base.text_purpose; + if (purpose > ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL) + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; + zwp_text_input_v3_set_content_type(seat->text_input, + ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, purpose); + const char *surrounding = w->base.text_surrounding ? w->base.text_surrounding : ""; + size_t length = w->base.text_surrounding ? w->base.text_length : 0; + size_t cursor = w->base.text_surrounding ? w->base.text_end : 0; + size_t anchor = w->base.text_surrounding ? w->base.text_begin : 0; + if (cursor > length) cursor = length; + if (anchor > length) anchor = length; + if (length <= INT32_MAX && cursor <= INT32_MAX && anchor <= INT32_MAX) + zwp_text_input_v3_set_surrounding_text(seat->text_input, surrounding, + (int32_t)cursor, (int32_t)anchor); + uint32_t scale = w->scale_120 ? w->scale_120 : YASL_PRIVATE_SCALE_UNIT; zwp_text_input_v3_set_cursor_rectangle(seat->text_input, - yasl_scale_to_logical(w->ime_caret.x, scale), - yasl_scale_to_logical(w->ime_caret.y, scale), - yasl_scale_to_logical(w->ime_caret.width, scale), - yasl_scale_to_logical(w->ime_caret.height, scale)); + yasl_private_scale_to_logical(w->ime_caret.x, scale), + yasl_private_scale_to_logical(w->ime_caret.y, scale), + yasl_private_scale_to_logical(w->ime_caret.width, scale), + yasl_private_scale_to_logical(w->ime_caret.height, scale)); zwp_text_input_v3_commit(seat->text_input); + return true; } bool wayland_surface_ime_preedit_cursor(const struct wayland_surface *w, int *begin, int *end) @@ -50,7 +94,7 @@ bool wayland_surface_ime_preedit_cursor(const struct wayland_surface *w, int *be return true; } -void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_rect2i caret) +void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_private_rect2i caret) { if (!w || (w->ime_caret.x == caret.x && w->ime_caret.y == caret.y && w->ime_caret.width == caret.width && w->ime_caret.height == caret.height)) @@ -62,17 +106,14 @@ void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_rect2i static void text_input_enter(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface) { + (void)text_input; struct seat *s = data; struct wayland_surface *w = surface_root(surface ? (struct wayland_surface *)wl_surface_get_user_data(surface) : NULL); s->ime_focus = w; if (!w) return; - zwp_text_input_v3_enable(text_input); - zwp_text_input_v3_set_content_type(text_input, - ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, - ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL); w->ime_seat = s; - ime_send_caret(w); + wayland_surface_update_text_input(w); } static void text_input_leave(void *data, struct zwp_text_input_v3 *text_input, @@ -83,8 +124,11 @@ static void text_input_leave(void *data, struct zwp_text_input_v3 *text_input, struct wayland_surface *w = s->ime_focus; s->ime_focus = NULL; if (!w) return; - zwp_text_input_v3_disable(text_input); - zwp_text_input_v3_commit(text_input); + if (s->ime_enabled) { + zwp_text_input_v3_disable(text_input); + zwp_text_input_v3_commit(text_input); + s->ime_enabled = false; + } if (w->ime_seat == s) w->ime_seat = NULL; ime_reset(w); @@ -110,7 +154,7 @@ static void text_input_preedit_string(void *data, { (void)text_input; struct wayland_surface *w = ((struct seat *)data)->ime_focus; - if (!w) return; + if (!w || !w->base.text_input_active) return; keyboard_text_reset((struct seat *)data); free(w->ime_preedit); w->ime_preedit = text ? copy_text(text) : NULL; @@ -126,7 +170,7 @@ static void text_input_commit_string(void *data, { (void)text_input; struct wayland_surface *w = ((struct seat *)data)->ime_focus; - if (!w) return; + if (!w || !w->base.text_input_active) return; keyboard_text_reset((struct seat *)data); free(w->ime_commit); w->ime_commit = (text && text[0]) ? copy_text(text) : NULL; @@ -136,7 +180,31 @@ static void text_input_delete_surrounding_text(void *data, struct zwp_text_input_v3 *text_input, uint32_t before_length, uint32_t after_length) { - (void)data; (void)text_input; (void)before_length; (void)after_length; + (void)text_input; + struct seat *seat = data; + struct wayland_surface *w = seat->ime_focus; + if (!w || !w->base.text_input_active || !w->base.text_surrounding) + return; + size_t length = w->base.text_length; + size_t cursor = w->base.text_end <= length ? w->base.text_end : length; + while (cursor && cursor < length && + (((unsigned char)w->base.text_surrounding[cursor] & 0xc0) == 0x80)) + cursor--; + size_t begin = before_length > cursor ? 0 : cursor - before_length; + size_t end = after_length > length - cursor ? length : cursor + after_length; + while (begin && begin < length && + (((unsigned char)w->base.text_surrounding[begin] & 0xc0) == 0x80)) + begin--; + while (end < length && (((unsigned char)w->base.text_surrounding[end] & 0xc0) == 0x80)) + end++; + /* text_input_v3 batches deletion and commit into one `done` group. Keep + * the replacement range paired with that group's committed text and + * enqueue both together below, preserving snapshot metadata. */ + w->ime_delete_pending = true; + w->ime_delete_begin = begin; + w->ime_delete_end = end; + w->ime_delete_revision = w->base.text_revision; + w->ime_delete_generation = w->base.text_generation; } static void text_input_done(void *data, struct zwp_text_input_v3 *text_input, @@ -144,17 +212,46 @@ static void text_input_done(void *data, struct zwp_text_input_v3 *text_input, { (void)text_input; (void)serial; struct wayland_surface *w = ((struct seat *)data)->ime_focus; - if (!w) return; - if (w->ime_commit) { - struct yasl_event ev = { - .type = YASL_EVENT_TEXT, + if (!w || !w->base.text_input_active) return; + bool has_snapshot = w->base.text_surrounding != NULL && + w->base.text_length <= INT32_MAX; + bool has_commit = w->ime_commit && w->ime_commit[0]; + bool edit = w->ime_delete_pending || (has_commit && has_snapshot); + if (edit) { + size_t begin = w->ime_delete_pending ? w->ime_delete_begin : w->base.text_begin; + size_t end = w->ime_delete_pending ? w->ime_delete_end : w->base.text_end; + if (begin > w->base.text_length) begin = w->base.text_length; + if (end > w->base.text_length) end = w->base.text_length; + if (end < begin) end = begin; + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_TEXT_EDIT, + .surface = &w->base, + .text_edit = { + .data = has_commit ? w->ime_commit : "", + .len = has_commit ? strlen(w->ime_commit) : 0, + .revision = w->ime_delete_revision, + .generation = w->ime_delete_generation, + .begin = begin, + .end = end, + }, + }; + if (!w->ime_delete_pending) { + ev.text_edit.revision = w->base.text_revision; + ev.text_edit.generation = w->base.text_generation; + } + input_queue_push(&w->input, &ev); + w->ime_delete_pending = false; + } + if (w->ime_commit && !edit) { + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_TEXT, .surface = &w->base, .text = { .data = w->ime_commit, .len = strlen(w->ime_commit) }, }; input_queue_push(&w->input, &ev); - free(w->ime_commit); - w->ime_commit = NULL; } + free(w->ime_commit); + w->ime_commit = NULL; char *preedit = w->ime_preedit && w->ime_preedit[0] ? w->ime_preedit : NULL; if (!preedit) free(w->ime_preedit); -- cgit v1.2.3