summaryrefslogtreecommitdiff
path: root/src/platform/wayland/ime.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 23:41:31 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 23:41:31 +0300
commit76073fe5fad8799e5850805f2f090af2cb62c741 (patch)
tree9658019babe4760a3634bdab4da0731a10e42e68 /src/platform/wayland/ime.c
parentdd1775a9927922c14f8679ccbc4d3ca8d5917e20 (diff)
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 <noreply@anthropic.com>
Diffstat (limited to 'src/platform/wayland/ime.c')
-rw-r--r--src/platform/wayland/ime.c147
1 files changed, 122 insertions, 25 deletions
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 <errno.h>
+
#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);