1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2013, 2017 Jan Arne Petersen
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "gtkimcontextwayland.h"
19
20 #include "wayland-text-input-unstable-v1-client-protocol.h"
21
22 #include <gdk/gdk.h>
23 #include "gdk/gdkwayland.h"
24
25 #include <xkbcommon/xkbcommon.h>
26
27 #include <string.h>
28
29 struct _GtkIMContextWaylandPrivate
30 {
31 struct zwp_text_input_v1 *text_input;
32
33 GdkWindow *window;
34 GdkRectangle cursor_rectangle;
35
36 /* Current pre-edit */
37 PangoAttrList *preedit_attrs;
38 int32_t preedit_cursor;
39 char *preedit_text;
40 char *preedit_commit;
41
42 /* Pending pre-edit */
43 PangoAttrList *pending_preedit_attrs;
44 int32_t pending_preedit_cursor;
45
46 /* Pending commit */
47 int32_t pending_delete_index;
48 uint32_t pending_delete_length;
49 int32_t pending_cursor;
50 int32_t pending_anchor;
51
52 uint32_t serial;
53 uint32_t reset_serial;
54 };
55
56 static struct zwp_text_input_manager_v1 *text_input_manager = NULL;
57
58 G_DEFINE_DYNAMIC_TYPE (GtkIMContextWayland, gtk_im_context_wayland, GTK_TYPE_IM_CONTEXT);
59
60 static void
registry_handle_global(void * data,struct wl_registry * registry,uint32_t id,const char * interface,uint32_t version)61 registry_handle_global (void *data,
62 struct wl_registry *registry,
63 uint32_t id,
64 const char *interface,
65 uint32_t version)
66 {
67 if (strcmp (interface, "zwp_text_input_manager_v1") == 0)
68 text_input_manager = wl_registry_bind (registry, id, &zwp_text_input_manager_v1_interface, 1);
69 }
70
71 static void
registry_handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)72 registry_handle_global_remove (void *data,
73 struct wl_registry *registry,
74 uint32_t name)
75 {
76 }
77
78 static const struct wl_registry_listener registry_listener =
79 {
80 registry_handle_global,
81 registry_handle_global_remove
82 };
83
84 static gpointer
bind_text_input_manager(gpointer data)85 bind_text_input_manager (gpointer data)
86 {
87 GdkDisplay *display;
88 struct wl_display *wl_display;
89 struct wl_registry *registry;
90
91 display = gdk_display_get_default ();
92 wl_display = gdk_wayland_display_get_wl_display (display);
93 registry = wl_display_get_registry (wl_display);
94 wl_registry_add_listener (registry, ®istry_listener, NULL);
95 wl_display_dispatch (wl_display);
96
97 return NULL;
98 }
99
100 static void
ensure_text_input_manager(void)101 ensure_text_input_manager (void)
102 {
103 static GOnce text_input_manager_once = G_ONCE_INIT;
104 g_once (&text_input_manager_once, bind_text_input_manager, NULL);
105 }
106
107 static void
reset_preedit(GtkIMContextWayland * self)108 reset_preedit (GtkIMContextWayland *self)
109 {
110 GtkIMContextWaylandPrivate *priv = self->priv;
111 gboolean old_preedit;
112
113 old_preedit = priv->preedit_text && priv->preedit_text[0] != '\0';
114
115 g_clear_pointer (&priv->preedit_text, g_free);
116 priv->preedit_cursor = 0;
117 g_clear_pointer (&priv->preedit_attrs, (GDestroyNotify) pango_attr_list_unref);
118
119 g_clear_pointer (&priv->pending_preedit_attrs, (GDestroyNotify) pango_attr_list_unref);
120 priv->pending_preedit_cursor = 0;
121
122 if (old_preedit)
123 {
124 g_signal_emit_by_name (self, "preedit-changed");
125 g_signal_emit_by_name (self, "preedit-end");
126 }
127 }
128
129 static gboolean
check_serial(GtkIMContextWayland * self,uint32_t serial)130 check_serial (GtkIMContextWayland *self,
131 uint32_t serial)
132 {
133 GtkIMContextWaylandPrivate *priv = self->priv;
134
135 return priv->serial - serial > priv->serial - priv->reset_serial;
136 }
137
138 static uint32_t
to_wayland_hints(GtkInputHints hints,GtkInputPurpose purpose)139 to_wayland_hints(GtkInputHints hints, GtkInputPurpose purpose) {
140 uint32_t wl_hints = ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE;
141
142 if (hints & GTK_INPUT_HINT_SPELLCHECK)
143 wl_hints |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CORRECTION;
144 if (hints & GTK_INPUT_HINT_NO_SPELLCHECK)
145 wl_hints &= ~ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CORRECTION;
146 if (hints & GTK_INPUT_HINT_WORD_COMPLETION)
147 wl_hints |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION;
148 if (hints & GTK_INPUT_HINT_LOWERCASE)
149 wl_hints |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_LOWERCASE;
150 if (hints & GTK_INPUT_HINT_UPPERCASE_CHARS)
151 wl_hints |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_UPPERCASE;
152 if (hints & GTK_INPUT_HINT_UPPERCASE_WORDS)
153 wl_hints |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_TITLECASE;
154 if (hints & GTK_INPUT_HINT_UPPERCASE_SENTENCES)
155 wl_hints |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CAPITALIZATION;
156
157 if (purpose == GTK_INPUT_PURPOSE_PASSWORD ||
158 purpose == GTK_INPUT_PURPOSE_PIN) {
159 wl_hints |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_PASSWORD;
160 }
161
162 return wl_hints;
163 }
164
165 static uint32_t
to_wayland_purpose(GtkInputPurpose purpose)166 to_wayland_purpose(GtkInputPurpose purpose) {
167 switch (purpose) {
168 case GTK_INPUT_PURPOSE_FREE_FORM:
169 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NORMAL;
170 case GTK_INPUT_PURPOSE_ALPHA:
171 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_ALPHA;
172 case GTK_INPUT_PURPOSE_DIGITS:
173 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DIGITS;
174 case GTK_INPUT_PURPOSE_NUMBER:
175 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER;
176 case GTK_INPUT_PURPOSE_PHONE:
177 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PHONE;
178 case GTK_INPUT_PURPOSE_URL:
179 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_URL;
180 case GTK_INPUT_PURPOSE_EMAIL:
181 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_EMAIL;
182 case GTK_INPUT_PURPOSE_NAME:
183 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NAME;
184 case GTK_INPUT_PURPOSE_PASSWORD:
185 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD;
186 case GTK_INPUT_PURPOSE_PIN:
187 return ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DIGITS;
188 }
189 }
190
191 static void
update_text_input_state(GtkIMContextWayland * self)192 update_text_input_state (GtkIMContextWayland *self)
193 {
194 GtkIMContextWaylandPrivate *priv = self->priv;
195 char *surrounding = NULL;
196 int cursor;
197 GtkInputHints hints;
198 GtkInputPurpose purpose;
199
200 zwp_text_input_v1_set_cursor_rectangle (priv->text_input,
201 priv->cursor_rectangle.x,
202 priv->cursor_rectangle.y,
203 priv->cursor_rectangle.width,
204 priv->cursor_rectangle.height);
205
206 if (gtk_im_context_get_surrounding (GTK_IM_CONTEXT (self),
207 &surrounding,
208 &cursor))
209 {
210 /* anchor is not supported in GtkIMContext */
211 zwp_text_input_v1_set_surrounding_text (priv->text_input,
212 surrounding,
213 cursor, cursor);
214
215 g_free (surrounding);
216 }
217
218 g_object_get (self,
219 "input-hints", &hints,
220 "input-purpose", &purpose,
221 NULL);
222
223 zwp_text_input_v1_set_content_type(priv->text_input,
224 to_wayland_hints(hints, purpose),
225 to_wayland_purpose(purpose));
226
227 priv->serial += 1;
228 zwp_text_input_v1_commit_state (priv->text_input, priv->serial);
229 }
230
231 static void
text_input_commit_string(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,const char * text)232 text_input_commit_string (void *data,
233 struct zwp_text_input_v1 *text_input,
234 uint32_t serial,
235 const char *text)
236 {
237 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (data);
238 GtkIMContextWaylandPrivate *priv = self->priv;
239 gchar *surrounding = NULL;
240 gint cursor;
241 glong delete_offset, delete_length;
242
243 if (check_serial (self, serial))
244 {
245 /* Ignore commit ? reset_pending(); */
246 return;
247 }
248
249 reset_preedit (self);
250 g_clear_pointer (&priv->preedit_commit, (GDestroyNotify) g_free);
251
252 if (priv->pending_delete_length)
253 {
254 gchar *p;
255
256 gtk_im_context_get_surrounding (GTK_IM_CONTEXT (self),
257 &surrounding,
258 &cursor);
259
260 p = surrounding + cursor;
261 delete_offset = g_utf8_pointer_to_offset (p,
262 p + priv->pending_delete_index);
263
264 p += priv->pending_delete_index;
265 delete_length = g_utf8_pointer_to_offset (p,
266 p + priv->pending_delete_length);
267 g_free (surrounding);
268
269 gtk_im_context_delete_surrounding (GTK_IM_CONTEXT (self),
270 delete_offset, delete_length);
271 }
272
273 g_signal_emit_by_name (self, "commit", text);
274
275 priv->pending_delete_index = 0;
276 priv->pending_delete_length = 0;
277 priv->pending_cursor = 0;
278 priv->pending_anchor = 0;
279
280 /* GTK+ does not support to change cursor/anchor */
281
282 update_text_input_state (self);
283 }
284
285 static void
text_input_preedit_string(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,const char * text,const char * commit)286 text_input_preedit_string (void *data,
287 struct zwp_text_input_v1 *text_input,
288 uint32_t serial,
289 const char *text,
290 const char *commit)
291 {
292 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (data);
293 GtkIMContextWaylandPrivate *priv = self->priv;
294 gboolean old_preedit;
295
296 old_preedit = priv->preedit_text && priv->preedit_text[0] != '\0';
297
298 if (check_serial (self, serial))
299 return;
300
301 priv->preedit_attrs = priv->pending_preedit_attrs;
302 priv->pending_preedit_attrs = NULL;
303 priv->preedit_cursor = priv->pending_preedit_cursor;
304 priv->pending_preedit_cursor = 0;
305
306 g_clear_pointer (&priv->preedit_text, g_free);
307 priv->preedit_text = g_strdup (text);
308
309 g_clear_pointer (&priv->preedit_commit, g_free);
310 priv->preedit_commit = g_strdup (commit);
311
312 if (!old_preedit)
313 g_signal_emit_by_name (self, "preedit-start");
314
315 g_signal_emit_by_name (self, "preedit-changed");
316
317 if (priv->preedit_text && priv->preedit_text[0] != '\0')
318 g_signal_emit_by_name (self, "preedit-end");
319
320 update_text_input_state (self);
321 }
322
323 static void
text_input_delete_surrounding_text(void * data,struct zwp_text_input_v1 * text_input,int32_t index,uint32_t length)324 text_input_delete_surrounding_text (void *data,
325 struct zwp_text_input_v1 *text_input,
326 int32_t index,
327 uint32_t length)
328 {
329 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (data);
330 GtkIMContextWaylandPrivate *priv = self->priv;
331
332 priv->pending_delete_index = index;
333 priv->pending_delete_length = length;
334 }
335
336 static void
text_input_preedit_styling(void * data,struct zwp_text_input_v1 * text_input,uint32_t index,uint32_t length,uint32_t style)337 text_input_preedit_styling (void *data,
338 struct zwp_text_input_v1 *text_input,
339 uint32_t index,
340 uint32_t length,
341 uint32_t style)
342 {
343 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (data);
344 GtkIMContextWaylandPrivate *priv = self->priv;
345 PangoAttribute *attr = NULL;
346
347 if (length == 0)
348 return;
349
350 if (!priv->pending_preedit_attrs)
351 priv->pending_preedit_attrs = pango_attr_list_new ();
352
353 switch (style)
354 {
355 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_DEFAULT:
356 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE:
357 attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
358 break;
359 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INCORRECT:
360 attr = pango_attr_underline_new (PANGO_UNDERLINE_ERROR);
361 break;
362 }
363
364 if (attr)
365 {
366 attr->start_index = index;
367 attr->end_index = index + length;
368 pango_attr_list_insert (priv->pending_preedit_attrs, attr);
369 }
370 }
371
372 static void
text_input_cursor_position(void * data,struct zwp_text_input_v1 * text_input,int32_t index,int32_t anchor)373 text_input_cursor_position (void *data,
374 struct zwp_text_input_v1 *text_input,
375 int32_t index,
376 int32_t anchor)
377 {
378 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (data);
379 GtkIMContextWaylandPrivate *priv = self->priv;
380
381 priv->pending_cursor = index;
382 priv->pending_anchor = anchor;
383 }
384
385 static void
text_input_preedit_cursor(void * data,struct zwp_text_input_v1 * text_input,int32_t index)386 text_input_preedit_cursor (void *data,
387 struct zwp_text_input_v1 *text_input,
388 int32_t index)
389 {
390 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (data);
391
392 self->priv->pending_preedit_cursor = index;
393 }
394
395 static void
text_input_modifiers_map(void * data,struct zwp_text_input_v1 * text_input,struct wl_array * map)396 text_input_modifiers_map (void *data,
397 struct zwp_text_input_v1 *text_input,
398 struct wl_array *map)
399 {
400 }
401
402 static void
text_input_keysym(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,uint32_t time,uint32_t sym,uint32_t state,uint32_t modifiers)403 text_input_keysym (void *data,
404 struct zwp_text_input_v1 *text_input,
405 uint32_t serial,
406 uint32_t time,
407 uint32_t sym,
408 uint32_t state,
409 uint32_t modifiers)
410 {
411 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (data);
412 GtkIMContextWaylandPrivate *priv = self->priv;
413 GdkDisplay *display;
414 GdkSeat *seat;
415 GdkEvent *event;
416 struct timespec ts;
417 GdkKeymapKey* keys;
418 gint n_keys;
419
420 display = gdk_display_get_default();
421 seat = gdk_display_get_default_seat (display);
422
423 reset_preedit(self);
424 g_clear_pointer (&priv->preedit_commit, (GDestroyNotify) g_free);
425
426 if (sym == XKB_KEY_NoSymbol)
427 return;
428
429 event = gdk_event_new (state ? GDK_KEY_PRESS : GDK_KEY_RELEASE);
430 event->key.window = priv->window ? g_object_ref (priv->window) : NULL;
431 event->key.send_event = FALSE;
432
433 event->key.time = time;
434 event->key.state = 0; // TODO add support for modifiers
435 event->key.keyval = sym;
436
437 if (event->key.keyval != 0 &&
438 gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(),
439 event->key.keyval, &keys, &n_keys)) {
440 event->key.hardware_keycode = keys[0].keycode;
441 event->key.group = keys[0].group;
442 g_free(keys);
443 }
444
445 event->key.is_modifier = FALSE;
446 gdk_event_set_device(event, gdk_seat_get_keyboard(seat));
447 gdk_event_set_source_device(event, gdk_seat_get_keyboard(seat));
448
449 gdk_event_put(event);
450 gdk_event_free(event);
451 }
452
453 static void
text_input_enter(void * data,struct zwp_text_input_v1 * text_input,struct wl_surface * surface)454 text_input_enter (void *data,
455 struct zwp_text_input_v1 *text_input,
456 struct wl_surface *surface)
457 {
458 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (data);
459 GtkIMContextWaylandPrivate *priv = self->priv;
460
461 update_text_input_state (self);
462
463 priv->reset_serial = priv->serial;
464 }
465
466 static void
text_input_leave(void * data,struct zwp_text_input_v1 * text_input)467 text_input_leave (void *data,
468 struct zwp_text_input_v1 *text_input)
469 {
470 }
471
472 static void
text_input_input_panel_state(void * data,struct zwp_text_input_v1 * text_input,uint32_t state)473 text_input_input_panel_state (void *data,
474 struct zwp_text_input_v1 *text_input,
475 uint32_t state)
476 {
477 }
478
479 static void
text_input_language(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,const char * language)480 text_input_language (void *data,
481 struct zwp_text_input_v1 *text_input,
482 uint32_t serial,
483 const char *language)
484 {
485 /* Not supported in GtkIMContext */
486 }
487
488 static void
text_input_text_direction(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,uint32_t direction)489 text_input_text_direction (void *data,
490 struct zwp_text_input_v1 *text_input,
491 uint32_t serial,
492 uint32_t direction)
493 {
494 /* Not supported in GtkIMContext */
495 }
496
497 static const struct zwp_text_input_v1_listener text_input_listener =
498 {
499 text_input_enter,
500 text_input_leave,
501 text_input_modifiers_map,
502 text_input_input_panel_state,
503 text_input_preedit_string,
504 text_input_preedit_styling,
505 text_input_preedit_cursor,
506 text_input_commit_string,
507 text_input_cursor_position,
508 text_input_delete_surrounding_text,
509 text_input_keysym,
510 text_input_language,
511 text_input_text_direction
512 };
513
514 GtkIMContext *
gtk_im_context_wayland_new(void)515 gtk_im_context_wayland_new (void)
516 {
517 return g_object_new (GTK_TYPE_IM_CONTEXT_WAYLAND, NULL);
518 }
519
520 static void
gtk_im_context_wayland_set_client_window(GtkIMContext * context,GdkWindow * window)521 gtk_im_context_wayland_set_client_window (GtkIMContext *context,
522 GdkWindow *window)
523 {
524 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (context);
525
526 self->priv->window = window;
527 }
528
529 static void
gtk_im_context_wayland_get_preedit_string(GtkIMContext * context,gchar ** str,PangoAttrList ** attrs,gint * cursor_pos)530 gtk_im_context_wayland_get_preedit_string (GtkIMContext *context,
531 gchar **str,
532 PangoAttrList **attrs,
533 gint *cursor_pos)
534 {
535 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (context);
536 GtkIMContextWaylandPrivate *priv = self->priv;
537
538 if (str != NULL)
539 *str = g_strdup (priv->preedit_text ? priv->preedit_text : "");
540
541 if (attrs != NULL)
542 *attrs = priv->preedit_attrs ? pango_attr_list_ref (priv->preedit_attrs) : pango_attr_list_new ();
543
544 if (cursor_pos != NULL)
545 *cursor_pos = priv->preedit_cursor;
546 }
547
548 static void
gtk_im_context_wayland_focus_in(GtkIMContext * context)549 gtk_im_context_wayland_focus_in (GtkIMContext *context)
550 {
551 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (context);
552 GtkIMContextWaylandPrivate *priv = self->priv;
553 GdkDisplay *display;
554 GdkSeat *seat;
555 struct wl_surface *surface;
556
557 g_return_if_fail (GDK_IS_WAYLAND_WINDOW (priv->window));
558 g_return_if_fail (priv->text_input);
559
560 surface = gdk_wayland_window_get_wl_surface (priv->window);
561
562 if (!surface)
563 return;
564
565 display = gdk_display_get_default ();
566 seat = gdk_display_get_default_seat (display);
567
568 zwp_text_input_v1_show_input_panel (priv->text_input);
569 zwp_text_input_v1_activate (priv->text_input,
570 gdk_wayland_seat_get_wl_seat (seat),
571 surface);
572 }
573
574 static void
commit_and_reset_preedit(GtkIMContextWayland * self)575 commit_and_reset_preedit (GtkIMContextWayland *self)
576 {
577 GtkIMContextWaylandPrivate *priv = self->priv;
578
579 reset_preedit (self);
580
581 if (priv->preedit_commit && priv->preedit_commit[0] != '\0')
582 g_signal_emit_by_name (self, "commit", priv->preedit_commit);
583
584 g_clear_pointer (&priv->preedit_commit, (GDestroyNotify) g_free);
585 }
586
587 static void
gtk_im_context_wayland_focus_out(GtkIMContext * context)588 gtk_im_context_wayland_focus_out (GtkIMContext *context)
589 {
590 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (context);
591 GtkIMContextWaylandPrivate *priv = self->priv;
592 GdkDisplay *display;
593 GdkSeat *seat;
594
595 g_return_if_fail (GDK_IS_WAYLAND_WINDOW (priv->window));
596 g_return_if_fail (self->priv->text_input);
597
598 display = gdk_display_get_default ();
599 seat = gdk_display_get_default_seat (display);
600
601 commit_and_reset_preedit (self);
602
603 zwp_text_input_v1_deactivate (priv->text_input,
604 gdk_wayland_seat_get_wl_seat (seat));
605 }
606
607 static void
gtk_im_context_wayland_reset(GtkIMContext * context)608 gtk_im_context_wayland_reset (GtkIMContext *context)
609 {
610 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (context);
611 GtkIMContextWaylandPrivate *priv = self->priv;
612
613 g_return_if_fail (self->priv->text_input);
614
615 commit_and_reset_preedit (self);
616
617 zwp_text_input_v1_reset (priv->text_input);
618
619 update_text_input_state (self);
620
621 priv->reset_serial = priv->serial;
622 }
623
624 static void
gtk_im_context_wayland_set_cursor_location(GtkIMContext * context,GdkRectangle * area)625 gtk_im_context_wayland_set_cursor_location (GtkIMContext *context,
626 GdkRectangle *area)
627 {
628 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (context);
629 GtkIMContextWaylandPrivate *priv = self->priv;
630
631 g_return_if_fail (self->priv->text_input);
632
633 priv->cursor_rectangle.x = area->x;
634 priv->cursor_rectangle.y = area->y;
635 priv->cursor_rectangle.width = area->width;
636 priv->cursor_rectangle.height = area->height;
637
638 update_text_input_state (self);
639 }
640
641 static void
gtk_im_context_wayland_set_use_preedit(GtkIMContext * context,gboolean use_preedit)642 gtk_im_context_wayland_set_use_preedit (GtkIMContext *context,
643 gboolean use_preedit)
644 {
645 }
646
647 static gboolean
gtk_im_context_wayland_filter_keypress(GtkIMContext * context,GdkEventKey * event)648 gtk_im_context_wayland_filter_keypress (GtkIMContext *context,
649 GdkEventKey *event)
650 {
651 GtkIMContextWayland *self;
652 GtkIMContextWaylandPrivate *priv;
653 GdkDisplay *display;
654 GdkModifierType no_text_input_mask;
655
656 g_return_val_if_fail (GTK_IS_IM_CONTEXT_WAYLAND(context), FALSE);
657 g_return_val_if_fail (event, FALSE);
658
659 self = GTK_IM_CONTEXT_WAYLAND (context);
660 priv = self->priv;
661
662 display = gdk_window_get_display (event->window);
663
664 no_text_input_mask = gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
665 GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
666
667 if (event->type == GDK_KEY_PRESS &&
668 (event->state & no_text_input_mask) == 0)
669 {
670 gunichar ch;
671
672 ch = gdk_keyval_to_unicode (event->keyval);
673 if (ch != 0 && !g_unichar_iscntrl (ch)) {
674 char utf8[10];
675 int len;
676
677 len = g_unichar_to_utf8(ch, utf8);
678 utf8[len] = '\0';
679
680 commit_and_reset_preedit(self);
681 g_signal_emit_by_name(self, "commit", utf8);
682 return TRUE;
683 }
684 }
685
686 return FALSE;
687 }
688
689 static void
gtk_im_context_wayland_init(GtkIMContextWayland * self)690 gtk_im_context_wayland_init (GtkIMContextWayland *self)
691 {
692 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
693 GTK_TYPE_IM_CONTEXT_WAYLAND,
694 GtkIMContextWaylandPrivate);
695
696 ensure_text_input_manager ();
697 if (text_input_manager)
698 self->priv->text_input = zwp_text_input_manager_v1_create_text_input (text_input_manager);
699
700 zwp_text_input_v1_add_listener (self->priv->text_input, &text_input_listener, self);
701 }
702
703 static void
gtk_im_context_wayland_finalize(GObject * obj)704 gtk_im_context_wayland_finalize (GObject *obj)
705 {
706 GtkIMContextWayland *self = GTK_IM_CONTEXT_WAYLAND (obj);
707
708 pango_attr_list_unref (self->priv->preedit_attrs);
709 pango_attr_list_unref (self->priv->pending_preedit_attrs);
710 g_free (self->priv->preedit_text);
711 g_free (self->priv->preedit_commit);
712
713 G_OBJECT_CLASS (gtk_im_context_wayland_parent_class)->finalize (obj);
714 }
715
716 static void
gtk_im_context_wayland_class_init(GtkIMContextWaylandClass * klass)717 gtk_im_context_wayland_class_init (GtkIMContextWaylandClass *klass)
718 {
719 GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (klass);
720 GObjectClass *object_class = G_OBJECT_CLASS (klass);
721
722 im_context_class->set_client_window = gtk_im_context_wayland_set_client_window;
723 im_context_class->get_preedit_string = gtk_im_context_wayland_get_preedit_string;
724 im_context_class->focus_in = gtk_im_context_wayland_focus_in;
725 im_context_class->focus_out = gtk_im_context_wayland_focus_out;
726 im_context_class->reset = gtk_im_context_wayland_reset;
727 im_context_class->set_cursor_location = gtk_im_context_wayland_set_cursor_location;
728 im_context_class->set_use_preedit = gtk_im_context_wayland_set_use_preedit;
729 im_context_class->filter_keypress = gtk_im_context_wayland_filter_keypress;
730
731 object_class->finalize = gtk_im_context_wayland_finalize;
732
733 g_type_class_add_private (klass, sizeof (GtkIMContextWaylandPrivate));
734 }
735
736 static void
gtk_im_context_wayland_class_finalize(GtkIMContextWaylandClass * klass)737 gtk_im_context_wayland_class_finalize (GtkIMContextWaylandClass *klass)
738 {
739 }
740
741 void
gtk_im_context_wayland_register(GTypeModule * type_module)742 gtk_im_context_wayland_register (GTypeModule *type_module)
743 {
744 gtk_im_context_wayland_register_type (type_module);
745 }
746