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, &registry_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