1 #include <Elementary.h>
3 /** @defgroup Elm_Gesture_Layer Gesture Layer */
6 #define ELM_MOUSE_DEVICE 0
7 /* ELM_GESTURE_NEGATIVE_ANGLE - magic number says we didn't compute this yet */
8 #define ELM_GESTURE_NEGATIVE_ANGLE (-1.0) /* Magic number */
9 #define ELM_GESTURE_MOMENTUM_DELAY 25
10 #define ELM_GESTURE_MOMENTUM_TIMEOUT 50
11 #define ELM_GESTURE_MULTI_TIMEOUT 50
12 #define ELM_GESTURE_MINIMUM_MOMENTUM 0.001
14 #define ELM_GESTURE_TAP_TIMEOUT 0.2
16 /* Some Trigo values */
17 #define RAD_90DEG M_PI_2
18 #define RAD_180DEG M_PI
19 #define RAD_270DEG (M_PI_2 * 3)
20 #define RAD_360DEG (M_PI * 2)
21 /* #define DEBUG_GESTURE_LAYER 1 */
23 #define RAD2DEG(x) ((x) * 57.295779513)
24 #define DEG2RAD(x) ((x) / 57.295779513)
27 _glayer_bufdup(void *buf, size_t size)
34 #define COPY_EVENT_INFO(EV) _glayer_bufdup(EV, sizeof(*EV))
37 #define SET_TEST_BIT(P) do { \
38 P->test = P->fn[ELM_GESTURE_STATE_START].cb || P->fn[ELM_GESTURE_STATE_MOVE].cb || P->fn[ELM_GESTURE_STATE_END].cb || P->fn[ELM_GESTURE_STATE_ABORT].cb; \
41 #define IS_TESTED(T) ((wd->gesture[T]) ? wd->gesture[T]->test : EINA_FALSE)
47 * Struct holds callback information.
49 * @ingroup Elm_Gesture_Layer
53 void *user_data; /**< Holds user data to CB (like sd) */
54 Elm_Gesture_Event_Cb cb;
61 * type for callback information
63 * @ingroup Elm_Gesture_Layer
65 typedef struct _Func_Data Func_Data;
70 * @struct _Gesture_Info
71 * Struct holds gesture info
73 * @ingroup Elm_Gesture_Layer
78 void *data; /**< Holds gesture intemidiate processing data */
79 Func_Data fn[ELM_GESTURE_STATE_ABORT + 1]; /**< Callback info for states */
80 Elm_Gesture_Type g_type; /**< gesture type */
81 Elm_Gesture_State state; /**< gesture state */
82 void *info; /**< Data for the state callback */
83 Eina_Bool test; /**< if true this gesture should be tested on input */
89 * @typedef Gesture_Info
90 * Type for _Gesture_Info
92 * @ingroup Elm_Gesture_Layer
94 typedef struct _Gesture_Info Gesture_Info;
99 * @struct _Event_History
100 * Struct holds event history.
101 * These events are repeated if no gesture found.
103 * @ingroup Elm_Gesture_Layer
105 struct _Event_History
109 Evas_Callback_Type event_type;
115 * @typedef Event_History
116 * Type for _Event_History
118 * @ingroup Elm_Gesture_Layer
120 typedef struct _Event_History Event_History;
125 * @struct _Pointer_Event
126 * Struct holds pointer-event info
127 * This is a generic pointer event structure
129 * @ingroup Elm_Gesture_Layer
131 struct _Pointer_Event
134 unsigned int timestamp;
136 Evas_Callback_Type event_type;
142 * @typedef Pointer_Event
143 * Type for generic pointer event structure
145 * @ingroup Elm_Gesture_Layer
147 typedef struct _Pointer_Event Pointer_Event;
149 /* All *Type structs hold result for the user in 'info' field
150 * The rest is gesture processing intermediate data.
151 * NOTE: info field must be FIRST in the struct.
152 * This is used when reporting ABORT in event_history_clear() */
155 Elm_Gesture_Taps_Info info;
158 unsigned int n_taps_needed;
162 typedef struct _Taps_Type Taps_Type;
164 struct _Long_Tap_Type
166 Elm_Gesture_Taps_Info info;
169 unsigned int max_touched;
170 Ecore_Timer *timeout; /* When this expires, long tap STARTed */
173 typedef struct _Long_Tap_Type Long_Tap_Type;
175 struct _Momentum_Type
176 { /* Fields used by _line_test() */
177 Elm_Gesture_Momentum_Info info;
178 Evas_Coord_Point line_st;
179 Evas_Coord_Point line_end;
180 unsigned int t_st_x; /* Time start on X */
181 unsigned int t_st_y; /* Time start on Y */
182 unsigned int t_end; /* Time end */
183 unsigned int t_up; /* Recent up event time */
186 typedef struct _Momentum_Type Momentum_Type;
190 Evas_Coord_Point line_st;
191 Evas_Coord_Point line_end;
192 Evas_Coord line_length;
193 unsigned int t_st; /* Time start */
194 unsigned int t_end; /* Time end */
196 double line_angle; /* Current angle of line */
198 typedef struct _Line_Data Line_Data;
201 { /* Fields used by _line_test() */
202 Elm_Gesture_Line_Info info;
203 Eina_List *list; /* List of Line_Data */
205 typedef struct _Line_Type Line_Type;
208 { /* Fields used by _zoom_test() */
209 Elm_Gesture_Zoom_Info info;
210 Pointer_Event zoom_st;
211 Pointer_Event zoom_mv;
212 Pointer_Event zoom_st1;
213 Pointer_Event zoom_mv1;
214 Evas_Event_Mouse_Wheel *zoom_wheel;
215 Evas_Coord zoom_base; /* Holds gap between fingers on zoom-start */
216 Evas_Coord zoom_distance_tolerance;
217 unsigned int m_st_tm; /* momentum start time */
218 unsigned int m_prev_tm; /* momentum prev time */
219 int dir; /* Direction: 1=zoom-in, (-1)=zoom-out */
220 double m_base; /* zoom value when momentum starts */
223 typedef struct _Zoom_Type Zoom_Type;
226 { /* Fields used by _rotation_test() */
227 Elm_Gesture_Rotate_Info info;
228 Pointer_Event rotate_st;
229 Pointer_Event rotate_mv;
230 Pointer_Event rotate_st1;
231 Pointer_Event rotate_mv1;
232 unsigned int prev_momentum_tm; /* timestamp of prev_momentum */
233 double prev_momentum; /* Snapshot of momentum 0.01 sec ago */
234 double accum_momentum;
235 double rotate_angular_tolerance;
238 typedef struct _Rotate_Type Rotate_Type;
242 Evas_Object *target; /* Target Widget */
243 Event_History *event_history_list;
246 Evas_Coord zoom_distance_tolerance;
247 Evas_Coord line_distance_tolerance;
248 double line_angular_tolerance;
249 double zoom_wheel_factor; /* mouse wheel zoom steps */
250 double zoom_finger_factor; /* used for zoom factor */
251 double rotate_angular_tolerance;
252 unsigned int flick_time_limit_ms;
253 double long_tap_start_timeout;
254 Eina_Bool glayer_continues_enable;
259 Gesture_Info *gesture[ELM_GESTURE_LAST];
260 Ecore_Timer *dbl_timeout; /* When this expires, dbl click/taps ABORTed */
261 Eina_List *pending; /* List of devices need to refeed *UP event */
262 Eina_List *touched; /* Information of touched devices */
264 Eina_Bool repeat_events : 1;
266 typedef struct _Widget_Data Widget_Data;
268 static const char *widtype = NULL;
269 static void _del_hook(Evas_Object *obj);
271 static Eina_Bool _event_history_clear(Evas_Object *obj);
272 static void _reset_states(Widget_Data *wd);
273 static void _key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
274 static void _key_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
275 static void _zoom_with_wheel_test(Evas_Object *obj, void *event_info, Evas_Callback_Type event_type, Elm_Gesture_Type g_type);
276 static void _mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
277 static void _mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info);
278 static void _mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
279 static void _mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
281 static void _multi_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
282 static void _multi_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
283 static void _multi_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
285 /* START - Functions to manage touched-device list */
288 * This function is used to find if device is touched
290 * @ingroup Elm_Gesture_Layer
293 compare_device(const void *data1, const void *data2)
294 { /* Compare the two device numbers */
295 return (((Pointer_Event *) data1)->device -((Pointer_Event *) data2)->device);
301 * Remove Pointer Event from touched device list
302 * @param list Pointer to touched device list.
303 * @param Pointer_Event Pointer to PE.
305 * @ingroup Elm_Gesture_Layer
308 _remove_touched_device(Eina_List *list, Pointer_Event *pe)
310 Eina_List *lst = NULL;
311 Pointer_Event *p = eina_list_search_unsorted(list, compare_device, pe);
314 lst = eina_list_remove(list, p);
325 * Recoed Pointer Event in touched device list
326 * Note: This fuction allocates memory for PE event
327 * This memory is released in _remove_touched_device()
328 * @param list Pointer to touched device list.
329 * @param Pointer_Event Pointer to PE.
331 * @ingroup Elm_Gesture_Layer
334 _add_touched_device(Eina_List *list, Pointer_Event *pe)
336 Pointer_Event *p = eina_list_search_unsorted(list, compare_device, pe);
338 { /* We like to track device touch-position, overwrite info */
339 memcpy(p, pe, sizeof(Pointer_Event));
343 if ((pe->event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
344 (pe->event_type == EVAS_CALLBACK_MULTI_DOWN))
345 { /* Add touched device on DOWN event only */
346 p = malloc(sizeof(Pointer_Event));
347 /* Freed in _remove_touched_device() */
348 memcpy(p, pe, sizeof(Pointer_Event));
349 return eina_list_append(list, p);
354 /* END - Functions to manage touched-device list */
360 * @param event_info pointer to event.
362 * @ingroup Elm_Gesture_Layer
364 static Evas_Event_Flags
365 _get_event_flag(void *event_info, Evas_Callback_Type event_type)
369 case EVAS_CALLBACK_MOUSE_IN:
370 return ((Evas_Event_Mouse_In *) event_info)->event_flags;
371 case EVAS_CALLBACK_MOUSE_OUT:
372 return ((Evas_Event_Mouse_Out *) event_info)->event_flags;
373 case EVAS_CALLBACK_MOUSE_DOWN:
374 return ((Evas_Event_Mouse_Down *) event_info)->event_flags;
375 case EVAS_CALLBACK_MOUSE_MOVE:
376 return ((Evas_Event_Mouse_Move *) event_info)->event_flags;
377 case EVAS_CALLBACK_MOUSE_UP:
378 return ((Evas_Event_Mouse_Up *) event_info)->event_flags;
379 case EVAS_CALLBACK_MOUSE_WHEEL:
380 return ((Evas_Event_Mouse_Wheel *) event_info)->event_flags;
381 case EVAS_CALLBACK_MULTI_DOWN:
382 return ((Evas_Event_Multi_Down *) event_info)->event_flags;
383 case EVAS_CALLBACK_MULTI_MOVE:
384 return ((Evas_Event_Multi_Move *) event_info)->event_flags;
385 case EVAS_CALLBACK_MULTI_UP:
386 return ((Evas_Event_Multi_Up *) event_info)->event_flags;
387 case EVAS_CALLBACK_KEY_DOWN:
388 return ((Evas_Event_Key_Down *) event_info)->event_flags;
389 case EVAS_CALLBACK_KEY_UP:
390 return ((Evas_Event_Key_Up *) event_info)->event_flags;
392 return EVAS_EVENT_FLAG_NONE;
399 * Sets event flag to value returned from user callback
400 * @param wd Widget Data
401 * @param event_info pointer to event.
402 * @param event_type what type was ev (mouse down, etc...)
403 * @param ev_flags event flags
405 * @ingroup Elm_Gesture_Layer
408 consume_event(Widget_Data *wd, void *event_info,
409 Evas_Callback_Type event_type, Evas_Event_Flags ev_flags)
410 { /* Mark EVAS_EVENT_FLAG_ON_HOLD on events that are used by gesture layer */
411 /* ev_flags != EVAS_EVENT_FLAG_NONE means target used event and g-layer */
412 /* should not refeed this event. */
414 return; /* This happens when restarting gestures */
416 if (!wd->repeat_events) ev_flags |= EVAS_EVENT_FLAG_ON_HOLD;
422 case EVAS_CALLBACK_MOUSE_DOWN:
423 ((Evas_Event_Mouse_Down *) event_info)->event_flags |= ev_flags;
425 case EVAS_CALLBACK_MOUSE_MOVE:
426 ((Evas_Event_Mouse_Move *) event_info)->event_flags |= ev_flags;
428 case EVAS_CALLBACK_MOUSE_UP:
429 ((Evas_Event_Mouse_Up *) event_info)->event_flags |= ev_flags;
431 case EVAS_CALLBACK_MOUSE_WHEEL:
432 ((Evas_Event_Mouse_Wheel *) event_info)->event_flags |= ev_flags;
434 case EVAS_CALLBACK_MULTI_DOWN:
435 ((Evas_Event_Multi_Down *) event_info)->event_flags |= ev_flags;
437 case EVAS_CALLBACK_MULTI_MOVE:
438 ((Evas_Event_Multi_Move *) event_info)->event_flags |= ev_flags;
440 case EVAS_CALLBACK_MULTI_UP:
441 ((Evas_Event_Multi_Up *) event_info)->event_flags |= ev_flags;
443 case EVAS_CALLBACK_KEY_DOWN:
444 ((Evas_Event_Key_Down *) event_info)->event_flags |= ev_flags;
446 case EVAS_CALLBACK_KEY_UP:
447 ((Evas_Event_Key_Up *) event_info)->event_flags |= ev_flags;
458 * Report current state of a gesture by calling user callback.
459 * @param gesture what gesture state we report.
460 * @param info inforamtion for user callback
462 * @ingroup Elm_Gesture_Layer
464 static Evas_Event_Flags
465 _report_state(Gesture_Info *gesture, void *info)
466 { /* We report current state (START, MOVE, END, ABORT), once */
467 #if defined(DEBUG_GESTURE_LAYER)
468 printf("%s reporting gesture=<%d> state=<%d>\n" , __func__, gesture->g_type,
471 if ((gesture->state != ELM_GESTURE_STATE_UNDEFINED) &&
472 (gesture->fn[gesture->state].cb))
473 { /* Fill state-info struct and send ptr to user callback */
474 return gesture->fn[gesture->state].cb(
475 gesture->fn[gesture->state].user_data, info);
478 return EVAS_EVENT_FLAG_NONE;
484 * Update state for a given gesture.
485 * We may update gesture state to:
486 * UNDEFINED - current input did not start gesure yet.
487 * START - gesture started according to input.
488 * MOVE - gusture in progress.
489 * END - gesture completed according to input.
490 * ABORT - input does not matches gesure.
491 * note that we may move from UNDEFINED to ABORT
492 * because we may detect that gesture will not START
493 * with a given input.
495 * @param g given gesture to change state.
496 * @param s gesure new state.
497 * @param info buffer to be sent to user callback on report_state.
498 * @param force makes report_state to report the new-state even
499 * if its same as current state. Works for MOVE - gesture in progress.
501 * @ingroup Elm_Gesture_Layer
503 static Evas_Event_Flags
504 _set_state(Gesture_Info *g, Elm_Gesture_State s,
505 void *info, Eina_Bool force)
507 Elm_Gesture_State old_state;
508 if ((g->state == s) && (!force))
509 return EVAS_EVENT_FLAG_NONE;
511 old_state = g->state;
514 g->info = info; /* Information for user callback */
515 if ((g->state == ELM_GESTURE_STATE_ABORT) ||
516 (g->state == ELM_GESTURE_STATE_END))
517 g->test = EINA_FALSE;
519 if ((g->state != ELM_GESTURE_STATE_UNDEFINED) &&
520 (!((old_state == ELM_GESTURE_STATE_UNDEFINED) &&
521 (s == ELM_GESTURE_STATE_ABORT))))
522 return _report_state(g, g->info);
524 return EVAS_EVENT_FLAG_NONE;
530 * This resets all gesture states and sets test-bit.
531 * this is used for restarting gestures to listen to input.
532 * happens after we complete a gesture or no gesture was detected.
533 * @param wd Widget data of the gesture-layer object.
535 * @ingroup Elm_Gesture_Layer
538 _reset_states(Widget_Data *wd)
542 for (i = ELM_GESTURE_FIRST; i < ELM_GESTURE_LAST; i++)
547 _set_state(p, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE);
556 * if gesture was NOT detected AND we only have gestures in ABORT state
557 * we clear history immediately to be ready for input.
559 * @param obj The gesture-layer object.
560 * @return TRUE on event history_clear
562 * @ingroup Elm_Gesture_Layer
565 _clear_if_finished(Evas_Object *obj)
567 Widget_Data *wd = elm_widget_data_get(obj);
568 if (!wd) return EINA_FALSE;
571 /* Clear history if all we have aborted gestures */
572 Eina_Bool reset_s = EINA_TRUE, all_undefined = EINA_TRUE;
573 for (i = ELM_GESTURE_FIRST; i < ELM_GESTURE_LAST; i++)
574 { /* If no gesture started and all we have aborted gestures, reset all */
575 Gesture_Info *p = wd->gesture[i];
576 if ((p) && (p->state != ELM_GESTURE_STATE_UNDEFINED))
578 if ((p->state == ELM_GESTURE_STATE_START) ||
579 (p->state == ELM_GESTURE_STATE_MOVE))
580 reset_s = EINA_FALSE;
582 all_undefined = EINA_FALSE;
586 if (reset_s && (!all_undefined))
587 return _event_history_clear(obj);
593 _inside(Evas_Coord xx1, Evas_Coord yy1, Evas_Coord xx2, Evas_Coord yy2)
595 int w = _elm_config->finger_size >> 1; /* Finger size devided by 2 */
611 /* All *test_reset() funcs are called to clear
612 * gesture intermediate data.
613 * This happens when we need to reset our tests.
614 * for example when gesture is detected or all ABORTed. */
616 _tap_gestures_test_reset(Gesture_Info *gesture)
621 Widget_Data *wd = elm_widget_data_get(gesture->obj);
627 ecore_timer_del(wd->dbl_timeout);
628 wd->dbl_timeout = NULL;
634 EINA_LIST_FREE(((Taps_Type *) gesture->data)->l, data)
635 EINA_LIST_FREE(data, pe)
638 memset(gesture->data, 0, sizeof(Taps_Type));
641 /* All *test_reset() funcs are called to clear
642 * gesture intermediate data.
643 * This happens when we need to reset our tests.
644 * for example when gesture is detected or all ABORTed. */
646 _n_long_tap_test_reset(Gesture_Info *gesture)
654 Long_Tap_Type *st = gesture->data;
657 EINA_LIST_FOREACH(st->touched, l, p)
660 eina_list_free(st->touched);
663 ecore_timer_del(st->timeout);
666 memset(gesture->data, 0, sizeof(Long_Tap_Type));
670 _momentum_test_reset(Gesture_Info *gesture)
678 memset(gesture->data, 0, sizeof(Momentum_Type));
682 _line_data_reset(Line_Data *st)
687 memset(st, 0, sizeof(Line_Data));
688 st->line_angle = ELM_GESTURE_NEGATIVE_ANGLE;
692 _line_test_reset(Gesture_Info *gesture)
700 Line_Type *st = gesture->data;
701 Eina_List *list = st->list;
704 EINA_LIST_FOREACH(list, l, t_line)
707 eina_list_free(list);
712 _zoom_test_reset(Gesture_Info *gesture)
720 Widget_Data *wd = elm_widget_data_get(gesture->obj);
721 Zoom_Type *st = gesture->data;
722 Evas_Modifier_Mask mask = evas_key_modifier_mask_get(
723 evas_object_evas_get(wd->target), "Control");
724 evas_object_key_ungrab(wd->target, "Control_L", mask, 0);
725 evas_object_key_ungrab(wd->target, "Control_R", mask, 0);
727 memset(st, 0, sizeof(Zoom_Type));
728 st->zoom_distance_tolerance = wd->zoom_distance_tolerance;
733 _rotate_test_reset(Gesture_Info *gesture)
741 Widget_Data *wd = elm_widget_data_get(gesture->obj);
742 Rotate_Type *st = gesture->data;
744 memset(st, 0, sizeof(Rotate_Type));
745 st->info.base_angle = ELM_GESTURE_NEGATIVE_ANGLE;
746 st->rotate_angular_tolerance = wd->rotate_angular_tolerance;
753 * We register callbacks when gesture layer is attached to an object
754 * or when its enabled after disable.
756 * @param obj The gesture-layer object.
758 * @ingroup Elm_Gesture_Layer
761 _register_callbacks(Evas_Object *obj)
763 Widget_Data *wd = elm_widget_data_get(obj);
768 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_DOWN,
770 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_MOVE,
772 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_UP,
775 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_WHEEL,
778 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_DOWN,
780 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_MOVE,
782 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_UP,
785 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_KEY_DOWN,
787 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_KEY_UP,
795 * We unregister callbacks when gesture layer is disabled.
797 * @param obj The gesture-layer object.
799 * @ingroup Elm_Gesture_Layer
802 _unregister_callbacks(Evas_Object *obj)
804 Widget_Data *wd = elm_widget_data_get(obj);
809 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_DOWN,
811 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_MOVE,
813 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_UP,
816 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_WHEEL,
819 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_DOWN,
822 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_MOVE,
825 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_UP,
828 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_KEY_DOWN,
830 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_KEY_UP,
835 /* START - Event history list handling functions */
838 * This function is used to find if device number
839 * is found in a list of devices.
840 * The list contains devices for refeeding *UP event
842 * @ingroup Elm_Gesture_Layer
845 device_in_pending_list(const void *data1, const void *data2)
846 { /* Compare the two device numbers */
847 return (((intptr_t) data1) - ((intptr_t) data2));
853 * This functions adds device to refeed-pending device list
854 * @ingroup Elm_Gesture_Layer
857 _add_device_pending(Eina_List *list, void *event, Evas_Callback_Type event_type)
859 int device = ELM_MOUSE_DEVICE;
862 case EVAS_CALLBACK_MOUSE_DOWN:
864 case EVAS_CALLBACK_MULTI_DOWN:
865 device = ((Evas_Event_Multi_Down *) event)->device;
871 if (!eina_list_search_unsorted_list(list, device_in_pending_list,
872 (void *)(intptr_t)device))
874 return eina_list_append(list, (void *)(intptr_t)device);
883 * This functions returns pending-device node
884 * @ingroup Elm_Gesture_Layer
887 _device_is_pending(Eina_List *list, void *event, Evas_Callback_Type event_type)
889 int device = ELM_MOUSE_DEVICE;
892 case EVAS_CALLBACK_MOUSE_UP:
894 case EVAS_CALLBACK_MULTI_UP:
895 device = ((Evas_Event_Multi_Up *) event)->device;
901 return eina_list_search_unsorted_list(list, device_in_pending_list,
902 (void *)(intptr_t)device);
908 * This function reports ABORT to all none-detected gestures
909 * Then resets test bits for all desired gesures
910 * and clears input-events history.
911 * note: if no gesture was detected, events from history list
912 * are streamed to the widget because it's unused by layer.
913 * user may cancel refeed of events by setting repeat events.
915 * @param obj The gesture-layer object.
917 * @ingroup Elm_Gesture_Layer
920 _event_history_clear(Evas_Object *obj)
922 Widget_Data *wd = elm_widget_data_get(obj);
923 if (!wd) return EINA_FALSE;
927 Evas *e = evas_object_evas_get(obj);
928 Eina_Bool gesture_found = EINA_FALSE;
929 for (i = ELM_GESTURE_FIRST; i < ELM_GESTURE_LAST; i++)
934 if (p->state == ELM_GESTURE_STATE_END)
935 gesture_found = EINA_TRUE;
937 { /* Report ABORT to all gestures that still not finished */
938 _set_state(p, ELM_GESTURE_STATE_ABORT, wd->gesture[i]->info,
944 _reset_states(wd); /* we are ready to start testing for gestures again */
946 /* Clear all gestures intermediate data */
947 if (IS_TESTED(ELM_GESTURE_N_LONG_TAPS))
948 { /* We do not clear a long-tap gesture if fingers still on surface */
949 /* and gesture timer still pending to test gesture state */
950 Long_Tap_Type *st = wd->gesture[ELM_GESTURE_N_LONG_TAPS]->data;
951 if ((st) && /* st not allocated if clear occurs before 1st input */
952 ((!eina_list_count(st->touched)) || (!st->timeout)))
953 _n_long_tap_test_reset(wd->gesture[ELM_GESTURE_N_LONG_TAPS]);
958 ecore_timer_del(wd->dbl_timeout);
959 wd->dbl_timeout = NULL;
962 _tap_gestures_test_reset(wd->gesture[ELM_GESTURE_N_TAPS]);
963 _tap_gestures_test_reset(wd->gesture[ELM_GESTURE_N_DOUBLE_TAPS]);
964 _tap_gestures_test_reset(wd->gesture[ELM_GESTURE_N_TRIPLE_TAPS]);
965 _momentum_test_reset(wd->gesture[ELM_GESTURE_MOMENTUM]);
966 _line_test_reset(wd->gesture[ELM_GESTURE_N_LINES]);
967 _line_test_reset(wd->gesture[ELM_GESTURE_N_FLICKS]);
968 _zoom_test_reset(wd->gesture[ELM_GESTURE_ZOOM]);
969 _rotate_test_reset(wd->gesture[ELM_GESTURE_ROTATE]);
971 /* Disable gesture layer so refeeded events won't be consumed by it */
972 _unregister_callbacks(obj);
973 while (wd->event_history_list)
976 t = wd->event_history_list;
977 Eina_List *pending = _device_is_pending(wd->pending,
978 wd->event_history_list->event,
979 wd->event_history_list->event_type);
981 /* Refeed events if no gesture matched input */
982 if (pending || ((!gesture_found) && (!wd->repeat_events)))
984 evas_event_refeed_event(e, wd->event_history_list->event,
985 wd->event_history_list->event_type);
989 wd->pending = eina_list_remove_list(wd->pending, pending);
993 wd->pending = _add_device_pending(wd->pending,
994 wd->event_history_list->event,
995 wd->event_history_list->event_type);
999 free(wd->event_history_list->event);
1000 wd->event_history_list = (Event_History *) eina_inlist_remove(
1001 EINA_INLIST_GET(wd->event_history_list),
1002 EINA_INLIST_GET(wd->event_history_list));
1005 _register_callbacks(obj);
1012 * This function copies input events.
1013 * We copy event info before adding it to history.
1014 * The memory is freed when we clear history.
1016 * @param event the event to copy
1017 * @param event_type event type to copy
1019 * @ingroup Elm_Gesture_Layer
1022 _copy_event_info(void *event, Evas_Callback_Type event_type)
1026 case EVAS_CALLBACK_MOUSE_DOWN:
1027 return COPY_EVENT_INFO((Evas_Event_Mouse_Down *) event);
1029 case EVAS_CALLBACK_MOUSE_MOVE:
1030 return COPY_EVENT_INFO((Evas_Event_Mouse_Move *) event);
1032 case EVAS_CALLBACK_MOUSE_UP:
1033 return COPY_EVENT_INFO((Evas_Event_Mouse_Up *) event);
1035 case EVAS_CALLBACK_MOUSE_WHEEL:
1036 return COPY_EVENT_INFO((Evas_Event_Mouse_Wheel *) event);
1038 case EVAS_CALLBACK_MULTI_DOWN:
1039 return COPY_EVENT_INFO((Evas_Event_Multi_Down *) event);
1041 case EVAS_CALLBACK_MULTI_MOVE:
1042 return COPY_EVENT_INFO((Evas_Event_Multi_Move *) event);
1044 case EVAS_CALLBACK_MULTI_UP:
1045 return COPY_EVENT_INFO((Evas_Event_Multi_Up *) event);
1047 case EVAS_CALLBACK_KEY_DOWN:
1048 return COPY_EVENT_INFO((Evas_Event_Key_Down *) event);
1050 case EVAS_CALLBACK_KEY_UP:
1051 return COPY_EVENT_INFO((Evas_Event_Key_Up *) event);
1059 _event_history_add(Evas_Object *obj, void *event, Evas_Callback_Type event_type)
1061 Widget_Data *wd = elm_widget_data_get(obj);
1063 if (!wd) return EINA_FALSE;
1065 ev = malloc(sizeof(Event_History));
1066 ev->event = _copy_event_info(event, event_type); /* Freed on event_history_clear */
1067 ev->event_type = event_type;
1068 wd->event_history_list = (Event_History *) eina_inlist_append(
1069 EINA_INLIST_GET(wd->event_history_list), EINA_INLIST_GET(ev));
1073 /* END - Event history list handling functions */
1076 _del_hook(Evas_Object *obj)
1078 Widget_Data *wd = elm_widget_data_get(obj);
1081 _event_history_clear(obj);
1082 eina_list_free(wd->pending);
1084 Pointer_Event *data;
1085 EINA_LIST_FREE(wd->touched, data)
1088 if (!elm_widget_disabled_get(obj))
1089 _unregister_callbacks(obj);
1091 /* Free all gestures internal data structures */
1093 for (i = 0; i < ELM_GESTURE_LAST; i++)
1096 if (wd->gesture[i]->data)
1097 free(wd->gesture[i]->data);
1099 free(wd->gesture[i]);
1106 compare_match_fingers(const void *data1, const void *data2)
1107 { /* Compare coords of first item in list to cur coords */
1108 const Pointer_Event *pe1 = eina_list_data_get(data1);
1109 const Pointer_Event *pe2 = data2;
1111 if (_inside(pe1->x, pe1->y, pe2->x, pe2->y))
1113 else if (pe1->x < pe2->x)
1117 if (pe1->x == pe2->x)
1118 return pe1->y - pe2->y;
1125 compare_pe_device(const void *data1, const void *data2)
1126 { /* Compare device of first item in list to our pe device */
1127 const Pointer_Event *pe1 = eina_list_data_get(data1);
1128 const Pointer_Event *pe2 = data2;
1130 /* Only match if last was a down event */
1131 if ((pe1->event_type != EVAS_CALLBACK_MULTI_DOWN) &&
1132 (pe1->event_type != EVAS_CALLBACK_MOUSE_DOWN))
1135 if (pe1->device == pe2->device)
1137 else if (pe1->device < pe2->device)
1144 _record_pointer_event(Taps_Type *st, Eina_List *pe_list, Pointer_Event *pe,
1145 Widget_Data *wd, void *event_info, Evas_Callback_Type event_type)
1146 { /* Keep copy of pe and record it in list */
1147 Pointer_Event *p = malloc(sizeof(Pointer_Event));
1148 memcpy(p, pe, sizeof(Pointer_Event));
1149 consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_NONE);
1155 /* This will also update middle-point to report to user later */
1156 st->info.x = st->sum_x / st->n_taps;
1157 st->info.y = st->sum_y / st->n_taps;
1158 st->info.timestamp = pe->timestamp;
1162 pe_list = eina_list_append(pe_list, p);
1163 st->l = eina_list_append(st->l, pe_list);
1166 pe_list = eina_list_append(pe_list, p);
1174 * This function checks if the tap gesture is done.
1176 * @param data gesture info pointer
1177 * @return EINA_TRUE if it is done.
1179 * @ingroup Elm_Gesture_Layer
1182 _tap_gesture_check_finish(Gesture_Info *gesture)
1184 /* Here we check if taps-gesture was completed successfuly */
1185 /* Count how many taps were recieved on each device then */
1186 /* determine if it matches n_taps_needed defined on START */
1187 Taps_Type *st = gesture->data;
1190 if (!st || !st->l) return EINA_FALSE;
1191 EINA_LIST_FOREACH(st->l, l, pe_list)
1193 if (eina_list_count(pe_list) != st->n_taps_needed)
1194 { /* No match taps number on device, ABORT */
1205 * This function sets state a tap-gesture to END or ABORT
1207 * @param data gesture info pointer
1209 * @ingroup Elm_Gesture_Layer
1212 _tap_gesture_finish(void *data)
1213 { /* This function will test each tap gesture when timer expires */
1214 Elm_Gesture_State s = ELM_GESTURE_STATE_ABORT;
1215 Gesture_Info *gesture = data;
1216 Taps_Type *st = gesture->data;
1218 if (_tap_gesture_check_finish(gesture))
1220 s = ELM_GESTURE_STATE_END;
1223 st->info.n = eina_list_count(st->l);
1224 _set_state(gesture, s, gesture->info, EINA_FALSE);
1225 _tap_gestures_test_reset(gesture);
1231 * when this timer expires we finish tap gestures.
1233 * @param data The gesture-layer object.
1234 * @return cancles callback for this timer.
1236 * @ingroup Elm_Gesture_Layer
1239 _multi_tap_timeout(void *data)
1241 Widget_Data *wd = elm_widget_data_get(data);
1242 if (!wd) return EINA_FALSE;
1244 if (IS_TESTED(ELM_GESTURE_N_TAPS))
1245 _tap_gesture_finish(wd->gesture[ELM_GESTURE_N_TAPS]);
1247 if (IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS))
1248 _tap_gesture_finish(wd->gesture[ELM_GESTURE_N_DOUBLE_TAPS]);
1250 if (IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS))
1251 _tap_gesture_finish(wd->gesture[ELM_GESTURE_N_TRIPLE_TAPS]);
1253 _clear_if_finished(data);
1254 wd->dbl_timeout = NULL;
1255 return ECORE_CALLBACK_CANCEL;
1261 * when this timer expires we START long tap gesture
1263 * @param data The gesture-layer object.
1264 * @return cancles callback for this timer.
1266 * @ingroup Elm_Gesture_Layer
1269 _long_tap_timeout(void *data)
1271 Gesture_Info *gesture = data;
1272 Long_Tap_Type *st = gesture->data;
1275 _set_state(gesture, ELM_GESTURE_STATE_START,
1276 gesture->data, EINA_FALSE);
1278 return ECORE_CALLBACK_CANCEL;
1285 * This function checks the state of a tap gesture.
1287 * @param wd Gesture Layer Widget Data.
1288 * @param pe The recent input event as stored in pe struct.
1289 * @param event_info Original input event pointer.
1290 * @param event_type Type of original input event.
1291 * @param gesture what gesture is tested
1292 * @param how many taps for this gesture (1, 2 or 3)
1294 * @ingroup Elm_Gesture_Layer
1297 _tap_gesture_test(Widget_Data *wd, Pointer_Event *pe,
1298 void *event_info, Evas_Callback_Type event_type,
1299 Gesture_Info *gesture, int taps)
1300 { /* Here we fill Tap struct */
1304 Taps_Type *st = gesture->data;
1306 { /* Allocated once on first time */
1307 st = calloc(1, sizeof(Taps_Type));
1309 _tap_gestures_test_reset(gesture);
1312 Eina_List *pe_list = NULL;
1313 Pointer_Event *pe_down = NULL;
1314 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1315 switch (pe->event_type)
1317 case EVAS_CALLBACK_MULTI_DOWN:
1318 case EVAS_CALLBACK_MOUSE_DOWN:
1319 /* Check if got tap on same cord was tapped before */
1320 pe_list = eina_list_search_unsorted(st->l, compare_match_fingers, pe);
1323 eina_list_search_unsorted(st->l, compare_pe_device, pe))
1324 { /* This device was touched in other cord before completion */
1325 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1326 &st->info, EINA_FALSE);
1327 consume_event(wd, event_info, event_type, ev_flag);
1332 pe_list = _record_pointer_event(st, pe_list, pe, wd, event_info, event_type);
1333 if (!wd->dbl_timeout)
1335 wd->dbl_timeout = ecore_timer_add(ELM_GESTURE_TAP_TIMEOUT,
1336 _multi_tap_timeout, gesture->obj);
1340 ecore_timer_reset(wd->dbl_timeout);
1343 if ((pe->device == 0) && (eina_list_count(pe_list) == 1))
1344 { /* This is the first mouse down we got */
1345 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
1346 &st->info, EINA_FALSE);
1347 consume_event(wd, event_info, event_type, ev_flag);
1349 st->n_taps_needed = taps * 2; /* count DOWN and UP */
1353 else if (eina_list_count(pe_list) > st->n_taps_needed)
1355 /* If we arleady got too many touches for this gesture. */
1356 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1357 &st->info, EINA_FALSE);
1362 case EVAS_CALLBACK_MULTI_UP:
1363 case EVAS_CALLBACK_MOUSE_UP:
1364 pe_list = eina_list_search_unsorted(st->l, compare_pe_device, pe);
1368 pe_list = _record_pointer_event(st, pe_list, pe, wd, event_info, event_type);
1370 if (((gesture->g_type == ELM_GESTURE_N_TAPS) &&
1371 !IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS) &&
1372 !IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS)) ||
1373 ((gesture->g_type == ELM_GESTURE_N_DOUBLE_TAPS) &&
1374 !IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS)))
1376 if (_tap_gesture_check_finish(gesture))
1378 _tap_gesture_finish(gesture);
1385 case EVAS_CALLBACK_MULTI_MOVE:
1386 case EVAS_CALLBACK_MOUSE_MOVE:
1387 /* Get first event in first list, this has to be a Mouse Down event */
1388 /* and verify that user didn't move out of this area before next tap */
1389 pe_list = eina_list_search_unsorted(st->l, compare_pe_device, pe);
1392 pe_down = eina_list_data_get(pe_list);
1393 if (!_inside(pe_down->x, pe_down->y, pe->x, pe->y))
1395 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1396 &st->info, EINA_FALSE);
1397 consume_event(wd, event_info, event_type, ev_flag);
1410 * This function computes center-point for long-tap gesture
1412 * @param st Long Tap gesture info pointer
1413 * @param pe The recent input event as stored in pe struct.
1415 * @ingroup Elm_Gesture_Layer
1418 _compute_taps_center(Long_Tap_Type *st,
1419 Evas_Coord *x_out, Evas_Coord *y_out, Pointer_Event *pe)
1421 if (!eina_list_count(st->touched))
1426 Evas_Coord x = 0, y = 0;
1427 EINA_LIST_FOREACH(st->touched, l, p)
1428 { /* Accumulate all then take avarage */
1429 if (p->device == pe->device)
1430 { /* This will take care of values coming from MOVE event */
1441 *x_out = x / eina_list_count(st->touched);
1442 *y_out = y / eina_list_count(st->touched);
1448 * This function checks N long-tap gesture.
1450 * @param obj The gesture-layer object.
1451 * @param pe The recent input event as stored in pe struct.
1452 * @param event_info Original input event pointer.
1453 * @param event_type Type of original input event.
1454 * @param g_type what Gesture we are testing.
1455 * @param taps How many click/taps we test for.
1457 * @ingroup Elm_Gesture_Layer
1460 _n_long_tap_test(Evas_Object *obj, Pointer_Event *pe,
1461 void *event_info, Evas_Callback_Type event_type,
1462 Elm_Gesture_Type g_type)
1463 { /* Here we fill Recent_Taps struct and fire-up click/tap timers */
1464 Widget_Data *wd = elm_widget_data_get(obj);
1467 if (!pe) /* this happens when unhandled event arrived */
1468 return; /* see _make_pointer_event function */
1469 Gesture_Info *gesture = wd->gesture[g_type];
1470 if (!gesture) return;
1472 Long_Tap_Type *st = gesture->data;
1474 { /* Allocated once on first time */
1475 st = calloc(1, sizeof(Long_Tap_Type));
1477 _n_long_tap_test_reset(gesture);
1480 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1481 switch (pe->event_type)
1483 case EVAS_CALLBACK_MULTI_DOWN:
1484 case EVAS_CALLBACK_MOUSE_DOWN:
1485 st->touched = _add_touched_device(st->touched, pe);
1486 st->info.n = eina_list_count(st->touched);
1487 if (st->info.n > st->max_touched)
1488 st->max_touched = st->info.n;
1490 { /* User removed finger from touch, then put back - ABORT */
1491 if ((gesture->state == ELM_GESTURE_STATE_START) ||
1492 (gesture->state == ELM_GESTURE_STATE_MOVE))
1494 ev_flag =_set_state(gesture, ELM_GESTURE_STATE_ABORT,
1495 &st->info, EINA_FALSE);
1496 consume_event(wd, event_info, event_type, ev_flag);
1500 if ((pe->device == 0) && (eina_list_count(st->touched) == 1))
1501 { /* This is the first mouse down we got */
1502 st->info.timestamp = pe->timestamp;
1504 /* To test long tap */
1505 /* When this timer expires, gesture STARTED */
1507 st->timeout = ecore_timer_add(wd->long_tap_start_timeout,
1508 _long_tap_timeout, gesture);
1511 consume_event(wd, event_info, event_type, ev_flag);
1512 _compute_taps_center(st, &st->info.x, &st->info.y, pe);
1513 st->center_x = st->info.x;
1514 st->center_y = st->info.y;
1517 case EVAS_CALLBACK_MULTI_UP:
1518 case EVAS_CALLBACK_MOUSE_UP:
1519 st->touched = _remove_touched_device(st->touched, pe);
1520 _compute_taps_center(st, &st->center_x, &st->center_y, pe);
1522 ((gesture->state == ELM_GESTURE_STATE_START) ||
1523 (gesture->state == ELM_GESTURE_STATE_MOVE)))
1524 { /* Report END only for gesture that STARTed */
1525 if (eina_list_count(st->touched) == 0)
1526 { /* Report END only at last release event */
1527 ev_flag =_set_state(gesture, ELM_GESTURE_STATE_END,
1528 &st->info, EINA_FALSE);
1529 consume_event(wd, event_info, event_type, ev_flag);
1533 { /* Stop test, user lifts finger before long-start */
1534 if (st->timeout) ecore_timer_del(st->timeout);
1536 ev_flag =_set_state(gesture, ELM_GESTURE_STATE_ABORT,
1537 &st->info, EINA_FALSE);
1538 consume_event(wd, event_info, event_type, ev_flag);
1543 case EVAS_CALLBACK_MULTI_MOVE:
1544 case EVAS_CALLBACK_MOUSE_MOVE:
1546 ((gesture->state == ELM_GESTURE_STATE_START) ||
1547 (gesture->state == ELM_GESTURE_STATE_MOVE)))
1548 { /* Report MOVE only if STARTED */
1551 Elm_Gesture_State state_to_report = ELM_GESTURE_STATE_MOVE;
1553 _compute_taps_center(st, &x, &y, pe);
1554 /* ABORT if user moved fingers out of tap area */
1555 #if defined(DEBUG_GESTURE_LAYER)
1556 printf("%s x,y=(%d,%d) st->info.x,st->info.y=(%d,%d)\n",__func__,x,y,st->info.x,st->info.y);
1558 if (!_inside(x, y, st->center_x, st->center_y))
1559 state_to_report = ELM_GESTURE_STATE_ABORT;
1561 /* Report MOVE if gesture started */
1562 ev_flag = _set_state(gesture, state_to_report,
1563 &st->info, EINA_TRUE);
1564 consume_event(wd, event_info, event_type, ev_flag);
1576 * This function computes momentum for MOMENTUM, LINE and FLICK gestures
1577 * This momentum value will be sent to widget when gesture is completed.
1579 * @param momentum pointer to buffer where we record momentum value.
1580 * @param x1 x coord where user started gesture.
1581 * @param y1 y coord where user started gesture.
1582 * @param x2 x coord where user completed gesture.
1583 * @param y2 y coord where user completed gesture.
1584 * @param t1x timestamp for X, when user started gesture.
1585 * @param t1y timestamp for Y, when user started gesture.
1586 * @param t2 timestamp when user completed gesture.
1588 * @ingroup Elm_Gesture_Layer
1591 _set_momentum(Elm_Gesture_Momentum_Info *momentum,
1592 Evas_Coord xx1, Evas_Coord yy1,
1593 Evas_Coord xx2, Evas_Coord yy2,
1594 unsigned int t1x, unsigned int t1y, unsigned int t2)
1596 Evas_Coord velx = 0, vely = 0, vel;
1597 Evas_Coord dx = xx2 - xx1;
1598 Evas_Coord dy = yy2 - yy1;
1602 velx = (dx * 1000) / dtx;
1605 vely = (dy * 1000) / dty;
1607 vel = sqrt((velx * velx) + (vely * vely));
1609 if ((_elm_config->thumbscroll_friction > 0.0) &&
1610 (vel > _elm_config->thumbscroll_momentum_threshold))
1611 { /* report momentum */
1612 momentum->mx = velx;
1613 momentum->my = vely;
1625 * This function is used for computing rotation angle (DEG).
1627 * @param x1 first finger x location.
1628 * @param y1 first finger y location.
1629 * @param x2 second finger x location.
1630 * @param y2 second finger y location.
1632 * @return angle of the line between (x1,y1), (x2,y2) in Deg.
1633 * Angles now are given in DEG, not RAD.
1634 * ZERO angle at 12-oclock, growing clockwise.
1636 * @ingroup Elm_Gesture_Layer
1639 get_angle(Evas_Coord xx1, Evas_Coord yy1, Evas_Coord xx2, Evas_Coord yy2)
1641 double a, xx, yy, rt = (-1);
1642 xx = fabs(xx2 - xx1);
1643 yy = fabs(yy2 - yy1);
1645 if (((int)xx) && ((int)yy))
1647 rt = a = RAD2DEG(atan(yy / xx));
1650 if (yy1 < yy2) rt = 360 - a;
1655 if (yy1 < yy2) rt = 180 + a;
1661 { /* Do this only if rt is not set */
1663 { /* Horizontal line */
1664 if (xx2 < xx1) rt = 180;
1668 { /* Vertical line */
1669 if (yy2 < yy1) rt = 90;
1674 /* Now we want to change from:
1676 * original circle 180 0 We want: 270 90
1680 if (rt >= 360) rt -= 360;
1688 * This function is used for computing the magnitude and direction
1689 * of vector between two points.
1691 * @param x1 first finger x location.
1692 * @param y1 first finger y location.
1693 * @param x2 second finger x location.
1694 * @param y2 second finger y location.
1695 * @param l length computed (output)
1696 * @param a angle computed (output)
1698 * @ingroup Elm_Gesture_Layer
1701 get_vector(Evas_Coord xx1, Evas_Coord yy1, Evas_Coord xx2, Evas_Coord yy2,
1702 Evas_Coord *l, double *a)
1707 *l = (Evas_Coord) sqrt((xx * xx) + (yy * yy));
1708 *a = get_angle(xx1, yy1, xx2, yy2);
1712 _get_direction(Evas_Coord xx1, Evas_Coord xx2)
1714 if (xx2 < xx1) return -1;
1715 if (xx2 > xx1) return 1;
1721 * This function tests momentum gesture.
1722 * @param obj The gesture-layer object.
1723 * @param pe The recent input event as stored in pe struct.
1724 * @param event_info recent input event.
1725 * @param event_type recent event type.
1726 * @param g_type what Gesture we are testing.
1728 * @ingroup Elm_Gesture_Layer
1731 _momentum_test(Evas_Object *obj, Pointer_Event *pe,
1732 void *event_info, Evas_Callback_Type event_type,
1733 Elm_Gesture_Type g_type)
1735 Widget_Data *wd = elm_widget_data_get(obj);
1737 Gesture_Info *gesture = wd->gesture[g_type];
1738 if (!gesture ) return;
1740 /* When continues enable = TRUE a gesture may START on MOVE event */
1741 /* We don't allow this to happen with the if-statement below. */
1742 /* When continues enable = FALSE a gesture may START on DOWN only */
1743 /* Therefor it would NOT start on MOVE event. */
1744 /* NOTE that touched list is updated AFTER this function returns */
1745 /* so (count == 0) when we get here on first touch on surface. */
1746 if ((wd->glayer_continues_enable) && (!eina_list_count(wd->touched)))
1747 return; /* Got move on mouse-over move */
1749 Momentum_Type *st = gesture->data;
1750 Elm_Gesture_State state_to_report = ELM_GESTURE_STATE_MOVE;
1752 { /* Allocated once on first time */
1753 st = calloc(1, sizeof(Momentum_Type));
1755 _momentum_test_reset(gesture);
1761 /* First make avarage of all touched devices to determine center point */
1764 Pointer_Event pe_local = *pe; /* Copy pe event info to local */
1765 unsigned int cnt = 1; /* We start counter counting current pe event */
1766 EINA_LIST_FOREACH(wd->touched, l, p)
1767 if (p->device != pe_local.device)
1775 /* Compute avarage to get center point */
1779 /* If user added finger - reset gesture */
1780 if ((st->info.n) && (st->info.n < cnt))
1781 state_to_report = ELM_GESTURE_STATE_ABORT;
1784 if (st->info.n < cnt)
1787 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1790 case EVAS_CALLBACK_MOUSE_DOWN:
1791 case EVAS_CALLBACK_MULTI_DOWN:
1792 case EVAS_CALLBACK_MOUSE_MOVE:
1793 case EVAS_CALLBACK_MULTI_MOVE:
1796 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
1797 (event_type == EVAS_CALLBACK_MULTI_DOWN) ||
1798 (wd->glayer_continues_enable)) /* start also on MOVE */
1799 { /* We start on MOVE when cont-enabled only */
1800 st->line_st.x = st->line_end.x = pe_local.x;
1801 st->line_st.y = st->line_end.y = pe_local.y;
1802 st->t_st_x = st->t_st_y = st->t_end = pe_local.timestamp;
1803 st->xdir = st->ydir = 0;
1804 st->info.x2 = st->info.x1 = pe_local.x;
1805 st->info.y2 = st->info.y1 = pe_local.y;
1806 st->info.tx = st->info.ty = pe_local.timestamp;
1807 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
1808 &st->info, EINA_FALSE);
1809 consume_event(wd, event_info, event_type, ev_flag);
1818 Eina_Bool force = EINA_TRUE; /* for move state */
1819 if ((st->t_up + ELM_GESTURE_MULTI_TIMEOUT) < pe_local.timestamp)
1820 { /* ABORT if got DOWN or MOVE event after UP+timeout */
1821 state_to_report = ELM_GESTURE_STATE_ABORT;
1825 /* We report state but don't compute momentum now */
1826 ev_flag = _set_state(gesture, state_to_report, &st->info,
1828 consume_event(wd, event_info, event_type, ev_flag);
1829 return; /* Stop computing when user remove finger */
1832 if ((pe_local.timestamp - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->t_end)
1833 { /* Too long of a wait, reset all values */
1834 st->line_st.x = pe_local.x;
1835 st->line_st.y = pe_local.y;
1836 st->t_st_y = st->t_st_x = pe_local.timestamp;
1837 st->info.tx = st->t_st_x;
1838 st->info.ty = st->t_st_y;
1839 st->xdir = st->ydir = 0;
1844 xdir = _get_direction(st->line_end.x, pe_local.x);
1845 ydir = _get_direction(st->line_end.y, pe_local.y);
1846 if (xdir && (xdir != st->xdir))
1848 st->line_st.x = st->line_end.x;
1849 st->info.tx = st->t_st_x = st->t_end;
1853 if (ydir && (ydir != st->ydir))
1855 st->line_st.y = st->line_end.y;
1856 st->info.ty = st->t_st_y = st->t_end;
1861 st->info.x2 = st->line_end.x = pe_local.x;
1862 st->info.y2 = st->line_end.y = pe_local.y;
1863 st->t_end = pe_local.timestamp;
1864 _set_momentum(&st->info, st->line_st.x, st->line_st.y,
1865 pe_local.x, pe_local.y, st->t_st_x, st->t_st_y,
1866 pe_local.timestamp);
1868 ev_flag = _set_state(gesture, state_to_report, &st->info,
1870 consume_event(wd, event_info, event_type, ev_flag);
1874 case EVAS_CALLBACK_MOUSE_UP:
1875 case EVAS_CALLBACK_MULTI_UP:
1876 st->t_up = pe_local.timestamp; /* Record recent up event time */
1877 if ((cnt > 1) || /* Ignore if more fingers touch surface */
1878 (!st->t_st_x)) /* IGNORE if info was cleared, long press,move */
1881 if ((pe_local.timestamp - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->t_end)
1882 { /* Too long of a wait, reset all values */
1883 st->line_st.x = pe_local.x;
1884 st->line_st.y = pe_local.y;
1885 st->t_st_y = st->t_st_x = pe_local.timestamp;
1886 st->xdir = st->ydir = 0;
1889 st->info.x2 = pe_local.x;
1890 st->info.y2 = pe_local.y;
1891 st->line_end.x = pe_local.x;
1892 st->line_end.y = pe_local.y;
1893 st->t_end = pe_local.timestamp;
1895 if ((fabs(st->info.mx) > ELM_GESTURE_MINIMUM_MOMENTUM) ||
1896 (fabs(st->info.my) > ELM_GESTURE_MINIMUM_MOMENTUM))
1897 state_to_report = ELM_GESTURE_STATE_END;
1899 state_to_report = ELM_GESTURE_STATE_ABORT;
1901 ev_flag = _set_state(gesture, state_to_report, &st->info,
1903 consume_event(wd, event_info, event_type, ev_flag);
1912 compare_line_device(const void *data1, const void *data2)
1913 { /* Compare device component of line struct */
1914 const Line_Data *ln1 = data1;
1915 const int *device = data2;
1917 if (ln1->t_st) /* Compare only with lines that started */
1918 return (ln1->device - (*device));
1926 * This function construct line struct from input.
1927 * @param info pointer to store line momentum.
1928 * @param st line info to store input data.
1929 * @param pe The recent input event as stored in pe struct.
1931 * @ingroup Elm_Gesture_Layer
1934 _single_line_process(Elm_Gesture_Line_Info *info, Line_Data *st,
1935 Pointer_Event *pe, Evas_Callback_Type event_type)
1936 { /* Record events and set momentum for line pointed by st */
1942 case EVAS_CALLBACK_MOUSE_DOWN:
1943 case EVAS_CALLBACK_MOUSE_MOVE:
1944 case EVAS_CALLBACK_MULTI_DOWN:
1945 case EVAS_CALLBACK_MULTI_MOVE:
1947 { /* This happens only when line starts */
1948 st->line_st.x = pe->x;
1949 st->line_st.y = pe->y;
1950 st->t_st = pe->timestamp;
1951 st->device = pe->device;
1952 info->momentum.x1 = pe->x;
1953 info->momentum.y1 = pe->y;
1954 info->momentum.tx = pe->timestamp;
1955 info->momentum.ty = pe->timestamp;
1962 case EVAS_CALLBACK_MOUSE_UP:
1963 case EVAS_CALLBACK_MULTI_UP:
1964 /* IGNORE if line info was cleared, like long press, move */
1968 st->line_end.x = pe->x;
1969 st->line_end.y = pe->y;
1970 st->t_end = pe->timestamp;
1979 _line_data_reset(st);
1983 info->momentum.x2 = pe->x;
1984 info->momentum.y2 = pe->y;
1985 _set_momentum(&info->momentum, st->line_st.x, st->line_st.y, pe->x, pe->y,
1986 st->t_st, st->t_st, pe->timestamp);
1994 * This function test for (n) line gesture.
1995 * @param obj The gesture-layer object.
1996 * @param pe The recent input event as stored in pe struct.
1997 * @param event_info Original input event pointer.
1998 * @param event_type Type of original input event.
1999 * @param g_type what Gesture we are testing.
2001 * @ingroup Elm_Gesture_Layer
2004 _n_line_test(Evas_Object *obj, Pointer_Event *pe, void *event_info,
2005 Evas_Callback_Type event_type, Elm_Gesture_Type g_type)
2009 Widget_Data *wd = elm_widget_data_get(obj);
2011 Gesture_Info *gesture = wd->gesture[g_type];
2012 if (!gesture ) return;
2014 /* When continues enable = TRUE a gesture may START on MOVE event */
2015 /* We don't allow this to happen with the if-statement below. */
2016 /* When continues enable = FALSE a gesture may START on DOWN only */
2017 /* Therefor it would NOT start on MOVE event. */
2018 /* NOTE that touched list is updated AFTER this function returns */
2019 /* so (count == 0) when we get here on first touch on surface. */
2020 if ((wd->glayer_continues_enable) && (!eina_list_count(wd->touched)))
2021 return; /* Got move on mouse-over move */
2023 Line_Type *st = gesture->data;
2026 st = calloc(1, sizeof(Line_Type));
2030 Line_Data *line = NULL;
2031 Eina_List *list = st->list;
2032 unsigned cnt = eina_list_count(list);
2035 { /* list is not empty, locate this device on list */
2036 line = (Line_Data *) eina_list_search_unsorted(st->list,
2037 compare_line_device, &pe->device);
2041 { /* List is empty or device not found, new line-struct on START only */
2042 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
2043 (event_type == EVAS_CALLBACK_MULTI_DOWN) ||
2044 ((wd->glayer_continues_enable) && /* START on MOVE also */
2045 ((event_type == EVAS_CALLBACK_MOUSE_MOVE) ||
2046 (event_type == EVAS_CALLBACK_MULTI_MOVE))))
2047 { /* Allocate new item on START only */
2048 line = calloc(1, sizeof(Line_Data));
2049 _line_data_reset(line);
2050 list = eina_list_append(list, line);
2055 if (!line) /* This may happen on MOVE that comes before DOWN */
2056 return; /* No line-struct to work with, can't continue testing */
2058 if (_single_line_process(&st->info, line, pe, event_type)) /* update st with input */
2059 consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_NONE);
2061 /* Get direction and magnitude of the line */
2063 get_vector(line->line_st.x, line->line_st.y, pe->x, pe->y,
2064 &line->line_length, &angle);
2066 /* These are used later to compare lines length */
2067 Evas_Coord shortest_line_len = line->line_length;
2068 Evas_Coord longest_line_len = line->line_length;
2069 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2071 /* Now update line-state */
2073 { /* Analyze line only if line started */
2074 if (line->line_angle >= 0.0)
2075 { /* if line direction was set, we test if broke tolerance */
2076 double a = fabs(angle - line->line_angle);
2078 double d = (tan(DEG2RAD(a))) * line->line_length; /* Distance from line */
2079 #if defined(DEBUG_GESTURE_LAYER)
2080 printf("%s a=<%f> d=<%f>\n", __func__, a, d);
2082 if ((d > wd->line_distance_tolerance) || (a > wd->line_angular_tolerance))
2083 { /* Broke tolerance: abort line and start a new one */
2084 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
2085 &st->info, EINA_FALSE);
2086 consume_event(wd, event_info, event_type, ev_flag);
2090 if (wd->glayer_continues_enable)
2091 { /* We may finish line if momentum is zero */
2092 /* This is for continues-gesture */
2093 if ((!st->info.momentum.mx) && (!st->info.momentum.my))
2094 { /* Finish line on zero momentum for continues gesture */
2095 line->line_end.x = pe->x;
2096 line->line_end.y = pe->y;
2097 line->t_end = pe->timestamp;
2102 { /* Record the line angle as it broke minimum length for line */
2103 if (line->line_length >= wd->line_min_length)
2104 st->info.angle = line->line_angle = angle;
2110 if (line->line_angle < 0.0)
2111 { /* it's not a line, too short more close to a tap */
2112 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
2113 &st->info, EINA_FALSE);
2114 consume_event(wd, event_info, event_type, ev_flag);
2120 /* Count how many lines already started / ended */
2123 unsigned int tm_start = pe->timestamp;
2124 unsigned int tm_end = pe->timestamp;
2127 double base_angle = ELM_GESTURE_NEGATIVE_ANGLE;
2128 Eina_Bool lines_parallel = EINA_TRUE;
2129 EINA_LIST_FOREACH(list, l, t_line)
2132 base_angle = t_line->line_angle;
2135 if (t_line->line_angle >= 0)
2136 { /* Compare angle only with lines with direction defined */
2137 if (fabs(base_angle - t_line->line_angle) >
2138 wd->line_angular_tolerance)
2139 lines_parallel = EINA_FALSE;
2143 if (t_line->line_length)
2144 { /* update only if this line is used */
2145 if (shortest_line_len > t_line->line_length)
2146 shortest_line_len = t_line->line_length;
2148 if (longest_line_len < t_line->line_length)
2149 longest_line_len = t_line->line_length;
2155 if (t_line->t_st < tm_start)
2156 tm_start = t_line->t_st;
2162 if (t_line->t_end < tm_end)
2163 tm_end = t_line->t_end;
2167 st->info.momentum.n = started;
2171 ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
2172 (event_type == EVAS_CALLBACK_MULTI_DOWN)))
2173 { /* user lift one finger then starts again without line-end - ABORT */
2174 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
2176 consume_event(wd, event_info, event_type, ev_flag);
2180 if (!lines_parallel)
2181 { /* Lines are NOT at same direction, abort this gesture */
2182 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
2184 consume_event(wd, event_info, event_type, ev_flag);
2189 /* We report ABORT if lines length are NOT matching when fingers are up */
2190 if ((longest_line_len - shortest_line_len) > (_elm_config->finger_size * 2))
2192 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
2194 consume_event(wd, event_info, event_type, ev_flag);
2198 if ((g_type == ELM_GESTURE_N_FLICKS) && ((tm_end - tm_start) > wd->flick_time_limit_ms))
2199 { /* We consider FLICK as a fast line.ABORT if take too long to finish */
2200 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
2202 consume_event(wd, event_info, event_type, ev_flag);
2208 case EVAS_CALLBACK_MOUSE_UP:
2209 case EVAS_CALLBACK_MULTI_UP:
2210 if ((started) && (started == ended))
2212 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END,
2213 &st->info, EINA_FALSE);
2214 consume_event(wd, event_info, event_type, ev_flag);
2219 case EVAS_CALLBACK_MOUSE_DOWN:
2220 case EVAS_CALLBACK_MULTI_DOWN:
2221 case EVAS_CALLBACK_MOUSE_MOVE:
2222 case EVAS_CALLBACK_MULTI_MOVE:
2225 if (wd->glayer_continues_enable && (started == ended))
2226 { /* For continues gesture */
2227 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END,
2228 &st->info, EINA_FALSE);
2229 consume_event(wd, event_info, event_type, ev_flag);
2232 { /* When continues, may START on MOVE event too */
2233 Elm_Gesture_State s = ELM_GESTURE_STATE_MOVE;
2235 /* This happens when: on n > 1 lines then one finger up */
2236 /* caused abort, then put finger down. */
2237 /* This will stop line from starting again. */
2238 /* Number of lines, MUST match touched-device in list */
2239 if ((!wd->glayer_continues_enable) &&
2240 (eina_list_count(st->list) < eina_list_count(wd->touched)))
2241 s = ELM_GESTURE_STATE_ABORT;
2243 if (gesture->state == ELM_GESTURE_STATE_UNDEFINED)
2244 s = ELM_GESTURE_STATE_START;
2246 ev_flag = _set_state(gesture, s, &st->info, EINA_TRUE);
2247 consume_event(wd, event_info, event_type, ev_flag);
2253 return; /* Unhandeld event type */
2260 * This function is used to check if rotation gesture started.
2261 * @param st Contains current rotation values from user input.
2262 * @return TRUE/FALSE if we need to set rotation START.
2264 * @ingroup Elm_Gesture_Layer
2267 rotation_broke_tolerance(Rotate_Type *st)
2269 if (st->info.base_angle < 0)
2270 return EINA_FALSE; /* Angle has to be computed first */
2272 if (st->rotate_angular_tolerance < 0)
2275 double low = st->info.base_angle - st->rotate_angular_tolerance;
2276 double high = st->info.base_angle + st->rotate_angular_tolerance;
2277 double t = st->info.angle;
2301 #if defined(DEBUG_GESTURE_LAYER)
2302 printf("%s angle=<%f> low=<%f> high=<%f>\n", __func__, t, low, high);
2304 if ((t < low) || (t > high))
2305 { /* This marks that roation action has started */
2306 st->rotate_angular_tolerance = ELM_GESTURE_NEGATIVE_ANGLE;
2307 st->info.base_angle = st->info.angle; /* Avoid jump in angle value */
2317 * This function is used for computing the gap between fingers.
2318 * It returns the length and center point between fingers.
2320 * @param x1 first finger x location.
2321 * @param y1 first finger y location.
2322 * @param x2 second finger x location.
2323 * @param y2 second finger y location.
2324 * @param x Gets center point x cord (output)
2325 * @param y Gets center point y cord (output)
2327 * @return length of the line between (x1,y1), (x2,y2) in pixels.
2329 * @ingroup Elm_Gesture_Layer
2332 get_finger_gap_length(Evas_Coord xx1, Evas_Coord yy1,
2333 Evas_Coord xx2, Evas_Coord yy2,
2334 Evas_Coord *x, Evas_Coord *y)
2336 double a, b, xx, yy, gap;
2337 xx = fabs(xx2 - xx1);
2338 yy = fabs(yy2 - yy1);
2339 gap = sqrt((xx * xx) + (yy * yy));
2341 /* START - Compute zoom center point */
2342 /* The triangle defined as follows:
2350 * http://en.wikipedia.org/wiki/Trigonometric_functions
2351 *************************************/
2352 if (((int)xx) && ((int)yy))
2354 double A = atan((yy / xx));
2355 #if defined(DEBUG_GESTURE_LAYER)
2356 printf("xx=<%f> yy=<%f> A=<%f>\n", xx, yy, A);
2358 a = (Evas_Coord) ((gap / 2) * sin(A));
2359 b = (Evas_Coord) ((gap / 2) * cos(A));
2360 *x = (Evas_Coord) ((xx2 > xx1) ? (xx1 + b) : (xx2 + b));
2361 *y = (Evas_Coord) ((yy2 > yy1) ? (yy1 + a) : (yy2 + a));
2366 { /* horiz line, take half width */
2367 #if defined(DEBUG_GESTURE_LAYER)
2368 printf("==== HORIZ ====\n");
2370 *x = (Evas_Coord) ((xx1 + xx2) / 2);
2371 *y = (Evas_Coord) (yy1);
2375 { /* vert line, take half width */
2376 #if defined(DEBUG_GESTURE_LAYER)
2377 printf("==== VERT ====\n");
2379 *x = (Evas_Coord) (xx1);
2380 *y = (Evas_Coord) ((yy1 + yy2) / 2);
2383 /* END - Compute zoom center point */
2385 return (Evas_Coord) gap;
2391 * This function is used for computing zoom value.
2393 * @param st Pointer to zoom data based on user input.
2394 * @param tm_end Recent input event timestamp.
2395 * @param zoom_val Current computed zoom value.
2397 * @return zoom momentum
2399 * @ingroup Elm_Gesture_Layer
2402 _zoom_momentum_get(Zoom_Type *st, unsigned int tm_end, double zoom_val)
2404 unsigned int tm_total;
2406 { /* Init, and we don't start computing momentum yet */
2407 st->m_st_tm = st->m_prev_tm = tm_end;
2408 st->m_base = zoom_val;
2412 if ((tm_end - ELM_GESTURE_MOMENTUM_DELAY) < st->m_st_tm)
2413 return 0.0; /* we don't start to compute momentum yet */
2416 { /* if direction was already defined, check if changed */
2417 if (((st->dir < 0) && (zoom_val > st->info.zoom)) ||
2418 ((st->dir > 0) && (zoom_val < st->info.zoom)))
2419 { /* Direction changed, reset momentum */
2421 st->dir = (-st->dir);
2426 st->dir = (zoom_val > st->info.zoom) ? 1 : -1; /* init */
2428 if ((tm_end - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->m_prev_tm)
2430 st->m_st_tm = 0; /* Rest momentum when waiting too long */
2434 st->m_prev_tm = tm_end;
2435 tm_total = tm_end - st->m_st_tm;
2438 return ((zoom_val - st->m_base) * 1000) / tm_total;
2446 * This function is used for computing zoom value.
2448 * @param st Pointer to zoom data based on user input.
2449 * @param x1 first finger x location.
2450 * @param y1 first finger y location.
2451 * @param x2 second finger x location.
2452 * @param y2 second finger y location.
2453 * @param factor zoom-factor, used to determine how fast zoom works.
2455 * @return zoom value, when 1.0 means no zoom, 0.5 half size...
2457 * @ingroup Elm_Gesture_Layer
2460 compute_zoom(Zoom_Type *st,
2461 Evas_Coord xx1, Evas_Coord yy1,
2462 Evas_Coord xx2, Evas_Coord yy2,
2463 double zoom_finger_factor)
2466 unsigned int tm_end = (st->zoom_mv.timestamp > st->zoom_mv1.timestamp) ?
2467 st->zoom_mv.timestamp : st->zoom_mv1.timestamp;
2469 Evas_Coord diam = get_finger_gap_length(xx1, yy1, xx2, yy2,
2470 &st->info.x, &st->info.y);
2472 st->info.radius = diam / 2;
2476 st->zoom_base = diam;
2477 return st->info.zoom;
2480 if (st->zoom_distance_tolerance)
2481 { /* zoom tolerance <> ZERO, means zoom action NOT started yet */
2482 if (diam < (st->zoom_base - st->zoom_distance_tolerance))
2483 { /* avoid jump with zoom value when break tolerance */
2484 st->zoom_base -= st->zoom_distance_tolerance;
2485 st->zoom_distance_tolerance = 0;
2488 if (diam > (st->zoom_base + st->zoom_distance_tolerance))
2489 { /* avoid jump with zoom value when break tolerance */
2490 st->zoom_base += st->zoom_distance_tolerance;
2491 st->zoom_distance_tolerance = 0;
2497 /* We use factor only on the difference between gap-base */
2498 /* if gap=120, base=100, we get ((120-100)/100)=0.2*factor */
2499 rt = ((1.0) + ((((float) diam - (float) st->zoom_base) /
2500 (float) st->zoom_base) * zoom_finger_factor));
2502 /* Momentum: zoom per second: */
2503 st->info.momentum = _zoom_momentum_get(st, tm_end, rt);
2511 * This function handles zoom with mouse wheel.
2512 * thats a combination of wheel + CTRL key.
2513 * @param obj The gesture-layer object.
2514 * @param event_info Original input event pointer.
2515 * @param event_type Type of original input event.
2516 * @param g_type what Gesture we are testing.
2518 * @ingroup Elm_Gesture_Layer
2521 _zoom_with_wheel_test(Evas_Object *obj, void *event_info,
2522 Evas_Callback_Type event_type, Elm_Gesture_Type g_type)
2524 Widget_Data *wd = elm_widget_data_get(obj);
2526 if (!wd->gesture[g_type]) return;
2528 Gesture_Info *gesture_zoom = wd->gesture[g_type];
2529 Zoom_Type *st = gesture_zoom->data;
2530 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2532 { /* Allocated once on first time, used for zoom intermediate data */
2533 st = calloc(1, sizeof(Zoom_Type));
2534 gesture_zoom->data = st;
2535 _zoom_test_reset(gesture_zoom);
2540 case EVAS_CALLBACK_KEY_UP:
2542 Evas_Event_Key_Up *p = event_info;
2543 if ((!strcmp(p->keyname, "Control_L")) ||
2544 (!strcmp(p->keyname, "Control_R")))
2545 { /* Test if we ended a zoom gesture when releasing CTRL */
2546 if ((st->zoom_wheel) &&
2547 ((gesture_zoom->state == ELM_GESTURE_STATE_START) ||
2548 (gesture_zoom->state == ELM_GESTURE_STATE_MOVE)))
2549 { /* User released CTRL after zooming */
2550 st->info.momentum = _zoom_momentum_get(st,
2551 p->timestamp, st->info.zoom);
2553 ev_flag = _set_state(gesture_zoom,
2554 ELM_GESTURE_STATE_END, &st->info, EINA_FALSE);
2555 consume_event(wd, event_info, event_type, ev_flag);
2563 case EVAS_CALLBACK_MOUSE_WHEEL:
2566 Elm_Gesture_State s;
2567 if (!evas_key_modifier_is_set(
2568 ((Evas_Event_Mouse_Wheel *) event_info)->modifiers,
2570 { /* if using wheel witout CTRL after starting zoom */
2571 if ((st->zoom_wheel) &&
2572 ((gesture_zoom->state == ELM_GESTURE_STATE_START) ||
2573 (gesture_zoom->state == ELM_GESTURE_STATE_MOVE)))
2575 ev_flag = _set_state(gesture_zoom,
2576 ELM_GESTURE_STATE_END, &st->info, EINA_FALSE);
2577 consume_event(wd, event_info, event_type, ev_flag);
2582 return; /* Ignore mouse-wheel without control */
2585 /* Using mouse wheel with CTRL for zoom */
2586 if (st->zoom_wheel || (st->zoom_distance_tolerance == 0))
2587 { /* (zoom_wheel == NULL) and (zoom_distance_tolerance == 0)
2588 we continue a zoom gesture */
2590 s = ELM_GESTURE_STATE_MOVE;
2593 { /* On first wheel event, report START */
2594 Evas_Modifier_Mask mask = evas_key_modifier_mask_get(
2595 evas_object_evas_get(wd->target), "Control");
2597 s = ELM_GESTURE_STATE_START;
2598 if (!evas_object_key_grab(wd->target, "Control_L", mask, 0, EINA_FALSE))
2599 ERR("Failed to Grabbed CTRL_L");
2600 if (!evas_object_key_grab(wd->target, "Control_R", mask, 0, EINA_FALSE))
2601 ERR("Failed to Grabbed CTRL_R");
2604 st->zoom_distance_tolerance = 0; /* Cancel tolerance */
2605 st->zoom_wheel = (Evas_Event_Mouse_Wheel *) event_info;
2606 st->info.x = st->zoom_wheel->canvas.x;
2607 st->info.y = st->zoom_wheel->canvas.y;
2609 if (st->zoom_wheel->z < 0) /* zoom in */
2610 st->info.zoom += (wd->zoom_finger_factor * wd->zoom_wheel_factor);
2612 if (st->zoom_wheel->z > 0) /* zoom out */
2613 st->info.zoom -= (wd->zoom_finger_factor * wd->zoom_wheel_factor);
2615 if (st->info.zoom < 0.0)
2616 st->info.zoom = 0.0;
2618 st->info.momentum = _zoom_momentum_get(st,
2619 st->zoom_wheel->timestamp, st->info.zoom);
2621 ev_flag = _set_state(gesture_zoom, s, &st->info, force);
2622 consume_event(wd, event_info, event_type, ev_flag);
2634 * This function is used to test zoom gesture.
2635 * user may combine zoom, rotation together.
2636 * so its possible that both will be detected from input.
2637 * (both are two-finger movement-oriented gestures)
2639 * @param obj The gesture-layer object.
2640 * @param event_info Pointer to recent input event.
2641 * @param event_type Recent input event type.
2642 * @param g_type what Gesture we are testing.
2644 * @ingroup Elm_Gesture_Layer
2647 _zoom_test(Evas_Object *obj, Pointer_Event *pe, void *event_info,
2648 Evas_Callback_Type event_type, Elm_Gesture_Type g_type)
2652 Widget_Data *wd = elm_widget_data_get(obj);
2654 if (!wd->gesture[g_type]) return;
2656 Gesture_Info *gesture_zoom = wd->gesture[g_type];
2657 Zoom_Type *st = gesture_zoom->data;
2660 { /* Allocated once on first time, used for zoom data */
2661 st = calloc(1, sizeof(Zoom_Type));
2662 gesture_zoom->data = st;
2663 _zoom_test_reset(gesture_zoom);
2667 /* Start - new zoom testing, letting all fingers start */
2668 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2671 case EVAS_CALLBACK_MOUSE_MOVE:
2672 case EVAS_CALLBACK_MULTI_MOVE:
2673 /* if non-continues mode and gesture NOT started, ignore MOVE */
2674 if ((!wd->glayer_continues_enable) &&
2675 (!st->zoom_st.timestamp))
2678 case EVAS_CALLBACK_MOUSE_DOWN:
2679 case EVAS_CALLBACK_MULTI_DOWN:
2680 { /* Here we take care of zoom-start and zoom move */
2684 if (eina_list_count(wd->touched) > 2)
2685 { /* Process zoom only when 2 fingers on surface */
2686 ev_flag = _set_state(gesture_zoom,
2687 ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE);
2688 consume_event(wd, event_info, event_type, ev_flag);
2693 if (!st->zoom_st.timestamp)
2694 { /* Now scan touched-devices list and find other finger */
2695 EINA_LIST_FOREACH(wd->touched, l, p)
2696 { /* Device of other finger <> pe device */
2697 if (p->device != pe->device)
2701 if (!p) /* Single finger on touch */
2704 /* Record down fingers */
2705 consume_event(wd, event_info, event_type, ev_flag);
2706 memcpy(&st->zoom_st, pe, sizeof(Pointer_Event));
2707 memcpy(&st->zoom_st1, p, sizeof(Pointer_Event));
2709 /* Set mv field as well to be ready for MOVE events */
2710 memcpy(&st->zoom_mv, pe, sizeof(Pointer_Event));
2711 memcpy(&st->zoom_mv1, p, sizeof(Pointer_Event));
2713 /* Here we have zoom_st, zoom_st1 set, report START */
2714 /* Set zoom-base after BOTH down events recorded */
2715 /* Compute length of line between fingers zoom start */
2716 st->info.zoom = 1.0;
2717 st->zoom_base = get_finger_gap_length(st->zoom_st1.x,
2718 st->zoom_st1.y, st->zoom_st.x, st->zoom_st.y,
2719 &st->info.x, &st->info.y);
2721 st->info.radius = st->zoom_base / 2;
2723 if ((gesture_zoom->state != ELM_GESTURE_STATE_START) &&
2724 (gesture_zoom->state != ELM_GESTURE_STATE_MOVE))
2725 { /* zoom started with mouse-wheel, don't report twice */
2726 ev_flag = _set_state(gesture_zoom,
2727 ELM_GESTURE_STATE_START, &st->info, EINA_FALSE);
2728 consume_event(wd, event_info, event_type, ev_flag);
2731 return; /* Zoom started */
2732 } /* End of ZOOM_START handling */
2735 /* if we got here, we have (exacally) two fingers on surfce */
2736 /* we also after START, report MOVE */
2737 /* First detect which finger moved */
2738 if (pe->device == st->zoom_mv.device)
2739 memcpy(&st->zoom_mv, pe, sizeof(Pointer_Event));
2740 else if (pe->device == st->zoom_mv1.device)
2741 memcpy(&st->zoom_mv1, pe, sizeof(Pointer_Event));
2743 /* Compute change in zoom as fingers move */
2744 st->info.zoom = compute_zoom(st,
2745 st->zoom_mv.x, st->zoom_mv.y,
2746 st->zoom_mv1.x, st->zoom_mv1.y,
2747 wd->zoom_finger_factor);
2749 if (!st->zoom_distance_tolerance)
2750 { /* Zoom broke tolerance, report move */
2751 double d = st->info.zoom - st->next_step;
2755 if (d >= wd->zoom_step)
2756 { /* Report move in steps */
2757 st->next_step = st->info.zoom;
2759 ev_flag = _set_state(gesture_zoom,
2760 ELM_GESTURE_STATE_MOVE,
2761 &st->info, EINA_TRUE);
2762 consume_event(wd, event_info, event_type, ev_flag);
2764 } /* End of ZOOM_MOVE handling */
2769 case EVAS_CALLBACK_MOUSE_UP:
2770 case EVAS_CALLBACK_MULTI_UP:
2771 /* Reset timestamp of finger-up.This is used later
2772 by _zoom_test_reset() to retain finger-down data */
2773 consume_event(wd, event_info, event_type, ev_flag);
2774 if (((st->zoom_wheel) || (st->zoom_base)) &&
2775 (st->zoom_distance_tolerance == 0))
2777 ev_flag = _set_state(gesture_zoom, ELM_GESTURE_STATE_END,
2778 &st->info, EINA_FALSE);
2779 consume_event(wd, event_info, event_type, ev_flag);
2784 /* if we got here not a ZOOM */
2785 if (gesture_zoom->state != ELM_GESTURE_STATE_UNDEFINED)
2786 { /* Must be != undefined, if gesture started */
2787 ev_flag = _set_state(gesture_zoom,
2788 ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE);
2789 consume_event(wd, event_info, event_type, ev_flag);
2792 _zoom_test_reset(gesture_zoom);
2802 _get_rotate_properties(Rotate_Type *st,
2803 Evas_Coord xx1, Evas_Coord yy1,
2804 Evas_Coord xx2, Evas_Coord yy2,
2806 { /* FIXME: Fix momentum computation, it's wrong */
2807 double prev_angle = *angle;
2808 st->info.radius = get_finger_gap_length(xx1, yy1, xx2, yy2,
2809 &st->info.x, &st->info.y) / 2;
2811 *angle = get_angle(xx1, yy1, xx2, yy2);
2813 if (angle == &st->info.angle)
2814 { /* Fingers are moving, compute momentum */
2815 unsigned int tm_start =
2816 (st->rotate_st.timestamp > st->rotate_st1.timestamp)
2817 ? st->rotate_st.timestamp : st->rotate_st1.timestamp;
2818 unsigned int tm_end =
2819 (st->rotate_mv.timestamp > st->rotate_mv1.timestamp)
2820 ? st->rotate_mv.timestamp : st->rotate_mv1.timestamp;
2822 unsigned int tm_total = tm_end - tm_start;
2824 { /* Momentum computed as:
2825 accumulated roation angle (deg) divided by time */
2827 if (((prev_angle < 90) && ((*angle) > 270)) ||
2828 ((prev_angle > 270) && ((*angle) < 90)))
2829 { /* We circle passing ZERO point */
2830 prev_angle = (*angle);
2832 else m = prev_angle - (*angle);
2834 st->accum_momentum += m;
2836 if ((tm_end - st->prev_momentum_tm) < 100)
2837 st->prev_momentum += m;
2840 if (fabs(st->prev_momentum) < 0.002)
2841 st->accum_momentum = 0.0; /* reset momentum */
2843 st->prev_momentum = 0.0; /* Start again */
2846 st->prev_momentum_tm = tm_end;
2847 st->info.momentum = (st->accum_momentum * 1000) / tm_total;
2851 st->info.momentum = 0;
2857 * This function is used to test rotation gesture.
2858 * user may combine zoom, rotation together.
2859 * so its possible that both will be detected from input.
2860 * (both are two-finger movement-oriented gestures)
2862 * @param obj The gesture-layer object.
2863 * @param event_info Pointer to recent input event.
2864 * @param event_type Recent input event type.
2865 * @param g_type what Gesture we are testing.
2867 * @ingroup Elm_Gesture_Layer
2870 _rotate_test(Evas_Object *obj, Pointer_Event *pe, void *event_info,
2871 Evas_Callback_Type event_type, Elm_Gesture_Type g_type)
2876 Widget_Data *wd = elm_widget_data_get(obj);
2878 if (!wd->gesture[g_type]) return;
2880 Gesture_Info *gesture = wd->gesture[g_type];
2886 { /* Allocated once on first time */
2887 st = calloc(1, sizeof(Rotate_Type));
2889 _rotate_test_reset(gesture);
2893 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2896 case EVAS_CALLBACK_MOUSE_MOVE:
2897 case EVAS_CALLBACK_MULTI_MOVE:
2898 /* if non-continues mode and gesture NOT started, ignore MOVE */
2899 if ((!wd->glayer_continues_enable) &&
2900 (!st->rotate_st.timestamp))
2903 case EVAS_CALLBACK_MOUSE_DOWN:
2904 case EVAS_CALLBACK_MULTI_DOWN:
2905 { /* Here we take care of rotate-start and rotate move */
2909 if (eina_list_count(wd->touched) > 2)
2910 { /* Process rotate only when 2 fingers on surface */
2911 ev_flag = _set_state(gesture,
2912 ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE);
2913 consume_event(wd, event_info, event_type, ev_flag);
2918 if (!st->rotate_st.timestamp)
2919 { /* Now scan touched-devices list and find other finger */
2920 EINA_LIST_FOREACH(wd->touched, l, p)
2921 { /* Device of other finger <> pe device */
2922 if (p->device != pe->device)
2927 return; /* Single finger on touch */
2929 /* Record down fingers */
2930 consume_event(wd, event_info, event_type, ev_flag);
2931 memcpy(&st->rotate_st, pe, sizeof(Pointer_Event));
2932 memcpy(&st->rotate_st1, p, sizeof(Pointer_Event));
2934 /* Set mv field as well to be ready for MOVE events */
2935 memcpy(&st->rotate_mv, pe, sizeof(Pointer_Event));
2936 memcpy(&st->rotate_mv1, p, sizeof(Pointer_Event));
2938 /* Here we have rotate_st, rotate_st1 set, report START */
2939 /* Set rotate-base after BOTH down events recorded */
2940 /* Compute length of line between fingers rotate start */
2941 _get_rotate_properties(st,
2942 st->rotate_st.x, st->rotate_st.y,
2943 st->rotate_st1.x, st->rotate_st1.y,
2944 &st->info.base_angle);
2946 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
2947 &st->info, EINA_FALSE);
2948 consume_event(wd, event_info, event_type, ev_flag);
2950 return; /* Rotate started */
2951 } /* End of ROTATE_START handling */
2954 /* if we got here, we have (exacally) two fingers on surfce */
2955 /* we also after START, report MOVE */
2956 /* First detect which finger moved */
2957 if (pe->device == st->rotate_mv.device)
2958 memcpy(&st->rotate_mv, pe, sizeof(Pointer_Event));
2959 else if (pe->device == st->rotate_mv1.device)
2960 memcpy(&st->rotate_mv1, pe, sizeof(Pointer_Event));
2962 /* Compute change in rotate as fingers move */
2963 _get_rotate_properties(st,
2964 st->rotate_mv.x, st->rotate_mv.y,
2965 st->rotate_mv1.x, st->rotate_mv1.y,
2968 if (rotation_broke_tolerance(st))
2969 { /* Rotation broke tolerance, report move */
2970 double d = st->info.angle - st->next_step;
2974 if (d >= wd->rotate_step)
2975 { /* Report move in steps */
2976 st->next_step = st->info.angle;
2978 ev_flag = _set_state(gesture,
2979 ELM_GESTURE_STATE_MOVE, &st->info, EINA_TRUE);
2980 consume_event(wd, event_info, event_type, ev_flag);
2982 } /* End of ROTATE_MOVE handling */
2987 case EVAS_CALLBACK_MOUSE_UP:
2988 case EVAS_CALLBACK_MULTI_UP:
2989 consume_event(wd, event_info, event_type, ev_flag);
2990 /* Reset timestamp of finger-up.This is used later
2991 by rotate_test_reset() to retain finger-down data */
2992 if (st->rotate_angular_tolerance < 0)
2994 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END,
2995 &st->info, EINA_FALSE);
2996 consume_event(wd, event_info, event_type, ev_flag);
3001 if (gesture->state != ELM_GESTURE_STATE_UNDEFINED)
3002 { /* Must be != undefined, if gesture started */
3003 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
3004 &st->info, EINA_FALSE);
3005 consume_event(wd, event_info, event_type, ev_flag);
3008 _rotate_test_reset(gesture);
3019 * This function is used to save input events in an abstract struct
3020 * to be used later by getsure-testing functions.
3022 * @param data The gesture-layer object.
3023 * @param event_info Pointer to recent input event.
3024 * @param event_type Recent input event type.
3025 * @param pe The abstract data-struct (output).
3027 * @ingroup Elm_Gesture_Layer
3030 _make_pointer_event(void *data, void *event_info,
3031 Evas_Callback_Type event_type, Pointer_Event *pe)
3033 Widget_Data *wd = elm_widget_data_get(data);
3034 if (!wd) return EINA_FALSE;
3036 memset(pe, '\0', sizeof(*pe));
3039 case EVAS_CALLBACK_MOUSE_DOWN:
3040 pe->x = ((Evas_Event_Mouse_Down *) event_info)->canvas.x;
3041 pe->y = ((Evas_Event_Mouse_Down *) event_info)->canvas.y;
3042 pe->timestamp = ((Evas_Event_Mouse_Down *) event_info)->timestamp;
3043 pe->device = ELM_MOUSE_DEVICE;
3046 case EVAS_CALLBACK_MOUSE_UP:
3047 pe->x = ((Evas_Event_Mouse_Up *) event_info)->canvas.x;
3048 pe->y = ((Evas_Event_Mouse_Up *) event_info)->canvas.y;
3049 pe->timestamp = ((Evas_Event_Mouse_Up *) event_info)->timestamp;
3050 pe->device = ELM_MOUSE_DEVICE;
3053 case EVAS_CALLBACK_MOUSE_MOVE:
3054 pe->x = ((Evas_Event_Mouse_Move *) event_info)->cur.canvas.x;
3055 pe->y = ((Evas_Event_Mouse_Move *) event_info)->cur.canvas.y;
3056 pe->timestamp = ((Evas_Event_Mouse_Move *) event_info)->timestamp;
3057 pe->device = ELM_MOUSE_DEVICE;
3060 case EVAS_CALLBACK_MULTI_DOWN:
3061 pe->x = ((Evas_Event_Multi_Down *) event_info)->canvas.x;
3062 pe->y = ((Evas_Event_Multi_Down *) event_info)->canvas.y;
3063 pe->timestamp = ((Evas_Event_Multi_Down *) event_info)->timestamp;
3064 pe->device = ((Evas_Event_Multi_Down *) event_info)->device;
3067 case EVAS_CALLBACK_MULTI_UP:
3068 pe->x = ((Evas_Event_Multi_Up *) event_info)->canvas.x;
3069 pe->y = ((Evas_Event_Multi_Up *) event_info)->canvas.y;
3070 pe->timestamp = ((Evas_Event_Multi_Up *) event_info)->timestamp;
3071 pe->device = ((Evas_Event_Multi_Up *) event_info)->device;
3074 case EVAS_CALLBACK_MULTI_MOVE:
3075 pe->x = ((Evas_Event_Multi_Move *) event_info)->cur.canvas.x;
3076 pe->y = ((Evas_Event_Multi_Move *) event_info)->cur.canvas.y;
3077 pe->timestamp = ((Evas_Event_Multi_Move *) event_info)->timestamp;
3078 pe->device = ((Evas_Event_Multi_Move *) event_info)->device;
3085 pe->event_type = event_type;
3092 * This function restartes line, flick, zoom and rotate gestures
3093 * when gesture-layer continues-gestures enabled.
3094 * Example of continues-gesture:
3095 * When doing a line, user stops moving finger but keeps fingers on touch.
3096 * This will cause line-end, then as user continues moving his finger
3097 * it re-starts line gesture.
3098 * When continue mode is disabled, user has to lift finger from touch
3099 * to end a gesture. Them touch-again to start a new one.
3101 * @param data The gesture-layer object.
3102 * @param wd gesture layer widget data.
3103 * @param states_reset flag that marks gestures were reset in history clear.
3105 * @ingroup Elm_Gesture_Layer
3108 continues_gestures_restart(void *data, Eina_Bool states_reset)
3110 Widget_Data *wd = elm_widget_data_get(data);
3113 /* Run through events to restart gestures */
3115 Eina_Bool n_momentum, n_lines, n_flicks, zoom, rotate;
3116 /* We turn-on flag for finished, aborted, not-started gestures */
3117 g = wd->gesture[ELM_GESTURE_MOMENTUM];
3118 n_momentum = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START)
3119 && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE;
3122 _momentum_test_reset(wd->gesture[ELM_GESTURE_MOMENTUM]);
3123 _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE);
3127 g = wd->gesture[ELM_GESTURE_N_LINES];
3128 n_lines = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START)
3129 && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE;
3132 _line_test_reset(wd->gesture[ELM_GESTURE_N_LINES]);
3133 _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE);
3137 g = wd->gesture[ELM_GESTURE_N_FLICKS];
3138 n_flicks = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START)
3139 && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE;
3142 _line_test_reset(wd->gesture[ELM_GESTURE_N_FLICKS]);
3143 _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE);
3147 g = wd->gesture[ELM_GESTURE_ZOOM];
3148 zoom = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START)
3149 && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE;
3152 _zoom_test_reset(wd->gesture[ELM_GESTURE_ZOOM]);
3153 _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE);
3157 g = wd->gesture[ELM_GESTURE_ROTATE];
3158 rotate = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START)
3159 && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE;
3162 _rotate_test_reset(wd->gesture[ELM_GESTURE_ROTATE]);
3163 _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE);
3171 * This function the core-function where input handling is done.
3172 * Here we get user input and stream it to gesture testing.
3173 * We notify user about any gestures with new state:
3175 * START - gesture started.
3176 * MOVE - gesture is ongoing.
3177 * END - gesture was completed.
3178 * ABORT - gesture was aborted after START, MOVE (will NOT be completed)
3180 * We also check if a gesture was detected, then reset event history
3181 * If no gestures were found we reset gesture test flag
3182 * after streaming event-history to widget.
3183 * (stream to the widget all events not consumed as a gesture)
3185 * @param data The gesture-layer object.
3186 * @param event_info Pointer to recent input event.
3187 * @param event_type Recent input event type.
3189 * @ingroup Elm_Gesture_Layer
3192 _event_process(void *data, Evas_Object *obj __UNUSED__,
3193 void *event_info, Evas_Callback_Type event_type)
3196 Pointer_Event *pe = NULL;
3197 Widget_Data *wd = elm_widget_data_get(data);
3199 #if defined(DEBUG_GESTURE_LAYER)
3202 printf("Gesture | State | is tested\n");
3203 for (i = ELM_GESTURE_N_TAPS; i < ELM_GESTURE_LAST; i++)
3207 printf(" %d %d %d\n", i, g->state, g->test);
3211 /* Start testing candidate gesture from here */
3212 if (_make_pointer_event(data, event_info, event_type, &_pe))
3215 if (IS_TESTED(ELM_GESTURE_N_LONG_TAPS))
3216 _n_long_tap_test(data, pe, event_info, event_type,
3217 ELM_GESTURE_N_LONG_TAPS);
3219 if (IS_TESTED(ELM_GESTURE_N_TAPS))
3220 _tap_gesture_test(wd, pe, event_info, event_type,
3221 wd->gesture[ELM_GESTURE_N_TAPS], 1);
3223 if (IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS))
3224 _tap_gesture_test(wd, pe, event_info, event_type,
3225 wd->gesture[ELM_GESTURE_N_DOUBLE_TAPS], 2);
3227 if (IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS))
3228 _tap_gesture_test(wd, pe, event_info, event_type,
3229 wd->gesture[ELM_GESTURE_N_TRIPLE_TAPS], 3);
3231 if (IS_TESTED(ELM_GESTURE_MOMENTUM))
3232 _momentum_test(data, pe, event_info, event_type,
3233 ELM_GESTURE_MOMENTUM);
3235 if (IS_TESTED(ELM_GESTURE_N_LINES))
3236 _n_line_test(data, pe, event_info, event_type, ELM_GESTURE_N_LINES);
3238 if (IS_TESTED(ELM_GESTURE_N_FLICKS))
3239 _n_line_test(data, pe, event_info, event_type, ELM_GESTURE_N_FLICKS);
3241 if (_elm_config->glayer_zoom_finger_enable && IS_TESTED(ELM_GESTURE_ZOOM))
3242 _zoom_test(data, pe, event_info, event_type, ELM_GESTURE_ZOOM);
3244 if (IS_TESTED(ELM_GESTURE_ZOOM))
3245 _zoom_with_wheel_test(data, event_info, event_type, ELM_GESTURE_ZOOM);
3247 if (_elm_config->glayer_rotate_finger_enable && IS_TESTED(ELM_GESTURE_ROTATE))
3248 _rotate_test(data, pe, event_info, event_type, ELM_GESTURE_ROTATE);
3250 if (_get_event_flag(event_info, event_type) & EVAS_EVENT_FLAG_ON_HOLD)
3251 _event_history_add(data, event_info, event_type);
3253 /* we maintain list of touched devices */
3254 /* We also use move to track current device x.y pos */
3255 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
3256 (event_type == EVAS_CALLBACK_MULTI_DOWN) ||
3257 (event_type == EVAS_CALLBACK_MOUSE_MOVE) ||
3258 (event_type == EVAS_CALLBACK_MULTI_MOVE))
3260 wd->touched = _add_touched_device(wd->touched, pe);
3262 else if ((event_type == EVAS_CALLBACK_MOUSE_UP) ||
3263 (event_type == EVAS_CALLBACK_MULTI_UP))
3265 wd->touched = _remove_touched_device(wd->touched, pe);
3268 /* Report current states and clear history if needed */
3269 Eina_Bool states_reset = _clear_if_finished(data);
3270 if (wd->glayer_continues_enable)
3271 continues_gestures_restart(data, states_reset);
3276 * For all _mouse_* / multi_* functions wethen send this event to
3277 * _event_process function.
3279 * @param data The gesture-layer object.
3280 * @param event_info Pointer to recent input event.
3282 * @ingroup Elm_Gesture_Layer
3285 _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3288 Widget_Data *wd = elm_widget_data_get(data);
3290 if (((Evas_Event_Mouse_Down *) event_info)->button != 1)
3291 return; /* We only process left-click at the moment */
3293 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_DOWN);
3297 _mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3300 Widget_Data *wd = elm_widget_data_get(data);
3303 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_MOVE);
3307 _key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3310 Widget_Data *wd = elm_widget_data_get(data);
3313 _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_DOWN);
3317 _key_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3320 Widget_Data *wd = elm_widget_data_get(data);
3323 _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_UP);
3327 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3330 Widget_Data *wd = elm_widget_data_get(data);
3333 if (((Evas_Event_Mouse_Up *) event_info)->button != 1)
3334 return; /* We only process left-click at the moment */
3336 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_UP);
3340 _mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3343 Widget_Data *wd = elm_widget_data_get(data);
3346 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_WHEEL);
3350 _multi_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3353 Widget_Data *wd = elm_widget_data_get(data);
3356 _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_DOWN);
3360 _multi_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3363 Widget_Data *wd = elm_widget_data_get(data);
3366 _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_MOVE);
3370 _multi_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3373 Widget_Data *wd = elm_widget_data_get(data);
3376 _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_UP);
3380 elm_gesture_layer_hold_events_get(const Evas_Object *obj)
3382 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
3384 Widget_Data *wd = elm_widget_data_get(obj);
3385 if (!wd) return EINA_FALSE;
3387 return !wd->repeat_events;
3391 elm_gesture_layer_hold_events_set(Evas_Object *obj, Eina_Bool hold_events)
3393 ELM_CHECK_WIDTYPE(obj, widtype);
3395 Widget_Data *wd = elm_widget_data_get(obj);
3398 wd->repeat_events = !(!!hold_events);
3402 elm_gesture_layer_zoom_step_get(const Evas_Object *obj)
3404 ELM_CHECK_WIDTYPE(obj, widtype) 0;
3406 Widget_Data *wd = elm_widget_data_get(obj);
3409 return wd->zoom_step;
3413 elm_gesture_layer_zoom_step_set(Evas_Object *obj, double step)
3415 ELM_CHECK_WIDTYPE(obj, widtype);
3417 Widget_Data *wd = elm_widget_data_get(obj);
3420 if (step < 0) return;
3422 wd->zoom_step = step;
3426 elm_gesture_layer_rotate_step_get(const Evas_Object *obj)
3428 ELM_CHECK_WIDTYPE(obj, widtype) 0;
3430 Widget_Data *wd = elm_widget_data_get(obj);
3433 return wd->rotate_step;
3437 elm_gesture_layer_rotate_step_set(Evas_Object *obj, double step)
3439 ELM_CHECK_WIDTYPE(obj, widtype);
3441 Widget_Data *wd = elm_widget_data_get(obj);
3444 if (step < 0) return;
3446 wd->rotate_step = step;
3450 elm_gesture_layer_attach(Evas_Object *obj, Evas_Object *target)
3452 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
3454 Widget_Data *wd = elm_widget_data_get(obj);
3455 if (!wd) return EINA_FALSE;
3457 if (!target) return EINA_FALSE;
3459 /* if was attached before, unregister callbacks first */
3461 _unregister_callbacks(obj);
3463 wd->target = target;
3465 _register_callbacks(obj);
3470 elm_gesture_layer_cb_set(Evas_Object *obj, Elm_Gesture_Type idx,
3471 Elm_Gesture_State cb_type, Elm_Gesture_Event_Cb cb, void *data)
3473 ELM_CHECK_WIDTYPE(obj, widtype);
3475 Widget_Data *wd = elm_widget_data_get(obj);
3479 if (!wd->gesture[idx])
3480 wd->gesture[idx] = calloc(1, sizeof(Gesture_Info));
3481 if (!wd->gesture[idx]) return;
3483 p = wd->gesture[idx];
3486 p->fn[cb_type].cb = cb;
3487 p->fn[cb_type].user_data = data;
3488 p->state = ELM_GESTURE_STATE_UNDEFINED;
3493 _disable_hook(Evas_Object *obj)
3495 if (elm_widget_disabled_get(obj))
3496 _unregister_callbacks(obj);
3498 _register_callbacks(obj);
3502 elm_gesture_layer_add(Evas_Object *parent)
3508 ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
3510 ELM_SET_WIDTYPE(widtype, "gesture_layer");
3511 elm_widget_type_set(obj, "gesture_layer");
3512 elm_widget_sub_object_add(parent, obj);
3513 elm_widget_data_set(obj, wd);
3514 elm_widget_del_hook_set(obj, _del_hook);
3515 elm_widget_disable_hook_set(obj, _disable_hook);
3518 wd->line_min_length =_elm_config->glayer_line_min_length * _elm_config->finger_size;
3519 wd->zoom_distance_tolerance = _elm_config->glayer_zoom_distance_tolerance * _elm_config->finger_size;
3520 wd->line_distance_tolerance = _elm_config->glayer_line_distance_tolerance * _elm_config->finger_size;
3521 wd->zoom_finger_factor = _elm_config->glayer_zoom_finger_factor;
3522 wd->zoom_wheel_factor = _elm_config->glayer_zoom_wheel_factor; /* mouse wheel zoom steps */
3523 wd->rotate_angular_tolerance = _elm_config->glayer_rotate_angular_tolerance;
3524 wd->line_angular_tolerance = _elm_config->glayer_line_angular_tolerance;
3525 wd->flick_time_limit_ms = _elm_config->glayer_flick_time_limit_ms;
3526 wd->long_tap_start_timeout = _elm_config->glayer_long_tap_start_timeout;
3527 wd->repeat_events = EINA_TRUE;
3528 wd->glayer_continues_enable = _elm_config->glayer_continues_enable;
3530 #if defined(DEBUG_GESTURE_LAYER)
3531 printf("size of Gestures = <%d>\n", sizeof(wd->gesture));
3532 printf("initial values:\n\tzoom_finger_factor=<%f>\n\tzoom_distance_tolerance=<%d>\n\tline_min_length=<%d>\n\tline_distance_tolerance=<%d>\n\tzoom_wheel_factor=<%f>\n\trotate_angular_tolerance=<%f>\n\twd->line_angular_tolerance=<%f>\n\twd->flick_time_limit_ms=<%d>\n\twd->long_tap_start_timeout=<%f>\n\twd->zoom_step=<%f>\n\twd->rotate_step=<%f>\n\twd->glayer_continues_enable=<%d>\n ", wd->zoom_finger_factor, wd->zoom_distance_tolerance, wd->line_min_length, wd->line_distance_tolerance, wd->zoom_wheel_factor, wd->rotate_angular_tolerance, wd->line_angular_tolerance, wd->flick_time_limit_ms, wd->long_tap_start_timeout, wd->zoom_step, wd->rotate_step, wd->glayer_continues_enable);
3534 memset(wd->gesture, 0, sizeof(wd->gesture));