#include "internal.h" #define IME_TEXT_MAX (256u * 1024) static void show_preedit(struct wayland_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) wayland_surface_schedule_redraw(w); } void ime_reset(struct wayland_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 wayland_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); } bool wayland_surface_ime_preedit_cursor(const struct wayland_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 wayland_surface_set_ime_caret(struct wayland_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 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); } 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 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 (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 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; 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 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; } 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 wayland_surface *w = ((struct seat *)data)->ime_focus; if (!w) return; if (w->ime_commit) { struct yasl_event ev = { .type = YASL_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; } 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, };