#include #include "e_kbd.h" #include "e_mod_layout.h" #include "e_cfg.h" // FIXME: need a way to turn off vkbd if a real kbd is plugged in... // FIXME: autodeteck real kbd needed too... how? static Evas_List *handlers = NULL; static Evas_List *kbds = NULL; static Ecore_X_Atom atom_mb_im_invoker_command = 0; static Ecore_X_Atom atom_mtp_im_invoker_command = 0; static Evas_List *border_hooks = NULL; static E_Border *focused_border = NULL; static Ecore_X_Atom focused_vkbd_state = 0; //#define ICONIFY_TO_HIDE static _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 _e_kbd_border_show(E_Border *bd) { if (!bd) return; e_border_uniconify(bd); e_border_layer_set(bd, 190); e_border_show(bd); e_border_raise(bd); } static int _e_kbd_cb_animate(void *data) { E_Kbd *kbd; double t, v; kbd = data; t = ecore_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->actually_visible = kbd->visible; } _e_mod_layout_apply_all(); return 0; } return 1; } static void _e_kbd_free(E_Kbd *kbd) { kbds = evas_list_remove(kbds, kbd); if (kbd->animator) ecore_animator_del(kbd->animator); if (kbd->delay_hide) ecore_timer_del(kbd->delay_hide); if (kbd->border) kbd->border->stolen = 0; while (kbd->waiting_borders) { E_Border *bd; bd = kbd->waiting_borders->data; bd->stolen = 0; kbd->waiting_borders = evas_list_remove_list(kbd->waiting_borders, kbd->waiting_borders); } free(kbd); } static void _e_kbd_slide(E_Kbd *kbd, int visible, double len) { _e_mod_layout_apply_all(); kbd->start = ecore_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_hide(E_Kbd *kbd) { if (kbd->visible) return; if (illume_cfg->sliding.kbd.duration <= 0) { _e_kbd_border_hide(kbd->border); kbd->actually_visible = kbd->visible; _e_mod_layout_apply_all(); } else _e_kbd_slide(kbd, 0, (double)illume_cfg->sliding.kbd.duration / 1000.0); } static int _e_kbd_border_is_keyboard(E_Border *bd) { 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_border_adopt(E_Kbd *kbd, E_Border *bd) { kbd->border = bd; e_border_layer_set(kbd->border, 190); if (!kbd->actually_visible) e_border_fx_offset(kbd->border, 0, kbd->border->h); kbd->h = kbd->border->h; } static E_Kbd * _e_kbd_by_border_get(E_Border *bd) { Evas_List *l, *l2; if (!bd->stolen) return NULL; for (l = kbds; l; l = l->next) { E_Kbd *kbd; kbd = l->data; if (kbd->border == bd) return kbd; for (l2 = kbd->waiting_borders; l2; l2 = l2->next) { if (l2->data == bd) return kbd; } } return NULL; } static int _e_kbd_cb_delayed_hide(void *data) { E_Kbd *kbd; kbd = data; _e_kbd_hide(kbd); kbd->delay_hide = NULL; return 0; } static void _e_kbd_all_show(void) { Evas_List *l; for (l = kbds; l; l = l->next) { E_Kbd *kbd; kbd = l->data; e_kbd_show(kbd); } } static void _e_kbd_all_layout_set(E_Kbd_Lauyout layout) { Evas_List *l; for (l = kbds; l; l = l->next) { E_Kbd *kbd; kbd = l->data; e_kbd_layout_set(kbd, layout); } } static void _e_kbd_all_hide(void) { Evas_List *l; for (l = kbds; l; l = l->next) { E_Kbd *kbd; kbd = l->data; e_kbd_hide(kbd); } } static void _e_kbd_all_toggle(void) { Evas_List *l; for (l = kbds; l; l = l->next) { E_Kbd *kbd; kbd = l->data; if (kbd->visible) e_kbd_hide(kbd); else e_kbd_show(kbd); } } 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()) && ((ev->message_type == atom_mb_im_invoker_command) || (ev->message_type == atom_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_add(void *data, int type, void *event) { E_Event_Border_Add *ev; ev = event; // nothing - border hooks do this return 1; } static int _e_kbd_cb_border_remove(void *data, int type, void *event) { E_Event_Border_Remove *ev; Evas_List *l; E_Kbd *kbd; ev = event; if (ev->border == focused_border) { focused_border = NULL; focused_vkbd_state = 0; return 1; } // if border is in a created kbd - unstore kbd = _e_kbd_by_border_get(ev->border); if (!kbd) return 1; if (kbd->border == ev->border) { kbd->border = NULL; if (kbd->waiting_borders) { E_Border *bd; bd = kbd->waiting_borders->data; kbd->waiting_borders = evas_list_remove_list(kbd->waiting_borders, kbd->waiting_borders); _e_kbd_border_adopt(kbd, bd); } if (kbd->visible) { kbd->visible = 0; _e_kbd_border_hide(kbd->border); kbd->actually_visible = kbd->visible; e_kbd_show(kbd); } _e_mod_layout_apply_all(); } else kbd->waiting_borders = evas_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; // FIXME: if ev->border->client.vkbd.state == 0 then this app doesnt know // how to request for a virtual keyboard and so we should have a manual // override focused_border = ev->border; focused_vkbd_state = ev->border->client.vkbd.state; if (ev->border->client.vkbd.state == 0) return 1; if (ev->border->client.vkbd.state == ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF) { _e_kbd_all_layout_set(E_KBD_LAYOUT_NONE); _e_kbd_all_hide(); return 1; } else if (ev->border->client.vkbd.state == ECORE_X_VIRTUAL_KEYBOARD_STATE_ALPHA) _e_kbd_all_layout_set(E_KBD_LAYOUT_ALPHA); else if (ev->border->client.vkbd.state == ECORE_X_VIRTUAL_KEYBOARD_STATE_NUMERIC) _e_kbd_all_layout_set(E_KBD_LAYOUT_NUMERIC); 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_layout_set(E_KBD_LAYOUT_NONE); _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 (_e_kbd_by_border_get(ev->border)) return 1; if (!ev->border->focused) return 1; /* nothing happened to vkbd prop - leave everything alone */ if ((ev->border == focused_border) && (ev->border->client.vkbd.state == focused_vkbd_state)) return 1; focused_vkbd_state = ev->border->client.vkbd.state; /* app doesn't know what to do - just leave everything as-is */ if (ev->border->client.vkbd.state == 0) return 1; /* app wats kbd off - then kbd off it is */ else if (ev->border->client.vkbd.state == ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF) _e_kbd_all_hide(); /* app wants something else than off... */ else { if (ev->border->client.vkbd.state == ECORE_X_VIRTUAL_KEYBOARD_STATE_ALPHA) _e_kbd_all_layout_set(E_KBD_LAYOUT_ALPHA); else if (ev->border->client.vkbd.state == ECORE_X_VIRTUAL_KEYBOARD_STATE_NUMERIC) _e_kbd_all_layout_set(E_KBD_LAYOUT_NUMERIC); else _e_kbd_all_layout_set(E_KBD_LAYOUT_DEFAULT); _e_kbd_all_show(); } return 1; } static void _e_kbd_cb_border_hook_pre_post_fetch(void *data, E_Border *bd) { // check if bd has special kbd properites - if so, store in created kbd if (!bd->new_client) return; if (_e_kbd_by_border_get(bd)) return; if (_e_kbd_border_is_keyboard(bd)) { Evas_List *l; for (l = kbds; l; l = l->next) { E_Kbd *kbd; kbd = l->data; if (!kbd->border) { _e_kbd_border_adopt(kbd, bd); bd->stolen = 1; if (bd->remember) { if (bd->bordername) { evas_stringshare_del(bd->bordername); bd->bordername = NULL; bd->client.border.changed = 1; } e_remember_unuse(bd->remember); bd->remember = NULL; } if (bd->bordername) evas_stringshare_del(bd->bordername); bd->bordername = evas_stringshare_add("borderless"); bd->client.border.changed = 1; return; } else { kbd->waiting_borders = evas_list_append(kbd->waiting_borders, bd); bd->stolen = 1; if (bd->remember) { if (bd->bordername) { evas_stringshare_del(bd->bordername); bd->bordername = NULL; bd->client.border.changed = 1; } e_remember_unuse(bd->remember); bd->remember = NULL; } if (bd->bordername) evas_stringshare_del(bd->bordername); bd->bordername = evas_stringshare_add("borderless"); bd->client.border.changed = 1; return; } } } } static void _e_kbd_cb_border_hook_post_fetch(void *data, E_Border *bd) { // nothing - all done in _e_kbd_cb_border_hook_pre_post_fetch() if (!_e_kbd_by_border_get(bd)) return; } static void _e_kbd_cb_border_hook_post_border_assign(void *data, E_Border *bd) { E_Kbd *kbd; int pbx, pby, pbw, pbh; kbd = _e_kbd_by_border_get(bd); if (!kbd) return; pbx = bd->x; pby = bd->y; pbw = bd->w; pbh = bd->h; bd->lock_border = 1; bd->lock_client_location = 1; bd->lock_client_size = 1; bd->lock_client_desk = 1; bd->lock_client_sticky = 1; bd->lock_client_shade = 1; bd->lock_client_maximize = 1; bd->lock_user_location = 1; bd->lock_user_size = 1; bd->lock_user_desk = 1; bd->lock_user_sticky = 1; bd->lock_user_shade = 1; bd->lock_user_maximize = 1; bd->client.icccm.accepts_focus = 0; bd->client.icccm.take_focus = 0; bd->w = bd->zone->w; bd->h = bd->h; bd->x = bd->zone->x; bd->y = bd->zone->y + bd->zone->h - bd->h; bd->client.w = bd->w - bd->client_inset.l - bd->client_inset.r; bd->client.h = bd->h - bd->client_inset.t - bd->client_inset.b; bd->changes.size = 1; bd->placed = 1; if ((pbx != bd->x) || (pby != bd->y) || (pbw != bd->w) || (pbh != bd->h)) { if (bd->internal_ecore_evas) { ecore_evas_managed_move(bd->internal_ecore_evas, bd->x + bd->fx.x + bd->client_inset.l, bd->y + bd->fx.y + bd->client_inset.t); } ecore_x_icccm_move_resize_send(bd->client.win, bd->x + bd->fx.x + bd->client_inset.l, bd->y + bd->fx.y + bd->client_inset.t, bd->client.w, bd->client.h); bd->changed = 1; bd->changes.pos = 1; bd->changes.size = 1; } if (bd == kbd->border) { if (kbd->h != bd->h) { if (kbd->animator) { if (kbd->adjust_end > kbd->adjust_start) { kbd->adjust_start -= (bd->h - kbd->h); kbd->adjust_end -= (bd->h - kbd->h); } } else if (!kbd->actually_visible) e_border_fx_offset(kbd->border, 0, kbd->border->h); kbd->h = bd->h; } } } static void _e_kbd_cb_border_hook_end(void *data, E_Border *bd) { E_Kbd *kbd; kbd = _e_kbd_by_border_get(bd); if (!kbd) return; if (kbd->border == bd) { if (!kbd->actually_visible) _e_kbd_border_hide(kbd->border); } else _e_kbd_border_hide(bd); } EAPI int e_kbd_init(void) { focused_border = NULL; focused_vkbd_state = 0; atom_mb_im_invoker_command = ecore_x_atom_get("_MB_IM_INVOKER_COMMAND"); atom_mtp_im_invoker_command = ecore_x_atom_get("_MTP_IM_INVOKER_COMMAND"); handlers = evas_list_append(handlers, ecore_event_handler_add (ECORE_X_EVENT_CLIENT_MESSAGE, _e_kbd_cb_client_message, NULL)); handlers = evas_list_append(handlers, ecore_event_handler_add (E_EVENT_BORDER_ADD, _e_kbd_cb_border_add, NULL)); handlers = evas_list_append(handlers, ecore_event_handler_add (E_EVENT_BORDER_REMOVE, _e_kbd_cb_border_remove, NULL)); handlers = evas_list_append(handlers, ecore_event_handler_add (E_EVENT_BORDER_FOCUS_IN, _e_kbd_cb_border_focus_in, NULL)); handlers = evas_list_append(handlers, ecore_event_handler_add (E_EVENT_BORDER_FOCUS_OUT, _e_kbd_cb_border_focus_out, NULL)); handlers = evas_list_append(handlers, ecore_event_handler_add (E_EVENT_BORDER_PROPERTY, _e_kbd_cb_border_property, NULL)); border_hooks = evas_list_append(border_hooks, e_border_hook_add (E_BORDER_HOOK_EVAL_PRE_POST_FETCH, _e_kbd_cb_border_hook_pre_post_fetch, NULL)); border_hooks = evas_list_append(border_hooks, e_border_hook_add (E_BORDER_HOOK_EVAL_POST_FETCH, _e_kbd_cb_border_hook_post_fetch, NULL)); border_hooks = evas_list_append(border_hooks, e_border_hook_add (E_BORDER_HOOK_EVAL_POST_BORDER_ASSIGN, _e_kbd_cb_border_hook_post_border_assign, NULL)); border_hooks = evas_list_append(border_hooks, e_border_hook_add (E_BORDER_HOOK_EVAL_END, _e_kbd_cb_border_hook_end, NULL)); return 1; } EAPI int e_kbd_shutdown(void) { while (border_hooks) { e_border_hook_del(border_hooks->data); border_hooks = evas_list_remove_list(border_hooks, border_hooks); } while (handlers) { ecore_event_handler_del(handlers->data); handlers = evas_list_remove_list(handlers, handlers); } focused_border = NULL; focused_vkbd_state = 0; return 1; } EAPI E_Kbd * e_kbd_new(E_Zone *zone, const char *themedir, const char *syskbds, const char *sysdicts) { E_Kbd *kbd; kbd = E_OBJECT_ALLOC(E_Kbd, E_KBD_TYPE, _e_kbd_free); if (!kbd) return NULL; kbds = evas_list_append(kbds, kbd); return kbd; } EAPI void e_kbd_show(E_Kbd *kbd) { if (kbd->delay_hide) { ecore_timer_del(kbd->delay_hide); kbd->delay_hide = NULL; } if (kbd->visible) return; kbd->visible = 1; kbd->actually_visible = kbd->visible; if (illume_cfg->sliding.kbd.duration <= 0) { if (kbd->border) { e_border_fx_offset(kbd->border, 0, 0); _e_kbd_border_show(kbd->border); } kbd->actually_visible = kbd->visible; _e_mod_layout_apply_all(); } else { if (kbd->border) { e_border_fx_offset(kbd->border, 0, kbd->border->h - kbd->adjust); _e_kbd_border_show(kbd->border); } _e_kbd_slide(kbd, 1, (double)illume_cfg->sliding.kbd.duration / 1000.0); } } EAPI void e_kbd_layout_set(E_Kbd *kbd, E_Kbd_Lauyout layout) { Ecore_X_Virtual_Keyboard_State type; type = ECORE_X_VIRTUAL_KEYBOARD_STATE_ON; if (layout == E_KBD_LAYOUT_DEFAULT) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_ON; else if (layout == E_KBD_LAYOUT_ALPHA) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_ALPHA; else if (layout == E_KBD_LAYOUT_NUMERIC) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_NUMERIC; else if (layout == E_KBD_LAYOUT_NONE) type = ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF; if (kbd->border) ecore_x_e_virtual_keyboard_state_send(kbd->border->client.win, type); } EAPI void e_kbd_hide(E_Kbd *kbd) { if (!kbd->visible) return; kbd->visible = 0; if (!kbd->delay_hide) kbd->delay_hide = ecore_timer_add(0.2, _e_kbd_cb_delayed_hide, kbd); } EAPI void e_kbd_safe_app_region_get(E_Zone *zone, int *x, int *y, int *w, int *h) { Evas_List *l; if (x) *x = zone->x; if (y) *y = zone->y; if (w) *w = zone->w; if (h) *h = zone->h; for (l = kbds; l; l = l->next) { E_Kbd *kbd; kbd = l->data; if ((kbd->border) && (kbd->border->zone == zone)) { if ((kbd->visible) && (!kbd->animator)) /* out finished */ { if (h) { *h -= kbd->border->h; if (*h < 0) *h = 0; } } return; } } }