Merge branch 'devs/iscaro/event-filter'

Series of patches adding support to filter input and focus
events per seat for any evas object.

So it will make it possible to block or unblock keyboard,
mouse and focus events that was originated by a specific seat.
Requested on RFC regarding multiseaet support on Edje -
such feature will be also supported on Edje later.

Patches by Guilherme Iscaro <iscaro@profusion.mobi>

Reviewed By: bdilly, cedric, jpeg

Differential Revision: https://phab.enlightenment.org/D4468
This commit is contained in:
Bruno Dilly 2016-12-12 16:04:58 -02:00
commit ddf3558628
8 changed files with 410 additions and 0 deletions

View File

@ -19,6 +19,7 @@
/evas_aspect_hints
/evas_box
/evas_buffer_simple
/evas_event_filter
/evas_canvas3d_aabb
/evas_canvas3d_blending
/evas_canvas3d_colorpick

View File

@ -119,6 +119,11 @@ evas_events_SOURCES = evas-events.c
evas_events_LDADD = $(ECORE_EVAS_COMMON_LDADD)
evas_events_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS)
EXTRA_PROGRAMS += evas_event_filter
evas_event_filter_SOURCES = evas-event-filter.c
evas_event_filter_LDADD = $(ECORE_EVAS_COMMON_LDADD)
evas_event_filter_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS)
EXTRA_PROGRAMS += evas_multiseat_events
evas_multiseat_events_SOURCES = evas-multiseat-events.c
evas_multiseat_events_LDADD = $(ECORE_EVAS_COMMON_LDADD)
@ -369,6 +374,7 @@ evas-aspect-hints.c \
evas-box.c \
evas-buffer-simple.c \
evas-events.c \
evas-event-filter.c \
evas-hints.c \
evas-images.c \
evas-images2.c \

View File

