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>
35 #include "input-method-client-protocol.h"
36 #include "text-client-protocol.h"
42 Evas_Object *edje_obj;
43 const char *ee_engine;
46 struct wl_surface *surface;
47 struct wl_input_panel *ip;
48 struct wl_input_method *im;
49 struct wl_output *output;
50 struct wl_input_method_context *im_ctx;
52 char *surrounding_text;
56 uint32_t text_direction;
57 uint32_t preedit_style;
58 uint32_t content_hint;
59 uint32_t content_purpose;
60 uint32_t surrounding_cursor;
62 Eina_Bool context_changed;
66 _cb_wkb_delete_request(Ecore_Evas *ee EINA_UNUSED)
68 if (!wkb_ibus_shutdown())
69 ecore_main_loop_quit();
73 _wkb_insert_text(const char *text, uint32_t offset, const char *insert)
75 char *new_text = malloc(strlen(text) + strlen(insert) + 1);
77 strncat(new_text, text, offset);
78 new_text[offset] = '\0';
79 strcat(new_text, insert);
80 strcat(new_text, text + offset);
86 _wkb_commit_preedit_str(struct weekeyboard *wkb)
88 char *surrounding_text;
90 if (!wkb->preedit_str || !strlen(wkb->preedit_str) == 0)
93 wl_input_method_context_cursor_position(wkb->im_ctx, 0, 0);
94 wl_input_method_context_commit_string(wkb->im_ctx, wkb_ibus_input_context_serial(), wkb->preedit_str);
96 if (wkb->surrounding_text)
98 surrounding_text = _wkb_insert_text(wkb->surrounding_text, wkb->surrounding_cursor, wkb->preedit_str);
99 free(wkb->surrounding_text);
100 wkb->surrounding_text = surrounding_text;
101 wkb->surrounding_cursor += strlen(wkb->preedit_str);
105 wkb->surrounding_text = strdup(wkb->preedit_str);
106 wkb->surrounding_cursor = strlen(wkb->preedit_str);
109 free(wkb->preedit_str);
110 wkb->preedit_str = strdup("");
114 _wkb_send_preedit_str(struct weekeyboard *wkb, int cursor)
116 unsigned int index = strlen(wkb->preedit_str);
118 if (wkb->preedit_style)
119 wl_input_method_context_preedit_styling(wkb->im_ctx, 0, strlen(wkb->preedit_str), wkb->preedit_style);
124 wl_input_method_context_preedit_cursor(wkb->im_ctx, index);
125 wl_input_method_context_preedit_string(wkb->im_ctx, wkb_ibus_input_context_serial(), wkb->preedit_str, wkb->preedit_str);
129 _wkb_update_preedit_str(struct weekeyboard *wkb, const char *key)
133 if (!wkb->preedit_str)
134 wkb->preedit_str = strdup("");
136 tmp = calloc(1, strlen(wkb->preedit_str) + strlen(key) + 1);
137 sprintf(tmp, "%s%s", wkb->preedit_str, key);
138 free(wkb->preedit_str);
139 wkb->preedit_str = tmp;
141 if (strcmp(key, " ") == 0)
142 _wkb_commit_preedit_str(wkb);
144 _wkb_send_preedit_str(wkb, -1);
148 _wkb_ignore_key(struct weekeyboard *wkb, const char *key)
152 if (!wkb->ignore_keys)
155 for (i = 0; wkb->ignore_keys[i] != NULL; i++)
156 if (!strcmp(key, wkb->ignore_keys[i]))
163 _cb_wkb_on_key_down(void *data, Evas_Object *obj, const char *emission EINA_UNUSED, const char *source)
165 struct weekeyboard *wkb = data;
169 src = strdup(source);
170 key = strtok(src, ":"); /* ignore group */
171 key = strtok(NULL, ":");
175 if (_wkb_ignore_key(wkb, key))
177 DBG("Ignoring key: '%s'", key);
181 wkb_ibus_input_context_process_key_event(key);
183 else if (strcmp(key, "backspace") == 0)
185 if (strlen(wkb->preedit_str) == 0)
187 wl_input_method_context_delete_surrounding_text(wkb->im_ctx, -1, 1);
188 wl_input_method_context_commit_string(wkb->im_ctx, wkb->serial, "");
192 wkb->preedit_str[strlen(wkb->preedit_str) - 1] = '\0';
193 _wkb_send_preedit_str(wkb, -1);
198 else if (strcmp(key, "enter") == 0)
200 _wkb_commit_preedit_str(wkb);
201 wl_input_method_context_keysym(wkb->im_ctx, wkb->serial, 0,
202 XKB_KEY_Return, WL_KEYBOARD_KEY_STATE_PRESSED,
206 else if (strcmp(key, "space") == 0)
211 DBG("Key pressed: '%s'", key);
213 _wkb_update_preedit_str(wkb, key);
220 _wkb_im_ctx_surrounding_text(void *data, struct wl_input_method_context *im_ctx, const char *text, uint32_t cursor, uint32_t anchor)
223 struct weekeyboard *wkb = data;
225 free(wkb->surrounding_text);
226 wkb->surrounding_text = strdup(text);
227 wkb->surrounding_cursor = cursor;
232 _wkb_im_ctx_reset(void *data, struct wl_input_method_context *im_ctx)
235 struct weekeyboard *wkb = data;
237 if (strlen(wkb->preedit_str))
239 free(wkb->preedit_str);
240 wkb->preedit_str = strdup("");
246 _wkb_im_ctx_content_type(void *data, struct wl_input_method_context *im_ctx, uint32_t hint, uint32_t purpose)
248 struct weekeyboard *wkb = data;
250 DBG("im_context = %p hint = %d purpose = %d", im_ctx, hint, purpose);
252 if (!wkb->context_changed)
257 case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS:
258 case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER:
260 edje_object_signal_emit(wkb->edje_obj, "show,numeric", "");
265 edje_object_signal_emit(wkb->edje_obj, "show,alphanumeric", "");
270 wkb->content_hint = hint;
271 wkb->content_purpose = purpose;
273 wkb->context_changed = EINA_FALSE;
277 _wkb_im_ctx_invoke_action(void *data, struct wl_input_method_context *im_ctx, uint32_t button, uint32_t index)
280 struct weekeyboard *wkb = data;
282 if (button != BTN_LEFT)
285 _wkb_send_preedit_str(wkb, index);
290 _wkb_im_ctx_commit_state(void *data, struct wl_input_method_context *im_ctx, uint32_t serial)
292 struct weekeyboard *wkb = data;
294 if (wkb->surrounding_text)
295 INF("Surrounding text updated: %s", wkb->surrounding_text);
297 wkb_ibus_input_context_set_serial(serial);
300 wl_input_method_context_language(im_ctx, wkb_ibus_input_context_serial(), "en");//wkb->language);
301 wl_input_method_context_text_direction(im_ctx, wkb_ibus_input_context_serial(), WL_TEXT_INPUT_TEXT_DIRECTION_LTR);//wkb->text_direction);
306 _wkb_im_ctx_preferred_language(void *data, struct wl_input_method_context *im_ctx, const char *language)
309 struct weekeyboard *wkb = data;
311 if (language && wkb->language && !strcmp(language, wkb->language))
317 wkb->language = NULL;
322 wkb->language = strdup(language);
323 INF("Language changed, new: '%s'", language);
328 static const struct wl_input_method_context_listener wkb_im_context_listener = {
329 _wkb_im_ctx_surrounding_text,
331 _wkb_im_ctx_content_type,
332 _wkb_im_ctx_invoke_action,
333 _wkb_im_ctx_commit_state,
334 _wkb_im_ctx_preferred_language,
338 _wkb_im_activate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
340 struct weekeyboard *wkb = data;
345 wl_input_method_context_destroy(wkb->im_ctx);
347 if (wkb->preedit_str)
348 free(wkb->preedit_str);
350 wkb->preedit_str = strdup("");
351 wkb->content_hint = WL_TEXT_INPUT_CONTENT_HINT_NONE;
352 wkb->content_purpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NORMAL;
355 wkb->language = NULL;
357 free(wkb->surrounding_text);
358 wkb->surrounding_text = NULL;
360 wkb_ibus_input_context_set_serial(0);
362 wkb->im_ctx = im_ctx;
363 wl_input_method_context_add_listener(im_ctx, &wkb_im_context_listener, wkb);
364 wkb_ibus_input_context_create(im_ctx);
367 struct wl_array modifiers_map;
368 wl_array_init(&modifiers_map);
370 keysym_modifiers_add(&modifiers_map, "Shift");
371 keysym_modifiers_add(&modifiers_map, "Control");
372 keysym_modifiers_add(&modifiers_map, "Mod1");
374 wl_input_method_context_modifiers_map(im_ctx, &modifiers_map);
376 wkb->keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift");
378 wl_array_release(&modifiers_map);
382 wl_input_method_context_language(im_ctx, wkb_ibus_input_context_serial(), "en");//wkb->language);
383 wl_input_method_context_text_direction(im_ctx, wkb_ibus_input_context_serial(), WL_TEXT_INPUT_TEXT_DIRECTION_LTR);//wkb->text_direction);
385 wkb->context_changed = EINA_TRUE;
386 evas_object_show(wkb->edje_obj);
390 _wkb_im_deactivate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
392 struct weekeyboard *wkb = data;
396 wkb_ibus_input_context_destroy();
400 wl_input_method_context_destroy(wkb->im_ctx);
404 evas_object_hide(wkb->edje_obj);
407 static const struct wl_input_method_listener wkb_im_listener = {
414 _wkb_ui_setup(struct weekeyboard *wkb)
421 ecore_evas_alpha_set(wkb->ee, EINA_TRUE);
422 ecore_evas_title_set(wkb->ee, "Weekeyboard");
424 evas = ecore_evas_get(wkb->ee);
425 wkb->edje_obj = edje_object_add(evas);
426 /*ecore_evas_object_associate(wkb->ee, edje_obj, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);*/
428 /* Check which theme we should use according to the screen width */
429 ecore_wl_screen_size_get(&w, &h);
435 sprintf(path, PKGDATADIR"/default_%d.edj", w);
436 DBG("Loading edje file: '%s'", path);
438 if (!edje_object_file_set(wkb->edje_obj, path, "main"))
440 int err = edje_object_load_error_get(wkb->edje_obj);
441 ERR("Unable to load the edje file: '%s'", edje_load_error_str(err));
445 edje_object_size_min_get(wkb->edje_obj, &w, &h);
446 if (w == 0 || h == 0)
448 edje_object_size_min_restricted_calc(wkb->edje_obj, &w, &h, w, h);
449 if (w == 0 || h == 0)
450 edje_object_parts_extends_calc(wkb->edje_obj, NULL, NULL, &w, &h);
453 ecore_evas_move_resize(wkb->ee, 0, 0, w, h);
454 evas_object_move(wkb->edje_obj, 0, 0);
455 evas_object_resize(wkb->edje_obj, w, h);
456 evas_object_size_hint_min_set(wkb->edje_obj, w, h);
457 evas_object_size_hint_max_set(wkb->edje_obj, w, h);
459 edje_object_signal_callback_add(wkb->edje_obj, "key_down", "*", _cb_wkb_on_key_down, wkb);
460 ecore_evas_callback_delete_request_set(wkb->ee, _cb_wkb_delete_request);
463 * The keyboard surface is bigger than it appears so that we can show the
464 * key pressed animation without requiring the use of subsurfaces. Here we
465 * resize the input region of the surface to match the keyboard background
466 * image, so that we can pass mouse events to the surfaces that may be
467 * located below the keyboard.
472 struct wl_region *input = wl_compositor_create_region(wkb->win->display->wl.compositor);
474 edje_object_part_geometry_get(wkb->edje_obj, "background", &x, &y, &w, &h);
475 wl_region_add(input, x, y, w, h);
476 wl_surface_set_input_region(wkb->surface, input);
477 wl_region_destroy(input);
481 ignore_keys = edje_file_data_get(path, "ignore-keys");
484 ERR("Special keys file not found in: '%s'", path);
488 DBG("Got ignore keys: '%s'", ignore_keys);
489 wkb->ignore_keys = eina_str_split(ignore_keys, "\n", 0);
493 ecore_evas_show(wkb->ee);
498 _wkb_setup(struct weekeyboard *wkb)
500 struct wl_list *globals;
501 struct wl_registry *registry;
502 Ecore_Wl_Global *global;
504 struct wl_input_panel_surface *ips;
506 globals = ecore_wl_globals_get();
507 registry = ecore_wl_registry_get();
508 wl_list_for_each(global, globals, link)
510 if (strcmp(global->interface, "wl_input_panel") == 0)
511 wkb->ip = wl_registry_bind(registry, global->id, &wl_input_panel_interface, 1);
512 else if (strcmp(global->interface, "wl_input_method") == 0)
513 wkb->im = wl_registry_bind(registry, global->id, &wl_input_method_interface, 1);
514 else if (strcmp(global->interface, "wl_output") == 0)
515 wkb->output = wl_registry_bind(registry, global->id, &wl_output_interface, 1);
518 /* Set input panel surface */
519 DBG("Setting up input panel");
520 wkb->win = ecore_evas_wayland_window_get(wkb->ee);
521 ecore_wl_window_type_set(wkb->win, ECORE_WL_WINDOW_TYPE_NONE);
522 wkb->surface = ecore_wl_window_surface_create(wkb->win);
523 ips = wl_input_panel_get_input_panel_surface(wkb->ip, wkb->surface);
524 wl_input_panel_surface_set_toplevel(ips, wkb->output, WL_INPUT_PANEL_SURFACE_POSITION_CENTER_BOTTOM);
526 /* Input method listener */
527 DBG("Adding wl_input_method listener");
528 wl_input_method_add_listener(wkb->im, &wkb_im_listener, wkb);
532 _wkb_free(struct weekeyboard *wkb)
535 wl_input_method_context_destroy(wkb->im_ctx);
538 evas_object_del(wkb->edje_obj);
540 if (wkb->ignore_keys)
542 free(*wkb->ignore_keys);
543 free(wkb->ignore_keys);
546 free(wkb->preedit_str);
547 free(wkb->surrounding_text);
551 _wkb_check_evas_engine(struct weekeyboard *wkb)
553 Eina_Bool ret = EINA_FALSE;
554 char *env = getenv("ECORE_EVAS_ENGINE");
558 if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_SHM))
560 else if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_EGL))
564 ERR("ERROR: Ecore_Evas does must be compiled with support for Wayland engines");
568 else if (strcmp(env, "wayland_shm") != 0 && strcmp(env, "wayland_egl") != 0)
570 ERR("ERROR: ECORE_EVAS_ENGINE must be set to either 'wayland_shm' or 'wayland_egl'");
574 wkb->ee_engine = env;
582 _wkb_check_ibus_connection(void *data)
584 static int tries = 0;
588 CRITICAL("Unable to establish connection to IBus.");
589 return ECORE_CALLBACK_DONE;
592 return !wkb_ibus_is_connected();
596 main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
598 struct weekeyboard wkb = {0};
599 int ret = EXIT_FAILURE;
601 if (!wkb_log_init("weekeyboard"))
604 if (!ecore_evas_init())
610 if (!_wkb_check_evas_engine(&wkb))
613 DBG("Selected engine: '%s'", wkb.ee_engine);
614 wkb.ee = ecore_evas_new(wkb.ee_engine, 0, 0, 1, 1, "frame=0");
618 ERR("ERROR: Unable to create Ecore_Evas object");
626 if (!_wkb_ui_setup(&wkb))
630 ecore_timer_add(1, _wkb_check_ibus_connection, NULL);
631 ecore_main_loop_begin();
637 ecore_evas_free(wkb.ee);
643 ecore_evas_shutdown();