ecore_x: Add key router feature related with client side.

Currently app only grab key using Xgrabkey.
Keyrouter will support several keygrab mode and apps can use it.
This commit is contained in:
Ji-Youn Park 2015-05-13 20:47:36 +09:00
parent 01b3803931
commit 5c3a08433a
6 changed files with 684 additions and 0 deletions

View File

@ -82,6 +82,7 @@ lib/ecore_x/xlib/ecore_x_composite.c \
lib/ecore_x/xlib/ecore_x_error.c \
lib/ecore_x/xlib/ecore_x_events.c \
lib/ecore_x/xlib/ecore_x_icccm.c \
lib/ecore_x/xlib/ecore_x_keygrab.c \
lib/ecore_x/xlib/ecore_x_netwm.c \
lib/ecore_x/xlib/ecore_x_mwm.c \
lib/ecore_x/xlib/ecore_x_e.c \

View File

@ -2708,6 +2708,26 @@ EAPI void ecore_x_e_window_rotation_change_prep
EAPI void ecore_x_e_window_rotation_change_request_send(Ecore_X_Window win, int rot); /**< @since 1.9 */
EAPI void ecore_x_e_window_rotation_change_done_send(Ecore_X_Window root, Ecore_X_Window win, int rot, int w, int h); /**< @since 1.9 */
//this enum and API for keyrouter and client window side
//keycode (8~255)
typedef enum
{
ECORE_X_WIN_KEYGRAB_UNKNOWN = 0, /**< Unknown keygrab mode */
ECORE_X_WIN_KEYGRAB_SHARED = (1 << 8), /**< Getting the grabbed-key together with the other client windows */
ECORE_X_WIN_KEYGRAB_TOPMOST = (1 << 9), /**< Getting the grabbed-key only when window is top of the stack */
ECORE_X_WIN_KEYGRAB_EXCLUSIVE = (1 << 10), /**< Getting the grabbed-key exclusively regardless of window's position */
ECORE_X_WIN_KEYGRAB_OVERRIDE_EXCLUSIVE = (1 << 11) /**< Getting the grabbed-key exclusively regardless of window's position. Being overrided the grab by the other client window */
} Ecore_X_Win_Keygrab_Mode;
//add mod, anymod, priority for the future.
//we will support modifier and priority feature later.
EAPI Eina_Bool ecore_x_window_keygrab_set(Ecore_X_Window win, const char *key, int mod, int any_mod, int priority, Ecore_X_Win_Keygrab_Mode grab_mode);
EAPI Eina_Bool ecore_x_window_keygrab_unset(Ecore_X_Window win, const char *key, int mod, int any_mod);
//this API for keyrouter protocol
EAPI void ecore_x_e_keyrouter_set(Ecore_X_Window root, Eina_Bool on); //Key router set keyrouter flag using this
EAPI Eina_Bool ecore_x_e_keyrouter_get(Ecore_X_Window root); //Client check the existance of keyrouter using this
#include <Ecore_X_Atoms.h>
#include <Ecore_X_Cursor.h>

View File

@ -333,4 +333,8 @@ EAPI extern Ecore_X_Atom ECORE_X_ATOM_E_WINDOW_AUX_HINT_SUPPORTED_LIST; /**< @si
EAPI extern Ecore_X_Atom ECORE_X_ATOM_E_WINDOW_AUX_HINT_SUPPORT; /**< @since 1.10 */
EAPI extern Ecore_X_Atom ECORE_X_ATOM_E_WINDOW_AUX_HINT; /**< @since 1.10 */
EAPI extern Ecore_X_Atom ECORE_X_ATOM_E_WINDOW_AUX_HINT_ALLOWED; /**< @since 1.10 */
/* E keyrouter protocol */
EAPI extern Ecore_X_Atom ECORE_X_ATOM_E_KEYROUTER_SUPPORTED; /**< @since 1.15 */
EAPI extern Ecore_X_Atom ECORE_X_ATOM_E_KEYROUTER_WINDOW_KEYTABLE; /**< @since 1.15 */
#endif /* _ECORE_X_ATOMS_H */

View File

