summaryrefslogtreecommitdiff
path: root/src/wayland/ime.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/wayland/ime.c')
-rw-r--r--src/wayland/ime.c174
1 files changed, 174 insertions, 0 deletions
diff --git a/src/wayland/ime.c b/src/wayland/ime.c
new file mode 100644
index 0000000..9da019c
--- /dev/null
+++ b/src/wayland/ime.c
@@ -0,0 +1,174 @@
+#include "internal.h"
+
+#define IME_TEXT_MAX (256u * 1024)
+
+static void show_preedit(struct yasl_surface *w, char *text, int begin, int end)
+{
+ bool changed = (text || w->ime_shown) &&
+ (!text || !w->ime_shown || strcmp(text, w->ime_shown) ||
+ begin != w->ime_shown_begin || end != w->ime_shown_end);
+ free(w->ime_shown);
+ w->ime_shown = text;
+ w->ime_shown_begin = begin;
+ w->ime_shown_end = end;
+ if (changed)
+ yasl_surface_schedule_redraw(w);
+}
+
+void ime_reset(struct yasl_surface *w)
+{
+ free(w->ime_preedit);
+ w->ime_preedit = NULL;
+ free(w->ime_commit);
+ w->ime_commit = NULL;
+ show_preedit(w, NULL, -1, -1);
+}
+
+void ime_send_caret(struct yasl_surface *w)
+{
+ struct seat *seat = w->ime_seat;
+ if (!seat || !seat->text_input)
+ return;
+ uint32_t scale = w->scale_120 ? w->scale_120 : YASL_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));
+ zwp_text_input_v3_commit(seat->text_input);
+}
+
+const char *yasl_surface_ime_preedit(const struct yasl_surface *w)
+{
+ return w ? w->ime_shown : NULL;
+}
+
+bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *w, int *begin, int *end)
+{
+ if (!w || !w->ime_shown || w->ime_shown_begin < 0)
+ return false;
+ *begin = w->ime_shown_begin;
+ *end = w->ime_shown_end;
+ return true;
+}
+
+void yasl_surface_set_ime_caret(struct yasl_surface *w, struct yasl_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))
+ return;
+ w->ime_caret = caret;
+ ime_send_caret(w);
+}
+
+static void text_input_enter(void *data, struct zwp_text_input_v3 *text_input,
+ struct wl_surface *surface)
+{
+ struct seat *s = data;
+ struct yasl_surface *w = surface_root(conn_surface_for(s->conn, surface));
+ 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);
+}
+
+static void text_input_leave(void *data, struct zwp_text_input_v3 *text_input,
+ struct wl_surface *surface)
+{
+ (void)surface;
+ struct seat *s = data;
+ struct yasl_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 (w->ime_seat == s)
+ w->ime_seat = NULL;
+ ime_reset(w);
+}
+
+static void text_input_preedit_string(void *data,
+ struct zwp_text_input_v3 *text_input, const char *text,
+ int32_t cursor_begin, int32_t cursor_end)
+{
+ (void)text_input;
+ struct yasl_surface *w = ((struct seat *)data)->ime_focus;
+ if (!w) return;
+ 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;
+ bool shown = cursor_begin >= 0 && cursor_end >= cursor_begin &&
+ cursor_end <= len;
+ w->ime_cursor_begin = shown ? cursor_begin : -1;
+ w->ime_cursor_end = shown ? cursor_end : -1;
+}
+
+static void text_input_commit_string(void *data,
+ struct zwp_text_input_v3 *text_input, const char *text)
+{
+ (void)text_input;
+ struct yasl_surface *w = ((struct seat *)data)->ime_focus;
+ if (!w) return;
+ free(w->ime_commit);
+ w->ime_commit = (text && text[0]) ? strndup(text, IME_TEXT_MAX) : NULL;
+}
+
+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;
+}
+
+static void text_input_done(void *data, struct zwp_text_input_v3 *text_input,
+ uint32_t serial)
+{
+ (void)text_input; (void)serial;
+ struct yasl_surface *w = ((struct seat *)data)->ime_focus;
+ if (!w) return;
+ if (w->ime_commit) {
+ struct yasl_input_event ev = {
+ .kind = YASL_INPUT_TEXT,
+ .surface = w,
+ .text = { .text = w->ime_commit, .len = strlen(w->ime_commit) },
+ };
+ input_queue_push(&w->input, &ev);
+ 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);
+ w->ime_preedit = NULL;
+ show_preedit(w, preedit, preedit ? w->ime_cursor_begin : -1,
+ preedit ? w->ime_cursor_end : -1);
+ w->ime_cursor_begin = w->ime_cursor_end = -1;
+}
+
+static void text_input_action(void *data, struct zwp_text_input_v3 *text_input,
+ uint32_t action, uint32_t serial)
+{ (void)data; (void)text_input; (void)action; (void)serial; }
+
+static void text_input_language(void *data, struct zwp_text_input_v3 *text_input,
+ const char *language)
+{ (void)data; (void)text_input; (void)language; }
+
+static void text_input_preedit_hint(void *data, struct zwp_text_input_v3 *text_input,
+ uint32_t start, uint32_t end, uint32_t hint)
+{ (void)data; (void)text_input; (void)start; (void)end; (void)hint; }
+
+const struct zwp_text_input_v3_listener text_input_listener = {
+ .action = text_input_action,
+ .language = text_input_language,
+ .preedit_hint = text_input_preedit_hint,
+ .enter = text_input_enter,
+ .leave = text_input_leave,
+ .preedit_string = text_input_preedit_string,
+ .commit_string = text_input_commit_string,
+ .delete_surrounding_text = text_input_delete_surrounding_text,
+ .done = text_input_done,
+};