enlightenment/src/modules/illume2/e_mod_kbd.c

625 lines
18 KiB
C

#include "e_illume_private.h"
#include "e_mod_kbd.h"
#include "e_mod_kbd_dbus.h"
/* local function prototypes */
static int _e_mod_kbd_cb_client_message(void *data __UNUSED__, int type __UNUSED__, void *event);
static int _e_mod_kbd_cb_border_remove(void *data __UNUSED__, int type __UNUSED__, void *event);
static int _e_mod_kbd_cb_border_focus_in(void *data __UNUSED__, int type __UNUSED__, void *event);
static int _e_mod_kbd_cb_border_focus_out(void *data __UNUSED__, int type __UNUSED__, void *event);
static int _e_mod_kbd_cb_border_property(void *data __UNUSED__, int type __UNUSED__, void *event);
static void _e_mod_kbd_cb_border_pre_post_fetch(void *data __UNUSED__, void *data2);
static void _e_mod_kbd_cb_free(E_Illume_Keyboard *kbd);
static int _e_mod_kbd_cb_delay_hide(void *data __UNUSED__);
static void _e_mod_kbd_hide(void);
static void _e_mod_kbd_slide(int visible, double len);
static int _e_mod_kbd_cb_animate(void *data __UNUSED__);
static E_Illume_Keyboard *_e_mod_kbd_by_border_get(E_Border *bd);
static void _e_mod_kbd_border_adopt(E_Border *bd);
static void _e_mod_kbd_layout_send(void);
static void _e_mod_kbd_geometry_send(void);
/* local variables */
static Eina_List *_kbd_hdls = NULL;
static E_Border_Hook *_kbd_hook = NULL;
static Ecore_X_Atom _focused_state = 0;
static E_Border *_focused_border = NULL;
int
e_mod_kbd_init(void)
{
/* add handlers for events we are interested in */
_kbd_hdls =
eina_list_append(_kbd_hdls,
ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE,
_e_mod_kbd_cb_client_message,
NULL));
_kbd_hdls =
eina_list_append(_kbd_hdls,
ecore_event_handler_add(E_EVENT_BORDER_REMOVE,
_e_mod_kbd_cb_border_remove,
NULL));
_kbd_hdls =
eina_list_append(_kbd_hdls,
ecore_event_handler_add(E_EVENT_BORDER_FOCUS_IN,
_e_mod_kbd_cb_border_focus_in,
NULL));
_kbd_hdls =
eina_list_append(_kbd_hdls,
ecore_event_handler_add(E_EVENT_BORDER_FOCUS_OUT,
_e_mod_kbd_cb_border_focus_out,
NULL));
_kbd_hdls =
eina_list_append(_kbd_hdls,
ecore_event_handler_add(ECORE_X_EVENT_WINDOW_PROPERTY,
_e_mod_kbd_cb_border_property,
NULL));
/* add hooks for events we are interested in */
_kbd_hook = e_border_hook_add(E_BORDER_HOOK_EVAL_PRE_POST_FETCH,
_e_mod_kbd_cb_border_pre_post_fetch, NULL);
/* initialize the dbus subsystem */
e_mod_kbd_dbus_init();
return 1;
}
int
e_mod_kbd_shutdown(void)
{
Ecore_Event_Handler *hdl;
/* shutdown the dbus subsystem */
e_mod_kbd_dbus_shutdown();
/* destroy the hook */
e_border_hook_del(_kbd_hook);
/* destroy the handlers and free the list */
EINA_LIST_FREE(_kbd_hdls, hdl)
ecore_event_handler_del(hdl);
return 1;
}
E_Illume_Keyboard *
e_mod_kbd_new(void)
{
E_Illume_Keyboard *kbd;
/* try to allocate our new keyboard object */
kbd = E_OBJECT_ALLOC(E_Illume_Keyboard, E_ILLUME_KBD_TYPE, _e_mod_kbd_cb_free);
if (!kbd) return NULL;
/* set default layout on new keyboard */
kbd->layout = E_ILLUME_KEYBOARD_LAYOUT_ALPHA;
kbd->visible = 1;
return kbd;
}
void
e_mod_kbd_enable(void)
{
/* don't try to enable a keyboard that is already enabled */
if (!_e_illume_kbd->disabled) return;
/* set keyboard to enabled */
_e_illume_kbd->disabled = 0;
/* show it if we need to */
if (!_e_illume_kbd->visible) e_mod_kbd_show();
}
void
e_mod_kbd_disable(void)
{
/* don't try to disable a keyboard that is already disabled */
if (_e_illume_kbd->disabled) return;
/* hide it if we need to */
if (_e_illume_kbd->visible) e_mod_kbd_hide();
/* set keyboard to disabled */
_e_illume_kbd->disabled = 1;
}
void
e_mod_kbd_show(void)
{
/* destroy existing timer */
if (_e_illume_kbd->timer) ecore_timer_del(_e_illume_kbd->timer);
_e_illume_kbd->timer = NULL;
/* destroy the animator if it exists */
if (_e_illume_kbd->animator) ecore_animator_del(_e_illume_kbd->animator);
_e_illume_kbd->animator = NULL;
if ((_focused_border) && (_e_illume_kbd->border))
{
if (_e_illume_kbd->border->zone != _focused_border->zone)
e_border_zone_set(_e_illume_kbd->border, _focused_border->zone);
}
/* if it's disabled, get out */
if (_e_illume_kbd->disabled) return;
_e_mod_kbd_layout_send();
/* if we are not animating, just show it */
if (_e_illume_cfg->animation.vkbd.duration <= 0)
{
/* show the border */
if (_e_illume_kbd->border)
{
e_border_fx_offset(_e_illume_kbd->border, 0, 0);
if (!_e_illume_kbd->border->visible)
e_border_show(_e_illume_kbd->border);
e_border_raise(_e_illume_kbd->border);
}
_e_illume_kbd->visible = 1;
_e_mod_kbd_geometry_send();
}
else
{
/* show the border */
if (_e_illume_kbd->border)
{
if (!_e_illume_kbd->border->visible)
e_border_show(_e_illume_kbd->border);
e_border_raise(_e_illume_kbd->border);
}
/* animate it */
_e_mod_kbd_slide(1, (double)_e_illume_cfg->animation.vkbd.duration / 1000.0);
}
}
void
e_mod_kbd_hide(void)
{
/* cannot hide keyboard that is not visible */
if (!_e_illume_kbd->visible) return;
/* create new hide timer if it doesn't exist */
if (!_e_illume_kbd->timer)
_e_illume_kbd->timer = ecore_timer_add(0.2, _e_mod_kbd_cb_delay_hide, NULL);
}
void
e_mod_kbd_toggle(void)
{
if (_e_illume_kbd->visible) e_mod_kbd_hide();
else e_mod_kbd_show();
}
void
e_mod_kbd_fullscreen_set(E_Zone *zone, int fullscreen)
{
if (!_e_illume_kbd->border) return;
if (_e_illume_kbd->border->zone != zone) return;
if ((!!fullscreen) != _e_illume_kbd->fullscreen)
_e_illume_kbd->fullscreen = fullscreen;
}
void
e_mod_kbd_layout_set(E_Illume_Keyboard_Layout layout)
{
if (!_e_illume_kbd->border) return;
_e_illume_kbd->layout = layout;
_e_mod_kbd_layout_send();
}
/* local functions */
static int
_e_mod_kbd_cb_client_message(void *data __UNUSED__, int type __UNUSED__, void *event)
{
Ecore_X_Event_Client_Message *ev;
ev = event;
if (ev->win != ecore_x_window_root_first_get()) return 1;
/* legacy illume 1 code */
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_mod_kbd_show();
else if (ev->data.l[0] == 2) e_mod_kbd_hide();
else if (ev->data.l[0] == 3) e_mod_kbd_toggle();
}
return 1;
}
static int
_e_mod_kbd_cb_border_remove(void *data __UNUSED__, int type __UNUSED__, void *event)
{
E_Event_Border_Remove *ev;
E_Illume_Keyboard *kbd;
ev = event;
/* if we removed the focused border, reset some variables */
if ((_focused_border) && (_focused_border == ev->border))
{
e_mod_kbd_hide();
_focused_border = NULL;
_focused_state = 0;
return 1;
}
/* try to find the keyboard for this border */
if (!(kbd = _e_mod_kbd_by_border_get(ev->border))) return 1;
if ((kbd->border) && (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_mod_kbd_border_adopt(bd);
}
if (kbd->visible)
{
e_border_hide(ev->border, 2);
e_mod_kbd_hide();
}
}
else if (!kbd->border)
kbd->waiting_borders = eina_list_remove(kbd->waiting_borders, ev->border);
return 1;
}
static int
_e_mod_kbd_cb_border_focus_in(void *data __UNUSED__, int type __UNUSED__, void *event)
{
E_Event_Border_Focus_In *ev;
ev = event;
if (_e_mod_kbd_by_border_get(ev->border)) return 1;
// printf("Kbd Focus in: %s\n", ev->border->client.icccm.name);
/* set focused border for kbd */
_focused_border = ev->border;
_focused_state = ev->border->client.vkbd.state;
if (_focused_state <= ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF)
e_mod_kbd_hide();
else
e_mod_kbd_show();
return 1;
}
static int
_e_mod_kbd_cb_border_focus_out(void *data __UNUSED__, int type __UNUSED__, void *event)
{
E_Event_Border_Focus_Out *ev;
ev = event;
if (_e_mod_kbd_by_border_get(ev->border)) return 1;
// printf("Kbd Focus Out: %s\n", ev->border->client.icccm.name);
/* tell this border it changed so layout gets updated for hidden
* keyboard */
if (!e_illume_border_is_conformant(ev->border))
{
ev->border->changes.size = 1;
ev->border->changed = 1;
}
/* hide the keyboard */
e_mod_kbd_hide();
/* reset some variables */
_focused_border = NULL;
_focused_state = 0;
return 1;
}
static int
_e_mod_kbd_cb_border_property(void *data __UNUSED__, int type __UNUSED__, void *event)
{
Ecore_X_Event_Window_Property *ev;
E_Border *bd;
int fullscreen = 0;
ev = event;
/* only interested in vkbd state changes here */
if (ev->atom != ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_STATE) return 1;
/* make sure we have a border */
if (!(bd = e_border_find_by_client_window(ev->win))) return 1;
// printf("Kbd Border Property Change: %s\n", bd->client.icccm.name);
/* if it's not focused, we don't care */
if ((!bd->focused) || (_e_mod_kbd_by_border_get(bd))) return 1;
/* NB: Not sure why, but we seem to need to fetch kbd state here. This could
* be a result of filtering the container_hook_layout. Not real happy
* with this because it is an X round-trip, but it is here because this
* needs more time to investigate. */
e_hints_window_virtual_keyboard_state_get(bd);
if ((_focused_border) && (_focused_border == bd))
{
/* if focused state is the same, get out */
if (_focused_state == bd->client.vkbd.state) return 1;
}
/* set our variables */
_focused_border = bd;
_focused_state = bd->client.vkbd.state;
/* handle a border needing fullscreen keyboard */
if ((bd->need_fullscreen) || (bd->fullscreen)) fullscreen = 1;
if (_e_illume_kbd->fullscreen != fullscreen)
e_mod_kbd_fullscreen_set(bd->zone, fullscreen);
if (_focused_state <= ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF)
e_mod_kbd_hide();
else
e_mod_kbd_show();
return 1;
}
static void
_e_mod_kbd_cb_border_pre_post_fetch(void *data __UNUSED__, void *data2)
{
E_Border *bd;
if (!(bd = data2)) return;
if (!bd->new_client) return;
if (_e_mod_kbd_by_border_get(bd)) return;
if (e_illume_border_is_keyboard(bd))
{
if (!_e_illume_kbd->border)
_e_mod_kbd_border_adopt(bd);
else
{
_e_illume_kbd->waiting_borders =
eina_list_append(_e_illume_kbd->waiting_borders, bd);
}
bd->stolen = 1;
}
}
static void
_e_mod_kbd_cb_free(E_Illume_Keyboard *kbd)
{
E_Border *bd;
/* destroy the animator if it exists */
if (kbd->animator) ecore_animator_del(kbd->animator);
kbd->animator = NULL;
/* destroy the timer if it exists */
if (kbd->timer) ecore_timer_del(kbd->timer);
kbd->timer = NULL;
/* free the list of waiting borders */
EINA_LIST_FREE(kbd->waiting_borders, bd)
bd->stolen = 0;
/* free the keyboard structure */
E_FREE(kbd);
}
static int
_e_mod_kbd_cb_delay_hide(void *data __UNUSED__)
{
_e_mod_kbd_hide();
_e_illume_kbd->timer = NULL;
return 0;
}
static void
_e_mod_kbd_hide(void)
{
/* destroy existing timer */
if (_e_illume_kbd->timer) ecore_timer_del(_e_illume_kbd->timer);
_e_illume_kbd->timer = NULL;
/* can't hide keyboard if it's not visible, or disabled */
if ((!_e_illume_kbd->visible) || (_e_illume_kbd->disabled)) return;
_e_mod_kbd_layout_send();
/* if we are not animating, just hide it */
if (_e_illume_cfg->animation.vkbd.duration <= 0)
{
if (_e_illume_kbd->border)
{
e_border_fx_offset(_e_illume_kbd->border, 0,
_e_illume_kbd->border->h);
e_border_hide(_e_illume_kbd->border, 2);
}
_e_illume_kbd->visible = 0;
_e_mod_kbd_geometry_send();
}
else
_e_mod_kbd_slide(0, (double)_e_illume_cfg->animation.vkbd.duration / 1000.0);
}
static void
_e_mod_kbd_slide(int visible, double len)
{
_e_illume_kbd->start = ecore_loop_time_get();
_e_illume_kbd->len = len;
_e_illume_kbd->adjust_start = _e_illume_kbd->adjust;
_e_illume_kbd->adjust_end = 0;
if ((visible) && (_e_illume_kbd->border))
_e_illume_kbd->adjust_end = _e_illume_kbd->border->h;
if (!_e_illume_kbd->animator)
_e_illume_kbd->animator = ecore_animator_add(_e_mod_kbd_cb_animate, NULL);
}
static int
_e_mod_kbd_cb_animate(void *data __UNUSED__)
{
double t, v;
t = (ecore_loop_time_get() - _e_illume_kbd->start);
if (t > _e_illume_kbd->len) t = _e_illume_kbd->len;
if (_e_illume_kbd->len > 0.0)
{
v = (t / _e_illume_kbd->len);
v = (1.0 - v);
v = (v * v * v * v);
v = (1.0 - v);
}
else
{
t = _e_illume_kbd->len;
v = 1.0;
}
_e_illume_kbd->adjust = ((_e_illume_kbd->adjust_end * v) +
(_e_illume_kbd->adjust_start * (1.0 - v)));
if (_e_illume_kbd->border)
{
e_border_fx_offset(_e_illume_kbd->border, 0,
(_e_illume_kbd->border->h - _e_illume_kbd->adjust));
}
if (t == _e_illume_kbd->len)
{
_e_illume_kbd->animator = NULL;
if ((_e_illume_kbd->visible) &&
(_focused_state <= ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF))
{
if (_e_illume_kbd->border)
e_border_hide(_e_illume_kbd->border, 2);
_e_illume_kbd->visible = 0;
}
else
_e_illume_kbd->visible = 1;
_e_mod_kbd_geometry_send();
/* tell the focused border it changed so layout gets udpated */
if (_focused_border)
{
if (!e_illume_border_is_conformant(_focused_border))
{
_focused_border->changes.size = 1;
_focused_border->changed = 1;
}
}
return 0;
}
return 1;
}
static E_Illume_Keyboard *
_e_mod_kbd_by_border_get(E_Border *bd)
{
Eina_List *l;
E_Border *over;
if ((!bd) || (!bd->stolen)) return NULL;
/* if this border is the one that vkbd is working with, return the kbd */
if (_e_illume_kbd->border == bd) return _e_illume_kbd;
/* loop the waiting borders */
EINA_LIST_FOREACH(_e_illume_kbd->waiting_borders, l, over)
if (over == bd) return _e_illume_kbd;
return NULL;
}
static void
_e_mod_kbd_border_adopt(E_Border *bd)
{
if ((!_e_illume_kbd) || (!bd)) return;
_e_illume_kbd->border = bd;
if (!_e_illume_kbd->visible)
{
e_border_fx_offset(bd, 0, bd->h);
_e_mod_kbd_layout_send();
}
}
static void
_e_mod_kbd_layout_send(void)
{
Ecore_X_Virtual_Keyboard_State type;
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF;
if ((!_e_illume_kbd->visible) && (!_e_illume_kbd->disabled))
{
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_ON;
if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_DEFAULT)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_ON;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_ALPHA)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_ALPHA;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_NUMERIC)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_NUMERIC;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_PIN)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_PIN;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_PHONE_NUMBER)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_PHONE_NUMBER;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_HEX)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_HEX;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_TERMINAL)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_TERMINAL;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_PASSWORD)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_PASSWORD;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_IP)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_IP;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_HOST)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_HOST;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_FILE)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_FILE;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_URL)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_URL;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_KEYPAD)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_KEYPAD;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_J2ME)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_J2ME;
else if (_e_illume_kbd->layout == E_ILLUME_KEYBOARD_LAYOUT_NONE)
type = ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF;
}
if (_e_illume_kbd->border)
ecore_x_e_virtual_keyboard_state_send(_e_illume_kbd->border->client.win, type);
}
static void
_e_mod_kbd_geometry_send(void)
{
E_Zone *zone;
int y = 0;
/* make sure we have a keyboard border */
if (!_e_illume_kbd->border) return;
/* adjust Y for keyboard visibility */
if (_e_illume_kbd->border->fx.y <= 0)
y = _e_illume_kbd->border->y;
if (_focused_border) zone = _focused_border->zone;
else zone = _e_illume_kbd->border->zone;
ecore_x_e_illume_keyboard_geometry_set(zone->black_win,
_e_illume_kbd->border->x, y,
_e_illume_kbd->border->w,
_e_illume_kbd->border->h);
}