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,
193 XKB_KEY_Return, WL_KEYBOARD_KEY_STATE_PRESSED,
197 else if (strcmp(key, "space") == 0)
202 fprintf(stderr,"KEY = '%s'\n", key);
204 _wkb_update_preedit_str(wkb, key);
211 _wkb_im_ctx_surrounding_text(void *data, struct wl_input_method_context *im_ctx, const char *text, uint32_t cursor, uint32_t anchor)
213 struct weekeyboard *wkb = data;
215 fprintf(stderr,"%s()\n", __FUNCTION__);
216 free(wkb->surrounding_text);
217 wkb->surrounding_text = strdup(text);
218 wkb->surrounding_cursor = cursor;
222 _wkb_im_ctx_reset(void *data, struct wl_input_method_context *im_ctx)
224 struct weekeyboard *wkb = data;
226 fprintf(stderr,"%s()\n", __FUNCTION__);
228 if (strlen(wkb->preedit_str))
230 free(wkb->preedit_str);
231 wkb->preedit_str = strdup("");
236 _wkb_im_ctx_content_type(void *data, struct wl_input_method_context *im_ctx, uint32_t hint, uint32_t purpose)
238 struct weekeyboard *wkb = data;
240 fprintf(stderr,"%s(): im_context = %p hint = %d purpose = %d\n", __FUNCTION__, im_ctx, hint, purpose);
242 if (!wkb->context_changed)
247 case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS:
248 case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER:
250 edje_object_signal_emit(wkb->edje_obj, "show,numeric", "");
255 edje_object_signal_emit(wkb->edje_obj, "show,alphanumeric", "");
260 wkb->content_hint = hint;
261 wkb->content_purpose = purpose;
263 wkb->context_changed = EINA_FALSE;
267 _wkb_im_ctx_invoke_action(void *data, struct wl_input_method_context *im_ctx, uint32_t button, uint32_t index)
269 struct weekeyboard *wkb = data;
271 fprintf(stderr,"%s()\n", __FUNCTION__);
272 if (button != BTN_LEFT)
275 _wkb_send_preedit_str(wkb, index);
279 _wkb_im_ctx_commit_state(void *data, struct wl_input_method_context *im_ctx, uint32_t serial)
281 struct weekeyboard *wkb = data;
283 fprintf(stderr,"%s()\n", __FUNCTION__);
284 if (wkb->surrounding_text)
285 fprintf(stderr, "Surrounding text updated: %s\n", wkb->surrounding_text);
287 wkb->serial = serial;
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);
294 _wkb_im_ctx_preferred_language(void *data, struct wl_input_method_context *im_ctx, const char *language)
296 struct weekeyboard *wkb = data;
298 if (language && wkb->language && !strcmp(language, wkb->language))
304 wkb->language = NULL;
309 wkb->language = strdup(language);
310 fprintf(stderr,"Language changed, new: '%s\n", language);
314 static const struct wl_input_method_context_listener wkb_im_context_listener = {
315 _wkb_im_ctx_surrounding_text,
317 _wkb_im_ctx_content_type,
318 _wkb_im_ctx_invoke_action,
319 _wkb_im_ctx_commit_state,
320 _wkb_im_ctx_preferred_language,
324 _wkb_im_activate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
326 struct weekeyboard *wkb = data;
327 struct wl_array modifiers_map;
330 wl_input_method_context_destroy(wkb->im_ctx);
332 if (wkb->preedit_str)
333 free(wkb->preedit_str);
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;
339 wkb->language = NULL;
340 free(wkb->surrounding_text);
341 wkb->surrounding_text = NULL;
344 wkb->im_ctx = im_ctx;
345 wl_input_method_context_add_listener(im_ctx, &wkb_im_context_listener, wkb);
348 wl_array_init(&modifiers_map);
350 keysym_modifiers_add(&modifiers_map, "Shift");
351 keysym_modifiers_add(&modifiers_map, "Control");
352 keysym_modifiers_add(&modifiers_map, "Mod1");
354 wl_input_method_context_modifiers_map(im_ctx, &modifiers_map);
356 wkb->keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift");
358 wl_array_release(&modifiers_map);
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);
365 wkb->context_changed = EINA_TRUE;
366 evas_object_show(wkb->edje_obj);
370 _wkb_im_deactivate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
372 struct weekeyboard *wkb = data;
377 wl_input_method_context_destroy(wkb->im_ctx);
379 evas_object_hide(wkb->edje_obj);
382 static const struct wl_input_method_listener wkb_im_listener = {
389 _wkb_ui_setup(struct weekeyboard *wkb)
396 ecore_evas_alpha_set(wkb->ee, EINA_TRUE);
397 ecore_evas_title_set(wkb->ee, "EFL virtual keyboard");
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);*/
403 /* Check which theme we should use according to the screen width */
404 ecore_wl_screen_size_get(&w, &h);
410 sprintf(path, PKGDATADIR"/default_%d.edj", w);
411 fprintf(stderr,"Loading edje file: '%s'\n", path);
413 if (!edje_object_file_set(wkb->edje_obj, path, "main"))
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));
420 edje_object_size_min_get(wkb->edje_obj, &w, &h);
421 if (w == 0 || h == 0)
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);
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);
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);
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.
447 struct wl_region *input = wl_compositor_create_region(wkb->win->display->wl.compositor);
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);
456 ignore_keys = edje_file_data_get(path, "ignore-keys");
459 fprintf(stderr,"Special keys file not found in '%s'\n", path);
463 fprintf(stderr,"Got ignore keys = %s\n", ignore_keys);
464 wkb->ignore_keys = eina_str_split(ignore_keys, "\n", 0);
468 ecore_evas_show(wkb->ee);
473 _wkb_setup(struct weekeyboard *wkb)
475 struct wl_list *globals;
476 struct wl_registry *registry;
477 Ecore_Wl_Global *global;
479 struct wl_input_panel_surface *ips;
481 globals = ecore_wl_globals_get();
482 registry = ecore_wl_registry_get();
483 wl_list_for_each(global, globals, link)
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);
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);
500 /* Input method listener */
501 wl_input_method_add_listener(wkb->im, &wkb_im_listener, wkb);
505 _wkb_free(struct weekeyboard *wkb)
508 wl_input_method_context_destroy(wkb->im_ctx);
510 if (wkb->ignore_keys)
512 free(*wkb->ignore_keys);
513 free(wkb->ignore_keys);
516 evas_object_del(wkb->edje_obj);
517 free(wkb->preedit_str);
518 free(wkb->surrounding_text);
522 _wkb_check_evas_engine(struct weekeyboard *wkb)
524 Eina_Bool ret = EINA_FALSE;
525 char *env = getenv("ECORE_EVAS_ENGINE");
529 if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_SHM))
531 else if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_EGL))
535 fprintf(stderr,"ERROR: Ecore_Evas does must be compiled with support for Wayland engines\n");
539 else if (strcmp(env, "wayland_shm") != 0 && strcmp(env, "wayland_egl") != 0)
541 fprintf(stderr,"ERROR: ECORE_EVAS_ENGINE must be set to either 'wayland_shm' or 'wayland_egl'\n");
545 wkb->ee_engine = "wayland_shm";//env;
553 main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
555 struct weekeyboard wkb = {0};
556 int ret = EXIT_FAILURE;
561 if (!ecore_evas_init())
567 if (!_wkb_check_evas_engine(&wkb))
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");
575 fprintf(stderr,"ERROR: Unable to create Ecore_Evas object\n");
581 if (!_wkb_ui_setup(&wkb))
584 ecore_main_loop_begin();
591 ecore_evas_free(wkb.ee);
597 ecore_evas_shutdown();