@ -0,0 +1,277 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Eina.h>
#include <Ecore.h>
#include <Evas.h>
#include <Ecore_Evas.h>
#include <stdio.h>
#define W (300)
#define H (300)
#define RECT_W (50)
#define RECT_H (50)
typedef struct _Context {
Evas_Object *filtered_obj;
Efl_Input_Device *allowed_seat;
} Context;
static void
_ee_del_request_cb(Ecore_Evas *ee EINA_UNUSED)
{
ecore_main_loop_quit();
}
static void
_ee_resize_cb(Ecore_Evas *ee)
{
Evas_Object *bg;
int w, h;
ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
bg = ecore_evas_data_get(ee, "bg");
evas_object_resize(bg, w, h);
}
static Eina_Bool
_allowed_seat_get(Evas_Object *filtered_obj,
Efl_Input_Device **allowed_seat)
{
const Eina_List *devs, *l;
Efl_Input_Device *seat;
Eina_Bool allowed_seat_changed = EINA_FALSE;
if (!filtered_obj) return EINA_TRUE;
devs = evas_device_list(evas_object_evas_get(filtered_obj), NULL);
EINA_LIST_FOREACH(devs, l, seat)
{
if ((efl_input_device_type_get(seat) != EFL_INPUT_DEVICE_CLASS_SEAT) ||
(*allowed_seat == seat))
continue;
if (!allowed_seat_changed)
{
printf("The '%s' shall only receive events from seat '%s'\n",
evas_object_name_get(filtered_obj),
efl_input_device_name_get(seat));
allowed_seat_changed = EINA_TRUE;
*allowed_seat = seat;
efl_input_seat_event_filter_set(filtered_obj, seat, EINA_TRUE);
if (!efl_canvas_object_seat_focus_add(filtered_obj, seat))
{
fprintf(stderr, "ERROR: The '%s' could not be focused by the seat '%s'\n",
evas_object_name_get(filtered_obj),
efl_input_device_name_get(seat));
return EINA_FALSE;
}
}
else
{
if (efl_canvas_object_seat_focus_add(filtered_obj, seat))
{
fprintf(stderr, "ERROR: The '%s' should not be focused by the seat '%s'\n",
evas_object_name_get(filtered_obj),
efl_input_device_name_get(seat));
return EINA_FALSE;
}
}
}
if (!allowed_seat_changed)
*allowed_seat = NULL;
return EINA_TRUE;
}
static const char *
_event_as_string(const void *desc)
{
if (desc == EFL_EVENT_FOCUS_IN)
return "FOCUS_IN";
else if (desc == EFL_EVENT_FOCUS_OUT)
return "FOCUS_OUT";
else if (desc == EFL_EVENT_KEY_DOWN)
return "KEY_DOWN";
else if (desc == EFL_EVENT_KEY_UP)
return "KEY_UP";
else if (desc == EFL_EVENT_HOLD)
return "HOLD";
else if (desc == EFL_EVENT_POINTER_IN)
return "POINTER_IN";
else if (desc == EFL_EVENT_POINTER_OUT)
return "POINTER_OUT";
else if (desc == EFL_EVENT_POINTER_DOWN)
return "POINTER_DOWN";
else if (desc == EFL_EVENT_POINTER_UP)
return "POINTER_UP";
else if (desc == EFL_EVENT_POINTER_MOVE)
return "POINTER_MOVE";
else
return "MOUSE_WHEEL";
}
static void
_obj_events_cb(void *data, const Efl_Event *event)
{
Efl_Input_Device *seat = efl_input_device_seat_get(efl_input_device_get(event->info));
Context *ctx = data;
const char *event_name;
event_name = _event_as_string(event->desc);
if (seat != ctx->allowed_seat && event->object == ctx->filtered_obj)
{
fprintf(stderr, "ERROR: The object '%s' should not receive the event"
"'%s' from the seat '%s'\n",
evas_object_name_get(event->object), event_name,
efl_input_device_name_get(seat));
ecore_main_loop_quit();
}
else
printf("The object '%s' recevied a '%s' event from seat '%s'\n",
evas_object_name_get(event->object), event_name,
efl_input_device_name_get(seat));
}
static void
_obj_del_event_cb(void *data, const Efl_Event *event)
{
Context *ctx = data;
if (event->object == ctx->filtered_obj)
ctx->filtered_obj = NULL;
}
static void
_device_added_removed_cb(void *data, const Efl_Event *event)
{
Efl_Input_Device *dev = event->info;
Context *ctx = data;
if (efl_input_device_type_get(dev) != EFL_INPUT_DEVICE_CLASS_SEAT)
return;
if (event->desc == EFL_CANVAS_EVENT_DEVICE_ADDED)
{
if (ctx->allowed_seat)
efl_input_seat_event_filter_set(ctx->filtered_obj,
ctx->allowed_seat, EINA_FALSE);
ctx->allowed_seat = dev;
}
else
{
if (!_allowed_seat_get(ctx->filtered_obj, &ctx->allowed_seat))
ecore_main_loop_quit();
}
}
EFL_CALLBACKS_ARRAY_DEFINE(_device_callbacks,
{ EFL_CANVAS_EVENT_DEVICE_ADDED, _device_added_removed_cb },
{ EFL_CANVAS_EVENT_DEVICE_REMOVED, _device_added_removed_cb });
EFL_CALLBACKS_ARRAY_DEFINE(_obj_callbacks,
{ EFL_EVENT_FOCUS_IN, _obj_events_cb },
{ EFL_EVENT_FOCUS_OUT, _obj_events_cb },
{ EFL_EVENT_KEY_DOWN, _obj_events_cb },
{ EFL_EVENT_KEY_UP, _obj_events_cb },
{ EFL_EVENT_HOLD, _obj_events_cb },
{ EFL_EVENT_POINTER_IN, _obj_events_cb },
{ EFL_EVENT_POINTER_OUT,_obj_events_cb },
{ EFL_EVENT_POINTER_DOWN, _obj_events_cb },
{ EFL_EVENT_POINTER_UP, _obj_events_cb },
{ EFL_EVENT_POINTER_MOVE, _obj_events_cb },
{ EFL_EVENT_POINTER_WHEEL, _obj_events_cb },
{ EFL_EVENT_DEL, _obj_del_event_cb });
static Evas_Object *
_rect_add(Evas *e, Context *ctx, const char *name,
int w, int h, int x, int y, int r, int g, int b,
Eina_Bool add_callbacks)
{
Evas_Object *obj;
obj = evas_object_rectangle_add(e);
if (!obj)
{
fprintf(stderr, "Could not create the BG\n");
return NULL;
}
evas_object_color_set(obj, r, g, b, 255);
evas_object_resize(obj, w, h);
evas_object_move(obj, x, y);
evas_object_show(obj);
evas_object_name_set(obj, name);
if (add_callbacks)
efl_event_callback_array_add(obj, _obj_callbacks(), ctx);
return obj;
}
int
main(int argc EINA_UNUSED, char *argv[] EINA_UNUSED)
{
Context ctx = { 0 };
Ecore_Evas *ee;
Evas_Object *obj;
Evas *e;
if (!ecore_evas_init())
{
fprintf(stderr, "Could not init Ecore_Evas\n");
return EXIT_FAILURE;
}
ee = ecore_evas_new(NULL, 0, 0, W, H, NULL);
if (!ee)
{
fprintf(stderr, "Coult not create the Ecore_Evas\n");
goto err_ee;
}
e = ecore_evas_get(ee);
obj = _rect_add(e, &ctx, "bg", W, H, 0, 0, 255, 255, 255, EINA_FALSE);
if (!obj)
{
fprintf(stderr, "Could not create the BG\n");
goto err_obj;
}
ecore_evas_data_set(ee, "bg", obj);
obj = _rect_add(e, &ctx, "Red Rectangle", RECT_W, RECT_H, W/2 - RECT_W/2,
H/2 - RECT_H/2, 255, 0, 0, EINA_TRUE);
if (!obj)
{
fprintf(stderr, "Could not create the red rectangle\n");
goto err_obj;
}
ctx.filtered_obj = obj;
obj = _rect_add(e, &ctx, "Blue Rectangle", RECT_W, RECT_H,
100, 100, 0, 0, 255, EINA_TRUE);
if (!obj)
{
fprintf(stderr, "Could not create the blue rectangle\n");
goto err_obj;
}
printf("The '%s' shall receive events from any seat\n",
evas_object_name_get(obj));
if (!_allowed_seat_get(ctx.filtered_obj, &ctx.allowed_seat))
goto err_obj;
efl_event_callback_array_add(e, _device_callbacks(), &ctx);
ecore_evas_callback_resize_set(ee, _ee_resize_cb);
ecore_evas_callback_delete_request_set(ee, _ee_del_request_cb);
ecore_evas_show(ee);
ecore_main_loop_begin();
ecore_evas_free(ee);
ecore_evas_shutdown();
return EXIT_SUCCESS;
err_obj:
ecore_evas_free(ee);
err_ee:
ecore_evas_shutdown();
return EXIT_FAILURE;
}

View File

@ -624,6 +624,8 @@ abstract Efl.Canvas.Object (Efl.Object, Efl.Gfx, Efl.Gfx.Stack, Efl.Animator,
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Object.dbg_info_get;
Efl.Object.event_callback_legacy_call;
Efl.Object.event_callback_call;
Efl.Object.provider_find;
Efl.Gfx.visible.set;
Efl.Gfx.visible.get;
@ -666,5 +668,7 @@ abstract Efl.Canvas.Object (Efl.Object, Efl.Gfx, Efl.Gfx.Stack, Efl.Animator,
Efl.Gfx.Size.Hint.hint_weight.get;
Efl.Gfx.Map.map_enable.set;
Efl.Gfx.Map.map_enable.get;
Efl.Input.Interface.seat_event_filter.set;
Efl.Input.Interface.seat_event_filter.get;
}
}

View File

@ -79,6 +79,22 @@ interface Efl.Input.Interface ()
}
return: iterator<const(Efl.Input.Pointer)>; [[Iterator to pointer positions]]
}
@property seat_event_filter {
set {
[[Add or remove a given seat to the filter list. If the filter list is empty this object
will report mouse, keyboard and focus events from any seat, otherwise those events will
only be reported if the event comes from a seat that is in the list.]]
}
get {
[[Check if input events from a given seat is enabled.]]
}
keys {
seat: Efl.Input.Device; [[The seat to act on.]]
}
values {
enable: bool; [[$true to enable events for a seat or $false otherwise.]]
}
}
}
events {
pointer,move: Efl.Input.Pointer; [[Main pointer move (current and previous positions are known).]]

View File

@ -167,6 +167,9 @@ _efl_canvas_object_seat_focus_add(Eo *eo_obj,
if (efl_input_device_type_get(seat) != EFL_INPUT_DEVICE_CLASS_SEAT)
return EINA_FALSE;
if (!efl_input_seat_event_filter_get(eo_obj, seat))
return EINA_FALSE;
if (_already_focused(obj->focused_by_seats, seat))
goto end;

View File

@ -887,6 +887,101 @@ evas_object_del(Evas_Object *eo_obj)
efl_del(eo_obj);
}
EOLIAN static Eina_Bool
_efl_canvas_object_efl_input_interface_seat_event_filter_get(Eo *eo_obj EINA_UNUSED,
Evas_Object_Protected_Data *obj,
Efl_Input_Device *seat)
{
//If the list is empty this object accept events from any seat.
if (!obj->events_whitelist)
return EINA_TRUE;
return eina_list_data_find(obj->events_whitelist, seat) ?
EINA_TRUE : EINA_FALSE;
}
static void
_whitelist_events_device_remove_cb(void *data, const Efl_Event *event)
{
Evas_Object_Protected_Data *obj = data;
obj->events_whitelist = eina_list_remove(obj->events_whitelist,
event->object);
}
EOLIAN static void
_efl_canvas_object_efl_input_interface_seat_event_filter_set(Eo *eo_obj,
Evas_Object_Protected_Data *obj,
Efl_Input_Device *seat,
Eina_Bool add)
{
EINA_SAFETY_ON_NULL_RETURN(seat);
if (efl_input_device_type_get(seat) != EFL_INPUT_DEVICE_CLASS_SEAT) return;
if (add)
{
if (eina_list_data_find(obj->events_whitelist, seat)) return;
if (efl_canvas_object_seat_focus_check(eo_obj, seat))
efl_canvas_object_seat_focus_del(eo_obj, seat);
obj->events_whitelist = eina_list_append(obj->events_whitelist, seat);
efl_event_callback_add(seat, EFL_EVENT_DEL,
_whitelist_events_device_remove_cb, obj);
}
else
{
obj->events_whitelist = eina_list_remove(obj->events_whitelist, seat);
efl_event_callback_del(seat, EFL_EVENT_DEL,
_whitelist_events_device_remove_cb, obj);
}
}
static Eina_Bool
_is_event_blocked(Eo *eo_obj, const Efl_Event_Description *desc,
void *event_info)
{
if ((desc == EFL_EVENT_FOCUS_IN) ||
(desc == EFL_EVENT_FOCUS_OUT) ||
(desc == EFL_EVENT_KEY_DOWN) ||
(desc == EFL_EVENT_KEY_UP) ||
(desc == EFL_EVENT_HOLD) ||
(desc == EFL_EVENT_POINTER_IN) ||
(desc == EFL_EVENT_POINTER_OUT) ||
(desc == EFL_EVENT_POINTER_DOWN) ||
(desc == EFL_EVENT_POINTER_UP) ||
(desc == EFL_EVENT_POINTER_MOVE) ||
(desc == EFL_EVENT_POINTER_WHEEL) ||
(desc == EFL_EVENT_POINTER_CANCEL) ||
(desc == EFL_EVENT_POINTER_AXIS) ||
(desc == EFL_EVENT_FINGER_MOVE) ||
(desc == EFL_EVENT_FINGER_DOWN) ||
(desc == EFL_EVENT_FINGER_UP))
{
Efl_Input_Device *seat = efl_input_device_seat_get(efl_input_device_get(event_info));
return !efl_input_seat_event_filter_get(eo_obj, seat);
}
return EINA_FALSE;
}
EOLIAN static Eina_Bool
_efl_canvas_object_efl_object_event_callback_call(Eo *eo_obj,
Evas_Object_Protected_Data *obj EINA_UNUSED,
const Efl_Event_Description *desc,
void *event_info)
{
if (_is_event_blocked(eo_obj, desc, event_info)) return EINA_FALSE;
return efl_event_callback_call(efl_super(eo_obj, MY_CLASS),
desc, event_info);
}
EOLIAN static Eina_Bool
_efl_canvas_object_efl_object_event_callback_legacy_call(Eo *eo_obj,
Evas_Object_Protected_Data *obj EINA_UNUSED,
const Efl_Event_Description *desc,
void *event_info)
{
if (_is_event_blocked(eo_obj, desc, event_info)) return EINA_FALSE;
return efl_event_callback_legacy_call(efl_super(eo_obj, MY_CLASS),
desc, event_info);
}
EOLIAN static void
_efl_canvas_object_efl_object_destructor(Eo *eo_obj, Evas_Object_Protected_Data *obj)
{
@ -914,6 +1009,8 @@ _efl_canvas_object_efl_object_destructor(Eo *eo_obj, Evas_Object_Protected_Data
evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_DEL, NULL, _evas_object_event_new(), NULL);
if ((obj->layer) && (obj->layer->evas))
_evas_post_event_callback_call(obj->layer->evas->evas, obj->layer->evas);
EINA_LIST_FREE(obj->events_whitelist, dev)
efl_event_callback_del(dev, EFL_EVENT_DEL, _whitelist_events_device_remove_cb, obj);
if (obj->name) evas_object_name_set(eo_obj, NULL);
if (!obj->layer)
{

View File

@ -1116,6 +1116,12 @@ struct _Evas_Object_Protected_Data
Eina_List *grabs;
Eina_Inlist *callbacks;
/*
The list below contain the seats (Efl.Input.Devices) which this
object allows events to be reported (Mouse, Keybord and focus events).
If this list is empty, this object will allow events from any seat.
*/
Eina_List *events_whitelist;
struct {
Eina_List *clipees;