2 * Copyright © 2013 Intel Corporation
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 #include <Ecore_Wayland.h>
24 #include <Ecore_Evas.h>
27 #include <linux/input.h>
32 #include "input-method-client-protocol.h"
33 #include "text-client-protocol.h"
39 Evas_Object *edje_obj;
40 const char *ee_engine;
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;
49 char *surrounding_text;
53 uint32_t text_direction;
54 uint32_t preedit_style;
55 uint32_t content_hint;
56 uint32_t content_purpose;
58 uint32_t surrounding_cursor;
60 Eina_Bool context_changed;
64 _cb_wkb_delete_request(Ecore_Evas *ee EINA_UNUSED)
66 ecore_main_loop_quit();
70 _wkb_insert_text(const char *text, uint32_t offset, const char *insert)
72 char *new_text = malloc(strlen(text) + strlen(insert) + 1);
74 strncat(new_text, text, offset);
75 new_text[offset] = '\0';
76 strcat(new_text, insert);
77 strcat(new_text, text + offset);
83 _wkb_commit_preedit_str(struct weekeyboard *wkb)
85 char *surrounding_text;
87 if (!wkb->preedit_str || !strlen(wkb->preedit_str) == 0)
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);
93 if (wkb->surrounding_text)
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);
102 wkb->surrounding_text = strdup(wkb->preedit_str);
103 wkb->surrounding_cursor = strlen(wkb->preedit_str);
106 free(wkb->preedit_str);
107 wkb->preedit_str = strdup("");
111 _wkb_send_preedit_str(struct weekeyboard *wkb, int cursor)
113 unsigned int index = strlen(wkb->preedit_str);
115 if (wkb->preedit_style)
116 wl_input_method_context_preedit_styling(wkb->im_ctx, 0, strlen(wkb->preedit_str), wkb->preedit_style);
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);
126 _wkb_update_preedit_str(struct weekeyboard *wkb, const char *key)
130 if (!wkb->preedit_str)
131 wkb->preedit_str = strdup("");
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;
138 if (strcmp(key, " ") == 0)
139 _wkb_commit_preedit_str(wkb);
141 _wkb_send_preedit_str(wkb, -1);
145 _wkb_ignore_key(struct weekeyboard *wkb, const char *key)
149 if (!wkb->ignore_keys)
152 for (i = 0; wkb->ignore_keys[i] != NULL; i++)
153 if (!strcmp(key, wkb->ignore_keys[i]))
160 _cb_wkb_on_key_down(void *data, Evas_Object *obj, const char *emission EINA_UNUSED, const char *source)
162 struct weekeyboard *wkb = data;
166 src = strdup(source);
167 key = strtok(src, ":"); /* ignore group */
168 key = strtok(NULL, ":");
172 if (_wkb_ignore_key(wkb, key))
174 fprintf(stderr,"Ignoring key '%s'\n", key);
177 else if (strcmp(key, "backspace") == 0)
179 if (strlen(wkb->preedit_str) == 0)
180 wl_input_method_context_delete_surrounding_text(wkb->im_ctx, -1, 1);
183 wkb->preedit_str[strlen(wkb->preedit_str) - 1] = '\0';
184 _wkb_send_preedit_str(wkb, -1);
189 else if (strcmp(key, "enter") == 0)
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); */
195 else if (strcmp(key, "space") == 0)
200 fprintf(stderr,"KEY = '%s'\n", key);
202 _wkb_update_preedit_str(wkb, key);
209 _wkb_im_ctx_surrounding_text(void *data, struct wl_input_method_context *im_ctx, const char *text, uint32_t cursor, uint32_t anchor)
211 struct weekeyboard *wkb = data;
213 fprintf(stderr,"%s()\n", __FUNCTION__);
214 free(wkb->surrounding_text);
215 wkb->surrounding_text = strdup(text);
216 wkb->surrounding_cursor = cursor;
220 _wkb_im_ctx_reset(void *data, struct wl_input_method_context *im_ctx)
222 struct weekeyboard *wkb = data;
224 fprintf(stderr,"%s()\n", __FUNCTION__);
226 if (strlen(wkb->preedit_str))
228 free(wkb->preedit_str);
229 wkb->preedit_str = strdup("");
234 _wkb_im_ctx_content_type(void *data, struct wl_input_method_context *im_ctx, uint32_t hint, uint32_t purpose)
236 struct weekeyboard *wkb = data;
238 fprintf(stderr,"%s(): im_context = %p hint = %d purpose = %d\n", __FUNCTION__, im_ctx, hint, purpose);
240 if (!wkb->context_changed)
245 case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS:
246 case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER:
248 edje_object_signal_emit(wkb->edje_obj, "show,numeric", "");
253 edje_object_signal_emit(wkb->edje_obj, "show,alphanumeric", "");
258 wkb->content_hint = hint;
259 wkb->content_purpose = purpose;
261 wkb->context_changed = EINA_FALSE;
265 _wkb_im_ctx_invoke_action(void *data, struct wl_input_method_context *im_ctx, uint32_t button, uint32_t index)
267 struct weekeyboard *wkb = data;
269 fprintf(stderr,"%s()\n", __FUNCTION__);
270 if (button != BTN_LEFT)
273 _wkb_send_preedit_str(wkb, index);
277 _wkb_im_ctx_commit_state(void *data, struct wl_input_method_context *im_ctx, uint32_t serial)
279 struct weekeyboard *wkb = data;
281 fprintf(stderr,"%s()\n", __FUNCTION__);
282 if (wkb->surrounding_text)
283 fprintf(stderr, "Surrounding text updated: %s\n", wkb->surrounding_text);
285 wkb->serial = serial;
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);
292 _wkb_im_ctx_preferred_language(void *data, struct wl_input_method_context *im_ctx, const char *language)
294 struct weekeyboard *wkb = data;
296 if (language && wkb->language && !strcmp(language, wkb->language))
302 wkb->language = NULL;
307 wkb->language = strdup(language);
308 fprintf(stderr,"Language changed, new: '%s\n", language);
312 static const struct wl_input_method_context_listener wkb_im_context_listener = {
313 _wkb_im_ctx_surrounding_text,
315 _wkb_im_ctx_content_type,
316 _wkb_im_ctx_invoke_action,
317 _wkb_im_ctx_commit_state,
318 _wkb_im_ctx_preferred_language,
322 _wkb_im_activate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
324 struct weekeyboard *wkb = data;
325 struct wl_array modifiers_map;
328 wl_input_method_context_destroy(wkb->im_ctx);
330 if (wkb->preedit_str)
331 free(wkb->preedit_str);
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;
337 wkb->language = NULL;
338 free(wkb->surrounding_text);
339 wkb->surrounding_text = NULL;
342 wkb->im_ctx = im_ctx;
343 wl_input_method_context_add_listener(im_ctx, &wkb_im_context_listener, wkb);
346 wl_array_init(&modifiers_map);
348 keysym_modifiers_add(&modifiers_map, "Shift");
349 keysym_modifiers_add(&modifiers_map, "Control");
350 keysym_modifiers_add(&modifiers_map, "Mod1");
352 wl_input_method_context_modifiers_map(im_ctx, &modifiers_map);
354 wkb->keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift");
356 wl_array_release(&modifiers_map);
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);
363 wkb->context_changed = EINA_TRUE;
364 evas_object_show(wkb->edje_obj);
368 _wkb_im_deactivate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
370 struct weekeyboard *wkb = data;
375 wl_input_method_context_destroy(wkb->im_ctx);
377 evas_object_hide(wkb->edje_obj);
380 static const struct wl_input_method_listener wkb_im_listener = {
387 _wkb_ui_setup(struct weekeyboard *wkb)
394 ecore_evas_alpha_set(wkb->ee, EINA_TRUE);
395 ecore_evas_title_set(wkb->ee, "EFL virtual keyboard");
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);*/
401 /* Check which theme we should use according to the screen width */
402 ecore_wl_screen_size_get(&w, &h);
408 sprintf(path, PKGDATADIR"/default_%d.edj", w);
409 fprintf(stderr,"Loading edje file: '%s'\n", path);
411 if (!edje_object_file_set(wkb->edje_obj, path, "main"))
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));
418 edje_object_size_min_get(wkb->edje_obj, &w, &h);
419 if (w == 0 || h == 0)
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);
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);
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);
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.
445 struct wl_region *input = wl_compositor_create_region(wkb->win->display->wl.compositor);
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);
454 ignore_keys = edje_file_data_get(path, "ignore-keys");
457 fprintf(stderr,"Special keys file not found in '%s'\n", path);
461 fprintf(stderr,"Got ignore keys = %s\n", ignore_keys);
462 wkb->ignore_keys = eina_str_split(ignore_keys, "\n", 0);
466 ecore_evas_show(wkb->ee);
471 _wkb_setup(struct weekeyboard *wkb)
473 struct wl_list *globals;
474 struct wl_registry *registry;
475 Ecore_Wl_Global *global;
477 struct wl_input_panel_surface *ips;
479 globals = ecore_wl_globals_get();
480 registry = ecore_wl_registry_get();
481 wl_list_for_each(global, globals, link)
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);
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);
498 /* Input method listener */
499 wl_input_method_add_listener(wkb->im, &wkb_im_listener, wkb);
503 _wkb_free(struct weekeyboard *wkb)
506 wl_input_method_context_destroy(wkb->im_ctx);
508 if (wkb->ignore_keys)
510 free(*wkb->ignore_keys);
511 free(wkb->ignore_keys);
514 evas_object_del(wkb->edje_obj);
515 free(wkb->preedit_str);
516 free(wkb->surrounding_text);
520 _wkb_check_evas_engine(struct weekeyboard *wkb)
522 Eina_Bool ret = EINA_FALSE;
523 char *env = getenv("ECORE_EVAS_ENGINE");
527 if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_SHM))
529 else if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_EGL))
533 fprintf(stderr,"ERROR: Ecore_Evas does must be compiled with support for Wayland engines\n");
537 else if (strcmp(env, "wayland_shm") != 0 && strcmp(env, "wayland_egl") != 0)
539 fprintf(stderr,"ERROR: ECORE_EVAS_ENGINE must be set to either 'wayland_shm' or 'wayland_egl'\n");
543 wkb->ee_engine = env;
551 main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
553 struct weekeyboard wkb = {0};
554 int ret = EXIT_FAILURE;
559 if (!ecore_evas_init())
565 if (!_wkb_check_evas_engine(&wkb))
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");
573 fprintf(stderr,"ERROR: Unable to create Ecore_Evas object\n");
579 if (!_wkb_ui_setup(&wkb))
582 ecore_main_loop_begin();
589 ecore_evas_free(wkb.ee);
595 ecore_evas_shutdown();