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);
188 _wkb_im_ctx_surrounding_text(void *data, struct wl_input_method_context *im_ctx, const char *text, uint32_t cursor, uint32_t anchor)
191 struct weekeyboard *wkb = data;
193 free(wkb->surrounding_text);
194 wkb->surrounding_text = strdup(text);
195 wkb->surrounding_cursor = cursor;
200 _wkb_im_ctx_reset(void *data, struct wl_input_method_context *im_ctx)
203 struct weekeyboard *wkb = data;
205 if (strlen(wkb->preedit_str))
207 free(wkb->preedit_str);
208 wkb->preedit_str = strdup("");
214 _wkb_im_ctx_content_type(void *data, struct wl_input_method_context *im_ctx, uint32_t hint, uint32_t purpose)
216 struct weekeyboard *wkb = data;
218 DBG("im_context = %p hint = %d purpose = %d", im_ctx, hint, purpose);
220 if (!wkb->context_changed)
225 case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS:
226 case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER:
228 edje_object_signal_emit(wkb->edje_obj, "show,numeric", "");
233 edje_object_signal_emit(wkb->edje_obj, "show,alphanumeric", "");
238 wkb->content_hint = hint;
239 wkb->content_purpose = purpose;
241 wkb->context_changed = EINA_FALSE;
245 _wkb_im_ctx_invoke_action(void *data, struct wl_input_method_context *im_ctx, uint32_t button, uint32_t index)
248 struct weekeyboard *wkb = data;
250 if (button != BTN_LEFT)
253 _wkb_send_preedit_str(wkb, index);
258 _wkb_im_ctx_commit_state(void *data, struct wl_input_method_context *im_ctx, uint32_t serial)
260 struct weekeyboard *wkb = data;
262 if (wkb->surrounding_text)
263 INF("Surrounding text updated: %s", wkb->surrounding_text);
265 wkb_ibus_input_context_set_serial(serial);
268 wl_input_method_context_language(im_ctx, wkb_ibus_input_context_serial(), "en");//wkb->language);
269 wl_input_method_context_text_direction(im_ctx, wkb_ibus_input_context_serial(), WL_TEXT_INPUT_TEXT_DIRECTION_LTR);//wkb->text_direction);
274 _wkb_im_ctx_preferred_language(void *data, struct wl_input_method_context *im_ctx, const char *language)
277 struct weekeyboard *wkb = data;
279 if (language && wkb->language && !strcmp(language, wkb->language))
285 wkb->language = NULL;
290 wkb->language = strdup(language);
291 INF("Language changed, new: '%s'", language);
296 static const struct wl_input_method_context_listener wkb_im_context_listener = {
297 _wkb_im_ctx_surrounding_text,
299 _wkb_im_ctx_content_type,
300 _wkb_im_ctx_invoke_action,
301 _wkb_im_ctx_commit_state,
302 _wkb_im_ctx_preferred_language,
306 _wkb_im_activate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
308 struct weekeyboard *wkb = data;
313 wl_input_method_context_destroy(wkb->im_ctx);
315 if (wkb->preedit_str)
316 free(wkb->preedit_str);
318 wkb->preedit_str = strdup("");
319 wkb->content_hint = WL_TEXT_INPUT_CONTENT_HINT_NONE;
320 wkb->content_purpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NORMAL;
323 wkb->language = NULL;
325 free(wkb->surrounding_text);
326 wkb->surrounding_text = NULL;
328 wkb_ibus_input_context_set_serial(0);
330 wkb->im_ctx = im_ctx;
331 wl_input_method_context_add_listener(im_ctx, &wkb_im_context_listener, wkb);
332 wkb_ibus_input_context_create(im_ctx);
335 struct wl_array modifiers_map;
336 wl_array_init(&modifiers_map);
338 keysym_modifiers_add(&modifiers_map, "Shift");
339 keysym_modifiers_add(&modifiers_map, "Control");
340 keysym_modifiers_add(&modifiers_map, "Mod1");
342 wl_input_method_context_modifiers_map(im_ctx, &modifiers_map);
344 wkb->keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift");
346 wl_array_release(&modifiers_map);
350 wl_input_method_context_language(im_ctx, wkb_ibus_input_context_serial(), "en");//wkb->language);
351 wl_input_method_context_text_direction(im_ctx, wkb_ibus_input_context_serial(), WL_TEXT_INPUT_TEXT_DIRECTION_LTR);//wkb->text_direction);
353 wkb->context_changed = EINA_TRUE;
354 evas_object_show(wkb->edje_obj);
358 _wkb_im_deactivate(void *data, struct wl_input_method *input_method, struct wl_input_method_context *im_ctx)
360 struct weekeyboard *wkb = data;
364 wkb_ibus_input_context_destroy();
368 wl_input_method_context_destroy(wkb->im_ctx);
372 evas_object_hide(wkb->edje_obj);
375 static const struct wl_input_method_listener wkb_im_listener = {
382 _wkb_ui_setup(struct weekeyboard *wkb)
389 ecore_evas_alpha_set(wkb->ee, EINA_TRUE);
390 ecore_evas_title_set(wkb->ee, "Weekeyboard");
392 evas = ecore_evas_get(wkb->ee);
393 wkb->edje_obj = edje_object_add(evas);
394 /*ecore_evas_object_associate(wkb->ee, edje_obj, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);*/
396 /* Check which theme we should use according to the screen width */
397 ecore_wl_screen_size_get(&w, &h);
403 sprintf(path, PKGDATADIR"/default_%d.edj", w);
404 DBG("Loading edje file: '%s'", path);
406 if (!edje_object_file_set(wkb->edje_obj, path, "main"))
408 int err = edje_object_load_error_get(wkb->edje_obj);
409 ERR("Unable to load the edje file: '%s'", edje_load_error_str(err));
413 edje_object_size_min_get(wkb->edje_obj, &w, &h);
414 if (w == 0 || h == 0)
416 edje_object_size_min_restricted_calc(wkb->edje_obj, &w, &h, w, h);
417 if (w == 0 || h == 0)
418 edje_object_parts_extends_calc(wkb->edje_obj, NULL, NULL, &w, &h);
421 ecore_evas_move_resize(wkb->ee, 0, 0, w, h);
422 evas_object_move(wkb->edje_obj, 0, 0);
423 evas_object_resize(wkb->edje_obj, w, h);
424 evas_object_size_hint_min_set(wkb->edje_obj, w, h);
425 evas_object_size_hint_max_set(wkb->edje_obj, w, h);
427 edje_object_signal_callback_add(wkb->edje_obj, "key_down", "*", _cb_wkb_on_key_down, wkb);
428 ecore_evas_callback_delete_request_set(wkb->ee, _cb_wkb_delete_request);
431 * The keyboard surface is bigger than it appears so that we can show the
432 * key pressed animation without requiring the use of subsurfaces. Here we
433 * resize the input region of the surface to match the keyboard background
434 * image, so that we can pass mouse events to the surfaces that may be
435 * located below the keyboard.
440 struct wl_region *input = wl_compositor_create_region(wkb->win->display->wl.compositor);
442 edje_object_part_geometry_get(wkb->edje_obj, "background", &x, &y, &w, &h);
443 wl_region_add(input, x, y, w, h);
444 wl_surface_set_input_region(wkb->surface, input);
445 wl_region_destroy(input);
449 ignore_keys = edje_file_data_get(path, "ignore-keys");
452 ERR("Special keys file not found in: '%s'", path);
456 DBG("Got ignore keys: '%s'", ignore_keys);
457 wkb->ignore_keys = eina_str_split(ignore_keys, "\n", 0);
461 ecore_evas_show(wkb->ee);
466 _wkb_setup(struct weekeyboard *wkb)
468 struct wl_list *globals;
469 struct wl_registry *registry;
470 Ecore_Wl_Global *global;
472 struct wl_input_panel_surface *ips;
474 globals = ecore_wl_globals_get();
475 registry = ecore_wl_registry_get();
476 wl_list_for_each(global, globals, link)
478 if (strcmp(global->interface, "wl_input_panel") == 0)
479 wkb->ip = wl_registry_bind(registry, global->id, &wl_input_panel_interface, 1);
480 else if (strcmp(global->interface, "wl_input_method") == 0)
481 wkb->im = wl_registry_bind(registry, global->id, &wl_input_method_interface, 1);
482 else if (strcmp(global->interface, "wl_output") == 0)
483 wkb->output = wl_registry_bind(registry, global->id, &wl_output_interface, 1);
486 /* Set input panel surface */
487 DBG("Setting up input panel");
488 wkb->win = ecore_evas_wayland_window_get(wkb->ee);
489 ecore_wl_window_type_set(wkb->win, ECORE_WL_WINDOW_TYPE_NONE);
490 wkb->surface = ecore_wl_window_surface_create(wkb->win);
491 ips = wl_input_panel_get_input_panel_surface(wkb->ip, wkb->surface);
492 wl_input_panel_surface_set_toplevel(ips, wkb->output, WL_INPUT_PANEL_SURFACE_POSITION_CENTER_BOTTOM);
494 /* Input method listener */
495 DBG("Adding wl_input_method listener");
496 wl_input_method_add_listener(wkb->im, &wkb_im_listener, wkb);
500 _wkb_free(struct weekeyboard *wkb)
503 wl_input_method_context_destroy(wkb->im_ctx);
506 evas_object_del(wkb->edje_obj);
508 if (wkb->ignore_keys)
510 free(*wkb->ignore_keys);
511 free(wkb->ignore_keys);
514 free(wkb->preedit_str);
515 free(wkb->surrounding_text);
519 _wkb_check_evas_engine(struct weekeyboard *wkb)
521 Eina_Bool ret = EINA_FALSE;
522 char *env = getenv("ECORE_EVAS_ENGINE");
526 if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_SHM))
528 else if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_EGL))
532 ERR("ERROR: Ecore_Evas does must be compiled with support for Wayland engines");
536 else if (strcmp(env, "wayland_shm") != 0 && strcmp(env, "wayland_egl") != 0)
538 ERR("ERROR: ECORE_EVAS_ENGINE must be set to either 'wayland_shm' or 'wayland_egl'");
542 wkb->ee_engine = env;
550 _wkb_check_ibus_connection(void *data)
552 static int tries = 0;
556 CRITICAL("Unable to establish connection to IBus.");
557 return ECORE_CALLBACK_DONE;
560 return !wkb_ibus_is_connected();
564 main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
566 struct weekeyboard wkb = {0};
567 int ret = EXIT_FAILURE;
569 if (!wkb_log_init("weekeyboard"))
572 if (!ecore_evas_init())
578 if (!_wkb_check_evas_engine(&wkb))
581 DBG("Selected engine: '%s'", wkb.ee_engine);
582 wkb.ee = ecore_evas_new(wkb.ee_engine, 0, 0, 1, 1, "frame=0");
586 ERR("ERROR: Unable to create Ecore_Evas object");
594 if (!_wkb_ui_setup(&wkb))
598 ecore_timer_add(1, _wkb_check_ibus_connection, NULL);
599 ecore_main_loop_begin();
605 ecore_evas_free(wkb.ee);
611 ecore_evas_shutdown();