1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
#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) {
yasl_surface_notify_composition(&w->base, w->ime_shown,
w->ime_shown_begin, w->ime_shown_end);
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);
}
/* Copy protocol text, which libwayland always NUL-terminates, up to
* IME_TEXT_MAX bytes. Measuring with strlen keeps GCC from flagging a
* bounded read past a short caller buffer. */
static char *copy_text(const char *text)
{
size_t len = strlen(text);
if (len > IME_TEXT_MAX) len = IME_TEXT_MAX;
char *copy = malloc(len + 1);
if (!copy) return NULL;
memcpy(copy, text, len);
copy[len] = '\0';
return copy;
}
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 ? copy_text(text) : 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]) ? copy_text(text) : 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,
};
|