1 /*
2  * Copyright © 2012, 2013 Intel Corporation
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <Ecore.h>
28 #include <Ecore_Evas.h>
29 #include <Ecore_Input.h>
30 
31 #include "wayland_imcontext.h"
32 
33 #define HIDE_TIMER_INTERVAL     0.05
34 
35 static Eina_Bool _clear_hide_timer(void);
36 static Ecore_Timer *_hide_timer  = NULL;
37 
38 struct _WaylandIMContext
39 {
40    Ecore_IMF_Context *ctx;
41 
42    struct zwp_text_input_manager_v1 *text_input_manager;
43    struct zwp_text_input_v1 *text_input;
44 
45    Ecore_Wl2_Window *window;
46    Ecore_Wl2_Input  *input;
47    Evas             *canvas;
48 
49    char *preedit_text;
50    char *preedit_commit;
51    char *language;
52    Eina_List *preedit_attrs;
53    int32_t preedit_cursor;
54 
55    struct
56      {
57         Eina_List *attrs;
58         int32_t cursor;
59      } pending_preedit;
60 
61    struct
62      {
63         int32_t cursor;
64         int32_t anchor;
65         uint32_t delete_index;
66         uint32_t delete_length;
67      } pending_commit;
68 
69    struct
70      {
71         int x;
72         int y;
73         int width;
74         int height;
75         Eina_Bool do_set : 1;
76      } cursor_location;
77 
78    xkb_mod_mask_t control_mask;
79    xkb_mod_mask_t alt_mask;
80    xkb_mod_mask_t shift_mask;
81 
82    uint32_t serial;
83    uint32_t reset_serial;
84    uint32_t content_purpose;
85    uint32_t content_hint;
86 };
87 
88 static unsigned int
utf8_offset_to_characters(const char * str,int offset)89 utf8_offset_to_characters(const char *str, int offset)
90 {
91    int index = 0;
92    unsigned int i = 0;
93 
94    for (; index < offset; i++)
95      {
96         if (eina_unicode_utf8_next_get(str, &index) == 0)
97           break;
98      }
99 
100    return i;
101 }
102 
103 static void
update_state(WaylandIMContext * imcontext)104 update_state(WaylandIMContext *imcontext)
105 {
106    char *surrounding = NULL;
107    int cursor_pos;
108    Ecore_Evas *ee;
109    int canvas_x = 0, canvas_y = 0;
110    Eina_Bool changed = EINA_FALSE;
111 
112    if (!imcontext->ctx)
113      return;
114 
115    /* cursor_pos is a byte index */
116    if (ecore_imf_context_surrounding_get(imcontext->ctx, &surrounding, &cursor_pos))
117      {
118         if (imcontext->text_input)
119           {
120              zwp_text_input_v1_set_surrounding_text(imcontext->text_input,
121                                                     surrounding,
122                                                     cursor_pos, cursor_pos);
123              changed = EINA_TRUE;
124           }
125 
126         if (surrounding)
127           free(surrounding);
128      }
129 
130    if (imcontext->canvas)
131      {
132         ee = ecore_evas_ecore_evas_get(imcontext->canvas);
133         if (ee)
134           ecore_evas_geometry_get(ee, &canvas_x, &canvas_y, NULL, NULL);
135      }
136 
137    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "canvas (x: %d, y: %d)",
138                      canvas_x, canvas_y);
139 
140    if (imcontext->text_input)
141      {
142         if (imcontext->cursor_location.do_set)
143           {
144              zwp_text_input_v1_set_cursor_rectangle(imcontext->text_input,
145                                                     imcontext->cursor_location.x + canvas_x,
146                                                     imcontext->cursor_location.y + canvas_y,
147                                                     imcontext->cursor_location.width,
148                                                     imcontext->cursor_location.height);
149              imcontext->cursor_location.do_set = EINA_FALSE;
150              changed = EINA_TRUE;
151           }
152      }
153 
154    if (changed)
155      zwp_text_input_v1_commit_state(imcontext->text_input, ++imcontext->serial);
156 
157    _clear_hide_timer();
158 }
159 
160 static Eina_Bool
_clear_hide_timer(void)161 _clear_hide_timer(void)
162 {
163    if (_hide_timer)
164      {
165         ecore_timer_del(_hide_timer);
166         _hide_timer = NULL;
167         return EINA_TRUE;
168      }
169 
170    return EINA_FALSE;
171 }
172 
173 static void
_send_input_panel_hide_request(Ecore_IMF_Context * ctx)174 _send_input_panel_hide_request(Ecore_IMF_Context *ctx)
175 {
176    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
177    if (imcontext && imcontext->text_input)
178      zwp_text_input_v1_hide_input_panel(imcontext->text_input);
179 }
180 
181 static Eina_Bool
_hide_timer_handler(void * data)182 _hide_timer_handler(void *data)
183 {
184    Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)data;
185    _send_input_panel_hide_request(ctx);
186 
187    _hide_timer = NULL;
188    return ECORE_CALLBACK_CANCEL;
189 }
190 
191 static void
_input_panel_hide_timer_start(void * data)192 _input_panel_hide_timer_start(void *data)
193 {
194    if (!_hide_timer)
195      {
196         _hide_timer =
197           ecore_timer_add(HIDE_TIMER_INTERVAL, _hide_timer_handler, data);
198      }
199 }
200 
201 static void
_input_panel_hide(Ecore_IMF_Context * ctx,Eina_Bool instant)202 _input_panel_hide(Ecore_IMF_Context *ctx, Eina_Bool instant)
203 {
204    if (instant || (_hide_timer && ecore_timer_pending_get(_hide_timer) <= 0.0))
205      {
206         _clear_hide_timer();
207         _send_input_panel_hide_request(ctx);
208      }
209    else
210      {
211         _input_panel_hide_timer_start(ctx);
212      }
213 }
214 
215 static Eina_Bool
check_serial(WaylandIMContext * imcontext,uint32_t serial)216 check_serial(WaylandIMContext *imcontext, uint32_t serial)
217 {
218    Ecore_IMF_Preedit_Attr *attr;
219 
220    if ((imcontext->serial - serial) >
221        (imcontext->serial - imcontext->reset_serial))
222      {
223         EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
224                           "outdated serial: %u, current: %u, reset: %u",
225                           serial, imcontext->serial, imcontext->reset_serial);
226 
227         /* Clear pending data */
228         imcontext->pending_commit.delete_index = 0;
229         imcontext->pending_commit.delete_length = 0;
230         imcontext->pending_commit.cursor = 0;
231         imcontext->pending_commit.anchor = 0;
232 
233         imcontext->pending_preedit.cursor = 0;
234 
235         if (imcontext->pending_preedit.attrs)
236           {
237              EINA_LIST_FREE(imcontext->pending_preedit.attrs, attr) free(attr);
238              imcontext->pending_preedit.attrs = NULL;
239           }
240 
241         return EINA_FALSE;
242      }
243 
244    return EINA_TRUE;
245 }
246 
247 static void
clear_preedit(WaylandIMContext * imcontext)248 clear_preedit(WaylandIMContext *imcontext)
249 {
250    Ecore_IMF_Preedit_Attr *attr = NULL;
251 
252    imcontext->preedit_cursor = 0;
253 
254    if (imcontext->preedit_text)
255      {
256         free(imcontext->preedit_text);
257         imcontext->preedit_text = NULL;
258      }
259 
260    if (imcontext->preedit_commit)
261      {
262         free(imcontext->preedit_commit);
263         imcontext->preedit_commit = NULL;
264      }
265 
266    if (imcontext->preedit_attrs)
267      {
268         EINA_LIST_FREE(imcontext->preedit_attrs, attr)
269            free(attr);
270      }
271 
272    imcontext->preedit_attrs = NULL;
273 }
274 
275 static void
text_input_commit_string(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,uint32_t serial,const char * text)276 text_input_commit_string(void *data,
277                          struct zwp_text_input_v1 *text_input EINA_UNUSED,
278                          uint32_t serial, const char *text)
279 {
280    WaylandIMContext *imcontext = (WaylandIMContext *)data;
281    Eina_Bool old_preedit = EINA_FALSE;
282    char *surrounding = NULL;
283    int cursor_pos, cursor;
284    Ecore_IMF_Event_Delete_Surrounding ev;
285 
286    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
287                      "commit event (text: `%s', current pre-edit: `%s')",
288                      text,
289                      imcontext->preedit_text ? imcontext->preedit_text : "");
290 
291    old_preedit =
292      imcontext->preedit_text && strlen(imcontext->preedit_text) > 0;
293 
294    if (!imcontext->ctx)
295      return;
296 
297    if (!check_serial(imcontext, serial))
298      return;
299 
300    if (old_preedit)
301      {
302         ecore_imf_context_event_callback_call(imcontext->ctx,
303                                               ECORE_IMF_CALLBACK_PREEDIT_END,
304                                               NULL);
305      }
306 
307    clear_preedit(imcontext);
308 
309    if (imcontext->pending_commit.delete_length > 0)
310      {
311         /* cursor_pos is a byte index */
312         if (ecore_imf_context_surrounding_get(imcontext->ctx, &surrounding,
313                                               &cursor_pos))
314           {
315              ev.ctx = imcontext->ctx;
316              /* offset and n_chars are in characters */
317              ev.offset = utf8_offset_to_characters(surrounding, cursor_pos + imcontext->pending_commit.delete_index);
318              ev.n_chars = utf8_offset_to_characters(surrounding,
319                                                     cursor_pos + imcontext->pending_commit.delete_index + imcontext->pending_commit.delete_length) - ev.offset;
320 
321              /* cursor in characters */
322              cursor = utf8_offset_to_characters(surrounding, cursor_pos);
323 
324              ev.offset -= cursor;
325 
326              EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
327                      "delete on commit (text: `%s', offset `%d', length: `%d')",
328                      surrounding, ev.offset, ev.n_chars);
329 
330              if (surrounding)
331                free(surrounding);
332 
333              ecore_imf_context_event_callback_call(imcontext->ctx, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, &ev);
334           }
335      }
336 
337    imcontext->pending_commit.delete_index = 0;
338    imcontext->pending_commit.delete_length = 0;
339    imcontext->pending_commit.cursor = 0;
340    imcontext->pending_commit.anchor = 0;
341 
342    ecore_imf_context_event_callback_call(imcontext->ctx, ECORE_IMF_CALLBACK_COMMIT, (void *)text);
343 }
344 
345 static void
commit_preedit(WaylandIMContext * imcontext)346 commit_preedit(WaylandIMContext *imcontext)
347 {
348    if (!imcontext->preedit_commit)
349      return;
350 
351    if (!imcontext->ctx)
352      return;
353 
354    ecore_imf_context_event_callback_call(imcontext->ctx,
355                                          ECORE_IMF_CALLBACK_PREEDIT_CHANGED,
356                                          NULL);
357 
358    ecore_imf_context_event_callback_call(imcontext->ctx,
359                                          ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
360 
361    ecore_imf_context_event_callback_call(imcontext->ctx,
362                                          ECORE_IMF_CALLBACK_COMMIT,
363                                          (void *)imcontext->preedit_commit);
364 }
365 
366 static void
set_focus(Ecore_IMF_Context * ctx)367 set_focus(Ecore_IMF_Context *ctx)
368 {
369    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
370    Ecore_Wl2_Input *input;
371 
372    input = ecore_wl2_display_input_find_by_name(ecore_wl2_window_display_get(imcontext->window), "default");
373    if (!input)
374      return;
375 
376    struct wl_seat *seat = ecore_wl2_input_seat_get(input);
377    if (!seat)
378      return;
379 
380    imcontext->input = input;
381 
382    zwp_text_input_v1_activate(imcontext->text_input, seat,
383                           ecore_wl2_window_surface_get(imcontext->window));
384 }
385 
386 static Eina_Bool
show_input_panel(Ecore_IMF_Context * ctx)387 show_input_panel(Ecore_IMF_Context *ctx)
388 {
389    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
390    char *surrounding = NULL;
391    int cursor_pos;
392 
393    if ((!imcontext) || (!imcontext->window) || (!imcontext->text_input))
394      return EINA_FALSE;
395 
396    if (!imcontext->input)
397      set_focus(ctx);
398 
399    _clear_hide_timer();
400 
401    zwp_text_input_v1_set_content_type(imcontext->text_input,
402                                   imcontext->content_hint,
403                                   imcontext->content_purpose);
404 
405    if (ecore_imf_context_surrounding_get(imcontext->ctx, &surrounding, &cursor_pos))
406      {
407         if (imcontext->text_input)
408           zwp_text_input_v1_set_surrounding_text(imcontext->text_input, surrounding,
409                                              cursor_pos, cursor_pos);
410 
411         if (surrounding)
412           {
413             free(surrounding);
414             surrounding = NULL;
415           }
416      }
417 
418    zwp_text_input_v1_show_input_panel(imcontext->text_input);
419 
420    return EINA_TRUE;
421 }
422 
423 static void
text_input_preedit_string(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,uint32_t serial,const char * text,const char * commit)424 text_input_preedit_string(void *data,
425                           struct zwp_text_input_v1 *text_input EINA_UNUSED,
426                           uint32_t serial, const char *text, const char *commit)
427 {
428    WaylandIMContext *imcontext = (WaylandIMContext *)data;
429    Eina_Bool old_preedit = EINA_FALSE;
430 
431    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
432                      "preedit event (text: `%s', current pre-edit: `%s')",
433                      text,
434                      imcontext->preedit_text ? imcontext->preedit_text : "");
435 
436    if (!check_serial(imcontext, serial))
437      return;
438 
439    old_preedit =
440      imcontext->preedit_text && strlen(imcontext->preedit_text) > 0;
441 
442    clear_preedit(imcontext);
443 
444    imcontext->preedit_text = strdup(text);
445    imcontext->preedit_commit = strdup(commit);
446    imcontext->preedit_cursor =
447      utf8_offset_to_characters(text, imcontext->pending_preedit.cursor);
448    imcontext->preedit_attrs = imcontext->pending_preedit.attrs;
449 
450    imcontext->pending_preedit.attrs = NULL;
451 
452    if (!old_preedit)
453      {
454         ecore_imf_context_event_callback_call(imcontext->ctx,
455                                               ECORE_IMF_CALLBACK_PREEDIT_START,
456                                               NULL);
457      }
458 
459    ecore_imf_context_event_callback_call(imcontext->ctx,
460                                          ECORE_IMF_CALLBACK_PREEDIT_CHANGED,
461                                          NULL);
462 
463    if (imcontext->preedit_text && strlen(imcontext->preedit_text) == 0)
464      {
465         ecore_imf_context_event_callback_call(imcontext->ctx,
466                                               ECORE_IMF_CALLBACK_PREEDIT_END,
467                                               NULL);
468      }
469 }
470 
471 static void
text_input_delete_surrounding_text(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,int32_t index,uint32_t length)472 text_input_delete_surrounding_text(void *data,
473                                    struct zwp_text_input_v1 *text_input EINA_UNUSED,
474                                    int32_t index, uint32_t length)
475 {
476    WaylandIMContext *imcontext = (WaylandIMContext *)data;
477    Ecore_IMF_Event_Delete_Surrounding ev;
478    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
479                      "delete surrounding text (index: %d, length: %u)",
480                      index, length);
481 
482    imcontext->pending_commit.delete_index = ev.offset = index;
483    imcontext->pending_commit.delete_length = ev.n_chars = length;
484 
485    ecore_imf_context_event_callback_call(imcontext->ctx, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, &ev);
486 }
487 
488 static void
text_input_cursor_position(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,int32_t index,int32_t anchor)489 text_input_cursor_position(void *data,
490                            struct zwp_text_input_v1 *text_input EINA_UNUSED,
491                            int32_t index, int32_t anchor)
492 {
493    WaylandIMContext *imcontext = (WaylandIMContext *)data;
494 
495    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
496                      "cursor_position for next commit (index: %d, anchor: %d)",
497                      index, anchor);
498 
499    imcontext->pending_commit.cursor = index;
500    imcontext->pending_commit.anchor = anchor;
501 }
502 
503 static void
text_input_preedit_styling(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,uint32_t index,uint32_t length,uint32_t style)504 text_input_preedit_styling(void *data,
505                            struct zwp_text_input_v1 *text_input EINA_UNUSED,
506                            uint32_t index, uint32_t length, uint32_t style)
507 {
508    WaylandIMContext *imcontext = (WaylandIMContext *)data;
509    Ecore_IMF_Preedit_Attr *attr = calloc(1, sizeof(*attr));
510    if (!attr) return;
511 
512    switch (style)
513      {
514       case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_DEFAULT:
515       case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE:
516       case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INCORRECT:
517       case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT:
518       case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_ACTIVE:
519       case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INACTIVE:
520          attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB1;
521          break;
522       case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_SELECTION:
523          attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB2;
524          break;
525       default:
526          attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB1;
527          break;
528      }
529 
530    attr->start_index = index;
531    attr->end_index = index + length;
532 
533    imcontext->pending_preedit.attrs =
534      eina_list_append(imcontext->pending_preedit.attrs, attr);
535 }
536 
537 static void
text_input_preedit_cursor(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,int32_t index)538 text_input_preedit_cursor(void *data,
539                           struct zwp_text_input_v1 *text_input EINA_UNUSED,
540                           int32_t index)
541 {
542    WaylandIMContext *imcontext = (WaylandIMContext *)data;
543 
544    imcontext->pending_preedit.cursor = index;
545 }
546 
547 static xkb_mod_index_t
modifiers_get_index(struct wl_array * modifiers_map,const char * name)548 modifiers_get_index(struct wl_array *modifiers_map, const char *name)
549 {
550    xkb_mod_index_t index = 0;
551    char *p = modifiers_map->data;
552 
553    while ((const char *)p < ((const char *)modifiers_map->data + modifiers_map->size))
554      {
555         if (strcmp(p, name) == 0)
556           return index;
557 
558         index++;
559         p += strlen(p) + 1;
560      }
561 
562    return XKB_MOD_INVALID;
563 }
564 
565 static xkb_mod_mask_t
modifiers_get_mask(struct wl_array * modifiers_map,const char * name)566 modifiers_get_mask(struct wl_array *modifiers_map, const char *name)
567 {
568    xkb_mod_index_t index = modifiers_get_index(modifiers_map, name);
569 
570    if (index == XKB_MOD_INVALID)
571      return XKB_MOD_INVALID;
572 
573    return 1 << index;
574 }
575 
576 static void
text_input_modifiers_map(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,struct wl_array * map)577 text_input_modifiers_map(void *data,
578                          struct zwp_text_input_v1 *text_input EINA_UNUSED,
579                          struct wl_array *map)
580 {
581    WaylandIMContext *imcontext = (WaylandIMContext *)data;
582 
583    imcontext->shift_mask = modifiers_get_mask(map, "Shift");
584    imcontext->control_mask = modifiers_get_mask(map, "Control");
585    imcontext->alt_mask = modifiers_get_mask(map, "Mod1");
586 }
587 
588 static void
text_input_keysym(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,uint32_t serial EINA_UNUSED,uint32_t time,uint32_t sym,uint32_t state,uint32_t modifiers)589 text_input_keysym(void *data,
590                   struct zwp_text_input_v1 *text_input EINA_UNUSED,
591                   uint32_t serial EINA_UNUSED, uint32_t time, uint32_t sym,
592                   uint32_t state, uint32_t modifiers)
593 {
594    WaylandIMContext *imcontext = (WaylandIMContext *)data;
595    char string[32], key[32], keyname[32];
596    Ecore_Event_Key *e;
597 
598    memset(key, 0, sizeof(key));
599    xkb_keysym_get_name(sym, key, sizeof(key));
600 
601    memset(keyname, 0, sizeof(keyname));
602    xkb_keysym_get_name(sym, keyname, sizeof(keyname));
603    if (keyname[0] == '\0')
604      snprintf(keyname, sizeof(keyname), "Keysym-%u", sym);
605 
606    memset(string, 0, sizeof(string));
607    if (!xkb_keysym_to_utf8(sym, string, 32)) return;
608 
609    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
610                      "key event (key: %s)",
611                      keyname);
612 
613    e = calloc(1, sizeof(Ecore_Event_Key) + strlen(key) + strlen(keyname) +
614               strlen(string) + 3);
615    if (!e) return;
616 
617    e->keyname = (char *)(e + 1);
618    e->key = e->keyname + strlen(keyname) + 1;
619    e->string = e->key + strlen(key) + 1;
620    e->compose = e->string;
621 
622    strcpy((char *)e->keyname, keyname);
623    strcpy((char *)e->key, key);
624    strcpy((char *)e->string, string);
625 
626    e->window = (Ecore_Window)imcontext->window;
627    e->event_window = (Ecore_Window)imcontext->window;
628    e->timestamp = time;
629 
630    e->modifiers = 0;
631    if (modifiers & imcontext->shift_mask)
632      e->modifiers |= ECORE_EVENT_MODIFIER_SHIFT;
633 
634    if (modifiers & imcontext->control_mask)
635      e->modifiers |= ECORE_EVENT_MODIFIER_CTRL;
636 
637    if (modifiers & imcontext->alt_mask)
638      e->modifiers |= ECORE_EVENT_MODIFIER_ALT;
639 
640    if (state)
641      ecore_event_add(ECORE_EVENT_KEY_DOWN, e, NULL, NULL);
642    else
643      ecore_event_add(ECORE_EVENT_KEY_UP, e, NULL, NULL);
644 }
645 
646 static void
text_input_enter(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,struct wl_surface * surface EINA_UNUSED)647 text_input_enter(void *data,
648                  struct zwp_text_input_v1 *text_input EINA_UNUSED,
649                  struct wl_surface *surface EINA_UNUSED)
650 {
651    WaylandIMContext *imcontext = (WaylandIMContext *)data;
652 
653    update_state(imcontext);
654 
655    imcontext->reset_serial = imcontext->serial;
656 }
657 
658 static void
text_input_leave(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED)659 text_input_leave(void *data,
660                  struct zwp_text_input_v1 *text_input EINA_UNUSED)
661 {
662    WaylandIMContext *imcontext = (WaylandIMContext *)data;
663 
664    /* clear preedit */
665    commit_preedit(imcontext);
666    clear_preedit(imcontext);
667 }
668 
669 static void
text_input_input_panel_state(void * data EINA_UNUSED,struct zwp_text_input_v1 * text_input EINA_UNUSED,uint32_t state EINA_UNUSED)670 text_input_input_panel_state(void *data EINA_UNUSED,
671                              struct zwp_text_input_v1 *text_input EINA_UNUSED,
672                              uint32_t state EINA_UNUSED)
673 {
674 }
675 
676 static void
text_input_language(void * data,struct zwp_text_input_v1 * text_input EINA_UNUSED,uint32_t serial EINA_UNUSED,const char * language)677 text_input_language(void *data,
678                     struct zwp_text_input_v1 *text_input EINA_UNUSED,
679                     uint32_t serial EINA_UNUSED, const char *language)
680 {
681     WaylandIMContext *imcontext = (WaylandIMContext *)data;
682     Eina_Bool changed = EINA_FALSE;
683 
684     if (!imcontext || !language) return;
685 
686     if (imcontext->language)
687       {
688          if (strcmp(imcontext->language, language) != 0)
689            {
690               changed = EINA_TRUE;
691               free(imcontext->language);
692            }
693       }
694     else
695       changed = EINA_TRUE;
696 
697     if (changed)
698       {
699          imcontext->language = strdup(language);
700 
701          if (imcontext->ctx)
702            ecore_imf_context_input_panel_event_callback_call(imcontext->ctx, ECORE_IMF_INPUT_PANEL_LANGUAGE_EVENT, 0);
703       }
704 }
705 
706 static void
text_input_text_direction(void * data EINA_UNUSED,struct zwp_text_input_v1 * text_input EINA_UNUSED,uint32_t serial EINA_UNUSED,uint32_t direction EINA_UNUSED)707 text_input_text_direction(void *data EINA_UNUSED,
708                           struct zwp_text_input_v1 *text_input EINA_UNUSED,
709                           uint32_t serial EINA_UNUSED,
710                           uint32_t direction EINA_UNUSED)
711 {
712 }
713 
714 static const struct zwp_text_input_v1_listener text_input_listener =
715 {
716    text_input_enter,
717    text_input_leave,
718    text_input_modifiers_map,
719    text_input_input_panel_state,
720    text_input_preedit_string,
721    text_input_preedit_styling,
722    text_input_preedit_cursor,
723    text_input_commit_string,
724    text_input_cursor_position,
725    text_input_delete_surrounding_text,
726    text_input_keysym,
727    text_input_language,
728    text_input_text_direction
729 };
730 
731 void
wayland_im_context_add(Ecore_IMF_Context * ctx)732 wayland_im_context_add(Ecore_IMF_Context *ctx)
733 {
734    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
735 
736    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "context_add");
737 
738    imcontext->ctx = ctx;
739 
740    imcontext->text_input =
741      zwp_text_input_manager_v1_create_text_input(imcontext->text_input_manager);
742    if (imcontext->text_input)
743      zwp_text_input_v1_add_listener(imcontext->text_input,
744                                 &text_input_listener, imcontext);
745 }
746 
747 void
wayland_im_context_del(Ecore_IMF_Context * ctx)748 wayland_im_context_del(Ecore_IMF_Context *ctx)
749 {
750    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
751 
752    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "context_del");
753 
754    _clear_hide_timer();
755 
756    if (imcontext->language)
757      {
758         free(imcontext->language);
759         imcontext->language = NULL;
760      }
761 
762    if (imcontext->text_input)
763      zwp_text_input_v1_destroy(imcontext->text_input);
764 
765    clear_preedit(imcontext);
766 
767    free(imcontext);
768 }
769 
770 void
wayland_im_context_reset(Ecore_IMF_Context * ctx)771 wayland_im_context_reset(Ecore_IMF_Context *ctx)
772 {
773    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
774 
775    commit_preedit(imcontext);
776    clear_preedit(imcontext);
777 
778    if (imcontext->text_input)
779      zwp_text_input_v1_reset(imcontext->text_input);
780 
781    update_state(imcontext);
782 
783    imcontext->reset_serial = imcontext->serial;
784 }
785 
786 void
wayland_im_context_focus_in(Ecore_IMF_Context * ctx)787 wayland_im_context_focus_in(Ecore_IMF_Context *ctx)
788 {
789    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "focus-in");
790 
791    set_focus(ctx);
792 
793    if (ecore_imf_context_input_panel_enabled_get(ctx))
794      if (!ecore_imf_context_input_panel_show_on_demand_get (ctx))
795        show_input_panel(ctx);
796 }
797 
798 void
wayland_im_context_focus_out(Ecore_IMF_Context * ctx)799 wayland_im_context_focus_out(Ecore_IMF_Context *ctx)
800 {
801    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
802 
803    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "focus-out");
804 
805    if (!imcontext->input) return;
806 
807    if (imcontext->text_input)
808      {
809         if (ecore_imf_context_input_panel_enabled_get(ctx))
810           _input_panel_hide(ctx, EINA_FALSE);
811 
812         zwp_text_input_v1_deactivate(imcontext->text_input,
813                                  ecore_wl2_input_seat_get(imcontext->input));
814      }
815 
816    imcontext->input = NULL;
817 }
818 
819 void
wayland_im_context_preedit_string_get(Ecore_IMF_Context * ctx,char ** str,int * cursor_pos)820 wayland_im_context_preedit_string_get(Ecore_IMF_Context *ctx,
821                                       char **str, int *cursor_pos)
822 {
823    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
824 
825    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
826                      "pre-edit string requested (preedit: `%s')",
827                      imcontext->preedit_text ? imcontext->preedit_text : "");
828 
829    if (str)
830      *str = strdup(imcontext->preedit_text ? imcontext->preedit_text : "");
831 
832    if (cursor_pos)
833      *cursor_pos = imcontext->preedit_cursor;
834 }
835 
836 void
wayland_im_context_preedit_string_with_attributes_get(Ecore_IMF_Context * ctx,char ** str,Eina_List ** attrs,int * cursor_pos)837 wayland_im_context_preedit_string_with_attributes_get(Ecore_IMF_Context *ctx,
838                                                       char **str,
839                                                       Eina_List **attrs,
840                                                       int *cursor_pos)
841 {
842    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
843 
844    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
845                      "pre-edit string with attributes requested (preedit: `%s')",
846                      imcontext->preedit_text ? imcontext->preedit_text : "");
847 
848    if (str)
849      *str = strdup(imcontext->preedit_text ? imcontext->preedit_text : "");
850 
851    if (attrs)
852      {
853         Eina_List *l;
854         Ecore_IMF_Preedit_Attr *a, *attr;
855 
856         EINA_LIST_FOREACH(imcontext->preedit_attrs, l, a)
857           {
858              attr = malloc(sizeof(*attr));
859              if (attr)
860                {
861                   attr = memcpy(attr, a, sizeof(*attr));
862                   *attrs = eina_list_append(*attrs, attr);
863                }
864           }
865      }
866 
867    if (cursor_pos)
868      *cursor_pos = imcontext->preedit_cursor;
869 }
870 
871 void
wayland_im_context_cursor_position_set(Ecore_IMF_Context * ctx,int cursor_pos)872 wayland_im_context_cursor_position_set(Ecore_IMF_Context *ctx, int cursor_pos)
873 {
874    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
875 
876    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom,
877                      "set cursor position (cursor: %d)",
878                      cursor_pos);
879 
880    update_state(imcontext);
881 }
882 
883 void
wayland_im_context_use_preedit_set(Ecore_IMF_Context * ctx EINA_UNUSED,Eina_Bool use_preedit EINA_UNUSED)884 wayland_im_context_use_preedit_set(Ecore_IMF_Context *ctx EINA_UNUSED,
885                                    Eina_Bool use_preedit EINA_UNUSED)
886 {
887 }
888 
889 void
wayland_im_context_client_window_set(Ecore_IMF_Context * ctx,void * window)890 wayland_im_context_client_window_set(Ecore_IMF_Context *ctx, void *window)
891 {
892    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
893 
894    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "client window set (window: %p)", window);
895 
896    if (window != NULL)
897      {
898         imcontext->window = window;
899      }
900 }
901 
902 void
wayland_im_context_client_canvas_set(Ecore_IMF_Context * ctx,void * canvas)903 wayland_im_context_client_canvas_set(Ecore_IMF_Context *ctx, void *canvas)
904 {
905    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
906 
907    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "client canvas set (canvas: %p)", canvas);
908 
909    if (canvas != NULL)
910      imcontext->canvas = canvas;
911 }
912 
913 void
wayland_im_context_show(Ecore_IMF_Context * ctx)914 wayland_im_context_show(Ecore_IMF_Context *ctx)
915 {
916    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "context_show");
917 
918    show_input_panel(ctx);
919 }
920 
921 void
wayland_im_context_hide(Ecore_IMF_Context * ctx)922 wayland_im_context_hide(Ecore_IMF_Context *ctx)
923 {
924    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "context_hide");
925 
926    _input_panel_hide(ctx, EINA_FALSE);
927 }
928 
929 Eina_Bool
wayland_im_context_filter_event(Ecore_IMF_Context * ctx,Ecore_IMF_Event_Type type,Ecore_IMF_Event * event EINA_UNUSED)930 wayland_im_context_filter_event(Ecore_IMF_Context *ctx,
931                                 Ecore_IMF_Event_Type type,
932                                 Ecore_IMF_Event *event EINA_UNUSED)
933 {
934 
935    if (type == ECORE_IMF_EVENT_MOUSE_UP)
936      {
937         if (ecore_imf_context_input_panel_enabled_get(ctx))
938           show_input_panel(ctx);
939      }
940 
941    return EINA_FALSE;
942 }
943 
944 void
wayland_im_context_cursor_location_set(Ecore_IMF_Context * ctx,int x,int y,int width,int height)945 wayland_im_context_cursor_location_set(Ecore_IMF_Context *ctx, int x, int y, int width, int height)
946 {
947    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
948 
949    EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "cursor_location_set (x: %d, y: %d, w: %d, h: %d)", x, y, width, height);
950 
951    if ((imcontext->cursor_location.x != x) ||
952        (imcontext->cursor_location.y != y) ||
953        (imcontext->cursor_location.width != width) ||
954        (imcontext->cursor_location.height != height))
955      {
956         imcontext->cursor_location.x = x;
957         imcontext->cursor_location.y = y;
958         imcontext->cursor_location.width = width;
959         imcontext->cursor_location.height = height;
960         imcontext->cursor_location.do_set = EINA_TRUE;
961 
962         update_state(imcontext);
963      }
964 }
965 
966 void
wayland_im_context_autocapital_type_set(Ecore_IMF_Context * ctx,Ecore_IMF_Autocapital_Type autocapital_type)967 wayland_im_context_autocapital_type_set(Ecore_IMF_Context *ctx,
968                                         Ecore_IMF_Autocapital_Type autocapital_type)
969 {
970    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
971 
972    imcontext->content_hint &= ~(ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CAPITALIZATION |
973                                 ZWP_TEXT_INPUT_V1_CONTENT_HINT_UPPERCASE |
974                                 ZWP_TEXT_INPUT_V1_CONTENT_HINT_LOWERCASE);
975 
976    if (autocapital_type == ECORE_IMF_AUTOCAPITAL_TYPE_SENTENCE)
977      imcontext->content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CAPITALIZATION;
978    else if (autocapital_type == ECORE_IMF_AUTOCAPITAL_TYPE_ALLCHARACTER)
979      imcontext->content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_UPPERCASE;
980    else
981      imcontext->content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_LOWERCASE;
982 }
983 
984 void
wayland_im_context_input_panel_layout_set(Ecore_IMF_Context * ctx,Ecore_IMF_Input_Panel_Layout layout)985 wayland_im_context_input_panel_layout_set(Ecore_IMF_Context *ctx, Ecore_IMF_Input_Panel_Layout layout)
986 {
987    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
988 
989    switch (layout) {
990       case ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBER:
991          imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER;
992          break;
993       case ECORE_IMF_INPUT_PANEL_LAYOUT_EMAIL:
994          imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_EMAIL;
995          break;
996       case ECORE_IMF_INPUT_PANEL_LAYOUT_URL:
997          imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_URL;
998          break;
999       case ECORE_IMF_INPUT_PANEL_LAYOUT_PHONENUMBER:
1000          imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PHONE;
1001          break;
1002       case ECORE_IMF_INPUT_PANEL_LAYOUT_IP:
1003          imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER;
1004          break;
1005       case ECORE_IMF_INPUT_PANEL_LAYOUT_MONTH:
1006          imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DATE;
1007          break;
1008       case ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY:
1009         imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DIGITS;
1010         break;
1011       case ECORE_IMF_INPUT_PANEL_LAYOUT_TERMINAL:
1012         imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_TERMINAL;
1013         break;
1014       case ECORE_IMF_INPUT_PANEL_LAYOUT_PASSWORD:
1015         imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD;
1016         break;
1017       case ECORE_IMF_INPUT_PANEL_LAYOUT_DATETIME:
1018         imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DATETIME;
1019         break;
1020       default:
1021         imcontext->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NORMAL;
1022         break;
1023    }
1024 }
1025 
1026 void
wayland_im_context_input_mode_set(Ecore_IMF_Context * ctx,Ecore_IMF_Input_Mode input_mode)1027 wayland_im_context_input_mode_set(Ecore_IMF_Context *ctx,
1028                                   Ecore_IMF_Input_Mode input_mode)
1029 {
1030    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
1031 
1032    if (input_mode & ECORE_IMF_INPUT_MODE_INVISIBLE)
1033      imcontext->content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_PASSWORD;
1034    else
1035      imcontext->content_hint &= ~ZWP_TEXT_INPUT_V1_CONTENT_HINT_PASSWORD;
1036 }
1037 
1038 void
wayland_im_context_input_hint_set(Ecore_IMF_Context * ctx,Ecore_IMF_Input_Hints input_hints)1039 wayland_im_context_input_hint_set(Ecore_IMF_Context *ctx,
1040                                   Ecore_IMF_Input_Hints input_hints)
1041 {
1042    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
1043 
1044    if (input_hints & ECORE_IMF_INPUT_HINT_AUTO_COMPLETE)
1045      imcontext->content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION;
1046    else
1047      imcontext->content_hint &= ~ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION;
1048 
1049    if (input_hints & ECORE_IMF_INPUT_HINT_SENSITIVE_DATA)
1050      imcontext->content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_SENSITIVE_DATA;
1051    else
1052      imcontext->content_hint &= ~ZWP_TEXT_INPUT_V1_CONTENT_HINT_SENSITIVE_DATA;
1053 
1054    if (input_hints & ECORE_IMF_INPUT_HINT_MULTILINE)
1055      imcontext->content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_MULTILINE;
1056    else
1057      imcontext->content_hint &= ~ZWP_TEXT_INPUT_V1_CONTENT_HINT_MULTILINE;
1058 }
1059 
1060 void
wayland_im_context_input_panel_language_set(Ecore_IMF_Context * ctx,Ecore_IMF_Input_Panel_Lang lang)1061 wayland_im_context_input_panel_language_set(Ecore_IMF_Context *ctx,
1062                                             Ecore_IMF_Input_Panel_Lang lang)
1063 {
1064    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
1065 
1066    if (lang == ECORE_IMF_INPUT_PANEL_LANG_ALPHABET)
1067      imcontext->content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_LATIN;
1068    else
1069      imcontext->content_hint &= ~ZWP_TEXT_INPUT_V1_CONTENT_HINT_LATIN;
1070 }
1071 
1072 void
wayland_im_context_input_panel_language_locale_get(Ecore_IMF_Context * ctx,char ** locale)1073 wayland_im_context_input_panel_language_locale_get(Ecore_IMF_Context *ctx,
1074                                                    char **locale)
1075 {
1076    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
1077 
1078    if (locale)
1079      *locale = strdup(imcontext->language ? imcontext->language : "");
1080 }
1081 
1082 void
wayland_im_context_prediction_allow_set(Ecore_IMF_Context * ctx,Eina_Bool prediction)1083 wayland_im_context_prediction_allow_set(Ecore_IMF_Context *ctx,
1084                                         Eina_Bool prediction)
1085 {
1086    WaylandIMContext *imcontext = (WaylandIMContext *)ecore_imf_context_data_get(ctx);
1087 
1088    if (prediction)
1089      imcontext->content_hint |= ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION;
1090    else
1091      imcontext->content_hint &= ~ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION;
1092 }
1093 
1094 WaylandIMContext *
wayland_im_context_new(struct zwp_text_input_manager_v1 * text_input_manager)1095 wayland_im_context_new(struct zwp_text_input_manager_v1 *text_input_manager)
1096 {
1097    WaylandIMContext *context = calloc(1, sizeof(WaylandIMContext));
1098    if (context)
1099      {
1100         EINA_LOG_DOM_INFO(_ecore_imf_wayland_log_dom, "new context created");
1101         context->text_input_manager = text_input_manager;
1102      }
1103 
1104    return context;
1105 }
1106 
1107 /* vim:ts=8 sw=3 sts=3 expandtab cino=>5n-3f0^-2{2(0W1st0
1108 */
1109