summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Blumenkrantz <zmike@osg.samsung.com>2016-01-25 18:43:14 -0500
committerMike Blumenkrantz <zmike@osg.samsung.com>2017-06-26 12:53:28 -0400
commit6272b85b4920c1e752c1e0f6714fc3c2243801ff (patch)
tree542901bbdad34cccb667fe9673e25eea26278803
parent63f3fe949cb4f2169a7b9ba74a96df701617b1de (diff)
downloadenlightenment-devs/discomfitor/action_route.tar.gz
implement action routing for wayland clientsdevs/discomfitor/action_route
this allows a wayland client to request that a given action name be bound to the requested surface using a mode to restrict activation of the binding modes include: * shared - activated when any surface from the client has focus * topmost - activated when the requested surface has focus and is the topmost client * exclusive - activated when the requested surface has focus; blocks other action routes #SamsungFeatures Signed-off-by: Mike Blumenkrantz <zmike@osg.samsung.com>
-rw-r--r--src/bin/Makefile.mk4
-rw-r--r--src/bin/e_bindings.c37
-rw-r--r--src/bin/e_bindings.h1
-rw-r--r--src/bin/e_comp_wl.h4
-rw-r--r--src/bin/e_comp_wl_extensions.c224
-rw-r--r--src/protocol/action_route.xml49
6 files changed, 304 insertions, 15 deletions
diff --git a/src/bin/Makefile.mk b/src/bin/Makefile.mk
index 6106b22f21..aa9e2383c0 100644
--- a/src/bin/Makefile.mk
+++ b/src/bin/Makefile.mk
@@ -444,6 +444,8 @@ src/bin/generated/www-protocol.c \
src/bin/generated/www-server-protocol.h \
src/bin/generated/screenshooter-protocol.c \
src/bin/generated/screenshooter-server-protocol.h \
+src/bin/generated/action_route-protocol.c \
+src/bin/generated/action_route-server-protocol.h \
src/bin/generated/xdg-foreign-unstable-v1-protocol.c \
src/bin/generated/xdg-foreign-unstable-v1-server-protocol.h \
src/bin/generated/relative-pointer-unstable-v1-protocol.c \
@@ -456,6 +458,8 @@ src/bin/e_comp_wl_extensions.c: \
src/bin/generated/session-recovery-server-protocol.h \
src/bin/generated/xdg-foreign-unstable-v1-protocol.c \
src/bin/generated/xdg-foreign-unstable-v1-server-protocol.h \
+ src/bin/generated/action_route-protocol.c \
+ src/bin/generated/action_route-server-protocol.h \
src/bin/generated/relative-pointer-unstable-v1-protocol.c \
src/bin/generated/relative-pointer-unstable-v1-server-protocol.h \
src/bin/generated/pointer-constraints-unstable-v1-protocol.c \
diff --git a/src/bin/e_bindings.c b/src/bin/e_bindings.c
index 79b0fdfe9e..a21df86375 100644
--- a/src/bin/e_bindings.c
+++ b/src/bin/e_bindings.c
@@ -7,7 +7,6 @@ static void _e_bindings_edge_free(E_Binding_Edge *bind);
static void _e_bindings_signal_free(E_Binding_Signal *bind);
static void _e_bindings_wheel_free(E_Binding_Wheel *bind);
static void _e_bindings_acpi_free(E_Binding_Acpi *bind);
-static int _e_bindings_context_match(E_Binding_Context bctxt, E_Binding_Context ctxt);
static E_Binding_Modifier _e_bindings_modifiers(unsigned int modifiers);
static Eina_Bool _e_bindings_edge_cb_timer(void *data);
@@ -22,6 +21,8 @@ static Eina_List *acpi_bindings = NULL;
static unsigned int bindings_disabled = 0;
+EINTERN E_Action *(*e_binding_key_list_cb)(E_Binding_Context, Ecore_Event_Key*, E_Binding_Modifier, E_Binding_Key **);
+
typedef struct _E_Binding_Edge_Data E_Binding_Edge_Data;
struct _E_Binding_Edge_Data
@@ -382,7 +383,7 @@ e_bindings_mouse_grab(E_Binding_Context ctxt, Ecore_X_Window win)
EINA_LIST_FOREACH(mouse_bindings, l, binding)
{
if (binding->ctxt == E_BINDING_CONTEXT_ANY) continue;
- if (_e_bindings_context_match(binding->ctxt, ctxt))
+ if (e_bindings_context_match(binding->ctxt, ctxt))
{
#ifndef HAVE_WAYLAND_ONLY
ecore_x_window_button_grab(win, binding->button,
@@ -408,7 +409,7 @@ e_bindings_mouse_ungrab(E_Binding_Context ctxt, Ecore_X_Window win)
EINA_LIST_FOREACH(mouse_bindings, l, binding)
{
if (binding->ctxt == E_BINDING_CONTEXT_ANY) continue;
- if (_e_bindings_context_match(binding->ctxt, ctxt))
+ if (e_bindings_context_match(binding->ctxt, ctxt))
{
#ifndef HAVE_WAYLAND_ONLY
ecore_x_window_button_ungrab(win, binding->button,
@@ -444,7 +445,7 @@ e_bindings_mouse_button_find(E_Binding_Context ctxt, E_Binding_Event_Mouse_Butto
if ((binding->button == (int)ev->button) &&
((binding->any_mod) || (binding->mod == ev->modifiers)))
{
- if (!_e_bindings_context_match(binding->ctxt, ctxt)) continue;
+ if (!e_bindings_context_match(binding->ctxt, ctxt)) continue;
if (act && (binding->ctxt == E_BINDING_CONTEXT_ANY)) continue;
act = e_action_find(binding->action);
if (bind_ret) *bind_ret = binding;
@@ -620,7 +621,7 @@ e_bindings_key_grab(E_Binding_Context ctxt, Ecore_X_Window win)
EINA_LIST_FOREACH(key_bindings, l, binding)
{
- if (_e_bindings_context_match(binding->ctxt, ctxt))
+ if (e_bindings_context_match(binding->ctxt, ctxt))
{
if (e_bindings_key_allowed(binding->key))
{
@@ -644,7 +645,7 @@ e_bindings_key_ungrab(E_Binding_Context ctxt, Ecore_X_Window win)
EINA_LIST_FOREACH(key_bindings, l, binding)
{
- if (_e_bindings_context_match(binding->ctxt, ctxt))
+ if (e_bindings_context_match(binding->ctxt, ctxt))
{
if (e_bindings_key_allowed(binding->key))
{
@@ -701,12 +702,18 @@ e_bindings_key_event_find(E_Binding_Context ctxt, Ecore_Event_Key *ev, E_Binding
E_Action *act = NULL;
mod = _e_bindings_modifiers(ev->modifiers);
+ if (e_binding_key_list_cb)
+ {
+ act = e_binding_key_list_cb(ctxt, ev, mod, bind_ret);
+ if (act) return act;
+ if (bind_ret) *bind_ret = NULL;
+ }
EINA_LIST_FOREACH(key_bindings, l, binding)
{
if ((binding->key) && ((!strcmp(binding->key, ev->key)) || (!strcmp(binding->key, ev->keyname))) &&
((binding->any_mod) || (binding->mod == mod)))
{
- if (!_e_bindings_context_match(binding->ctxt, ctxt)) continue;
+ if (!e_bindings_context_match(binding->ctxt, ctxt)) continue;
if (act && (binding->ctxt == E_BINDING_CONTEXT_ANY)) continue;
act = e_action_find(binding->action);
if (bind_ret) *bind_ret = binding;
@@ -866,7 +873,7 @@ e_bindings_edge_event_find(E_Binding_Context ctxt, E_Event_Zone_Edge *ev, Eina_B
((binding->drag_only == ev->drag) || ev->drag) &&
((binding->any_mod) || (binding->mod == mod)))
{
- if (!_e_bindings_context_match(binding->ctxt, ctxt)) continue;
+ if (!e_bindings_context_match(binding->ctxt, ctxt)) continue;
if (act && (binding->ctxt == E_BINDING_CONTEXT_ANY)) continue;
act = e_action_find(binding->action);
if (bind_ret) *bind_ret = binding;
@@ -1037,7 +1044,7 @@ e_bindings_signal_find(E_Binding_Context ctxt, const char *sig, const char *src,
(e_util_glob_match(src, binding->src)) &&
((binding->any_mod) || (binding->mod == mod)))
{
- if (!_e_bindings_context_match(binding->ctxt, ctxt)) continue;
+ if (!e_bindings_context_match(binding->ctxt, ctxt)) continue;
if (act && (binding->ctxt == E_BINDING_CONTEXT_ANY)) continue;
act = e_action_find(binding->action);
if (bind_ret) *bind_ret = binding;
@@ -1120,7 +1127,7 @@ e_bindings_wheel_grab(E_Binding_Context ctxt, Ecore_X_Window win)
EINA_LIST_FOREACH(wheel_bindings, l, binding)
{
if (binding->ctxt == E_BINDING_CONTEXT_ANY) continue;
- if (_e_bindings_context_match(binding->ctxt, ctxt))
+ if (e_bindings_context_match(binding->ctxt, ctxt))
{
int button = 0;
@@ -1160,7 +1167,7 @@ e_bindings_wheel_ungrab(E_Binding_Context ctxt, Ecore_X_Window win)
EINA_LIST_FOREACH(wheel_bindings, l, binding)
{
if (binding->ctxt == E_BINDING_CONTEXT_ANY) continue;
- if (_e_bindings_context_match(binding->ctxt, ctxt))
+ if (e_bindings_context_match(binding->ctxt, ctxt))
{
int button = 0;
@@ -1214,7 +1221,7 @@ e_bindings_wheel_find(E_Binding_Context ctxt, E_Binding_Event_Wheel *ev, E_Bindi
(((binding->z < 0) && (ev->z < 0)) || ((binding->z > 0) && (ev->z > 0))) &&
((binding->any_mod) || (binding->mod == ev->modifiers)))
{
- if (!_e_bindings_context_match(binding->ctxt, ctxt)) continue;
+ if (!e_bindings_context_match(binding->ctxt, ctxt)) continue;
if (act && (binding->ctxt == E_BINDING_CONTEXT_ANY)) continue;
act = e_action_find(binding->action);
if (bind_ret) *bind_ret = binding;
@@ -1321,7 +1328,7 @@ e_bindings_acpi_find(E_Binding_Context ctxt, E_Event_Acpi *ev, E_Binding_Acpi **
/* binding status is set to something, compare event status */
if (binding->status != ev->status) continue;
}
- if (!_e_bindings_context_match(binding->ctxt, ctxt)) continue;
+ if (!e_bindings_context_match(binding->ctxt, ctxt)) continue;
if (act && (binding->ctxt == E_BINDING_CONTEXT_ANY)) continue;
act = e_action_find(binding->action);
if (bind_ret) *bind_ret = binding;
@@ -1442,8 +1449,8 @@ _e_bindings_acpi_free(E_Binding_Acpi *binding)
E_FREE(binding);
}
-static int
-_e_bindings_context_match(E_Binding_Context bctxt, E_Binding_Context ctxt)
+E_API int
+e_bindings_context_match(E_Binding_Context bctxt, E_Binding_Context ctxt)
{
if (bctxt == E_BINDING_CONTEXT_ANY &&
!(ctxt == E_BINDING_CONTEXT_ZONE)) return 1;
diff --git a/src/bin/e_bindings.h b/src/bin/e_bindings.h
index 0c71c1055c..8bc6ef43d1 100644
--- a/src/bin/e_bindings.h
+++ b/src/bin/e_bindings.h
@@ -202,6 +202,7 @@ E_API void e_bindings_evas_event_mouse_wheel_convert(const Evas_Event_Mouse_Whee
E_API void e_bindings_ecore_event_mouse_button_convert(const Ecore_Event_Mouse_Button *ev, E_Binding_Event_Mouse_Button *event);
E_API void e_bindings_ecore_event_mouse_wheel_convert(const Ecore_Event_Mouse_Wheel *ev, E_Binding_Event_Wheel *event);
+E_API int e_bindings_context_match(E_Binding_Context bctxt, E_Binding_Context ctxt);
E_API void e_bindings_disabled_set(Eina_Bool disabled);
#endif
diff --git a/src/bin/e_comp_wl.h b/src/bin/e_comp_wl.h
index f528306173..b43040c572 100644
--- a/src/bin/e_comp_wl.h
+++ b/src/bin/e_comp_wl.h
@@ -132,6 +132,10 @@ typedef struct E_Comp_Wl_Extension_Data
struct wl_global *global;
Eina_Hash *constraints;
} zwp_pointer_constraints_v1;
+ struct
+ {
+ struct wl_global *global;
+ } action_route;
} E_Comp_Wl_Extension_Data;
struct _E_Comp_Wl_Data
diff --git a/src/bin/e_comp_wl_extensions.c b/src/bin/e_comp_wl_extensions.c
index 3e9d409209..5816fbf825 100644
--- a/src/bin/e_comp_wl_extensions.c
+++ b/src/bin/e_comp_wl_extensions.c
@@ -9,6 +9,7 @@
#include "xdg-foreign-unstable-v1-server-protocol.h"
#include "relative-pointer-unstable-v1-server-protocol.h"
#include "pointer-constraints-unstable-v1-server-protocol.h"
+#include "action_route-server-protocol.h"
/* mutter uses 32, seems reasonable */
#define HANDLE_LEN 32
@@ -631,6 +632,218 @@ _e_comp_wl_zwp_pointer_constraints_v1_confine_pointer(struct wl_client *client,
}
/////////////////////////////////////////////////////////
+extern E_Action *(*e_binding_key_list_cb)(E_Binding_Context, Ecore_Event_Key*, E_Binding_Modifier, E_Binding_Key **);
+static Eina_Hash *key_bindings;
+
+typedef struct Action_Route
+{
+ union
+ {
+ E_Binding_Key key;
+ } binding;
+ uint32_t mode;
+ uint32_t state;
+ struct wl_resource *res;
+ struct wl_resource *surface;
+} Action_Route;
+
+static E_Action *
+_action_route_key_list_cb(E_Binding_Context ctxt, Ecore_Event_Key *ev, E_Binding_Modifier mod, E_Binding_Key **bind_ret)
+{
+ Eina_List *l, *ll;
+ Action_Route *ar;
+ E_Binding_Key *binding;
+
+ if (bind_ret) *bind_ret = NULL;
+ l = eina_hash_find(key_bindings, ev->key);
+ EINA_LIST_FOREACH(l, ll, ar)
+ {
+ E_Client *ec;
+
+ if (ar->state != ACTION_ROUTE_GRAB_STATE_ACTIVE) break;
+ binding = &ar->binding.key;
+ if ((!binding->any_mod) && (binding->mod != mod)) continue;
+ if (!e_bindings_context_match(binding->ctxt, ctxt)) continue;
+ ec = wl_resource_get_user_data(ar->surface);
+ if (!ec) continue;//wtf?
+ if (bind_ret) *bind_ret = binding;
+ switch (ar->mode)
+ {
+ /* if exclusive client has grab, activate. otherwise, no actions allowed */
+ case ACTION_ROUTE_MODE_EXCLUSIVE:
+ if (!ec->focused) return NULL;
+ return e_action_find(binding->action);
+ /* all surfaces for wl_client share the grab; if any surface has focus, activate. */
+ case ACTION_ROUTE_MODE_FOCUS_SHARED:
+ {
+ struct wl_client *client;
+
+ client = wl_resource_get_client(ar->surface);
+ if (!e_client_focused_get()) continue;
+ if (wl_resource_get_client(e_client_focused_get()->comp_data->surface) != client)
+ continue;
+ return e_action_find(binding->action);
+ }
+ /* only activate if surface has focus and is on top */
+ case ACTION_ROUTE_MODE_FOCUS_TOPMOST:
+ if (!ec->focused) continue;
+ {
+ E_Client *aec;
+ Eina_Bool valid = EINA_TRUE;
+
+ for (aec = e_client_above_get(ec); aec; aec = e_client_above_get(aec))
+ {
+ if (aec->layer > E_LAYER_CLIENT_PRIO)
+ return e_action_find(binding->action);
+ if (evas_object_visible_get(aec->frame))
+ {
+ valid = EINA_FALSE;
+ break;
+ }
+ }
+ if (valid)
+ return e_action_find(binding->action);
+ continue;
+ }
+ }
+ }
+ if (bind_ret) *bind_ret = NULL;
+ return NULL;
+}
+
+static Eina_Bool
+_action_route_is_allowed(const char *action, const char *params)
+{
+ /* FIXME: allow 1-2 bindings maybe */
+ return EINA_FALSE;
+}
+
+static Eina_Bool
+_action_route_can_override(const E_Binding_Key *binding)
+{
+ if (!binding) return EINA_TRUE;
+ /* FIXME: determine full list of overridable binding contexts */
+ if ((binding->ctxt == E_BINDING_CONTEXT_ANY) ||
+ (binding->ctxt == E_BINDING_CONTEXT_WINLIST))
+ return EINA_TRUE;
+ return EINA_FALSE;
+}
+
+static void
+_e_comp_wl_action_route_grab_del(struct wl_resource *resource)
+{
+ Eina_List *l, *ll;
+ Action_Route *ar;
+ Eina_Bool update;
+
+ /* FIXME: delete active actions? */
+ ar = wl_resource_get_user_data(resource);
+ if (!ar) return;
+ eina_hash_list_remove(key_bindings, ar->binding.key.key, ar);
+ update = (ar->mode == ACTION_ROUTE_MODE_EXCLUSIVE) && (ar->state == ACTION_ROUTE_GRAB_STATE_ACTIVE);
+ l = eina_hash_find(key_bindings, ar->binding.key.key);
+ eina_stringshare_del(ar->binding.key.key);
+ eina_stringshare_del(ar->binding.key.action);
+ eina_stringshare_del(ar->binding.key.params);
+ free(ar);
+ if (!update) return;
+ EINA_LIST_FOREACH(l, ll, ar)
+ {
+ /* all action routes are active until an exclusive or active grab has been reached */
+ if (ar->state == ACTION_ROUTE_GRAB_STATE_ACTIVE) break;//futureproofing...
+ ar->state = ACTION_ROUTE_GRAB_STATE_ACTIVE;
+ action_route_grab_send_status(ar->res, ar->state);
+ if (ar->mode == ACTION_ROUTE_MODE_EXCLUSIVE) break;
+ }
+}
+
+static void
+_e_comp_wl_action_route_grab_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ _e_comp_wl_action_route_grab_del(resource);
+}
+
+static const struct action_route_grab_interface _e_action_route_grab_interface =
+{
+ _e_comp_wl_action_route_grab_destroy,
+};
+
+static void
+_e_comp_wl_action_route_grab_key(struct wl_client *client, struct wl_resource *resource EINA_UNUSED,
+ uint32_t id,
+ struct wl_resource *surface,
+ const char *key,
+ uint32_t mode,
+ uint32_t modifiers,
+ const char *action,
+ const char *params)
+{
+ struct wl_resource *rt;
+ E_Binding_Key *binding;
+ Eina_List *l, *ll;
+ Action_Route *ar, *lar;
+ E_Binding_Context ctxt = E_BINDING_CONTEXT_WINDOW;
+
+ rt = wl_resource_create(client, &action_route_grab_interface, 1, id);
+ if (!rt)
+ {
+ ERR("Could not create action route");
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(rt, &_e_action_route_grab_interface, NULL,
+ _e_comp_wl_action_route_grab_del);
+ binding = e_bindings_key_find(key, modifiers, 0);
+ if ((!_action_route_is_allowed(action, params)) || (!_action_route_can_override(binding)))
+ {
+ action_route_grab_send_status(rt, ACTION_ROUTE_GRAB_STATE_REJECTED);
+ wl_resource_destroy(rt);
+ return;
+ }
+ ar = E_NEW(Action_Route, 1);
+ ar->mode = mode;
+ ar->res = rt;
+ ar->surface = surface;
+ binding = &ar->binding.key;
+ switch (mode)
+ {
+ case ACTION_ROUTE_MODE_EXCLUSIVE:
+ ctxt = E_BINDING_CONTEXT_ANY;
+ break;
+ case ACTION_ROUTE_MODE_FOCUS_SHARED:
+ ctxt = E_BINDING_CONTEXT_WINDOW;
+ break;
+ case ACTION_ROUTE_MODE_FOCUS_TOPMOST:
+ ctxt = E_BINDING_CONTEXT_WINDOW;
+ break;
+ }
+ binding->ctxt = ctxt;
+ binding->key = eina_stringshare_add(key);
+ binding->mod = modifiers;
+ binding->any_mod = 0;
+ binding->action = eina_stringshare_add(action);
+ binding->params = eina_stringshare_add(params);
+ wl_resource_set_user_data(rt, ar);
+ l = eina_hash_find(key_bindings, key);
+ EINA_LIST_FOREACH(l, ll, lar)
+ {
+ if (lar->mode == ACTION_ROUTE_MODE_EXCLUSIVE)
+ {
+ ar->state = ACTION_ROUTE_GRAB_STATE_QUEUED;
+ continue;
+ }
+ ar->state = ACTION_ROUTE_GRAB_STATE_ACTIVE;
+ if (ar->mode == ACTION_ROUTE_MODE_EXCLUSIVE)
+ l = eina_list_prepend_relative(l, ar, ll);
+ else
+ l = eina_list_append(l, ar);
+ break;
+ }
+ eina_hash_set(key_bindings, key, l);
+ action_route_grab_send_status(ar->res, ar->state);
+}
+
+/////////////////////////////////////////////////////////
static const struct zwp_e_session_recovery_interface _e_session_recovery_interface =
{
@@ -674,6 +887,11 @@ static const struct zwp_pointer_constraints_v1_interface _e_zwp_pointer_constrai
_e_comp_wl_zwp_pointer_constraints_v1_confine_pointer,
};
+static const struct action_route_interface _e_action_route_interface =
+{
+ _e_comp_wl_action_route_grab_key,
+};
+
#define GLOBAL_BIND_CB(NAME, IFACE, ...) \
static void \
_e_comp_wl_##NAME##_cb_bind(struct wl_client *client, void *data EINA_UNUSED, uint32_t version EINA_UNUSED, uint32_t id) \
@@ -688,6 +906,7 @@ _e_comp_wl_##NAME##_cb_bind(struct wl_client *client, void *data EINA_UNUSED, ui
}\
\
wl_resource_set_implementation(res, &_e_##NAME##_interface, NULL, NULL);\
+ __VA_ARGS__ \
}
GLOBAL_BIND_CB(session_recovery, zwp_e_session_recovery_interface)
@@ -697,6 +916,10 @@ GLOBAL_BIND_CB(zxdg_exporter_v1, zxdg_exporter_v1_interface)
GLOBAL_BIND_CB(zxdg_importer_v1, zxdg_importer_v1_interface)
GLOBAL_BIND_CB(zwp_relative_pointer_manager_v1, zwp_relative_pointer_manager_v1_interface)
GLOBAL_BIND_CB(zwp_pointer_constraints_v1, zwp_pointer_constraints_v1_interface)
+GLOBAL_BIND_CB(action_route, action_route_interface,
+ e_binding_key_list_cb = _action_route_key_list_cb;
+ key_bindings = eina_hash_string_superfast_new(NULL);
+)
#define GLOBAL_CREATE_OR_RETURN(NAME, IFACE, VERSION) \
@@ -744,6 +967,7 @@ e_comp_wl_extensions_init(void)
GLOBAL_CREATE_OR_RETURN(zwp_relative_pointer_manager_v1, zwp_relative_pointer_manager_v1_interface, 1);
GLOBAL_CREATE_OR_RETURN(zwp_pointer_constraints_v1, zwp_pointer_constraints_v1_interface, 1);
e_comp_wl->extensions->zwp_pointer_constraints_v1.constraints = eina_hash_pointer_new(NULL);
+ GLOBAL_CREATE_OR_RETURN(action_route, action_route_interface, 1);
ecore_event_handler_add(ECORE_WL2_EVENT_SYNC_DONE, _dmabuf_add, NULL);
diff --git a/src/protocol/action_route.xml b/src/protocol/action_route.xml
new file mode 100644
index 0000000000..5f2996aac2
--- /dev/null
+++ b/src/protocol/action_route.xml
@@ -0,0 +1,49 @@
+<protocol name="zwp_action_route">
+ <interface name="action_route" version="1">
+ <enum name="mode">
+ <description summary="types of state on the surface">
+ </description>
+ <entry name="focus_shared" value="1" summary="Activatable by any surface if its wl_client has focus">
+ </entry>
+ <entry name="focus_topmost" value="2" summary="Activatable by topmost surface with focus">
+ <description summary="Restricts activation to only the top-most client"/>
+ </entry>
+ <entry name="exclusive" value="3" summary="Activatable by one surface at any time">
+ <description summary="Allows activation for exactly one client at all times"/>
+ </entry>
+ </enum>
+ <enum name="modifiers">
+ <entry name="shift" value="1"></entry>
+ <entry name="control" value="2"></entry>
+ <entry name="alt" value="4"></entry>
+ <entry name="win" value="8"></entry>
+ <entry name="altgr" value="16"></entry>
+ </enum>
+ <request name="grab_key">
+ <description summary="Request a new grab for a key press">
+ </description>
+ <arg name="id" type="new_id" interface="action_route_grab"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="key" type="string"/>
+ <arg name="mode" type="uint"/>
+ <arg name="modifiers" type="uint"/>
+ <arg name="action" type="string"/>
+ <arg name="params" type="string"/>
+ </request>
+ </interface>
+ <interface name="action_route_grab" version="1">
+ <enum name="state">
+ <entry name="Rejected" value="0"></entry>
+ <entry name="Active" value="1"></entry>
+ <entry name="Queued" value="2"></entry>
+ </enum>
+ <event name="status">
+ <description summary="Status update on a grab request"></description>
+ <arg name="state" type="uint"/>
+ </event>
+ <request name="destroy" type="destructor">
+ <description summary="Destroy a requested grab">
+ </description>
+ </request>
+ </interface>
+</protocol>