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