diff --git a/src/examples/evas/.gitignore b/src/examples/evas/.gitignore index ae8e78e9c5..df145d0821 100644 --- a/src/examples/evas/.gitignore +++ b/src/examples/evas/.gitignore @@ -19,6 +19,7 @@ /evas_aspect_hints /evas_box /evas_buffer_simple +/evas_event_filter /evas_canvas3d_aabb /evas_canvas3d_blending /evas_canvas3d_colorpick diff --git a/src/examples/evas/Makefile.am b/src/examples/evas/Makefile.am index c4d155d356..c4ed9dea01 100644 --- a/src/examples/evas/Makefile.am +++ b/src/examples/evas/Makefile.am @@ -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 \ diff --git a/src/examples/evas/evas-event-filter.c b/src/examples/evas/evas-event-filter.c new file mode 100644 index 0000000000..aa41d8ec71 --- /dev/null +++ b/src/examples/evas/evas-event-filter.c @@ -0,0 +1,277 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#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; +} + diff --git a/src/lib/evas/canvas/efl_canvas_object.eo b/src/lib/evas/canvas/efl_canvas_object.eo index 606dd45213..24a706bd04 100644 --- a/src/lib/evas/canvas/efl_canvas_object.eo +++ b/src/lib/evas/canvas/efl_canvas_object.eo @@ -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; } } diff --git a/src/lib/evas/canvas/efl_input_interface.eo b/src/lib/evas/canvas/efl_input_interface.eo index 49a7f1ab00..5ee9782a77 100644 --- a/src/lib/evas/canvas/efl_input_interface.eo +++ b/src/lib/evas/canvas/efl_input_interface.eo @@ -79,6 +79,22 @@ interface Efl.Input.Interface () } return: iterator; [[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).]] diff --git a/src/lib/evas/canvas/evas_focus.c b/src/lib/evas/canvas/evas_focus.c index 9269985073..36f265c1b0 100644 --- a/src/lib/evas/canvas/evas_focus.c +++ b/src/lib/evas/canvas/evas_focus.c @@ -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; diff --git a/src/lib/evas/canvas/evas_object_main.c b/src/lib/evas/canvas/evas_object_main.c index 00c4590657..07518b6901 100644 --- a/src/lib/evas/canvas/evas_object_main.c +++ b/src/lib/evas/canvas/evas_object_main.c @@ -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) { diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index d5645fc668..77e393dd30 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -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;