diff options
author | xdavidwu <xdavidwuph@gmail.com> | 2019-10-18 18:57:17 +0800 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2020-04-04 11:42:04 +0200 |
commit | 5886187c6ef56307f15be475dc62785faf32ef35 (patch) | |
tree | 535283eca78c9a4ea63f26d9a13c1df48fe2b2f6 /sway/input | |
parent | swapped hiding the cursor and sending a touch event as a more logical sequence (diff) | |
download | sway-5886187c6ef56307f15be475dc62785faf32ef35.tar.gz sway-5886187c6ef56307f15be475dc62785faf32ef35.tar.zst sway-5886187c6ef56307f15be475dc62785faf32ef35.zip |
Port input method and text input from rootston
This ports swaywm/wlroots#1203, swaywm/wlroots#1303,
swaywm/wlroots#1308, swaywm/wlroots#1759 rootston part to sway.
Co-Authored-By: Leo Chen <leo881003@gmail.com>
Diffstat (limited to 'sway/input')
-rw-r--r-- | sway/input/seat.c | 4 | ||||
-rw-r--r-- | sway/input/text_input.c | 312 |
2 files changed, 316 insertions, 0 deletions
diff --git a/sway/input/seat.c b/sway/input/seat.c index 502bc0bc..c3eae65c 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -179,6 +179,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { | |||
179 | 179 | ||
180 | seat_keyboard_notify_enter(seat, view->surface); | 180 | seat_keyboard_notify_enter(seat, view->surface); |
181 | seat_tablet_pads_notify_enter(seat, view->surface); | 181 | seat_tablet_pads_notify_enter(seat, view->surface); |
182 | sway_input_method_relay_set_focus(&seat->im_relay, view->surface); | ||
182 | 183 | ||
183 | struct wlr_pointer_constraint_v1 *constraint = | 184 | struct wlr_pointer_constraint_v1 *constraint = |
184 | wlr_pointer_constraints_v1_constraint_for_surface( | 185 | wlr_pointer_constraints_v1_constraint_for_surface( |
@@ -562,6 +563,8 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
562 | wl_list_init(&seat->keyboard_groups); | 563 | wl_list_init(&seat->keyboard_groups); |
563 | wl_list_init(&seat->keyboard_shortcuts_inhibitors); | 564 | wl_list_init(&seat->keyboard_shortcuts_inhibitors); |
564 | 565 | ||
566 | sway_input_method_relay_init(seat, &seat->im_relay); | ||
567 | |||
565 | wl_list_insert(&server.input->seats, &seat->link); | 568 | wl_list_insert(&server.input->seats, &seat->link); |
566 | 569 | ||
567 | seatop_begin_default(seat); | 570 | seatop_begin_default(seat); |
@@ -1015,6 +1018,7 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1015 | view_close_popups(last_focus->sway_container->view); | 1018 | view_close_popups(last_focus->sway_container->view); |
1016 | } | 1019 | } |
1017 | seat_send_unfocus(last_focus, seat); | 1020 | seat_send_unfocus(last_focus, seat); |
1021 | sway_input_method_relay_set_focus(&seat->im_relay, NULL); | ||
1018 | seat->has_focus = false; | 1022 | seat->has_focus = false; |
1019 | return; | 1023 | return; |
1020 | } | 1024 | } |
diff --git a/sway/input/text_input.c b/sway/input/text_input.c new file mode 100644 index 00000000..3e446cb9 --- /dev/null +++ b/sway/input/text_input.c | |||
@@ -0,0 +1,312 @@ | |||
1 | #include <assert.h> | ||
2 | #include <stdlib.h> | ||
3 | #include "log.h" | ||
4 | #include "sway/input/seat.h" | ||
5 | #include "sway/input/text_input.h" | ||
6 | |||
7 | static struct sway_text_input *relay_get_focusable_text_input( | ||
8 | struct sway_input_method_relay *relay) { | ||
9 | struct sway_text_input *text_input = NULL; | ||
10 | wl_list_for_each(text_input, &relay->text_inputs, link) { | ||
11 | if (text_input->pending_focused_surface) { | ||
12 | return text_input; | ||
13 | } | ||
14 | } | ||
15 | return NULL; | ||
16 | } | ||
17 | |||
18 | static struct sway_text_input *relay_get_focused_text_input( | ||
19 | struct sway_input_method_relay *relay) { | ||
20 | struct sway_text_input *text_input = NULL; | ||
21 | wl_list_for_each(text_input, &relay->text_inputs, link) { | ||
22 | if (text_input->input->focused_surface) { | ||
23 | return text_input; | ||
24 | } | ||
25 | } | ||
26 | return NULL; | ||
27 | } | ||
28 | |||
29 | static void handle_im_commit(struct wl_listener *listener, void *data) { | ||
30 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
31 | input_method_commit); | ||
32 | |||
33 | struct sway_text_input *text_input = relay_get_focused_text_input(relay); | ||
34 | if (!text_input) { | ||
35 | return; | ||
36 | } | ||
37 | struct wlr_input_method_v2 *context = data; | ||
38 | assert(context == relay->input_method); | ||
39 | if (context->current.preedit.text) { | ||
40 | wlr_text_input_v3_send_preedit_string(text_input->input, | ||
41 | context->current.preedit.text, | ||
42 | context->current.preedit.cursor_begin, | ||
43 | context->current.preedit.cursor_end); | ||
44 | } | ||
45 | if (context->current.commit_text) { | ||
46 | wlr_text_input_v3_send_commit_string(text_input->input, | ||
47 | context->current.commit_text); | ||
48 | } | ||
49 | if (context->current.delete.before_length | ||
50 | || context->current.delete.after_length) { | ||
51 | wlr_text_input_v3_send_delete_surrounding_text(text_input->input, | ||
52 | context->current.delete.before_length, | ||
53 | context->current.delete.after_length); | ||
54 | } | ||
55 | wlr_text_input_v3_send_done(text_input->input); | ||
56 | } | ||
57 | |||
58 | static void text_input_set_pending_focused_surface( | ||
59 | struct sway_text_input *text_input, struct wlr_surface *surface) { | ||
60 | text_input->pending_focused_surface = surface; | ||
61 | wl_signal_add(&surface->events.destroy, | ||
62 | &text_input->pending_focused_surface_destroy); | ||
63 | } | ||
64 | |||
65 | static void text_input_clear_pending_focused_surface( | ||
66 | struct sway_text_input *text_input) { | ||
67 | wl_list_remove(&text_input->pending_focused_surface_destroy.link); | ||
68 | wl_list_init(&text_input->pending_focused_surface_destroy.link); | ||
69 | text_input->pending_focused_surface = NULL; | ||
70 | } | ||
71 | |||
72 | static void handle_im_destroy(struct wl_listener *listener, void *data) { | ||
73 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
74 | input_method_destroy); | ||
75 | struct wlr_input_method_v2 *context = data; | ||
76 | assert(context == relay->input_method); | ||
77 | relay->input_method = NULL; | ||
78 | struct sway_text_input *text_input = relay_get_focused_text_input(relay); | ||
79 | if (text_input) { | ||
80 | // keyboard focus is still there, so keep the surface at hand in case | ||
81 | // the input method returns | ||
82 | text_input_set_pending_focused_surface(text_input, | ||
83 | text_input->input->focused_surface); | ||
84 | wlr_text_input_v3_send_leave(text_input->input); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | static void relay_send_im_state(struct sway_input_method_relay *relay, | ||
89 | struct wlr_text_input_v3 *input) { | ||
90 | struct wlr_input_method_v2 *input_method = relay->input_method; | ||
91 | if (!input_method) { | ||
92 | sway_log(SWAY_INFO, "Sending IM_DONE but im is gone"); | ||
93 | return; | ||
94 | } | ||
95 | // TODO: only send each of those if they were modified | ||
96 | wlr_input_method_v2_send_surrounding_text(input_method, | ||
97 | input->current.surrounding.text, input->current.surrounding.cursor, | ||
98 | input->current.surrounding.anchor); | ||
99 | wlr_input_method_v2_send_text_change_cause(input_method, | ||
100 | input->current.text_change_cause); | ||
101 | wlr_input_method_v2_send_content_type(input_method, | ||
102 | input->current.content_type.hint, input->current.content_type.purpose); | ||
103 | wlr_input_method_v2_send_done(input_method); | ||
104 | // TODO: pass intent, display popup size | ||
105 | } | ||
106 | |||
107 | static struct sway_text_input *text_input_to_sway( | ||
108 | struct sway_input_method_relay *relay, | ||
109 | struct wlr_text_input_v3 *text_input) { | ||
110 | struct sway_text_input *sway_text_input = NULL; | ||
111 | wl_list_for_each(sway_text_input, &relay->text_inputs, link) { | ||
112 | if (sway_text_input->input == text_input) { | ||
113 | return sway_text_input; | ||
114 | } | ||
115 | } | ||
116 | return NULL; | ||
117 | } | ||
118 | |||
119 | static void handle_text_input_enable(struct wl_listener *listener, void *data) { | ||
120 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
121 | text_input_enable); | ||
122 | if (relay->input_method == NULL) { | ||
123 | sway_log(SWAY_INFO, "Enabling text input when input method is gone"); | ||
124 | return; | ||
125 | } | ||
126 | struct sway_text_input *text_input = text_input_to_sway(relay, | ||
127 | (struct wlr_text_input_v3*)data); | ||
128 | wlr_input_method_v2_send_activate(relay->input_method); | ||
129 | relay_send_im_state(relay, text_input->input); | ||
130 | } | ||
131 | |||
132 | static void handle_text_input_commit(struct wl_listener *listener, | ||
133 | void *data) { | ||
134 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
135 | text_input_commit); | ||
136 | struct sway_text_input *text_input = text_input_to_sway(relay, | ||
137 | (struct wlr_text_input_v3*)data); | ||
138 | if (!text_input->input->current_enabled) { | ||
139 | sway_log(SWAY_INFO, "Inactive text input tried to commit an update"); | ||
140 | return; | ||
141 | } | ||
142 | sway_log(SWAY_DEBUG, "Text input committed update"); | ||
143 | if (relay->input_method == NULL) { | ||
144 | sway_log(SWAY_INFO, "Text input committed, but input method is gone"); | ||
145 | return; | ||
146 | } | ||
147 | relay_send_im_state(relay, text_input->input); | ||
148 | } | ||
149 | |||
150 | static void relay_disable_text_input(struct sway_input_method_relay *relay, | ||
151 | struct sway_text_input *text_input) { | ||
152 | if (relay->input_method == NULL) { | ||
153 | sway_log(SWAY_DEBUG, "Disabling text input, but input method is gone"); | ||
154 | return; | ||
155 | } | ||
156 | wlr_input_method_v2_send_deactivate(relay->input_method); | ||
157 | relay_send_im_state(relay, text_input->input); | ||
158 | } | ||
159 | |||
160 | static void handle_text_input_disable(struct wl_listener *listener, | ||
161 | void *data) { | ||
162 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
163 | text_input_disable); | ||
164 | struct sway_text_input *text_input = text_input_to_sway(relay, | ||
165 | (struct wlr_text_input_v3*)data); | ||
166 | relay_disable_text_input(relay, text_input); | ||
167 | } | ||
168 | |||
169 | static void handle_text_input_destroy(struct wl_listener *listener, | ||
170 | void *data) { | ||
171 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
172 | text_input_destroy); | ||
173 | struct sway_text_input *text_input = text_input_to_sway(relay, | ||
174 | (struct wlr_text_input_v3*)data); | ||
175 | |||
176 | if (text_input->input->current_enabled) { | ||
177 | relay_disable_text_input(relay, text_input); | ||
178 | } | ||
179 | text_input_clear_pending_focused_surface(text_input); | ||
180 | wl_list_remove(&text_input->link); | ||
181 | text_input->input = NULL; | ||
182 | free(text_input); | ||
183 | } | ||
184 | |||
185 | static void handle_pending_focused_surface_destroy(struct wl_listener *listener, | ||
186 | void *data) { | ||
187 | struct sway_text_input *text_input = wl_container_of(listener, text_input, | ||
188 | pending_focused_surface_destroy); | ||
189 | struct wlr_surface *surface = data; | ||
190 | assert(text_input->pending_focused_surface == surface); | ||
191 | text_input->pending_focused_surface = NULL; | ||
192 | } | ||
193 | |||
194 | struct sway_text_input *sway_text_input_create( | ||
195 | struct sway_input_method_relay *relay, | ||
196 | struct wlr_text_input_v3 *text_input) { | ||
197 | struct sway_text_input *input = calloc(1, sizeof(struct sway_text_input)); | ||
198 | if (!input) { | ||
199 | return NULL; | ||
200 | } | ||
201 | input->input = text_input; | ||
202 | input->relay = relay; | ||
203 | |||
204 | relay->text_input_enable.notify = handle_text_input_enable; | ||
205 | wl_signal_add(&text_input->events.enable, &relay->text_input_enable); | ||
206 | |||
207 | relay->text_input_commit.notify = handle_text_input_commit; | ||
208 | wl_signal_add(&text_input->events.commit, &relay->text_input_commit); | ||
209 | |||
210 | relay->text_input_disable.notify = handle_text_input_disable; | ||
211 | wl_signal_add(&text_input->events.disable, &relay->text_input_disable); | ||
212 | |||
213 | relay->text_input_destroy.notify = handle_text_input_destroy; | ||
214 | wl_signal_add(&text_input->events.destroy, &relay->text_input_destroy); | ||
215 | |||
216 | input->pending_focused_surface_destroy.notify = | ||
217 | handle_pending_focused_surface_destroy; | ||
218 | wl_list_init(&input->pending_focused_surface_destroy.link); | ||
219 | return input; | ||
220 | } | ||
221 | |||
222 | static void relay_handle_text_input(struct wl_listener *listener, | ||
223 | void *data) { | ||
224 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
225 | text_input_new); | ||
226 | struct wlr_text_input_v3 *wlr_text_input = data; | ||
227 | if (relay->seat->wlr_seat != wlr_text_input->seat) { | ||
228 | return; | ||
229 | } | ||
230 | |||
231 | struct sway_text_input *text_input = sway_text_input_create(relay, | ||
232 | wlr_text_input); | ||
233 | if (!text_input) { | ||
234 | return; | ||
235 | } | ||
236 | wl_list_insert(&relay->text_inputs, &text_input->link); | ||
237 | } | ||
238 | |||
239 | static void relay_handle_input_method(struct wl_listener *listener, | ||
240 | void *data) { | ||
241 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
242 | input_method_new); | ||
243 | struct wlr_input_method_v2 *input_method = data; | ||
244 | if (relay->seat->wlr_seat != input_method->seat) { | ||
245 | return; | ||
246 | } | ||
247 | |||
248 | if (relay->input_method != NULL) { | ||
249 | sway_log(SWAY_INFO, "Attempted to connect second input method to a seat"); | ||
250 | wlr_input_method_v2_send_unavailable(input_method); | ||
251 | return; | ||
252 | } | ||
253 | |||
254 | relay->input_method = input_method; | ||
255 | wl_signal_add(&relay->input_method->events.commit, | ||
256 | &relay->input_method_commit); | ||
257 | relay->input_method_commit.notify = handle_im_commit; | ||
258 | wl_signal_add(&relay->input_method->events.destroy, | ||
259 | &relay->input_method_destroy); | ||
260 | relay->input_method_destroy.notify = handle_im_destroy; | ||
261 | |||
262 | struct sway_text_input *text_input = relay_get_focusable_text_input(relay); | ||
263 | if (text_input) { | ||
264 | wlr_text_input_v3_send_enter(text_input->input, | ||
265 | text_input->pending_focused_surface); | ||
266 | text_input_clear_pending_focused_surface(text_input); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | void sway_input_method_relay_init(struct sway_seat *seat, | ||
271 | struct sway_input_method_relay *relay) { | ||
272 | relay->seat = seat; | ||
273 | wl_list_init(&relay->text_inputs); | ||
274 | |||
275 | relay->text_input_new.notify = relay_handle_text_input; | ||
276 | wl_signal_add(&server.text_input->events.text_input, | ||
277 | &relay->text_input_new); | ||
278 | |||
279 | relay->input_method_new.notify = relay_handle_input_method; | ||
280 | wl_signal_add( | ||
281 | &server.input_method->events.input_method, | ||
282 | &relay->input_method_new); | ||
283 | } | ||
284 | |||
285 | void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay, | ||
286 | struct wlr_surface *surface) { | ||
287 | struct sway_text_input *text_input; | ||
288 | wl_list_for_each(text_input, &relay->text_inputs, link) { | ||
289 | if (text_input->pending_focused_surface) { | ||
290 | assert(text_input->input->focused_surface == NULL); | ||
291 | if (surface != text_input->pending_focused_surface) { | ||
292 | text_input_clear_pending_focused_surface(text_input); | ||
293 | } | ||
294 | } else if (text_input->input->focused_surface) { | ||
295 | assert(text_input->pending_focused_surface == NULL); | ||
296 | if (surface != text_input->input->focused_surface) { | ||
297 | relay_disable_text_input(relay, text_input); | ||
298 | wlr_text_input_v3_send_leave(text_input->input); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | if (surface | ||
303 | && wl_resource_get_client(text_input->input->resource) | ||
304 | == wl_resource_get_client(surface->resource)) { | ||
305 | if (relay->input_method) { | ||
306 | wlr_text_input_v3_send_enter(text_input->input, surface); | ||
307 | } else { | ||
308 | text_input_set_pending_focused_surface(text_input, surface); | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | } | ||