evas: Strengthen post-event callbacks

See T3144 that I marked as Wontfix.

Bryce in E manually feeds events from a post-event callback
resulting in Evas going insane and leading to frequent crashes.
The ideal solution (for E) would be to ensure that everything works
smoothly, the input event data is valid up until the post-event cb
is called, etc... Unfortunately, with recursive events the exact
order of operations may be messed up: the post-event

I don't want to add yet more complexity to Evas events here (it's
already spaghetti all over the place) so I'm simply blocking any
new event feed when running the post-event callback list.

It's not possible to just freeze the events (I tried, it failed).

**********************
Some more explanation:

post-event callbacks are used to implement reverse-order logic
where the on-hold flag of an input event may be set by an event
listener that does not come first.

Here's a situation to illustrate: scroller A inside scroller B.

As events are propagated from children to parents (assuming the
propagate flag is set), we'd assume the events to go first to A
and then to B, which means a mouse wheel event would make the
inner-most scroller (A) scroll, and the outer-most scroller (B)
wouldn't budge.

But as things are designed, A and B are not simple evas objects,
and the actual event-catching object is a top-most transparent
rectangle (top-most in Z stack order). Since A is inside B, B's
rectangle BR is over A's rectangle AR, thus catches the wheel
event first. But in terms of UX we still want A to scroll, not B.

The solution then is to reverse the event processing order and
post-event callbacks are the way to do that. This comes with the
consequence that the event_info needs to remain valid until the
post-event is called, and stay the same (so that the on-hold flag
set by A can be read by B).

Recursive events (by explicit feed or modifying the canvas so
that mouse,in or mouse,out are triggered) mess with this logic,
and trigger the post-events too early (event is not fully
processed) or too late (event_info is not valid anymore... and
crash!).

Thanks @raster for explaining the goal of post-event callbacks!
This commit is contained in:
Jean-Philippe Andre 2017-02-16 16:47:57 +09:00
parent e434653fc2
commit b184874fa5
3 changed files with 39 additions and 8 deletions

View File

@ -227,12 +227,12 @@ _evas_post_event_callback_call(Evas *eo_e, Evas_Public_Data *e)
Evas_Post_Callback *pc;
Eina_List *l, *l_next;
int skip = 0;
static int first_run = 1; // FIXME: This is a workaround to prevent this
// function from being called recursively.
if (e->delete_me || (!first_run)) return;
if (e->delete_me || e->running_post_events) return;
if (!e->post_events) return;
_evas_walk(e);
first_run = 0;
e->running_post_events = EINA_TRUE;
EINA_LIST_FOREACH_SAFE(e->post_events, l, l_next, pc)
{
e->post_events = eina_list_remove_list(e->post_events, l);
@ -242,7 +242,7 @@ _evas_post_event_callback_call(Evas *eo_e, Evas_Public_Data *e)
}
EVAS_MEMPOOL_FREE(_mp_pc, pc);
}
first_run = 1;
e->running_post_events = EINA_FALSE;
_evas_unwalk(e);
}

View File

