Revert "Force engine to Wayland SHM"
[profile/ivi/weekeyboard.git] / src / wkb-main.c
1 /*
2  * Copyright © 2013 Intel Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <Eina.h>
22 #include <Ecore.h>
23 #include <Ecore_Wayland.h>
24 #include <Ecore_Evas.h>
25 #include <Edje.h>
26
27 #include <linux/input.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "input-method-client-protocol.h"
33 #include "text-client-protocol.h"
34
35 struct weekeyboard
36 {
37    Ecore_Evas *ee;
38    Ecore_Wl_Window *win;
39    Evas_Object *edje_obj;
40    const char *ee_engine;
41    char **ignore_keys;
42
43    struct wl_surface *surface;
44    struct wl_input_panel *ip;
45    struct wl_input_method *im;
46    struct wl_output *output;
47    struct wl_input_method_context *im_ctx;
48
49    char *surrounding_text;
50    char *preedit_str;
51    char *language;
52
53    uint32_t text_direction;
54    uint32_t preedit_style;
55    uint32_t content_hint;
56    uint32_t content_purpose;
57    uint32_t serial;
58    uint32_t surrounding_cursor;
59
60    Eina_Bool context_changed;
61 };
62
63 static void
64 _cb_wkb_delete_request(Ecore_Evas *ee EINA_UNUSED)
65 {
66    ecore_main_loop_quit();
67 }
68
69 static char *
70 _wkb_insert_text(const char *text, uint32_t offset, const char *insert)
71 {
72    char *new_text = malloc(strlen(text) + strlen(insert) + 1);
73
74    strncat(new_text, text, offset);
75    new_text[offset] = '\0';
76    strcat(new_text, insert);
77    strcat(new_text, text + offset);
78
79    return new_text;
80 }
81
82 static void
83 _wkb_commit_preedit_str(struct weekeyboard *wkb)
84 {
85    char *surrounding_text;
86
87    if (!wkb->preedit_str || !strlen(wkb->preedit_str) == 0)
88       return;
89
90    wl_input_method_context_cursor_position(wkb->im_ctx, 0, 0);
91    wl_input_method_context_commit_string(wkb->im_ctx, wkb->serial, wkb->preedit_str);
92
93    if (wkb->surrounding_text)
94      {
95         surrounding_text = _wkb_insert_text(wkb->surrounding_text, wkb->surrounding_cursor, wkb->preedit_str);
96         free(wkb->surrounding_text);
97         wkb->surrounding_text = surrounding_text;
98         wkb->surrounding_cursor += strlen(wkb->preedit_str);
99      }
100    else
101      {
102         wkb->surrounding_text = strdup(wkb->preedit_str);
103         wkb->surrounding_cursor = strlen(wkb->preedit_str);
104      }
105
106    free(wkb->preedit_str);
107    wkb->preedit_str = strdup("");
108 }
109
110 static void
111 _wkb_send_preedit_str(struct weekeyboard *wkb, int cursor)
112 {
113    unsigned int index = strlen(wkb->preedit_str);
114
115    if (wkb->preedit_style)
116       wl_input_method_context_preedit_styling(wkb->im_ctx, 0, strlen(wkb->preedit_str), wkb->preedit_style);
117
118    if (cursor > 0)
119       index = cursor;
120
121    wl_input_method_context_preedit_cursor(wkb->im_ctx, index);
122    wl_input_method_context_preedit_string(wkb->im_ctx, wkb->serial, wkb->preedit_str, wkb->preedit_str);
123 }
124
125 static void
126 _wkb_update_preedit_str(struct weekeyboard *wkb, const char *key)
127 {
128    char *tmp;
129
130    if (!wkb->preedit_str)
131       wkb->preedit_str = strdup("");
132
133    tmp = calloc(1, strlen(wkb->preedit_str) + strlen(key) + 1);
134    sprintf(tmp, "%s%s", wkb->preedit_str, key);
135    free(wkb->preedit_str);
136    wkb->preedit_str = tmp;
137
138    if (strcmp(key, " ") == 0)
139       _wkb_commit_preedit_str(wkb);
140    else
141       _wkb_send_preedit_str(wkb, -1);
142 }
143
144 static Eina_Bool
145 _wkb_ignore_key(struct weekeyboard *wkb, const char *key)
146 {
147    int i;
148
149    if (!wkb->ignore_keys)
150        return EINA_FALSE;
151
152    for (i = 0; wkb->ignore_keys[i] != NULL; i++)
153       if (!strcmp(key, wkb->ignore_keys[i]))
154          return EINA_TRUE;
155
156    return EINA_FALSE;
157 }
158
159 static void
160 _cb_wkb_on_key_down(void *data, Evas_Object *obj, const char *emission EINA_UNUSED, const char *source)
161 {
162    struct weekeyboard *wkb = data;
163    char *src;
164    const char *key;
165
166    src = strdup(source);
167    key = strtok(src, ":"); /* ignore group */
168    key = strtok(NULL, ":");
169    if (key == NULL)
170        key = ":";
171
172    if (_wkb_ignore_key(wkb, key))
173      {
174         fprintf(stderr,"Ignoring key '%s'\n", key);
175         goto end;
176      }
177    else if (strcmp(key, "backspace") == 0)
178      {
179         if (strlen(wkb->preedit_str) == 0)
180              wl_input_method_context_delete_surrounding_text(wkb->im_ctx, -1, 1);
181         else
182           {
183              wkb->preedit_str[strlen(wkb->preedit_str) - 1] = '\0';
184              _wkb_send_preedit_str(wkb, -1);
185           }
186
187         goto end;
188      }
189    else if (strcmp(key, "enter") == 0)
190      {
191         _wkb_commit_preedit_str(wkb);
192         wl_input_method_context_keysym(wkb->im_ctx, wkb->serial, time,
193                         XKB_KEY_Return, WL_KEYBOARD_KEY_STATE_PRESSED,
194                         0);
195         goto end;
196      }
197    else if (strcmp(key, "space") == 0)
198      {
199         key = " ";
200      }
201
202    fprintf(stderr,"KEY = '%s'\n", key);
203
204    _wkb_update_preedit_str(wkb, key);
205
206 end:
207    free(src);
208 }
209
210 static void
211 _wkb_im_ctx_surrounding_text(void *data, struct wl_input_method_context *im_ctx, const char *text, uint32_t cursor, uint32_t anchor)
212 {
213    struct weekeyboard *wkb = data;
214
215    fprintf(stderr,"%s()\n", __FUNCTION__);
216    free(wkb->surrounding_text);
217    wkb->surrounding_text = strdup(text);
218    wkb->surrounding_cursor = cursor;
219 }
220
221 static void
222 _wkb_im_ctx_reset(void *data, struct wl_input_method_context *im_ctx)
223 {
224    struct weekeyboard *wkb = data;
225
226    fprintf(stderr,"%s()\n", __FUNCTION__);
227
228    if (strlen(wkb->preedit_str))
229      {
230         free(wkb->preedit_str);
231         wkb->preedit_str = strdup("");
232      }
233 }
234
235 static void
236 _wkb_im_ctx_content_type(void *data, struct wl_input_method_context *im_ctx, uint32_t hint, uint32_t purpose)
237 {
238    struct weekeyboard *wkb = data;
239
240    fprintf(stderr,"%s(): im_context = %p hint = %d purpose = %d\n", __FUNCTION__, im_ctx, hint, purpose);
241
242    if (!wkb->context_changed)
243       return;
244
245    switch (purpose)
246      {
247       case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS:
248       case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER:
249            {
250               edje_object_signal_emit(wkb->edje_obj, "show,numeric", "");
251               break;
252            }
253       default:
254            {
255               edje_object_signal_emit(wkb->edje_obj, "show,alphanumeric", "");
256               break;
257            }
258      }
259
260    wkb->content_hint = hint;
261    wkb->content_purpose = purpose;
262
263    wkb->context_changed = EINA_FALSE;
264 }
265
266 static void
267 _wkb_im_ctx_invoke_action(void *data, struct wl_input_method_context *im_ctx, uint32_t button, uint32_t index)
268 {
269    struct weekeyboard *wkb = data;
270
271    fprintf(stderr,"%s()\n", __FUNCTION__);
272    if (button != BTN_LEFT)
273       return;
274
275    _wkb_send_preedit_str(wkb, index);
276 }
277
278 static void
279 _wkb_im_ctx_commit_state(void *data, struct wl_input_method_context *im_ctx, uint32_t serial)
280 {
281    struct weekeyboard *wkb = data;
282
283    fprintf(stderr,"%s()\n", __FUNCTION__);
284    if (wkb->surrounding_text)
285       fprintf(stderr, "Surrounding text updated: %s\n", wkb->surrounding_text);
286
287    wkb->serial = serial;
288    /* FIXME */
289    wl_input_method_context_language(im_ctx, wkb->serial, "en");//wkb->language);
290    wl_input_method_context_text_direction(im_ctx, wkb->serial, WL_TEXT_INPUT_TEXT_DIRECTION_LTR);//wkb->text_direction);
291 }
292
293 static void
294 _wkb_im_ctx_preferred_language(void *data, struct wl_input_method_context *im_ctx, const char *language)
295 {
296    struct weekeyboard *wkb = data;
297
298    if (language && wkb->language && !strcmp(language, wkb->language))
299       return;
300
301    if (wkb->language)
302      {
303         free(wkb->language);
304         wkb->language = NULL;
305      }
306
307    if (language)
308      {
309         wkb->language = strdup(language);
310         fprintf(stderr,"Language changed, new: '%s\n", language);
311      }
312 }
313
314 static const struct wl_input_method_context_listener wkb_im_context_listener = {
315      _wkb_im_ctx_surrounding_text,
316      _wkb_im_ctx_reset,
317      _wkb_im_ctx_content_type,
318      _wkb_im_ctx_invoke_action,
319      _wkb_im_ctx_commit_state,
320      _wkb_im_ctx_preferred_language,
321 };
322
323 static void
324 _wkb_im_activate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
325 {
326    struct weekeyboard *wkb = data;
327    struct wl_array modifiers_map;
328
329    if (wkb->im_ctx)
330       wl_input_method_context_destroy(wkb->im_ctx);
331
332    if (wkb->preedit_str)
333       free(wkb->preedit_str);
334
335    wkb->preedit_str = strdup("");
336    wkb->content_hint = WL_TEXT_INPUT_CONTENT_HINT_NONE;
337    wkb->content_purpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NORMAL;
338    free(wkb->language);
339    wkb->language = NULL;
340    free(wkb->surrounding_text);
341    wkb->surrounding_text = NULL;
342    wkb->serial = 0;
343
344    wkb->im_ctx = im_ctx;
345    wl_input_method_context_add_listener(im_ctx, &wkb_im_context_listener, wkb);
346
347    /*
348    wl_array_init(&modifiers_map);
349
350    keysym_modifiers_add(&modifiers_map, "Shift");
351    keysym_modifiers_add(&modifiers_map, "Control");
352    keysym_modifiers_add(&modifiers_map, "Mod1");
353
354    wl_input_method_context_modifiers_map(im_ctx, &modifiers_map);
355
356    wkb->keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift");
357
358    wl_array_release(&modifiers_map);
359    */
360
361    /* FIXME */
362    wl_input_method_context_language(im_ctx, wkb->serial, "en");//wkb->language);
363    wl_input_method_context_text_direction(im_ctx, wkb->serial, WL_TEXT_INPUT_TEXT_DIRECTION_LTR);//wkb->text_direction);
364
365    wkb->context_changed = EINA_TRUE;
366    evas_object_show(wkb->edje_obj);
367 }
368
369 static void
370 _wkb_im_deactivate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
371 {
372    struct weekeyboard *wkb = data;
373
374    if (!wkb->im_ctx)
375       return;
376
377    wl_input_method_context_destroy(wkb->im_ctx);
378    wkb->im_ctx = NULL;
379    evas_object_hide(wkb->edje_obj);
380 }
381
382 static const struct wl_input_method_listener wkb_im_listener = {
383      _wkb_im_activate,
384      _wkb_im_deactivate
385 };
386
387
388 static Eina_Bool
389 _wkb_ui_setup(struct weekeyboard *wkb)
390 {
391    char path[PATH_MAX];
392    Evas *evas;
393    Evas_Coord w, h;
394    char *ignore_keys;
395
396    ecore_evas_alpha_set(wkb->ee, EINA_TRUE);
397    ecore_evas_title_set(wkb->ee, "EFL virtual keyboard");
398
399    evas = ecore_evas_get(wkb->ee);
400    wkb->edje_obj = edje_object_add(evas);
401    /*ecore_evas_object_associate(wkb->ee, edje_obj, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);*/
402
403    /* Check which theme we should use according to the screen width */
404    ecore_wl_screen_size_get(&w, &h);
405    if (w >= 720)
406       w = 720;
407    else
408       w = 600;
409
410    sprintf(path, PKGDATADIR"/default_%d.edj", w);
411    fprintf(stderr,"Loading edje file: '%s'\n", path);
412
413    if (!edje_object_file_set(wkb->edje_obj, path, "main"))
414      {
415         int err = edje_object_load_error_get(wkb->edje_obj);
416         fprintf(stderr, "error loading the edje file:%s\n", edje_load_error_str(err));
417         return EINA_FALSE;
418      }
419
420    edje_object_size_min_get(wkb->edje_obj, &w, &h);
421    if (w == 0 || h == 0)
422      {
423         edje_object_size_min_restricted_calc(wkb->edje_obj, &w, &h, w, h);
424         if (w == 0 || h == 0)
425            edje_object_parts_extends_calc(wkb->edje_obj, NULL, NULL, &w, &h);
426      }
427
428    ecore_evas_move_resize(wkb->ee, 0, 0, w, h);
429    evas_object_move(wkb->edje_obj, 0, 0);
430    evas_object_resize(wkb->edje_obj, w, h);
431    evas_object_size_hint_min_set(wkb->edje_obj, w, h);
432    evas_object_size_hint_max_set(wkb->edje_obj, w, h);
433
434    edje_object_signal_callback_add(wkb->edje_obj, "key_down", "*", _cb_wkb_on_key_down, wkb);
435    ecore_evas_callback_delete_request_set(wkb->ee, _cb_wkb_delete_request);
436
437    /*
438     * The keyboard surface is bigger than it appears so that we can show the
439     * key pressed animation without requiring the use of subsurfaces. Here we
440     * resize the input region of the surface to match the keyboard background
441     * image, so that we can pass mouse events to the surfaces that may be
442     * located below the keyboard.
443     */
444    if (wkb->win)
445      {
446         int x, y, w, h;
447         struct wl_region *input = wl_compositor_create_region(wkb->win->display->wl.compositor);
448
449         edje_object_part_geometry_get(wkb->edje_obj, "background", &x, &y, &w, &h);
450         wl_region_add(input, x, y, w, h);
451         wl_surface_set_input_region(wkb->surface, input);
452         wl_region_destroy(input);
453      }
454
455    /* special keys */
456    ignore_keys = edje_file_data_get(path, "ignore-keys");
457    if (!ignore_keys)
458      {
459         fprintf(stderr,"Special keys file not found in '%s'\n", path);
460         goto end;
461      }
462
463    fprintf(stderr,"Got ignore keys = %s\n", ignore_keys);
464    wkb->ignore_keys = eina_str_split(ignore_keys, "\n", 0);
465    free(ignore_keys);
466
467 end:
468    ecore_evas_show(wkb->ee);
469    return EINA_TRUE;
470 }
471
472 static void
473 _wkb_setup(struct weekeyboard *wkb)
474 {
475    struct wl_list *globals;
476    struct wl_registry *registry;
477    Ecore_Wl_Global *global;
478
479    struct wl_input_panel_surface *ips;
480
481    globals = ecore_wl_globals_get();
482    registry = ecore_wl_registry_get();
483    wl_list_for_each(global, globals, link)
484      {
485         if (strcmp(global->interface, "wl_input_panel") == 0)
486            wkb->ip = wl_registry_bind(registry, global->id, &wl_input_panel_interface, 1);
487         else if (strcmp(global->interface, "wl_input_method") == 0)
488            wkb->im = wl_registry_bind(registry, global->id, &wl_input_method_interface, 1);
489         else if (strcmp(global->interface, "wl_output") == 0)
490            wkb->output = wl_registry_bind(registry, global->id, &wl_output_interface, 1);
491      }
492
493    /* Set input panel surface */
494    wkb->win = ecore_evas_wayland_window_get(wkb->ee);
495    ecore_wl_window_type_set(wkb->win, ECORE_WL_WINDOW_TYPE_NONE);
496    wkb->surface = ecore_wl_window_surface_create(wkb->win);
497    ips = wl_input_panel_get_input_panel_surface(wkb->ip, wkb->surface);
498    wl_input_panel_surface_set_toplevel(ips, wkb->output, WL_INPUT_PANEL_SURFACE_POSITION_CENTER_BOTTOM);
499
500    /* Input method listener */
501    wl_input_method_add_listener(wkb->im, &wkb_im_listener, wkb);
502 }
503
504 static void
505 _wkb_free(struct weekeyboard *wkb)
506 {
507    if (wkb->im_ctx)
508       wl_input_method_context_destroy(wkb->im_ctx);
509
510    if (wkb->ignore_keys)
511      {
512         free(*wkb->ignore_keys);
513         free(wkb->ignore_keys);
514      }
515
516    evas_object_del(wkb->edje_obj);
517    free(wkb->preedit_str);
518    free(wkb->surrounding_text);
519 }
520
521 static Eina_Bool
522 _wkb_check_evas_engine(struct weekeyboard *wkb)
523 {
524    Eina_Bool ret = EINA_FALSE;
525    char *env = getenv("ECORE_EVAS_ENGINE");
526
527    if (!env)
528      {
529         if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_SHM))
530            env = "wayland_shm";
531         else if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_EGL))
532            env = "wayland_egl";
533         else
534           {
535              fprintf(stderr,"ERROR: Ecore_Evas does must be compiled with support for Wayland engines\n");
536              goto err;
537           }
538      }
539    else if (strcmp(env, "wayland_shm") != 0 && strcmp(env, "wayland_egl") != 0)
540      {
541         fprintf(stderr,"ERROR: ECORE_EVAS_ENGINE must be set to either 'wayland_shm' or 'wayland_egl'\n");
542         goto err;
543      }
544
545    wkb->ee_engine = env;
546    ret = EINA_TRUE;
547
548 err:
549    return ret;
550 }
551
552 int
553 main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
554 {
555    struct weekeyboard wkb = {0};
556    int ret = EXIT_FAILURE;
557
558    if (!ecore_init())
559       return ret;
560
561    if (!ecore_evas_init())
562       goto ecore_err;
563
564    if (!edje_init())
565       goto ee_err;
566
567    if (!_wkb_check_evas_engine(&wkb))
568       goto edj_err;
569
570    fprintf(stderr,"SELECTED ENGINE = %s\n", wkb.ee_engine);
571    wkb.ee = ecore_evas_new(wkb.ee_engine, 0, 0, 1, 1, "frame=0");
572
573    if (!wkb.ee)
574      {
575         fprintf(stderr,"ERROR: Unable to create Ecore_Evas object\n");
576         goto edj_err;
577      }
578
579    _wkb_setup(&wkb);
580
581    if (!_wkb_ui_setup(&wkb))
582       goto end;
583
584    ecore_main_loop_begin();
585
586    ret = EXIT_SUCCESS;
587
588    _wkb_free(&wkb);
589
590 end:
591    ecore_evas_free(wkb.ee);
592
593 edj_err:
594    edje_shutdown();
595
596 ee_err:
597    ecore_evas_shutdown();
598
599 ecore_err:
600    ecore_shutdown();
601
602    return ret;
603 }