implement action routing for wayland clients

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>
devs/discomfitor/action_route
Mike Blumenkrantz 7 years ago
parent 63f3fe949c
commit 6272b85b49
  1. 4
      src/bin/Makefile.mk
  2. 37
      src/bin/e_bindings.c
  3. 1
      src/bin/e_bindings.h
  4. 4
      src/bin/e_comp_wl.h
  5. 224
      src/bin/e_comp_wl_extensions.c
  6. 49
      src/protocol/action_route.xml

@ -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 \

@ -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;

@ -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

@ -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

@ -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
@ -630,6 +631,218 @@ _e_comp_wl_zwp_pointer_constraints_v1_confine_pointer(struct wl_client *client,
client, resource, id, surface, pointer, region, lifetime);
}
/////////////////////////////////////////////////////////
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);

@ -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>
Loading…
Cancel
Save