#include "E_Illume.h" #include "e_kbd.h" #include "e_kbd_dbus.h" #define ICONIFY_TO_HIDE 0 /* local function prototypes */ static void _e_kbd_cb_free(E_Kbd *kbd); static int _e_kbd_cb_delayed_hide(void *data); static int _e_kbd_cb_animate(void *data); static E_Kbd *_e_kbd_by_border_get(E_Border *bd); static void _e_kbd_layout_send(E_Kbd *kbd); static void _e_kbd_border_show(E_Border *bd); static void _e_kbd_border_hide(E_Border *bd); static void _e_kbd_border_adopt(E_Kbd *kbd, E_Border *bd); static int _e_kbd_border_is_keyboard(E_Border *bd); static void _e_kbd_hide(E_Kbd *kbd); static void _e_kbd_slide(E_Kbd *kbd, int visible, double len); static void _e_kbd_all_show(void); static void _e_kbd_all_hide(void); static void _e_kbd_all_toggle(void); static void _e_kbd_all_layout_set(E_Kbd_Layout layout); /* handlers */ static int _e_kbd_cb_client_message(void *data, int type, void *event); static int _e_kbd_cb_border_remove(void *data, int type, void *event); static int _e_kbd_cb_border_focus_in(void *data, int type, void *event); static int _e_kbd_cb_border_focus_out(void *data, int type, void *event); static int _e_kbd_cb_border_property(void *data, int type, void *event); /* hooks */ static void _e_kbd_cb_border_pre_post_fetch(void *data, void *data2); /* local variables */ static Eina_List *kbds = NULL, *handlers = NULL, *hooks = NULL; static E_Border *focused_border = NULL; static Ecore_X_Atom focused_vkbd_state = 0; /* public functions */ int e_kbd_init(void) { handlers = eina_list_append(handlers, ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE, _e_kbd_cb_client_message, NULL)); handlers = eina_list_append(handlers, ecore_event_handler_add(E_EVENT_BORDER_REMOVE, _e_kbd_cb_border_remove, NULL)); handlers = eina_list_append(handlers, ecore_event_handler_add(E_EVENT_BORDER_FOCUS_IN, _e_kbd_cb_border_focus_in, NULL)); handlers = eina_list_append(handlers, ecore_event_handler_add(E_EVENT_BORDER_FOCUS_OUT, _e_kbd_cb_border_focus_out, NULL)); handlers = eina_list_append(handlers, ecore_event_handler_add(E_EVENT_BORDER_PROPERTY, _e_kbd_cb_border_property, NULL)); hooks = eina_list_append(hooks, e_border_hook_add(E_BORDER_HOOK_EVAL_PRE_POST_FETCH, _e_kbd_cb_border_pre_post_fetch, NULL)); e_kbd_dbus_init(); return 1; } int e_kbd_shutdown(void) { E_Kbd *kbd; Ecore_Event_Handler *handler; E_Border_Hook *hook; EINA_LIST_FREE(handlers, handler) ecore_event_handler_del(handler); EINA_LIST_FREE(hooks, hook) e_border_hook_del(hook); EINA_LIST_FREE(kbds, kbd) e_object_del(E_OBJECT(kbd)); e_kbd_dbus_shutdown(); return 1; } E_Kbd * e_kbd_new(void) { E_Kbd *kbd; kbd = E_OBJECT_ALLOC(E_Kbd, E_KBD_TYPE, _e_kbd_cb_free); if (!kbd) return NULL; kbd->layout = E_KBD_LAYOUT_ALPHA; kbds = eina_list_append(kbds, kbd); return kbd; } void e_kbd_all_enable(void) { Eina_List *l; E_Kbd *kbd; EINA_LIST_FOREACH(kbds, l, kbd) e_kbd_enable(kbd); } void e_kbd_all_disable(void) { Eina_List *l; E_Kbd *kbd; EINA_LIST_FOREACH(kbds, l, kbd) e_kbd_disable(kbd); } void e_kbd_show(E_Kbd *kbd) { if (kbd->timer) ecore_timer_del(kbd->timer); kbd->timer = NULL; if (kbd->disabled) return; if (focused_border) { if (kbd->border->zone != focused_border->zone) e_border_zone_set(kbd->border, focused_border->zone); e_border_move(kbd->border, focused_border->zone->x, kbd->border->y); } if ((kbd->visible) || (kbd->disabled)) return; _e_kbd_layout_send(kbd); if (il_cfg->sliding.kbd.duration <= 0) { if (kbd->border) { e_border_fx_offset(kbd->border, 0, 0); _e_kbd_border_show(kbd->border); } kbd->visible = 1; } else { if (kbd->border) _e_kbd_border_show(kbd->border); _e_kbd_slide(kbd, 1, (double)il_cfg->sliding.kbd.duration / 1000.0); } } void e_kbd_hide(E_Kbd *kbd) { if (!kbd->visible) return; if (!kbd->timer) kbd->timer = ecore_timer_add(0.2, _e_kbd_cb_delayed_hide, kbd); } void e_kbd_enable(E_Kbd *kbd) { if (!kbd->disabled) return; kbd->disabled = 0; if (!kbd->visible) e_kbd_show(kbd); } void e_kbd_disable(E_Kbd *kbd) { if (kbd->disabled) return; if (kbd->visible) e_kbd_hide(kbd); kbd->disabled = 1; } void e_kbd_layout_set(E_Kbd *kbd, E_Kbd_Layout layout) { kbd->layout = layout; _e_kbd_layout_send(kbd); } void e_kbd_fullscreen_set(E_Zone *zone, int fullscreen) { Eina_List *l; E_Kbd *kbd; EINA_LIST_FOREACH(kbds, l, kbd) { if (!kbd->border) continue; if (kbd->border->zone != zone) continue; if ((!!fullscreen) != kbd->fullscreen) { kbd->fullscreen = fullscreen; if (kbd->fullscreen) e_border_layer_set(kbd->border, 250); else e_border_layer_set(kbd->border, 100); } } } EAPI void e_illume_kbd_safe_app_region_get(E_Zone *zone, int *x, int *y, int *w, int *h) { Eina_List *l; E_Kbd *kbd; if (x) *x = zone->x; if (y) *y = zone->y; if (w) *w = zone->w; if (h) *h = zone->h; EINA_LIST_FOREACH(kbds, l, kbd) { if (!kbd->border) continue; if (kbd->border->zone != zone) continue; if ((kbd->visible) && (!kbd->animator) && (!kbd->disabled)) { if (h) { *h -= kbd->border->h; if (*h < 0) *h = 0; } } return; } } /* local functions */ static void _e_kbd_cb_free(E_Kbd *kbd) { E_Border *bd; if (kbd->animator) ecore_animator_del(kbd->animator); kbd->animator = NULL; if (kbd->timer) ecore_timer_del(kbd->timer); kbd->timer = NULL; EINA_LIST_FREE(kbd->waiting_borders, bd) bd->stolen = 0; E_FREE(kbd); } static int _e_kbd_cb_delayed_hide(void *data) { E_Kbd *kbd; if (!(kbd = data)) return 0; _e_kbd_hide(kbd); kbd->timer = NULL; return 0; } static int _e_kbd_cb_animate(void *data) { E_Kbd *kbd; double t, v; if (!(kbd = data)) return 0; t = ecore_loop_time_get() - kbd->start; if (t > kbd->len) t = kbd->len; if (kbd->len > 0.0) { v = t / kbd->len; v = 1.0 - v; v = v * v * v * v; v = 1.0 - v; } else { t = kbd->len; v = 1.0; } kbd->adjust = (kbd->adjust_end * v) + (kbd->adjust_start * (1.0 - v)); if (kbd->border) e_border_fx_offset(kbd->border, 0, kbd->border->h - kbd->adjust); if (t == kbd->len) { kbd->animator = NULL; if (kbd->visible) { _e_kbd_border_hide(kbd->border); kbd->visible = 0; } else kbd->visible = 1; _e_kbd_layout_send(kbd); return 0; } return 1; } static E_Kbd * _e_kbd_by_border_get(E_Border *bd) { Eina_List *l, *ll; E_Border *over; E_Kbd *kbd; if ((!bd) || (!bd->stolen)) return NULL; EINA_LIST_FOREACH(kbds, l, kbd) { if (kbd->border == bd) return kbd; EINA_LIST_FOREACH(kbd->waiting_borders, ll, over) if (over == bd) return kbd; } return NULL; } static void _e_kbd_layout_send(E_Kbd *kbd) { Ecore_X_Virtual_Keyboard_State type; if (!kbd) return; if ((kbd->visible) && (!kbd->disabled)) { type = ECORE_X_VIRTUAL_KEYBOARD_STATE_ON; if (kbd->layout == E_KBD_LAYOUT_DEFAULT) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_ON; else if (kbd->layout == E_KBD_LAYOUT_ALPHA) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_ALPHA; else if (kbd->layout == E_KBD_LAYOUT_NUMERIC) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_NUMERIC; else if (kbd->layout == E_KBD_LAYOUT_PIN) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_PIN; else if (kbd->layout == E_KBD_LAYOUT_PHONE_NUMBER) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_PHONE_NUMBER; else if (kbd->layout == E_KBD_LAYOUT_HEX) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_HEX; else if (kbd->layout == E_KBD_LAYOUT_TERMINAL) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_TERMINAL; else if (kbd->layout == E_KBD_LAYOUT_PASSWORD) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_PASSWORD; else if (kbd->layout == E_KBD_LAYOUT_NONE) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF; } else type = ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF; if (kbd->border) ecore_x_e_virtual_keyboard_state_send(kbd->border->client.win, type); } static void _e_kbd_border_show(E_Border *bd) { if (!bd) return; #ifdef ICONIFY_TO_HIDE e_border_uniconify(bd); #endif e_border_show(bd); e_border_raise(bd); } static void _e_kbd_border_hide(E_Border *bd) { if (!bd) return; #ifdef ICONIFY_TO_HIDE e_border_iconify(bd); #else e_border_hide(bd, 2); #endif } static void _e_kbd_border_adopt(E_Kbd *kbd, E_Border *bd) { if ((!kbd) || (!bd)) return; kbd->border = bd; bd->sticky = 1; if (kbd->fullscreen) e_border_layer_set(kbd->border, 250); else e_border_layer_set(kbd->border, 100); if (!kbd->visible) { e_border_fx_offset(kbd->border, 0, kbd->border->h); _e_kbd_layout_send(kbd); } kbd->h = kbd->border->h; } static int _e_kbd_border_is_keyboard(E_Border *bd) { if (!bd) return 0; if ((bd->client.vkbd.vkbd) || /* explicit hint that its a virtual keyboard */ /* legacy */ ( /* trap the matchbox qwerty and multitap kbd's */ (((bd->client.icccm.title) && (!strcmp(bd->client.icccm.title, "Keyboard"))) || ((bd->client.icccm.name) && ((!strcmp(bd->client.icccm.name, "multitap-pad"))))) && (bd->client.netwm.state.skip_taskbar) && (bd->client.netwm.state.skip_pager))) return 1; return 0; } static void _e_kbd_hide(E_Kbd *kbd) { if (!kbd) return; if (kbd->timer) ecore_timer_del(kbd->timer); kbd->timer = NULL; if ((!kbd->visible) || (kbd->disabled)) return; _e_kbd_layout_send(kbd); if (il_cfg->sliding.kbd.duration <= 0) { if (kbd->border) { e_border_fx_offset(kbd->border, 0, kbd->border->h); _e_kbd_border_hide(kbd->border); } kbd->visible = 0; } else _e_kbd_slide(kbd, 0, (double)il_cfg->sliding.kbd.duration / 1000.0); } static void _e_kbd_slide(E_Kbd *kbd, int visible, double len) { if (!kbd) return; kbd->start = ecore_loop_time_get(); kbd->len = len; kbd->adjust_start = kbd->adjust; kbd->adjust_end = 0; if ((visible) && (kbd->border)) kbd->adjust_end = kbd->border->h; if (!kbd->animator) kbd->animator = ecore_animator_add(_e_kbd_cb_animate, kbd); } static void _e_kbd_all_show(void) { Eina_List *l; E_Kbd *kbd; EINA_LIST_FOREACH(kbds, l, kbd) e_kbd_show(kbd); } static void _e_kbd_all_hide(void) { Eina_List *l; E_Kbd *kbd; EINA_LIST_FOREACH(kbds, l, kbd) e_kbd_hide(kbd); } static void _e_kbd_all_toggle(void) { Eina_List *l; E_Kbd *kbd; EINA_LIST_FOREACH(kbds, l, kbd) { if (kbd->visible) e_kbd_hide(kbd); else e_kbd_show(kbd); } } static void _e_kbd_all_layout_set(E_Kbd_Layout layout) { Eina_List *l; E_Kbd *kbd; EINA_LIST_FOREACH(kbds, l, kbd) e_kbd_layout_set(kbd, layout); } /* handlers */ static int _e_kbd_cb_client_message(void *data, int type, void *event) { Ecore_X_Event_Client_Message *ev; ev = event; if (ev->win != ecore_x_window_root_first_get()) return 1; if ((ev->message_type == ecore_x_atom_get("_MB_IM_INVOKER_COMMAND")) || (ev->message_type == ecore_x_atom_get("_MTP_IM_INVOKER_COMMAND"))) { if (ev->data.l[0] == 1) _e_kbd_all_show(); else if (ev->data.l[0] == 2) _e_kbd_all_hide(); else if (ev->data.l[0] == 3) _e_kbd_all_toggle(); } return 1; } static int _e_kbd_cb_border_remove(void *data, int type, void *event) { E_Event_Border_Remove *ev; E_Kbd *kbd; ev = event; if (focused_border) { if (ev->border == focused_border) { focused_border = NULL; focused_vkbd_state = 0; return 1; } } if (!(kbd = _e_kbd_by_border_get(ev->border))) return 1; if (kbd->border) { if (kbd->border == ev->border) { kbd->border = NULL; if (kbd->waiting_borders) { E_Border *bd; bd = kbd->waiting_borders->data; kbd->waiting_borders = eina_list_remove_list(kbd->waiting_borders, kbd->waiting_borders); _e_kbd_border_adopt(kbd, bd); } if (kbd->visible) { _e_kbd_border_hide(ev->border); e_kbd_hide(kbd); } } } else kbd->waiting_borders = eina_list_remove(kbd->waiting_borders, ev->border); return 1; } static int _e_kbd_cb_border_focus_in(void *data, int type, void *event) { E_Event_Border_Focus_In *ev; ev = event; if (_e_kbd_by_border_get(ev->border)) return 1; focused_border = ev->border; focused_vkbd_state = ev->border->client.vkbd.state; if (focused_vkbd_state == 0) return 1; if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF) _e_kbd_all_hide(); else { if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_ALPHA) _e_kbd_all_layout_set(E_KBD_LAYOUT_ALPHA); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_NUMERIC) _e_kbd_all_layout_set(E_KBD_LAYOUT_NUMERIC); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_PIN) _e_kbd_all_layout_set(E_KBD_LAYOUT_PIN); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_PHONE_NUMBER) _e_kbd_all_layout_set(E_KBD_LAYOUT_PHONE_NUMBER); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_HEX) _e_kbd_all_layout_set(E_KBD_LAYOUT_HEX); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_TERMINAL) _e_kbd_all_layout_set(E_KBD_LAYOUT_TERMINAL); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_PASSWORD) _e_kbd_all_layout_set(E_KBD_LAYOUT_PASSWORD); else _e_kbd_all_layout_set(E_KBD_LAYOUT_DEFAULT); _e_kbd_all_show(); } return 1; } static int _e_kbd_cb_border_focus_out(void *data, int type, void *event) { E_Event_Border_Focus_Out *ev; ev = event; if (_e_kbd_by_border_get(ev->border)) return 1; _e_kbd_all_hide(); focused_border = NULL; focused_vkbd_state = 0; return 1; } static int _e_kbd_cb_border_property(void *data, int type, void *event) { E_Event_Border_Property *ev; ev = event; if (!ev->border->focused) return 1; if (_e_kbd_by_border_get(ev->border)) return 1; if (focused_border) { if ((ev->border == focused_border) && (ev->border->client.vkbd.state == focused_vkbd_state)) return 1; } focused_vkbd_state = ev->border->client.vkbd.state; if (focused_vkbd_state == 0) return 1; if ((ev->border->need_fullscreen) || (ev->border->fullscreen)) e_kbd_fullscreen_set(ev->border->zone, 1); else e_kbd_fullscreen_set(ev->border->zone, 0); /* app wants kbd off - then kbd off it is */ if (focused_vkbd_state <= ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF) _e_kbd_all_hide(); /* app wants something other than off... */ else { if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_ALPHA) _e_kbd_all_layout_set(E_KBD_LAYOUT_ALPHA); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_NUMERIC) _e_kbd_all_layout_set(E_KBD_LAYOUT_NUMERIC); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_PIN) _e_kbd_all_layout_set(E_KBD_LAYOUT_PIN); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_PHONE_NUMBER) _e_kbd_all_layout_set(E_KBD_LAYOUT_PHONE_NUMBER); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_HEX) _e_kbd_all_layout_set(E_KBD_LAYOUT_HEX); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_TERMINAL) _e_kbd_all_layout_set(E_KBD_LAYOUT_TERMINAL); else if (focused_vkbd_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_PASSWORD) _e_kbd_all_layout_set(E_KBD_LAYOUT_PASSWORD); else _e_kbd_all_layout_set(E_KBD_LAYOUT_DEFAULT); _e_kbd_all_show(); } return 1; } /* hooks */ static void _e_kbd_cb_border_pre_post_fetch(void *data, void *data2) { E_Border *bd; if (!(bd = data2)) return; if (!bd->new_client) return; if (_e_kbd_by_border_get(bd)) return; if (_e_kbd_border_is_keyboard(bd)) { Eina_List *l; E_Kbd *kbd; EINA_LIST_FOREACH(kbds, l, kbd) { if (!kbd->border) _e_kbd_border_adopt(kbd, bd); else kbd->waiting_borders = eina_list_append(kbd->waiting_borders, bd); bd->stolen = 1; if (bd->remember) { if (bd->bordername) { eina_stringshare_del(bd->bordername); bd->bordername = NULL; } e_remember_unuse(bd->remember); bd->remember = NULL; } eina_stringshare_replace(&bd->bordername, "borderless"); bd->client.border.changed = 1; return; } } }