@ -363,6 +363,10 @@ EAPI Ecore_X_Atom ECORE_X_ATOM_E_WINDOW_AUX_HINT_SUPPORT = 0;
EAPI Ecore_X_Atom ECORE_X_ATOM_E_WINDOW_AUX_HINT = 0;
EAPI Ecore_X_Atom ECORE_X_ATOM_E_WINDOW_AUX_HINT_ALLOWED = 0;
/* E keyrouter protocol */
EAPI Ecore_X_Atom ECORE_X_ATOM_E_KEYROUTER_SUPPORTED = 0;
EAPI Ecore_X_Atom ECORE_X_ATOM_E_KEYROUTER_WINDOW_KEYTABLE = 0;
typedef struct _Atom_Item Atom_Item;
struct _Atom_Item

View File

@ -2305,3 +2305,41 @@ ecore_x_e_window_rotation_change_done_send(Ecore_X_Window root,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
/*
* Does keyrouter exist?
*/
EAPI void
ecore_x_e_keyrouter_set(Ecore_X_Window win EINA_UNUSED,
Eina_Bool on)
{
//key router call this api when it start running
unsigned int val;
Ecore_X_Window root;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
root = DefaultRootWindow(_ecore_x_disp);
val = (on) ? 1 : 0;
ecore_x_window_prop_card32_set(root, ECORE_X_ATOM_E_KEYROUTER_SUPPORTED,
&val, 1);
}
EAPI Eina_Bool
ecore_x_e_keyrouter_get(Ecore_X_Window win EINA_UNUSED)
{
//check the existance of keyrouter
int ret;
unsigned int val;
Ecore_X_Window root;
LOGFN(__FILE__, __LINE__, __FUNCTION__);
root = DefaultRootWindow(_ecore_x_disp);
ret = ecore_x_window_prop_card32_get(root, ECORE_X_ATOM_E_KEYROUTER_SUPPORTED,
&val, 1);
if (ret != 1) return EINA_FALSE;
return val == 1 ? EINA_TRUE : EINA_FALSE;
}

View File

@ -0,0 +1,617 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* ifdef HAVE_CONFIG_H */
#include <stdlib.h>
#ifdef LOGRT
#include <dlfcn.h>
#endif /* ifdef LOGRT */
#include "ecore_x_private.h"
#include "Ecore_X.h"
#include "Ecore_X_Atoms.h"
//////////////////////////////////////////////////////////////////////////////
// This api and structure only for the key router and window client side
// Application do not use this
//this mask is defined by key router.
//after discussing with keyrouter module, this mask can be changed
#define GRAB_MASK 0xffff00
#define OVERRIDE_EXCLUSIVE_GRAB 0xf00000
#define EXCLUSIVE_GRAB 0x0f0000
#define TOPMOST_GRAB 0x00f000
#define SHARED_GRAB 0x000f00
//if _ecore_keyrouter = 0, not yet check keyrouter
//if _ecore_keyrouter = -1, keyrouter not exist
//if _ecore_keyrouter = 1, keyrouter exist
int _ecore_keyrouter = 0;
struct _Ecore_X_Window_Key_Table
{
Ecore_X_Window win; //windo ID
int *key_list; //list of key
unsigned long key_cnt; // the number of key
};
typedef struct _Ecore_X_Window_Key_Table Ecore_X_Window_Key_Table;
static int _ecore_x_window_keytable_key_search(Ecore_X_Window_Key_Table *keytable, int key);
static Eina_Bool _ecore_x_window_keytable_key_del(Ecore_X_Window_Key_Table *key_table, int key, Ecore_X_Atom keytable_atom);
static Eina_Bool _ecore_x_window_keytable_key_add(Ecore_X_Window_Key_Table *keytable,
int keycode,
Ecore_X_Win_Keygrab_Mode grab_mode);
static Eina_Bool _ecore_x_window_keygrab_set_internal(Ecore_X_Window win, const char *key, Ecore_X_Win_Keygrab_Mode grab_mode);
static Eina_Bool _ecore_x_window_keygrab_unset_internal(Ecore_X_Window win, const char *key);
static Eina_Bool _ecore_x_window_keytable_get(Ecore_X_Window win, Ecore_X_Window_Key_Table *keytable);
//(Below Atom and exclusiveness_get/set functions) should be changed after keyrouter finds the solution to avoid race condition
//solution 1. window manages two key table. keytable and keytable result
//solution 2. using client messabe between the window client and the key router.
static Atom _atom_grab_excl_win = None;
#define STR_ATOM_GRAB_EXCL_WIN "_GRAB_EXCL_WIN_KEYCODE"
static void
_keytable_free(Ecore_X_Window_Key_Table *keytable)
{
if (keytable->key_list)
free(keytable->key_list);
keytable->key_list = NULL;
keytable->win = 0;
keytable->key_cnt = 0;
}
static int
_keytable_property_list_get(Ecore_X_Window win,
Ecore_X_Atom atom,
unsigned int **plst)
{
unsigned char *prop_ret;
Atom type_ret;
unsigned long bytes_after, num_ret;
int format_ret;
unsigned int i, *val;
int num;
*plst = NULL;
prop_ret = NULL;
if (XGetWindowProperty(_ecore_x_disp, win, atom, 0, 0x7fffffff, False,
XA_CARDINAL, &type_ret, &format_ret, &num_ret,
&bytes_after, &prop_ret) != Success)
{
WRN("XGetWindowProperty failed");
return -1;
}
else if ((num_ret == 0) || (!prop_ret))
num = 0;
else
{
val = malloc(num_ret * sizeof(unsigned int));
if (!val)
{
if (prop_ret) XFree(prop_ret);
WRN("Memory alloc failed");
return -1;
}
for (i = 0; i < num_ret; i++)
val[i] = ((unsigned long *)prop_ret)[i];
num = num_ret;
*plst = val;
}
if (_ecore_xlib_sync) ecore_x_sync();
if (prop_ret)
XFree(prop_ret);
return num;
}
static Eina_Bool
_ecore_x_window_keytable_possible_global_exclusiveness_get(int keycode)
{
int ret = 0;
Ecore_X_Window_Key_Table keytable;
keytable.win = ecore_x_window_root_first_get();
keytable.key_list = NULL;
keytable.key_cnt = 0;
if(_atom_grab_excl_win == None )
_atom_grab_excl_win = XInternAtom(_ecore_x_disp, STR_ATOM_GRAB_EXCL_WIN, False);
ret = _keytable_property_list_get(keytable.win, _atom_grab_excl_win,
(unsigned int **)&(keytable.key_list));
if (ret < 0)
{
return EINA_FALSE;
}
keytable.key_cnt = ret;
if (keytable.key_cnt == 0)
{
WRN("There is no keygrab entry in the table");
return EINA_TRUE;
}
//check keycode exists in the global exclusiveness keytable
ret = _ecore_x_window_keytable_key_search(&keytable, keycode);
if (ret != -1)
{
WRN("Can't search keygrab entry in the table");
_keytable_free(&keytable);
return EINA_FALSE;
}
_keytable_free(&keytable);
return EINA_TRUE;
}
static Eina_Bool
_ecore_x_window_keytable_possible_global_exclusiveness_set(int keycode)
{
int ret = 0;
Ecore_X_Window_Key_Table keytable;
keytable.win = ecore_x_window_root_first_get();
keytable.key_list = NULL;
keytable.key_cnt = 0;
if(_atom_grab_excl_win == None )
_atom_grab_excl_win = XInternAtom(_ecore_x_disp, STR_ATOM_GRAB_EXCL_WIN, False);
ret = _keytable_property_list_get(keytable.win, _atom_grab_excl_win,
(unsigned int **)&(keytable.key_list));
if (ret < 0) return EINA_FALSE;
keytable.key_cnt = ret;
if (keytable.key_cnt == 0)
{
XChangeProperty(_ecore_x_disp, keytable.win, _atom_grab_excl_win, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&keycode, 1);
XSync(_ecore_x_disp, False);
_keytable_free(&keytable);
return EINA_TRUE;
}
//check keycode exists in the global exclusiveness keytable
ret = _ecore_x_window_keytable_key_search(&keytable, keycode);
if (ret != -1)
{
XChangeProperty(_ecore_x_disp, keytable.win, _atom_grab_excl_win, XA_CARDINAL, 32,
PropModeAppend, (unsigned char *)&keycode, 1);
XSync(_ecore_x_disp, False);
_keytable_free(&keytable);
return EINA_TRUE;
}
WRN("Already key is grabbed");
_keytable_free(&keytable);
return EINA_FALSE;
}
static Eina_Bool
_ecore_x_window_keytable_possible_global_exclusiveness_unset(int keycode)
{
int ret = 0;
Ecore_X_Window_Key_Table keytable;
keytable.win = ecore_x_window_root_first_get();
keytable.key_list = NULL;
keytable.key_cnt = 0;
if(_atom_grab_excl_win == None )
_atom_grab_excl_win = XInternAtom(_ecore_x_disp, STR_ATOM_GRAB_EXCL_WIN, False);
ret = _keytable_property_list_get(keytable.win, _atom_grab_excl_win,
(unsigned int **)&(keytable.key_list));
if (ret <= 0) return EINA_FALSE;
keytable.key_cnt = ret;
//check keycode exists in the global exclusiveness keytable
ret = _ecore_x_window_keytable_key_search(&keytable, keycode);
if (ret == -1)
{
WRN("Already key exists");
_keytable_free(&keytable);
return EINA_FALSE;
}
else
ret = _ecore_x_window_keytable_key_del(&keytable, keycode, _atom_grab_excl_win);
_keytable_free(&keytable);
return EINA_FALSE;
}
static Eina_Bool
_ecore_x_window_keytable_keycode_decode(int keycode_encoded,
int *keycode,
Ecore_X_Win_Keygrab_Mode *grab_mode)
{
int key_mask = 0;
*keycode = keycode_encoded & (~GRAB_MASK);
key_mask = keycode_encoded & GRAB_MASK;
if (key_mask == SHARED_GRAB)
{
*grab_mode = ECORE_X_WIN_KEYGRAB_SHARED;
return EINA_TRUE;
}
else if (key_mask == TOPMOST_GRAB)
{
*grab_mode = ECORE_X_WIN_KEYGRAB_TOPMOST;
return EINA_TRUE;
}
else if (key_mask == EXCLUSIVE_GRAB)
{
*grab_mode = ECORE_X_WIN_KEYGRAB_EXCLUSIVE;
return EINA_TRUE;
}
else if (key_mask == OVERRIDE_EXCLUSIVE_GRAB)
{
*grab_mode = ECORE_X_WIN_KEYGRAB_OVERRIDE_EXCLUSIVE;
return EINA_TRUE;
}
else
{
*grab_mode = ECORE_X_WIN_KEYGRAB_UNKNOWN;
WRN("Keycode decoding failed. Unknown Keygrab mode");
return EINA_FALSE;
}
}
static Eina_Bool
_ecore_x_window_keytable_keycode_encode(int keycode,
Ecore_X_Win_Keygrab_Mode grab_mode,
int *keycode_encoded)
{
if ((grab_mode <= ECORE_X_WIN_KEYGRAB_UNKNOWN) || (grab_mode > ECORE_X_WIN_KEYGRAB_OVERRIDE_EXCLUSIVE))
{
*keycode_encoded = 0;
WRN("Keycode encoding failed. Unknown Keygrab mode");
return EINA_FALSE;
}
if (grab_mode == ECORE_X_WIN_KEYGRAB_SHARED)
*keycode_encoded = keycode | SHARED_GRAB;
else if (grab_mode == ECORE_X_WIN_KEYGRAB_TOPMOST)
*keycode_encoded = keycode | TOPMOST_GRAB;
else if (grab_mode == ECORE_X_WIN_KEYGRAB_EXCLUSIVE)
*keycode_encoded = keycode | EXCLUSIVE_GRAB;
else if (grab_mode == ECORE_X_WIN_KEYGRAB_OVERRIDE_EXCLUSIVE)
*keycode_encoded = keycode | OVERRIDE_EXCLUSIVE_GRAB;
return EINA_TRUE;
}
static Eina_Bool
_ecore_x_window_keytable_get(Ecore_X_Window win,
Ecore_X_Window_Key_Table *keytable)
{
int ret = 0;
ret = _keytable_property_list_get(win, ECORE_X_ATOM_E_KEYROUTER_WINDOW_KEYTABLE,
(unsigned int **)&(keytable->key_list));
if (ret < 0) return EINA_FALSE;
keytable->key_cnt = ret;
return EINA_TRUE;
}
static int
_ecore_x_window_keytable_key_search(Ecore_X_Window_Key_Table *keytable,
int key)
{
int i;
int keycode = 0;
unsigned long key_cnt;
int *key_list = NULL;
keycode = key & (~GRAB_MASK);
key_cnt = keytable->key_cnt;
key_list = keytable->key_list;
for (i = key_cnt - 1; i >= 0; i--)
{
if ((key_list[i] & (~GRAB_MASK)) == keycode) break;
}
return i;
}
static Eina_Bool
_ecore_x_window_keytable_key_add(Ecore_X_Window_Key_Table *keytable,
int keycode,
Ecore_X_Win_Keygrab_Mode grab_mode)
{
int i = 0;
int keycode_masked = 0;
Ecore_Window win;
unsigned long key_cnt;
win = keytable->win;
key_cnt = keytable->key_cnt;
if (!_ecore_x_window_keytable_keycode_encode(keycode, grab_mode, &keycode_masked))
return EINA_FALSE;
if (key_cnt == 0)
{
XChangeProperty(_ecore_x_disp, win, ECORE_X_ATOM_E_KEYROUTER_WINDOW_KEYTABLE, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&keycode_masked, 1);
XSync(_ecore_x_disp, False);
return EINA_TRUE;
}
else
{
i = _ecore_x_window_keytable_key_search(keytable, keycode_masked);
if ( i != -1 )
{
//already exist key in key table
WRN("Already key exists");
return EINA_FALSE;
}
XChangeProperty(_ecore_x_disp, win, ECORE_X_ATOM_E_KEYROUTER_WINDOW_KEYTABLE, XA_CARDINAL, 32,
PropModeAppend, (unsigned char *)&keycode_masked, 1);
XSync(_ecore_x_disp, False);
return EINA_TRUE;
}
}
static Eina_Bool
_ecore_x_window_keytable_key_del(Ecore_X_Window_Key_Table *key_table,
int key,
Ecore_X_Atom keytable_atom)
{
int i;
int *new_key_list = NULL;
unsigned long key_cnt = 0;
// Only one element is exists in the list of grabbed key
i = _ecore_x_window_keytable_key_search(key_table, key);
if (i == -1)
{
WRN("Key doesn't exist in the key table.");
return EINA_FALSE;
}
(key_table->key_cnt)--;
key_cnt = key_table->key_cnt;
if (key_cnt == 0)
{
XDeleteProperty(_ecore_x_disp, key_table->win, keytable_atom);
XSync(_ecore_x_disp, False);
return EINA_TRUE;
}
// Shrink the buffer
new_key_list = malloc((key_cnt) * sizeof(int));
if (new_key_list == NULL)
return EINA_FALSE;
// copy head
if (i > 0)
memcpy(new_key_list, key_table->key_list, sizeof(int) * i);
// copy tail
if ((key_cnt) - i > 0)
{
memcpy(new_key_list + i,
key_table->key_list + i + 1,
sizeof(int) * (key_cnt - i));
}
XChangeProperty(_ecore_x_disp, key_table->win, keytable_atom, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)new_key_list, key_cnt);
XSync(_ecore_x_disp, False);
free(new_key_list);
return EINA_TRUE;
}
static Eina_Bool
_ecore_x_window_keygrab_set_internal(Ecore_X_Window win,
const char *key,
Ecore_X_Win_Keygrab_Mode grab_mode)
{
KeyCode keycode = 0;
KeySym keysym;
Eina_Bool ret = EINA_FALSE;
Ecore_X_Window_Key_Table keytable;
keytable.win = win;
keytable.key_list = NULL;
keytable.key_cnt = 0;
//check the key string
if (!strncmp(key, "Keycode-", 8))
keycode = atoi(key + 8);
else
{
keysym = XStringToKeysym(key);
if (keysym == NoSymbol)
{
WRN("Keysym of key(\"%s\") doesn't exist", key);
return ret;
}
keycode = XKeysymToKeycode(_ecore_x_disp, keysym);
}
if (keycode == 0)
{
WRN("Keycode of key(\"%s\") doesn't exist", key);
return ret;
}
if(grab_mode == ECORE_X_WIN_KEYGRAB_EXCLUSIVE)
{
//Only one window can grab this key;
//keyrouter should avoid race condition
if (!_ecore_x_window_keytable_possible_global_exclusiveness_get(keycode))
return EINA_FALSE;
}
if (!_ecore_x_window_keytable_get(win, &keytable))
return EINA_FALSE;
ret = _ecore_x_window_keytable_key_add(&keytable, keycode, grab_mode);
if (!ret)
{
WRN("Key(\"%s\") add failed", key);
return ret;
}
if(grab_mode == ECORE_X_WIN_KEYGRAB_EXCLUSIVE)
{
//Only one window can grab this key;
if(!_ecore_x_window_keytable_possible_global_exclusiveness_set(keycode))
{
_ecore_x_window_keytable_key_del(&keytable, keycode, ECORE_X_ATOM_E_KEYROUTER_WINDOW_KEYTABLE);
WRN("Key(\"%s\") already is grabbed", key);
goto error;
}
}
_keytable_free(&keytable);
return EINA_TRUE;
error:
_keytable_free(&keytable);
return EINA_FALSE;
}
static Eina_Bool
_ecore_x_window_keygrab_unset_internal(Ecore_X_Window win,
const char *key)
{
KeyCode keycode = 0;
KeySym keysym;
int i;
int key_masked = 0;
int key_decoded = 0;
Eina_Bool ret = EINA_FALSE;
Ecore_X_Window_Key_Table keytable;
Ecore_X_Win_Keygrab_Mode grab_mode = ECORE_X_WIN_KEYGRAB_UNKNOWN;
keytable.win = win;
keytable.key_list = NULL;
keytable.key_cnt = 0;
if (!strncmp(key, "Keycode-", 8))
keycode = atoi(key + 8);
else
{
keysym = XStringToKeysym(key);
if (keysym == NoSymbol)
{
WRN("Keysym of key(\"%s\") doesn't exist", key);
return EINA_FALSE;
}
keycode = XKeysymToKeycode(_ecore_x_disp, keysym);
}
if (keycode == 0)
{
WRN("Keycode of key(\"%s\") doesn't exist", key);
return EINA_FALSE;
}
//construct the keytable structure using Xproperty
if (!_ecore_x_window_keytable_get(win, &keytable))
return EINA_FALSE;
if (keytable.key_cnt <= 0)
return EINA_FALSE;
i = _ecore_x_window_keytable_key_search(&keytable, keycode);
if (i == -1) //cannot find key in keytable
{
WRN("Key(\"%s\") doesn't exist", key);
goto error;
}
//find key in keytable
key_masked = keytable.key_list[i];
ret = _ecore_x_window_keytable_keycode_decode(key_masked, &key_decoded, &grab_mode);
if (!ret)
goto error;
ret = _ecore_x_window_keytable_key_del(&keytable, key_masked, ECORE_X_ATOM_E_KEYROUTER_WINDOW_KEYTABLE);
if (!ret)
goto error;
if (grab_mode == ECORE_X_WIN_KEYGRAB_EXCLUSIVE)
{
ret = _ecore_x_window_keytable_possible_global_exclusiveness_unset(keycode);
}
return EINA_TRUE;
error:
_keytable_free(&keytable);
return EINA_FALSE;
}
EAPI Eina_Bool
ecore_x_window_keygrab_set(Ecore_X_Window win,
const char *key,
int mod EINA_UNUSED,
int not_mod EINA_UNUSED,
int priority EINA_UNUSED,
Ecore_X_Win_Keygrab_Mode grab_mode)
{
if (_ecore_keyrouter == 0)
{
if(ecore_x_e_keyrouter_get(win))
_ecore_keyrouter = 1;
else
{
WRN("Keyrouter is not supported");
_ecore_keyrouter = -1;
}
}
if (_ecore_keyrouter < 0)
return EINA_FALSE;
return _ecore_x_window_keygrab_set_internal(win, key, grab_mode);
}
EAPI Eina_Bool
ecore_x_window_keygrab_unset(Ecore_X_Window win,
const char *key,
int mod EINA_UNUSED,
int any_mod EINA_UNUSED)
{
if (_ecore_keyrouter != 1)
{
WRN("Keyrouter is not supported");
return EINA_FALSE;
}
return _ecore_x_window_keygrab_unset_internal(win, key);
}