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, XKB_KEY_Return, key_state, mod_mask); */
193         goto end;
194      }
195    else if (strcmp(key, "space") == 0)
196      {
197         key = " ";
198      }
199
200    fprintf(stderr,"KEY = '%s'\n", key);
201
202    _wkb_update_preedit_str(wkb, key);
203
204 end:
205    free(src);
206 }
207
208 static void
209 _wkb_im_ctx_surrounding_text(void *data, struct wl_input_method_context *im_ctx, const char *text, uint32_t cursor, uint32_t anchor)
210 {
211    struct weekeyboard *wkb = data;
212
213    fprintf(stderr,"%s()\n", __FUNCTION__);
214    free(wkb->surrounding_text);
215    wkb->surrounding_text = strdup(text);
216    wkb->surrounding_cursor = cursor;
217 }
218
219 static void
220 _wkb_im_ctx_reset(void *data, struct wl_input_method_context *im_ctx)
221 {
222    struct weekeyboard *wkb = data;
223
224    fprintf(stderr,"%s()\n", __FUNCTION__);
225
226    if (strlen(wkb->preedit_str))
227      {
228         free(wkb->preedit_str);
229         wkb->preedit_str = strdup("");
230      }
231 }
232
233 static void
234 _wkb_im_ctx_content_type(void *data, struct wl_input_method_context *im_ctx, uint32_t hint, uint32_t purpose)
235 {
236    struct weekeyboard *wkb = data;
237
238    fprintf(stderr,"%s(): im_context = %p hint = %d purpose = %d\n", __FUNCTION__, im_ctx, hint, purpose);
239
240    if (!wkb->context_changed)
241       return;
242
243    switch (purpose)
244      {
245       case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS:
246       case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER:
247            {
248               edje_object_signal_emit(wkb->edje_obj, "show,numeric", "");
249               break;
250            }
251       default:
252            {
253               edje_object_signal_emit(wkb->edje_obj, "show,alphanumeric", "");
254               break;
255            }
256      }
257
258    wkb->content_hint = hint;
259    wkb->content_purpose = purpose;
260
261    wkb->context_changed = EINA_FALSE;
262 }
263
264 static void
265 _wkb_im_ctx_invoke_action(void *data, struct wl_input_method_context *im_ctx, uint32_t button, uint32_t index)
266 {
267    struct weekeyboard *wkb = data;
268
269    fprintf(stderr,"%s()\n", __FUNCTION__);
270    if (button != BTN_LEFT)
271       return;
272
273    _wkb_send_preedit_str(wkb, index);
274 }
275
276 static void
277 _wkb_im_ctx_commit_state(void *data, struct wl_input_method_context *im_ctx, uint32_t serial)
278 {
279    struct weekeyboard *wkb = data;
280
281    fprintf(stderr,"%s()\n", __FUNCTION__);
282    if (wkb->surrounding_text)
283       fprintf(stderr, "Surrounding text updated: %s\n", wkb->surrounding_text);
284
285    wkb->serial = serial;
286    /* FIXME */
287    wl_input_method_context_language(im_ctx, wkb->serial, "en");//wkb->language);
288    wl_input_method_context_text_direction(im_ctx, wkb->serial, WL_TEXT_INPUT_TEXT_DIRECTION_LTR);//wkb->text_direction);
289 }
290
291 static void
292 _wkb_im_ctx_preferred_language(void *data, struct wl_input_method_context *im_ctx, const char *language)
293 {
294    struct weekeyboard *wkb = data;
295
296    if (language && wkb->language && !strcmp(language, wkb->language))
297       return;
298
299    if (wkb->language)
300      {
301         free(wkb->language);
302         wkb->language = NULL;
303      }
304
305    if (language)
306      {
307         wkb->language = strdup(language);
308         fprintf(stderr,"Language changed, new: '%s\n", language);
309      }
310 }
311
312 static const struct wl_input_method_context_listener wkb_im_context_listener = {
313      _wkb_im_ctx_surrounding_text,
314      _wkb_im_ctx_reset,
315      _wkb_im_ctx_content_type,
316      _wkb_im_ctx_invoke_action,
317      _wkb_im_ctx_commit_state,
318      _wkb_im_ctx_preferred_language,
319 };
320
321 static void
322 _wkb_im_activate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
323 {
324    struct weekeyboard *wkb = data;
325    struct wl_array modifiers_map;
326
327    if (wkb->im_ctx)
328       wl_input_method_context_destroy(wkb->im_ctx);
329
330    if (wkb->preedit_str)
331       free(wkb->preedit_str);
332
333    wkb->preedit_str = strdup("");
334    wkb->content_hint = WL_TEXT_INPUT_CONTENT_HINT_NONE;
335    wkb->content_purpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NORMAL;
336    free(wkb->language);
337    wkb->language = NULL;
338    free(wkb->surrounding_text);
339    wkb->surrounding_text = NULL;
340    wkb->serial = 0;
341
342    wkb->im_ctx = im_ctx;
343    wl_input_method_context_add_listener(im_ctx, &wkb_im_context_listener, wkb);
344
345    /*
346    wl_array_init(&modifiers_map);
347
348    keysym_modifiers_add(&modifiers_map, "Shift");
349    keysym_modifiers_add(&modifiers_map, "Control");
350    keysym_modifiers_add(&modifiers_map, "Mod1");
351
352    wl_input_method_context_modifiers_map(im_ctx, &modifiers_map);
353
354    wkb->keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift");
355
356    wl_array_release(&modifiers_map);
357    */
358
359    /* FIXME */
360    wl_input_method_context_language(im_ctx, wkb->serial, "en");//wkb->language);
361    wl_input_method_context_text_direction(im_ctx, wkb->serial, WL_TEXT_INPUT_TEXT_DIRECTION_LTR);//wkb->text_direction);
362
363    wkb->context_changed = EINA_TRUE;
364    evas_object_show(wkb->edje_obj);
365 }
366
367 static void
368 _wkb_im_deactivate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
369 {
370    struct weekeyboard *wkb = data;
371
372    if (!wkb->im_ctx)
373       return;
374
375    wl_input_method_context_destroy(wkb->im_ctx);
376    wkb->im_ctx = NULL;
377    evas_object_hide(wkb->edje_obj);
378 }
379
380 static const struct wl_input_method_listener wkb_im_listener = {
381      _wkb_im_activate,
382      _wkb_im_deactivate
383 };
384
385
386 static Eina_Bool
387 _wkb_ui_setup(struct weekeyboard *wkb)
388 {
389    char path[PATH_MAX];
390    Evas *evas;
391    Evas_Coord w, h;
392    char *ignore_keys;
393
394    ecore_evas_alpha_set(wkb->ee, EINA_TRUE);
395    ecore_evas_title_set(wkb->ee, "EFL virtual keyboard");
396
397    evas = ecore_evas_get(wkb->ee);
398    wkb->edje_obj = edje_object_add(evas);
399    /*ecore_evas_object_associate(wkb->ee, edje_obj, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);*/
400
401    /* Check which theme we should use according to the screen width */
402    ecore_wl_screen_size_get(&w, &h);
403    if (w >= 720)
404       w = 720;
405    else
406       w = 600;
407
408    sprintf(path, PKGDATADIR"/default_%d.edj", w);
409    fprintf(stderr,"Loading edje file: '%s'\n", path);
410
411    if (!edje_object_file_set(wkb->edje_obj, path, "main"))
412      {
413         int err = edje_object_load_error_get(wkb->edje_obj);
414         fprintf(stderr, "error loading the edje file:%s\n", edje_load_error_str(err));
415         return EINA_FALSE;
416      }
417
418    edje_object_size_min_get(wkb->edje_obj, &w, &h);
419    if (w == 0 || h == 0)
420      {
421         edje_object_size_min_restricted_calc(wkb->edje_obj, &w, &h, w, h);
422         if (w == 0 || h == 0)
423            edje_object_parts_extends_calc(wkb->edje_obj, NULL, NULL, &w, &h);
424      }
425
426    ecore_evas_move_resize(wkb->ee, 0, 0, w, h);
427    evas_object_move(wkb->edje_obj, 0, 0);
428    evas_object_resize(wkb->edje_obj, w, h);
429    evas_object_size_hint_min_set(wkb->edje_obj, w, h);
430    evas_object_size_hint_max_set(wkb->edje_obj, w, h);
431
432    edje_object_signal_callback_add(wkb->edje_obj, "key_down", "*", _cb_wkb_on_key_down, wkb);
433    ecore_evas_callback_delete_request_set(wkb->ee, _cb_wkb_delete_request);
434
435    /*
436     * The keyboard surface is bigger than it appears so that we can show the
437     * key pressed animation without requiring the use of subsurfaces. Here we
438     * resize the input region of the surface to match the keyboard background
439     * image, so that we can pass mouse events to the surfaces that may be
440     * located below the keyboard.
441     */
442    if (wkb->win)
443      {
444         int x, y, w, h;
445         struct wl_region *input = wl_compositor_create_region(wkb->win->display->wl.compositor);
446
447         edje_object_part_geometry_get(wkb->edje_obj, "background", &x, &y, &w, &h);
448         wl_region_add(input, x, y, w, h);
449         wl_surface_set_input_region(wkb->surface, input);
450         wl_region_destroy(input);
451      }
452
453    /* special keys */
454    ignore_keys = edje_file_data_get(path, "ignore-keys");
455    if (!ignore_keys)
456      {
457         fprintf(stderr,"Special keys file not found in '%s'\n", path);
458         goto end;
459      }
460
461    fprintf(stderr,"Got ignore keys = %s\n", ignore_keys);
462    wkb->ignore_keys = eina_str_split(ignore_keys, "\n", 0);
463    free(ignore_keys);
464
465 end:
466    ecore_evas_show(wkb->ee);
467    return EINA_TRUE;
468 }
469
470 static void
471 _wkb_setup(struct weekeyboard *wkb)
472 {
473    struct wl_list *globals;
474    struct wl_registry *registry;
475    Ecore_Wl_Global *global;
476
477    struct wl_input_panel_surface *ips;
478
479    globals = ecore_wl_globals_get();
480    registry = ecore_wl_registry_get();
481    wl_list_for_each(global, globals, link)
482      {
483         if (strcmp(global->interface, "wl_input_panel") == 0)
484            wkb->ip = wl_registry_bind(registry, global->id, &wl_input_panel_interface, 1);
485         else if (strcmp(global->interface, "wl_input_method") == 0)
486            wkb->im = wl_registry_bind(registry, global->id, &wl_input_method_interface, 1);
487         else if (strcmp(global->interface, "wl_output") == 0)
488            wkb->output = wl_registry_bind(registry, global->id, &wl_output_interface, 1);
489      }
490
491    /* Set input panel surface */
492    wkb->win = ecore_evas_wayland_window_get(wkb->ee);
493    ecore_wl_window_type_set(wkb->win, ECORE_WL_WINDOW_TYPE_NONE);
494    wkb->surface = ecore_wl_window_surface_create(wkb->win);
495    ips = wl_input_panel_get_input_panel_surface(wkb->ip, wkb->surface);
496    wl_input_panel_surface_set_toplevel(ips, wkb->output, WL_INPUT_PANEL_SURFACE_POSITION_CENTER_BOTTOM);
497
498    /* Input method listener */
499    wl_input_method_add_listener(wkb->im, &wkb_im_listener, wkb);
500 }
501
502 static void
503 _wkb_free(struct weekeyboard *wkb)
504 {
505    if (wkb->im_ctx)
506       wl_input_method_context_destroy(wkb->im_ctx);
507
508    if (wkb->ignore_keys)
509      {
510         free(*wkb->ignore_keys);
511         free(wkb->ignore_keys);
512      }
513
514    evas_object_del(wkb->edje_obj);
515    free(wkb->preedit_str);
516    free(wkb->surrounding_text);
517 }
518
519 static Eina_Bool
520 _wkb_check_evas_engine(struct weekeyboard *wkb)
521 {
522    Eina_Bool ret = EINA_FALSE;
523    char *env = getenv("ECORE_EVAS_ENGINE");
524
525    if (!env)
526      {
527         if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_SHM))
528            env = "wayland_shm";
529         else if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_EGL))
530            env = "wayland_egl";
531         else
532           {
533              fprintf(stderr,"ERROR: Ecore_Evas does must be compiled with support for Wayland engines\n");
534              goto err;
535           }
536      }
537    else if (strcmp(env, "wayland_shm") != 0 && strcmp(env, "wayland_egl") != 0)
538      {
539         fprintf(stderr,"ERROR: ECORE_EVAS_ENGINE must be set to either 'wayland_shm' or 'wayland_egl'\n");
540         goto err;
541      }
542
543    wkb->ee_engine = "wayland_shm";//env;
544    ret = EINA_TRUE;
545
546 err:
547    return ret;
548 }
549
550 int
551 main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
552 {
553    struct weekeyboard wkb = {0};
554    int ret = EXIT_FAILURE;
555
556    if (!ecore_init())
557       return ret;
558
559    if (!ecore_evas_init())
560       goto ecore_err;
561
562    if (!edje_init())
563       goto ee_err;
564
565    if (!_wkb_check_evas_engine(&wkb))
566       goto edj_err;
567
568    fprintf(stderr,"SELECTED ENGINE = %s\n", wkb.ee_engine);
569    wkb.ee = ecore_evas_new(wkb.ee_engine, 0, 0, 1, 1, "frame=0");
570
571    if (!wkb.ee)
572      {
573         fprintf(stderr,"ERROR: Unable to create Ecore_Evas object\n");
574         goto edj_err;
575      }
576
577    _wkb_setup(&wkb);
578
579    if (!_wkb_ui_setup(&wkb))
580       goto end;
581
582    ecore_main_loop_begin();
583
584    ret = EXIT_SUCCESS;
585
586    _wkb_free(&wkb);
587
588 end:
589    ecore_evas_free(wkb.ee);
590
591 edj_err:
592    edje_shutdown();
593
594 ee_err:
595    ecore_evas_shutdown();
596
597 ecore_err:
598    ecore_shutdown();
599
600    return ret;
601 }