@ -30,6 +30,17 @@ static void
_canvas_event_feed_mouse_move_legacy(Evas *eo_e, Evas_Public_Data *e, int x, int y,
unsigned int timestamp, const void *data);
static inline Eina_Bool
_evas_event_feed_allow(Evas_Public_Data *e)
{
if (EINA_LIKELY(!e->running_post_events)) return EINA_TRUE;
ERR("Can not feed input events while running post-event callbacks!");
return EINA_FALSE;
}
#define EVAS_EVENT_FEED_SAFETY_CHECK(evas, ...) do { \
if (!_evas_event_feed_allow(evas)) return __VA_ARGS__; } while (0)
static void
_evas_event_havemap_adjust_f(Evas_Object *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj, Eina_Vector2 *point, Eina_Bool mouse_grabbed)
{
@ -1390,6 +1401,7 @@ _canvas_event_feed_mouse_down_internal(Evas_Public_Data *e, Efl_Input_Pointer_Da
_efl_input_value_mask(EFL_INPUT_VALUE_BUTTON);
if (!e || !ev) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
@ -1509,13 +1521,13 @@ _post_up_handle(Evas_Public_Data *e, Efl_Input_Pointer *parent_ev,
Evas_Object_Pointer_Data *obj_pdata;
int event_id;
event_id = _evas_object_event_new();
/* Duplicating UP event */
evt = efl_input_dup(parent_ev);
ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
if (!ev) return 0;
event_id = _evas_object_event_new();
/* Actually we want an OUT */
ev->action = EFL_POINTER_ACTION_OUT;
@ -1633,6 +1645,7 @@ _canvas_event_feed_mouse_up_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data
_efl_input_value_mask(EFL_INPUT_VALUE_BUTTON);
if (!e || !ev) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
@ -1736,6 +1749,7 @@ _canvas_event_feed_mouse_updown(Eo *eo_e, int b, Evas_Button_Flags flags,
e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
if (!e) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
evt = efl_input_instance_get(EFL_INPUT_POINTER_CLASS, eo_e, (void **) &ev);
if (!ev) return;
@ -1803,6 +1817,7 @@ _canvas_event_feed_mouse_cancel_internal(Evas_Public_Data *e, Efl_Input_Pointer_
if (!e || !ev) return;
if (e->is_frozen) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
@ -1851,6 +1866,7 @@ evas_event_feed_mouse_cancel(Eo *eo_e, unsigned int timestamp, const void *data)
evt = efl_input_instance_get(EFL_INPUT_POINTER_CLASS, eo_e, (void **) &ev);
if (!ev) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
ev->timestamp = timestamp;
ev->data = (void *) data;
@ -1880,6 +1896,7 @@ _canvas_event_feed_mouse_wheel_internal(Eo *eo_e, Efl_Input_Pointer_Data *pe)
_efl_input_value_mask(EFL_INPUT_VALUE_WHEEL_DIRECTION);
if (e->is_frozen) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, pe->device);
if (!pdata) return;
@ -1981,6 +1998,7 @@ _canvas_event_feed_mouse_move_internal(Evas_Public_Data *e, Efl_Input_Pointer_Da
if (!e || !ev) return;
if (e->is_frozen) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
@ -2257,7 +2275,6 @@ nogrep:
// NOTE: was foreach + append without free (smelled bad)
newin = eina_list_merge(newin, ins);
EINA_LIST_FOREACH(lst, l, eo_obj)
{
obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
@ -2412,6 +2429,7 @@ _canvas_event_feed_mouse_in_internal(Evas *eo_e, Efl_Input_Pointer_Data *ev)
_efl_input_value_mask(EFL_INPUT_VALUE_TOOL);
if (!e || !ev) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
@ -2492,6 +2510,7 @@ _canvas_event_feed_mouse_out_internal(Evas *eo_e, Efl_Input_Pointer_Data *ev)
_efl_input_value_mask(EFL_INPUT_VALUE_TOOL);
if (!e || !ev) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
pdata->inside = 0;
@ -2608,6 +2627,7 @@ _canvas_event_feed_multi_down_internal(Evas_Public_Data *e, Efl_Input_Pointer_Da
_efl_input_value_mask(EFL_INPUT_VALUE_BUTTON);
if (!e || !ev) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
@ -2699,6 +2719,7 @@ _canvas_event_feed_multi_up_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data
_efl_input_value_mask(EFL_INPUT_VALUE_TOOL);
if (!e || !ev) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
@ -2776,6 +2797,7 @@ _canvas_event_feed_multi_internal(Evas *eo_e, Evas_Public_Data *e,
evt = efl_input_instance_get(EFL_INPUT_POINTER_CLASS, eo_e, (void **) &ev);
if (!e || !ev) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
if (EINA_DBL_EQ(fx, 0.0)) fx = x;
if (EINA_DBL_EQ(fy, 0.0)) fy = y;
@ -2874,6 +2896,7 @@ _canvas_event_feed_multi_move_internal(Evas_Public_Data *e, Efl_Input_Pointer_Da
_efl_input_value_mask(EFL_INPUT_VALUE_TOOL);
if (!e || !ev) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
@ -3069,6 +3092,7 @@ _canvas_event_feed_key_down_internal(Evas_Public_Data *e, Efl_Input_Key_Data *ev
if (!e || !ev) return;
if (e->is_frozen) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
e->last_timestamp = ev->timestamp;
_evas_walk(e);
@ -3155,6 +3179,7 @@ _canvas_event_feed_key_up_internal(Evas_Public_Data *e, Efl_Input_Key_Data *ev)
if (!e || !ev) return;
if (e->is_frozen) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
e->last_timestamp = ev->timestamp;
_evas_walk(e);
@ -3314,6 +3339,7 @@ evas_event_feed_hold(Eo *eo_e, int hold, unsigned int timestamp, const void *dat
Evas_Pointer_Data *pdata;
if (e->is_frozen) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
e->last_timestamp = timestamp;
event_id = _evas_object_event_new();
@ -3368,6 +3394,7 @@ _canvas_event_feed_axis_update_internal(Evas_Public_Data *e, Efl_Input_Pointer_D
if (!e || !ev) return;
if (e->is_frozen) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
pdata = _evas_pointer_data_by_device_get(e, ev->device);
if (!pdata) return;
@ -3413,6 +3440,9 @@ evas_event_feed_axis_update(Evas *eo_e, unsigned int timestamp, int device, int
double x = 0, y = 0;
int n;
if (!e) return;
EVAS_EVENT_FEED_SAFETY_CHECK(e);
evt = efl_input_instance_get(EFL_INPUT_POINTER_CLASS, eo_e, (void **) &ev);
if (!ev) return;

View File

@ -949,6 +949,7 @@ struct _Evas_Public_Data
Eina_Bool rendering : 1;
Eina_Bool render2 : 1;
Eina_Bool common_init : 1;
Eina_Bool running_post_events : 1;
};
struct _Evas_Layer