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_TIMEOUT 50
10 #define DBL_CLICK_TIME 400
12 /* Some Trigo values */
13 #define RAD_90DEG M_PI_2
14 #define RAD_180DEG M_PI
15 #define RAD_270DEG (M_PI_2 * 3)
16 #define RAD_360DEG (M_PI * 2)
19 _glayer_bufdup(void *buf, size_t size)
26 #define COPY_EVENT_INFO(EV) _glayer_bufdup(EV, sizeof(*EV))
29 #define SET_TEST_BIT(P) do { \
30 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; \
33 #define IS_TESTED(T) ((wd->gesture[T]) ? wd->gesture[T]->test : EINA_FALSE)
39 * Struct holds callback information.
41 * @ingroup Elm_Gesture_Layer
45 void *user_data; /**< Holds user data to CB (like sd) */
46 Elm_Gesture_Event_Cb cb;
53 * type for callback information
55 * @ingroup Elm_Gesture_Layer
57 typedef struct _Func_Data Func_Data;
62 * @struct _Gesture_Info
63 * Struct holds gesture info
65 * @ingroup Elm_Gesture_Layer
70 void *data; /**< Holds gesture intemidiate processing data */
71 Func_Data fn[ELM_GESTURE_STATE_ABORT + 1]; /**< Callback info for states */
72 Elm_Gesture_Types g_type; /**< gesture type */
73 Elm_Gesture_State state; /**< gesture state */
74 void *info; /**< Data for the state callback */
75 Eina_Bool test; /**< if true this gesture should be tested on input */
81 * @typedef Gesture_Info
82 * Type for _Gesture_Info
84 * @ingroup Elm_Gesture_Layer
86 typedef struct _Gesture_Info Gesture_Info;
91 * @struct _Event_History
92 * Struct holds event history.
93 * These events are repeated if no gesture found.
95 * @ingroup Elm_Gesture_Layer
101 Evas_Callback_Type event_type;
107 * @typedef Event_History
108 * Type for _Event_History
110 * @ingroup Elm_Gesture_Layer
112 typedef struct _Event_History Event_History;
117 * @struct _Pointer_Event
118 * Struct holds pointer-event info
119 * This is a generic pointer event structure
121 * @ingroup Elm_Gesture_Layer
123 struct _Pointer_Event
126 unsigned int timestamp;
128 Evas_Callback_Type event_type;
134 * @typedef Pointer_Event
135 * Type for generic pointer event structure
137 * @ingroup Elm_Gesture_Layer
139 typedef struct _Pointer_Event Pointer_Event;
141 /* All *Type structs hold result for the user in 'info' field
142 * The rest is gesture processing intermediate data.
143 * NOTE: info field must be FIRST in the struct.
144 * This is used when reporting ABORT in event_history_clear() */
147 Elm_Gesture_Taps_Info info;
148 unsigned int count_ups;
154 typedef struct _Taps_Type Taps_Type;
156 struct _Momentum_Type
157 { /* Fields used by _line_test() */
158 Elm_Gesture_Momentum_Info info;
159 Evas_Coord_Point line_st;
160 Evas_Coord_Point line_end;
161 unsigned int t_st_x; /* Time start on X */
162 unsigned int t_st_y; /* Time start on Y */
163 unsigned int t_end; /* Time end */
166 typedef struct _Momentum_Type Momentum_Type;
170 Evas_Coord_Point line_st;
171 Evas_Coord_Point line_end;
172 Evas_Coord line_length;
173 unsigned int t_st; /* Time start */
174 unsigned int t_end; /* Time end */
176 double line_angle; /* Current angle of line */
178 typedef struct _Line_Data Line_Data;
181 { /* Fields used by _line_test() */
182 Elm_Gesture_Line_Info info;
183 Eina_List *list; /* List of Line_Data */
185 typedef struct _Line_Type Line_Type;
188 { /* Fields used by _zoom_test() */
189 Elm_Gesture_Zoom_Info info;
190 Pointer_Event zoom_st;
191 Pointer_Event zoom_mv;
192 Pointer_Event zoom_st1;
193 Pointer_Event zoom_mv1;
194 Evas_Event_Mouse_Wheel *zoom_wheel;
195 Evas_Coord zoom_base; /* Holds gap between fingers on zoom-start */
196 Evas_Coord zoom_distance_tolerance;
199 typedef struct _Zoom_Type Zoom_Type;
202 { /* Fields used by _rotation_test() */
203 Elm_Gesture_Rotate_Info info;
204 Pointer_Event rotate_st;
205 Pointer_Event rotate_mv;
206 Pointer_Event rotate_st1;
207 Pointer_Event rotate_mv1;
208 double rotate_angular_tolerance;
211 typedef struct _Rotate_Type Rotate_Type;
215 Evas_Object *target; /* Target Widget */
216 Event_History *event_history_list;
219 Evas_Coord zoom_distance_tolerance;
220 Evas_Coord line_distance_tolerance;
221 double line_angular_tolerance;
222 double zoom_wheel_factor; /* mouse wheel zoom steps */
223 double zoom_finger_factor; /* used for zoom factor */
224 double rotate_angular_tolerance;
225 unsigned int flick_time_limit_ms;
230 Gesture_Info *gesture[ELM_GESTURE_LAST];
231 Ecore_Timer *dbl_timeout; /* When this expires, dbl click/taps ABORTed */
232 Eina_List *pending; /* List of devices need to refeed *UP event */
233 Eina_List *touched; /* Information of touched devices */
235 Eina_Bool repeat_events : 1;
237 typedef struct _Widget_Data Widget_Data;
239 static const char *widtype = NULL;
240 static void _del_hook(Evas_Object *obj);
242 static void _event_history_clear(Evas_Object *obj);
243 static void _reset_states(Widget_Data *wd);
244 static void _key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
245 static void _key_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
246 static void _zoom_with_wheel_test(Evas_Object *obj, void *event_info, Evas_Callback_Type event_type, Elm_Gesture_Types g_type);
247 static void _mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
248 static void _mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info);
249 static void _mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
250 static void _mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
252 static void _multi_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
253 static void _multi_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
254 static void _multi_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
256 /* START - Functions to manage touched-device list */
259 * This function is used to find if device is touched
261 * @ingroup Elm_Gesture_Layer
264 device_is_touched(const void *data1, const void *data2)
265 { /* Compare the two device numbers */
266 return (((Pointer_Event *) data1)->device -((Pointer_Event *) data2)->device);
272 * Recoed Pointer Event in touched device list
273 * Note: This fuction allocates memory for PE event
274 * This memory is released in _remove_touched_device()
275 * @param list Pointer to touched device list.
276 * @param Pointer_Event Pointer to PE.
278 * @ingroup Elm_Gesture_Layer
281 _add_touched_device(Eina_List *list, Pointer_Event *pe)
283 if (eina_list_search_unsorted_list(list, device_is_touched, pe))
286 Pointer_Event *p = malloc(sizeof(Pointer_Event ));
287 memcpy(p, pe, sizeof(Pointer_Event)); /* Freed in _remove_touched_device() */
288 return eina_list_append(list, p);
294 * Remove Pointer Event from touched device list
295 * @param list Pointer to touched device list.
296 * @param Pointer_Event Pointer to PE.
298 * @ingroup Elm_Gesture_Layer
301 _remove_touched_device(Eina_List *list, Pointer_Event *pe)
303 Pointer_Event *p = eina_list_search_unsorted(list, device_is_touched, pe);
307 return eina_list_remove(list, p);
312 /* END - Functions to manage touched-device list */
318 * @param event_info pointer to event.
320 * @ingroup Elm_Gesture_Layer
322 static Evas_Event_Flags
323 _get_event_flag(void *event_info, Evas_Callback_Type event_type)
327 case EVAS_CALLBACK_MOUSE_IN:
328 return ((Evas_Event_Mouse_In *) event_info)->event_flags;
329 case EVAS_CALLBACK_MOUSE_OUT:
330 return ((Evas_Event_Mouse_Out *) event_info)->event_flags;
331 case EVAS_CALLBACK_MOUSE_DOWN:
332 return ((Evas_Event_Mouse_Down *) event_info)->event_flags;
333 case EVAS_CALLBACK_MOUSE_MOVE:
334 return ((Evas_Event_Mouse_Move *) event_info)->event_flags;
335 case EVAS_CALLBACK_MOUSE_UP:
336 return ((Evas_Event_Mouse_Up *) event_info)->event_flags;
337 case EVAS_CALLBACK_MOUSE_WHEEL:
338 return ((Evas_Event_Mouse_Wheel *) event_info)->event_flags;
339 case EVAS_CALLBACK_MULTI_DOWN:
340 return ((Evas_Event_Multi_Down *) event_info)->event_flags;
341 case EVAS_CALLBACK_MULTI_MOVE:
342 return ((Evas_Event_Multi_Move *) event_info)->event_flags;
343 case EVAS_CALLBACK_MULTI_UP:
344 return ((Evas_Event_Multi_Up *) event_info)->event_flags;
345 case EVAS_CALLBACK_KEY_DOWN:
346 return ((Evas_Event_Key_Down *) event_info)->event_flags;
347 case EVAS_CALLBACK_KEY_UP:
348 return ((Evas_Event_Key_Up *) event_info)->event_flags;
350 return EVAS_EVENT_FLAG_NONE;
357 * Sets event flag to value returned from user callback
358 * @param wd Widget Data
359 * @param event_info pointer to event.
360 * @param event_type what type was ev (mouse down, etc...)
361 * @param ev_flags event flags
363 * @ingroup Elm_Gesture_Layer
366 consume_event(Widget_Data *wd, void *event_info,
367 Evas_Callback_Type event_type, Evas_Event_Flags ev_flags)
368 { /* Mark EVAS_EVENT_FLAG_ON_HOLD on events that are used by gesture layer */
369 /* ev_flags != EVAS_EVENT_FLAG_NONE means target used event and g-layer */
370 /* should not refeed this event. */
371 if ((ev_flags) || (!wd->repeat_events))
375 case EVAS_CALLBACK_MOUSE_DOWN:
376 ((Evas_Event_Mouse_Down *) event_info)->event_flags |= ev_flags;
378 case EVAS_CALLBACK_MOUSE_MOVE:
379 ((Evas_Event_Mouse_Move *) event_info)->event_flags |= ev_flags;
381 case EVAS_CALLBACK_MOUSE_UP:
382 ((Evas_Event_Mouse_Up *) event_info)->event_flags |= ev_flags;
384 case EVAS_CALLBACK_MOUSE_WHEEL:
385 ((Evas_Event_Mouse_Wheel *) event_info)->event_flags |= ev_flags;
387 case EVAS_CALLBACK_MULTI_DOWN:
388 ((Evas_Event_Multi_Down *) event_info)->event_flags |= ev_flags;
390 case EVAS_CALLBACK_MULTI_MOVE:
391 ((Evas_Event_Multi_Move *) event_info)->event_flags |= ev_flags;
393 case EVAS_CALLBACK_MULTI_UP:
394 ((Evas_Event_Multi_Up *) event_info)->event_flags |= ev_flags;
396 case EVAS_CALLBACK_KEY_DOWN:
397 ((Evas_Event_Key_Down *) event_info)->event_flags |= ev_flags;
399 case EVAS_CALLBACK_KEY_UP:
400 ((Evas_Event_Key_Up *) event_info)->event_flags |= ev_flags;
411 * Report current state of a gesture by calling user callback.
412 * @param gesture what gesture state we report.
413 * @param info inforamtion for user callback
415 * @ingroup Elm_Gesture_Layer
417 static Evas_Event_Flags
418 _report_state(Gesture_Info *gesture, void *info)
419 { /* We report current state (START, MOVE, END, ABORT), once */
420 #if defined(DEBUG_GESTURE_LAYER)
421 printf("%s reporting gesture=<%d> state=<%d>\n" , __func__, g_type,
424 if ((gesture->state != ELM_GESTURE_STATE_UNDEFINED) &&
425 (gesture->fn[gesture->state].cb))
426 { /* Fill state-info struct and send ptr to user callback */
427 return gesture->fn[gesture->state].cb(
428 gesture->fn[gesture->state].user_data, info);
431 return EVAS_EVENT_FLAG_NONE;
437 * Update state for a given gesture.
438 * We may update gesture state to:
439 * UNDEFINED - current input did not start gesure yet.
440 * START - gesture started according to input.
441 * MOVE - gusture in progress.
442 * END - gesture completed according to input.
443 * ABORT - input does not matches gesure.
444 * note that we may move from UNDEFINED to ABORT
445 * because we may detect that gesture will not START
446 * with a given input.
448 * @param g given gesture to change state.
449 * @param s gesure new state.
450 * @param info buffer to be sent to user callback on report_state.
451 * @param force makes report_state to report the new-state even
452 * if its same as current state. Works for MOVE - gesture in progress.
454 * @ingroup Elm_Gesture_Layer
456 static Evas_Event_Flags
457 _set_state(Gesture_Info *g, Elm_Gesture_State s,
458 void *info, Eina_Bool force)
460 Elm_Gesture_State old_state;
461 if ((g->state == s) && (!force))
462 return EVAS_EVENT_FLAG_NONE;
464 old_state = g->state;
467 g->info = info; /* Information for user callback */
468 if ((g->state == ELM_GESTURE_STATE_ABORT) ||
469 (g->state == ELM_GESTURE_STATE_END))
470 g->test = EINA_FALSE;
472 if ((g->state != ELM_GESTURE_STATE_UNDEFINED) &&
473 (!((old_state == ELM_GESTURE_STATE_UNDEFINED) &&
474 (s == ELM_GESTURE_STATE_ABORT))))
475 return _report_state(g, g->info);
477 return EVAS_EVENT_FLAG_NONE;
483 * This resets all gesture states and sets test-bit.
484 * this is used for restarting gestures to listen to input.
485 * happens after we complete a gesture or no gesture was detected.
486 * @param wd Widget data of the gesture-layer object.
488 * @ingroup Elm_Gesture_Layer
491 _reset_states(Widget_Data *wd)
495 for (i = ELM_GESTURE_FIRST; i < ELM_GESTURE_LAST; i++)
500 _set_state(p, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE);
509 * if gesture was NOT detected AND we only have gestures in ABORT state
510 * we clear history immediately to be ready for input.
512 * @param obj The gesture-layer object.
514 * @ingroup Elm_Gesture_Layer
517 _clear_if_finished(Evas_Object *obj)
519 Widget_Data *wd = elm_widget_data_get(obj);
523 /* Clear history if all we have aborted gestures */
524 Eina_Bool reset_s = EINA_TRUE, all_undefined = EINA_TRUE;
525 for (i = ELM_GESTURE_FIRST ; i < ELM_GESTURE_LAST; i++)
526 { /* If no gesture started and all we have aborted gestures, reset all */
527 Gesture_Info *p = wd->gesture[i];
528 if ((p) && (p->state != ELM_GESTURE_STATE_UNDEFINED))
530 if ((p->state == ELM_GESTURE_STATE_START) ||
531 (p->state == ELM_GESTURE_STATE_MOVE))
532 reset_s = EINA_FALSE;
534 all_undefined = EINA_FALSE;
538 // if ((!wd->touched) || (reset_s && !all_undefined))
539 /* (!wd->touched && reset_s) - don't stop zoom with mouse-wheel */
540 if (reset_s && (!eina_list_count(wd->touched) || !all_undefined))
541 _event_history_clear(obj);
545 _inside(Evas_Coord x1, Evas_Coord y1, Evas_Coord x2, Evas_Coord y2)
547 int w = elm_finger_size_get() >> 1; /* Finger size devided by 2 */
563 /* All *test_reset() funcs are called to clear
564 * gesture intermediate data.
565 * This happens when we need to reset our tests.
566 * for example when gesture is detected or all ABORTed. */
568 _dbl_click_test_reset(Gesture_Info *gesture)
573 Widget_Data *wd = elm_widget_data_get(gesture->obj);
574 if (wd->dbl_timeout) ecore_timer_del(wd->dbl_timeout);
575 wd->dbl_timeout = NULL;
582 EINA_LIST_FREE(((Taps_Type *) gesture->data)->l, data)
583 EINA_LIST_FREE(data, pe)
586 memset(gesture->data, 0, sizeof(Taps_Type));
590 _momentum_test_reset(Gesture_Info *gesture)
598 memset(gesture->data, 0, sizeof(Momentum_Type));
602 _line_data_reset(Line_Data *st)
607 memset(st, 0, sizeof(Line_Data));
608 st->line_angle = ELM_GESTURE_NEGATIVE_ANGLE;
612 _line_test_reset(Gesture_Info *gesture)
620 Line_Type *st = gesture->data;
621 Eina_List *list = st->list;
624 EINA_LIST_FOREACH(list, l, t_line)
627 eina_list_free(list);
632 _zoom_test_reset(Gesture_Info *gesture)
640 Widget_Data *wd = elm_widget_data_get(gesture->obj);
641 Zoom_Type *st = gesture->data;
642 Pointer_Event pe, pe1;
643 Evas_Modifier_Mask mask = evas_key_modifier_mask_get(
644 evas_object_evas_get(wd->target), "Control");
645 evas_object_key_ungrab(wd->target, "Control_L", mask, 0);
646 evas_object_key_ungrab(wd->target, "Control_R", mask, 0);
648 pe.timestamp = pe1.timestamp = 0;
650 if (eina_list_search_unsorted_list(wd->touched, device_is_touched,
652 memcpy(&pe, &st->zoom_st, sizeof(Pointer_Event));
654 if (eina_list_search_unsorted_list(wd->touched, device_is_touched,
656 memcpy(&pe1, &st->zoom_st1, sizeof(Pointer_Event));
658 memset(st, 0, sizeof(Zoom_Type));
660 /* If user released one finger only, restore down-info */
661 if (pe.timestamp && (!pe1.timestamp))
662 memcpy(&st->zoom_st, &pe, sizeof(Pointer_Event));
664 if (pe1.timestamp && (!pe.timestamp))
665 memcpy(&st->zoom_st1, &pe1, sizeof(Pointer_Event));
667 st->zoom_distance_tolerance = wd->zoom_distance_tolerance;
672 _rotate_test_reset(Gesture_Info *gesture)
680 Widget_Data *wd = elm_widget_data_get(gesture->obj);
681 Rotate_Type *st = gesture->data;
682 Pointer_Event pe, pe1;
684 pe.timestamp = pe1.timestamp = 0;
686 if (eina_list_search_unsorted_list(wd->touched, device_is_touched,
688 memcpy(&pe, &st->rotate_st, sizeof(Pointer_Event));
690 if (eina_list_search_unsorted_list(wd->touched, device_is_touched,
692 memcpy(&pe1, &st->rotate_st1, sizeof(Pointer_Event));
694 memset(st, 0, sizeof(Rotate_Type));
696 /* If user released one finger only, restore down-info */
697 if (pe.timestamp && (!pe1.timestamp))
698 memcpy(&st->rotate_st, &pe, sizeof(Pointer_Event));
700 if (pe1.timestamp && (!pe.timestamp))
701 memcpy(&st->rotate_st1, &pe1, sizeof(Pointer_Event));
704 st->info.base_angle = ELM_GESTURE_NEGATIVE_ANGLE;
705 st->rotate_angular_tolerance = wd->rotate_angular_tolerance;
712 * We register callbacks when gesture layer is attached to an object
713 * or when its enabled after disable.
715 * @param obj The gesture-layer object.
717 * @ingroup Elm_Gesture_Layer
720 _register_callbacks(Evas_Object *obj)
722 Widget_Data *wd = elm_widget_data_get(obj);
727 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_DOWN,
729 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_MOVE,
731 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_UP,
734 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_WHEEL,
737 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_DOWN,
739 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_MOVE,
741 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_UP,
744 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_KEY_DOWN,
746 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_KEY_UP,
754 * We unregister callbacks when gesture layer is disabled.
756 * @param obj The gesture-layer object.
758 * @ingroup Elm_Gesture_Layer
761 _unregister_callbacks(Evas_Object *obj)
763 Widget_Data *wd = elm_widget_data_get(obj);
768 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_DOWN,
770 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_MOVE,
772 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_UP,
775 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_WHEEL,
778 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_DOWN,
781 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_MOVE,
784 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_UP,
787 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_KEY_DOWN,
789 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_KEY_UP,
794 /* START - Event history list handling functions */
797 * This function is used to find if device number
798 * is found in a list of devices.
799 * The list contains devices for refeeding *UP event
801 * @ingroup Elm_Gesture_Layer
804 device_in_pending_list(const void *data1, const void *data2)
805 { /* Compare the two device numbers */
806 return (((int) data1) - ((int) data2));
812 * This functions adds device to refeed-pending device list
813 * @ingroup Elm_Gesture_Layer
816 _add_device_pending(Eina_List *list, void *event, Evas_Callback_Type event_type)
818 int device = ELM_MOUSE_DEVICE;
821 case EVAS_CALLBACK_MOUSE_DOWN:
823 case EVAS_CALLBACK_MULTI_DOWN:
824 device = ((Evas_Event_Multi_Down *) event)->device;
830 if (!eina_list_search_unsorted_list(list, device_in_pending_list,
833 return eina_list_append(list, (void *) device);
842 * This functions returns pending-device node
843 * @ingroup Elm_Gesture_Layer
846 _device_is_pending(Eina_List *list, void *event, Evas_Callback_Type event_type)
848 int device = ELM_MOUSE_DEVICE;
851 case EVAS_CALLBACK_MOUSE_UP:
853 case EVAS_CALLBACK_MULTI_UP:
854 device = ((Evas_Event_Multi_Up *) event)->device;
860 return eina_list_search_unsorted_list(list, device_in_pending_list,
867 * This function reports ABORT to all none-detected gestures
868 * Then resets test bits for all desired gesures
869 * and clears input-events history.
870 * note: if no gesture was detected, events from history list
871 * are streamed to the widget because it's unused by layer.
872 * user may cancel refeed of events by setting repeat events.
874 * @param obj The gesture-layer object.
876 * @ingroup Elm_Gesture_Layer
879 _event_history_clear(Evas_Object *obj)
881 Widget_Data *wd = elm_widget_data_get(obj);
886 Evas *e = evas_object_evas_get(obj);
887 Eina_Bool gesture_found = EINA_FALSE;
888 for (i = ELM_GESTURE_FIRST ; i < ELM_GESTURE_LAST; i++)
893 if (p->state == ELM_GESTURE_STATE_END)
894 gesture_found = EINA_TRUE;
896 { /* Report ABORT to all gestures that still not finished */
897 _set_state(p, ELM_GESTURE_STATE_ABORT, wd->gesture[i]->info,
903 _reset_states(wd); /* we are ready to start testing for gestures again */
905 /* Clear all gestures intermediate date */
906 _dbl_click_test_reset(wd->gesture[ELM_GESTURE_N_TAPS]);
907 _dbl_click_test_reset(wd->gesture[ELM_GESTURE_N_DOUBLE_TAPS]);
908 _dbl_click_test_reset(wd->gesture[ELM_GESTURE_N_TRIPLE_TAPS]);
909 _momentum_test_reset(wd->gesture[ELM_GESTURE_MOMENTUM]);
910 _line_test_reset(wd->gesture[ELM_GESTURE_N_LINES]);
911 _line_test_reset(wd->gesture[ELM_GESTURE_N_FLICKS]);
912 _zoom_test_reset(wd->gesture[ELM_GESTURE_ZOOM]);
913 _rotate_test_reset(wd->gesture[ELM_GESTURE_ROTATE]);
915 /* Disable gesture layer so refeeded events won't be consumed by it */
916 _unregister_callbacks(obj);
917 while (wd->event_history_list)
920 t = wd->event_history_list;
921 Eina_List *pending = _device_is_pending(wd->pending,
922 wd->event_history_list->event,
923 wd->event_history_list->event_type);
925 /* Refeed events if no gesture matched input */
926 if (pending || ((!gesture_found) && (!wd->repeat_events)))
928 evas_event_refeed_event(e, wd->event_history_list->event,
929 wd->event_history_list->event_type);
933 wd->pending = eina_list_remove_list(wd->pending, pending);
934 int device = ELM_MOUSE_DEVICE;
935 if (wd->event_history_list->event_type == EVAS_CALLBACK_MULTI_UP)
936 device = ((Evas_Event_Multi_Up *)
937 (wd->event_history_list->event))->device;
940 wd->pending = _add_device_pending(wd->pending,
941 wd->event_history_list->event,
942 wd->event_history_list->event_type);
945 free(wd->event_history_list->event);
946 wd->event_history_list = (Event_History *) eina_inlist_remove(
947 EINA_INLIST_GET(wd->event_history_list),
948 EINA_INLIST_GET(wd->event_history_list));
951 _register_callbacks(obj);
957 * This function copies input events.
958 * We copy event info before adding it to history.
959 * The memory is freed when we clear history.
961 * @param event the event to copy
962 * @param event_type event type to copy
964 * @ingroup Elm_Gesture_Layer
967 _copy_event_info(void *event, Evas_Callback_Type event_type)
971 case EVAS_CALLBACK_MOUSE_DOWN:
972 return COPY_EVENT_INFO((Evas_Event_Mouse_Down *) event);
974 case EVAS_CALLBACK_MOUSE_MOVE:
975 return COPY_EVENT_INFO((Evas_Event_Mouse_Move *) event);
977 case EVAS_CALLBACK_MOUSE_UP:
978 return COPY_EVENT_INFO((Evas_Event_Mouse_Up *) event);
980 case EVAS_CALLBACK_MOUSE_WHEEL:
981 return COPY_EVENT_INFO((Evas_Event_Mouse_Wheel *) event);
983 case EVAS_CALLBACK_MULTI_DOWN:
984 return COPY_EVENT_INFO((Evas_Event_Multi_Down *) event);
986 case EVAS_CALLBACK_MULTI_MOVE:
987 return COPY_EVENT_INFO((Evas_Event_Multi_Move *) event);
989 case EVAS_CALLBACK_MULTI_UP:
990 return COPY_EVENT_INFO((Evas_Event_Multi_Up *) event);
992 case EVAS_CALLBACK_KEY_DOWN:
993 return COPY_EVENT_INFO((Evas_Event_Key_Down *) event);
995 case EVAS_CALLBACK_KEY_UP:
996 return COPY_EVENT_INFO((Evas_Event_Key_Up *) event);
1004 _event_history_add(Evas_Object *obj, void *event, Evas_Callback_Type event_type)
1006 Widget_Data *wd = elm_widget_data_get(obj);
1008 if (!wd) return EINA_FALSE;
1010 ev = malloc(sizeof(Event_History));
1011 ev->event = _copy_event_info(event, event_type); /* Freed on event_history_clear */
1012 ev->event_type = event_type;
1013 wd->event_history_list = (Event_History *) eina_inlist_append(
1014 EINA_INLIST_GET(wd->event_history_list), EINA_INLIST_GET(ev));
1018 /* END - Event history list handling functions */
1021 _del_hook(Evas_Object *obj)
1023 Widget_Data *wd = elm_widget_data_get(obj);
1026 _event_history_clear(obj);
1027 eina_list_free(wd->pending);
1029 Pointer_Event *data;
1030 EINA_LIST_FREE(wd->touched, data)
1033 if (!elm_widget_disabled_get(obj))
1034 _unregister_callbacks(obj);
1036 /* Free all gestures internal data structures */
1038 for (i = 0; i < ELM_GESTURE_LAST; i++)
1041 if (wd->gesture[i]->data)
1042 free(wd->gesture[i]->data);
1044 free(wd->gesture[i]);
1051 compare_match_fingers(const void *data1, const void *data2)
1052 { /* Compare coords of first item in list to cur coords */
1053 const Pointer_Event *pe1 = eina_list_data_get(data1);
1054 const Pointer_Event *pe2 = data2;
1056 if (_inside(pe1->x, pe1->y, pe2->x, pe2->y))
1058 else if (pe1->x < pe2->x)
1062 if (pe1->x == pe2->x)
1063 return pe1->y - pe2->y;
1070 compare_pe_device(const void *data1, const void *data2)
1071 { /* Compare coords of first item in list to cur coords */
1072 const Pointer_Event *pe1 = eina_list_data_get(eina_list_last(data1));
1073 const Pointer_Event *pe2 = data2;
1075 /* Only match if last was a down event */
1076 if ((pe1->event_type != EVAS_CALLBACK_MULTI_DOWN) &&
1077 (pe1->event_type != EVAS_CALLBACK_MOUSE_DOWN))
1081 if (pe1->device == pe2->device)
1083 else if (pe1->device < pe2->device)
1090 _record_pointer_event(Taps_Type *st, Eina_List *pe_list, Pointer_Event *pe,
1091 Widget_Data *wd, void *event_info, Evas_Callback_Type event_type)
1092 { /* Keep copy of pe and record it in list */
1093 Pointer_Event *p = malloc(sizeof(Pointer_Event));
1094 memcpy(p, pe, sizeof(Pointer_Event));
1095 consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_NONE);
1101 /* This will also update middle-point to report to user later */
1102 st->info.x = st->sum_x / st->n_taps;
1103 st->info.y = st->sum_y / st->n_taps;
1104 st->info.timestamp = pe->timestamp;
1108 pe_list = eina_list_append(pe_list, p);
1109 st->l = eina_list_append(st->l, pe_list);
1112 pe_list = eina_list_append(pe_list, p);
1120 * when this timer expires we ABORT double click gesture.
1122 * @param data The gesture-layer object.
1123 * @return cancles callback for this timer.
1125 * @ingroup Elm_Gesture_Layer
1128 _dbl_click_timeout(void *data)
1130 Gesture_Info *gesture = data;
1131 Widget_Data *wd = elm_widget_data_get(gesture->obj);
1133 wd->dbl_timeout = NULL;
1134 _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1135 gesture->info, EINA_FALSE);
1137 _dbl_click_test_reset(gesture);
1138 _clear_if_finished(gesture->obj);
1139 return ECORE_CALLBACK_CANCEL;
1145 * This function checks all click/tap and double/triple taps
1147 * @param obj The gesture-layer object.
1148 * @param pe The recent input event as stored in pe struct.
1149 * @param event_info Original input event pointer.
1150 * @param event_type Type of original input event.
1151 * @param g_type what Gesture we are testing.
1152 * @param taps How many click/taps we test for.
1154 * @ingroup Elm_Gesture_Layer
1157 _dbl_click_test(Evas_Object *obj, Pointer_Event *pe,
1158 void *event_info, Evas_Callback_Type event_type,
1159 Elm_Gesture_Types g_type, int taps)
1160 { /* Here we fill Recent_Taps struct and fire-up click/tap timers */
1161 Widget_Data *wd = elm_widget_data_get(obj);
1164 if (!pe) /* this happens when unhandled event arrived */
1165 return; /* see _make_pointer_event function */
1167 Gesture_Info *gesture = wd->gesture[g_type];
1168 if (!gesture ) return;
1170 if ((gesture->state == ELM_GESTURE_STATE_UNDEFINED) &&
1171 eina_list_count(wd->touched))
1172 return; /* user left a finger on device, do NOT start */
1174 Taps_Type *st = gesture->data;
1176 { /* Allocated once on first time */
1177 st = calloc(1, sizeof(Taps_Type));
1179 _dbl_click_test_reset(gesture);
1182 Eina_List *pe_list = NULL;
1183 Pointer_Event *pe_down = NULL;
1184 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1185 switch (pe->event_type)
1187 case EVAS_CALLBACK_MULTI_DOWN:
1188 case EVAS_CALLBACK_MOUSE_DOWN:
1189 pe_list = eina_list_search_unsorted(st->l, compare_match_fingers, pe);
1190 pe_list = _record_pointer_event(st, pe_list, pe, wd, event_info, event_type);
1191 if ((pe->device == 0) && (eina_list_count(pe_list) == 1))
1192 { /* This is the first mouse down we got */
1193 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
1194 &st->info, EINA_FALSE);
1195 consume_event(wd, event_info, event_type, ev_flag);
1197 /* To test dbl_click/dbl_tap */
1198 /* When this timer expires, gesture ABORTed if not completed */
1199 if (!wd->dbl_timeout && (taps > 1))
1200 wd->dbl_timeout = ecore_timer_add(0.4, _dbl_click_timeout,
1207 case EVAS_CALLBACK_MULTI_UP:
1208 case EVAS_CALLBACK_MOUSE_UP:
1209 pe_list = eina_list_search_unsorted(st->l, compare_pe_device, pe);
1211 return; /* Got only first mouse_down and mouse_up */
1213 pe_list = _record_pointer_event(st, pe_list, pe, wd, event_info, event_type);
1215 if (eina_list_count(pe_list) <= (unsigned int) ((taps - 1) * 2))
1216 return; /* Got only first mouse_down and mouse_up */
1218 /* Get first event in first list, this has to be Mouse Down event */
1219 pe_down = eina_list_data_get(pe_list);
1221 if (_inside(pe_down->x, pe_down->y, pe->x, pe->y))
1227 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1228 &st->info, EINA_FALSE);
1229 consume_event(wd, event_info, event_type, ev_flag);
1233 if (st->count_ups == eina_list_count(st->l))
1235 /* Abort if we found a single click */
1236 if ((taps == 1) && (st->count_ups == 1))
1238 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1239 &st->info, EINA_FALSE);
1240 consume_event(wd, event_info, event_type, ev_flag);
1243 st->info.n = st->count_ups;
1244 ev_flag =_set_state(gesture, ELM_GESTURE_STATE_END,
1245 &st->info, EINA_FALSE);
1246 consume_event(wd, event_info, event_type, ev_flag);
1253 case EVAS_CALLBACK_MULTI_MOVE:
1254 case EVAS_CALLBACK_MOUSE_MOVE:
1255 /* Get first event in first list, this has to be a Mouse Down event */
1256 /* and verify that user didn't move out of this area before next tap */
1257 pe_list = eina_list_search_unsorted(st->l, compare_pe_device, pe);
1260 pe_down = eina_list_data_get(pe_list);
1261 if (!_inside(pe_down->x, pe_down->y, pe->x, pe->y))
1263 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1264 &st->info, EINA_FALSE);
1265 consume_event(wd, event_info, event_type, ev_flag);
1278 * This function computes momentum for MOMENTUM, LINE and FLICK gestures
1279 * This momentum value will be sent to widget when gesture is completed.
1281 * @param momentum pointer to buffer where we record momentum value.
1282 * @param x1 x coord where user started gesture.
1283 * @param y1 y coord where user started gesture.
1284 * @param x2 x coord where user completed gesture.
1285 * @param y2 y coord where user completed gesture.
1286 * @param t1x timestamp for X, when user started gesture.
1287 * @param t1y timestamp for Y, when user started gesture.
1288 * @param t2 timestamp when user completed gesture.
1290 * @ingroup Elm_Gesture_Layer
1293 _set_momentum(Elm_Gesture_Momentum_Info *momentum, Evas_Coord x1, Evas_Coord y1,
1294 Evas_Coord x2, Evas_Coord y2, unsigned int t1x, unsigned int t1y,
1297 Evas_Coord velx = 0, vely = 0, vel;
1298 Evas_Coord dx = x2 - x1;
1299 Evas_Coord dy = y2 - y1;
1303 velx = (dx * 1000) / dtx;
1306 vely = (dy * 1000) / dty;
1308 vel = sqrt((velx * velx) + (vely * vely));
1310 if ((_elm_config->thumbscroll_friction > 0.0) &&
1311 (vel > _elm_config->thumbscroll_momentum_threshold))
1312 { /* report momentum */
1313 momentum->mx = velx;
1314 momentum->my = vely;
1326 * This function is used for computing rotation angle (DEG).
1328 * @param x1 first finger x location.
1329 * @param y1 first finger y location.
1330 * @param x2 second finger x location.
1331 * @param y2 second finger y location.
1333 * @return angle of the line between (x1,y1), (x2,y2) in Radians.
1335 * @ingroup Elm_Gesture_Layer
1338 get_angle(Evas_Coord x1, Evas_Coord y1, Evas_Coord x2, Evas_Coord y2)
1344 if (((int) xx) && ((int) yy))
1351 return RAD_360DEG - a;
1362 return RAD_180DEG + a;
1366 return RAD_180DEG - a;
1372 { /* Horizontal line */
1397 * This function is used for computing the magnitude and direction
1398 * of vector between two points.
1400 * @param x1 first finger x location.
1401 * @param y1 first finger y location.
1402 * @param x2 second finger x location.
1403 * @param y2 second finger y location.
1404 * @param l length computed (output)
1405 * @param a angle computed (output)
1407 * @ingroup Elm_Gesture_Layer
1410 get_vector(Evas_Coord x1, Evas_Coord y1, Evas_Coord x2, Evas_Coord y2,
1411 Evas_Coord *l, double *a)
1416 *l = (Evas_Coord) sqrt(xx*xx + yy*yy);
1417 *a = get_angle(x1, y1, x2, y2);
1421 _get_direction(Evas_Coord x1, Evas_Coord x2)
1434 * This function tests momentum gesture.
1435 * @param obj The gesture-layer object.
1436 * @param pe The recent input event as stored in pe struct.
1437 * @param event_info recent input event.
1438 * @param event_type recent event type.
1439 * @param g_type what Gesture we are testing.
1441 * @ingroup Elm_Gesture_Layer
1444 _momentum_test(Evas_Object *obj, Pointer_Event *pe,
1445 void *event_info, Evas_Callback_Type event_type,
1446 Elm_Gesture_Types g_type)
1448 Widget_Data *wd = elm_widget_data_get(obj);
1450 Gesture_Info *gesture = wd->gesture[g_type];
1451 if (!gesture ) return;
1453 if ((gesture->state == ELM_GESTURE_STATE_UNDEFINED) &&
1454 eina_list_count(wd->touched))
1455 return; /* user left a finger on device, do NOT start */
1457 Momentum_Type *st = gesture->data;
1459 { /* Allocated once on first time */
1460 st = calloc(1, sizeof(Momentum_Type));
1462 _momentum_test_reset(gesture);
1468 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1469 switch (pe->event_type)
1471 case EVAS_CALLBACK_MOUSE_DOWN:
1472 st->line_st.x = st->line_end.x = pe->x;
1473 st->line_st.y = st->line_end.y = pe->y;
1474 st->t_st_x = st->t_st_y = st->t_end = pe->timestamp;
1475 st->xdir = st->ydir = 0;
1476 st->info.x2 = st->info.x1 = pe->x;
1477 st->info.y2 = st->info.y1 = pe->y;
1478 st->info.tx = st->info.ty = pe->timestamp;
1479 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
1480 &st->info, EINA_FALSE);
1481 consume_event(wd, event_info, event_type, ev_flag);
1484 case EVAS_CALLBACK_MOUSE_UP:
1485 /* IGNORE if line info was cleared, like long press, move */
1489 if ((pe->timestamp - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->t_end)
1491 /* Too long of a wait, reset all values */
1492 st->line_st.x = pe->x;
1493 st->line_st.y = pe->y;
1494 st->t_st_y = st->t_st_x = pe->timestamp;
1495 st->xdir = st->ydir = 0;
1498 st->info.x2 = pe->x;
1499 st->info.y2 = pe->y;
1500 st->line_end.x = pe->x;
1501 st->line_end.y = pe->y;
1502 st->t_end = pe->timestamp;
1504 _set_momentum(&st->info, st->line_st.x, st->line_st.y, pe->x, pe->y,
1505 st->t_st_x, st->t_st_y, pe->timestamp);
1507 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END, &st->info,
1509 consume_event(wd, event_info, event_type, ev_flag);
1513 case EVAS_CALLBACK_MOUSE_MOVE:
1514 /* IGNORE if line info was cleared, like long press, move */
1518 if ((pe->timestamp - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->t_end)
1520 /* Too long of a wait, reset all values */
1521 st->line_st.x = pe->x;
1522 st->line_st.y = pe->y;
1523 st->t_st_y = st->t_st_x = pe->timestamp;
1524 st->info.tx = st->t_st_x;
1525 st->info.ty = st->t_st_y;
1526 st->xdir = st->ydir = 0;
1531 xdir = _get_direction(st->line_end.x, pe->x);
1532 ydir = _get_direction(st->line_end.y, pe->y);
1533 if (!xdir || (xdir == (-st->xdir)))
1535 st->line_st.x = st->line_end.x;
1536 st->info.tx = st->t_st_x = st->t_end;
1540 if (!ydir || (ydir == (-st->ydir)))
1542 st->line_st.y = st->line_end.y;
1543 st->info.ty = st->t_st_y = st->t_end;
1548 st->info.x2 = st->line_end.x = pe->x;
1549 st->info.y2 = st->line_end.y = pe->y;
1550 st->t_end = pe->timestamp;
1551 _set_momentum(&st->info, st->line_st.x, st->line_st.y, pe->x, pe->y,
1552 st->t_st_x, st->t_st_y, pe->timestamp);
1553 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_MOVE, &st->info,
1555 consume_event(wd, event_info, event_type, ev_flag);
1558 case EVAS_CALLBACK_MULTI_UP:
1559 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
1561 consume_event(wd, event_info, event_type, ev_flag);
1570 compare_line_device(const void *data1, const void *data2)
1571 { /* Compare device component of line struct */
1572 const Line_Data *ln1 = data1;
1573 const int *device = data2;
1575 if (ln1->t_st) /* Compare only with lines that started */
1576 return (ln1->device - (*device));
1584 * This function construct line struct from input.
1585 * @param info pointer to store line momentum.
1586 * @param st line info to store input data.
1587 * @param pe The recent input event as stored in pe struct.
1589 * @ingroup Elm_Gesture_Layer
1592 _single_line_process(Elm_Gesture_Line_Info *info, Line_Data *st,
1594 { /* Record events and set momentum for line pointed by st */
1598 switch (pe->event_type)
1600 case EVAS_CALLBACK_MOUSE_DOWN:
1601 case EVAS_CALLBACK_MULTI_DOWN:
1602 st->line_st.x = pe->x;
1603 st->line_st.y = pe->y;
1604 st->t_st = pe->timestamp;
1605 st->device = pe->device;
1606 info->momentum.x1 = pe->x;
1607 info->momentum.y1 = pe->y;
1608 info->momentum.tx = pe->timestamp;
1609 info->momentum.ty = pe->timestamp;
1614 case EVAS_CALLBACK_MOUSE_UP:
1615 case EVAS_CALLBACK_MULTI_UP:
1616 /* IGNORE if line info was cleared, like long press, move */
1620 st->line_end.x = pe->x;
1621 st->line_end.y = pe->y;
1622 st->t_end = pe->timestamp;
1625 case EVAS_CALLBACK_MOUSE_MOVE:
1626 case EVAS_CALLBACK_MULTI_MOVE:
1627 /* IGNORE if line info was cleared, like long press, move */
1638 _line_data_reset(st);
1642 info->momentum.x2 = pe->x;
1643 info->momentum.y2 = pe->y;
1644 _set_momentum(&info->momentum, st->line_st.x, st->line_st.y, pe->x, pe->y,
1645 st->t_st, st->t_st, pe->timestamp);
1653 * This function test for (n) line gesture.
1654 * @param obj The gesture-layer object.
1655 * @param pe The recent input event as stored in pe struct.
1656 * @param event_info Original input event pointer.
1657 * @param event_type Type of original input event.
1658 * @param g_type what Gesture we are testing.
1660 * @ingroup Elm_Gesture_Layer
1663 _n_line_test(Evas_Object *obj, Pointer_Event *pe, void *event_info,
1664 Evas_Callback_Type event_type, Elm_Gesture_Types g_type)
1668 Widget_Data *wd = elm_widget_data_get(obj);
1670 Gesture_Info *gesture = wd->gesture[g_type];
1671 if (!gesture ) return;
1673 if ((gesture->state == ELM_GESTURE_STATE_UNDEFINED) &&
1674 eina_list_count(wd->touched))
1675 return; /* user left a finger on device, do NOT start */
1677 Line_Type *st = gesture->data;
1680 st = calloc(1, sizeof(Line_Type));
1684 Line_Data *line = NULL;
1685 Eina_List *list = st->list;
1686 unsigned int i, cnt = eina_list_count(list);
1689 { /* list is not empty, locate this device on list */
1690 line = (Line_Data *) eina_list_search_unsorted(st->list,
1691 compare_line_device, &pe->device);
1694 { /* Try to locate an empty-node */
1695 for (i = 0; i < cnt; i++)
1697 line = eina_list_nth(list, i);
1699 break; /* Found a free node */
1707 { /* List is empty or device not found, new line-struct on START only */
1708 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
1709 (event_type == EVAS_CALLBACK_MULTI_DOWN))
1710 { /* Allocate new item on START */
1711 line = calloc(1, sizeof(Line_Data));
1712 _line_data_reset(line);
1713 list = eina_list_append(list, line);
1718 if (!line) /* This may happen on MOVE that comes before DOWN */
1719 return; /* No line-struct to work with, can't continue testing */
1722 if (_single_line_process(&st->info, line, pe)) /* update st with input */
1723 consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_NONE);
1725 /* Get direction and magnitude of the line */
1727 get_vector(line->line_st.x, line->line_st.y, pe->x, pe->y,
1728 &line->line_length, &angle);
1730 /* These are used later to compare lines length */
1731 Evas_Coord shortest_line_len = line->line_length;
1732 Evas_Coord longest_line_len = line->line_length;
1733 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1735 /* Now update line-state */
1737 { /* Analyze line only if line started */
1738 if (line->line_angle >= 0.0)
1739 { /* if line direction was set, we test if broke tolerance */
1740 double a = fabs(angle - line->line_angle);
1742 double d = (tan(a)) * line->line_length; /* Distance from line */
1743 #if defined(DEBUG_GESTURE_LAYER)
1744 printf("%s a=<%f> d=<%f>\n", __func__, (a * 57.295779513), d);
1746 if ((d > wd->line_distance_tolerance) || (a > wd->line_angular_tolerance))
1747 { /* Broke tolerance: abort line and start a new one */
1748 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1749 &st->info, EINA_FALSE);
1750 consume_event(wd, event_info, event_type, ev_flag);
1755 { /* Record the line angle as it broke minimum length for line */
1756 if (line->line_length >= wd->line_min_length)
1757 st->info.angle = line->line_angle = angle;
1762 if (line->line_angle < 0.0)
1763 { /* it's not a line, too short more close to a tap */
1764 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1765 &st->info, EINA_FALSE);
1766 consume_event(wd, event_info, event_type, ev_flag);
1772 /* Count how many lines already started / ended */
1775 unsigned int tm_start = pe->timestamp;
1776 unsigned int tm_end = pe->timestamp;
1779 double base_angle = ELM_GESTURE_NEGATIVE_ANGLE;
1780 Eina_Bool lines_parallel = EINA_TRUE;
1781 EINA_LIST_FOREACH(list, l, t_line)
1784 base_angle = t_line->line_angle;
1787 if (t_line->line_angle >= 0)
1788 { /* Compare angle only with lines with direction defined */
1789 if (fabs(base_angle - t_line->line_angle) >
1790 wd->line_angular_tolerance)
1791 lines_parallel = EINA_FALSE;
1795 if (t_line->line_length)
1796 { /* update only if this line is used */
1797 if (shortest_line_len > t_line->line_length)
1798 shortest_line_len = t_line->line_length;
1800 if (longest_line_len < t_line->line_length)
1801 longest_line_len = t_line->line_length;
1807 if (t_line->t_st < tm_start)
1808 tm_start = t_line->t_st;
1814 if (t_line->t_end < tm_end)
1815 tm_end = t_line->t_end;
1819 st->info.n = started;
1823 ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
1824 (event_type == EVAS_CALLBACK_MULTI_DOWN)))
1825 { /* user lift one finger then starts again without line-end - ABORT */
1826 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
1828 consume_event(wd, event_info, event_type, ev_flag);
1832 if (!lines_parallel)
1833 { /* Lines are NOT at same direction, abort this gesture */
1834 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
1836 consume_event(wd, event_info, event_type, ev_flag);
1841 /* We report ABORT if lines length are NOT matching when fingers are up */
1842 if ((longest_line_len - shortest_line_len) > (elm_finger_size_get()*2))
1844 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
1846 consume_event(wd, event_info, event_type, ev_flag);
1850 if ((g_type == ELM_GESTURE_N_FLICKS) && ((tm_end - tm_start) > wd->flick_time_limit_ms))
1851 { /* We consider FLICK as a fast line.ABORT if take too long to finish */
1852 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
1854 consume_event(wd, event_info, event_type, ev_flag);
1860 case EVAS_CALLBACK_MOUSE_UP:
1861 case EVAS_CALLBACK_MULTI_UP:
1862 if ((started) && (started == ended))
1864 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END,
1865 &st->info, EINA_FALSE);
1866 consume_event(wd, event_info, event_type, ev_flag);
1871 case EVAS_CALLBACK_MOUSE_DOWN:
1872 case EVAS_CALLBACK_MULTI_DOWN:
1875 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
1876 &st->info, EINA_TRUE);
1877 consume_event(wd, event_info, event_type, ev_flag);
1882 case EVAS_CALLBACK_MOUSE_MOVE:
1883 case EVAS_CALLBACK_MULTI_MOVE:
1886 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_MOVE,
1887 &st->info, EINA_TRUE);
1888 consume_event(wd, event_info, event_type, ev_flag);
1894 return; /* Unhandeld event type */
1901 * This function is used to check if rotation gesture started.
1902 * @param st Contains current rotation values from user input.
1903 * @return TRUE/FALSE if we need to set rotation START.
1905 * @ingroup Elm_Gesture_Layer
1908 rotation_broke_tolerance(Rotate_Type *st)
1910 if (st->info.base_angle < 0)
1911 return EINA_FALSE; /* Angle has to be computed first */
1913 if (st->rotate_angular_tolerance < 0)
1916 double low = st->info.base_angle - st->rotate_angular_tolerance;
1917 double high = st->info.base_angle + st->rotate_angular_tolerance;
1918 double t = st->info.angle;
1931 if (high > RAD_360DEG)
1942 #if defined(DEBUG_GESTURE_LAYER)
1943 printf("%s angle=<%d> low=<%d> high=<%d>\n", __func__, t, low, high);
1945 if ((t < low) || (t > high))
1946 { /* This marks that roation action has started */
1947 st->rotate_angular_tolerance = ELM_GESTURE_NEGATIVE_ANGLE;
1948 st->info.base_angle = st->info.angle; /* Avoid jump in angle value */
1958 * This function is used for computing the gap between fingers.
1959 * It returns the length and center point between fingers.
1961 * @param x1 first finger x location.
1962 * @param y1 first finger y location.
1963 * @param x2 second finger x location.
1964 * @param y2 second finger y location.
1965 * @param x Gets center point x cord (output)
1966 * @param y Gets center point y cord (output)
1968 * @return length of the line between (x1,y1), (x2,y2) in pixels.
1970 * @ingroup Elm_Gesture_Layer
1973 get_finger_gap_length(Evas_Coord x1, Evas_Coord y1, Evas_Coord x2,
1974 Evas_Coord y2, Evas_Coord *x, Evas_Coord *y)
1976 double a, b, xx, yy, gap;
1979 gap = sqrt(xx*xx + yy*yy);
1981 /* START - Compute zoom center point */
1982 /* The triangle defined as follows:
1990 * http://en.wikipedia.org/wiki/Trigonometric_functions
1991 *************************************/
1992 if (((int) xx) && ((int) yy))
1994 double A = atan((yy / xx));
1995 #if defined(DEBUG_GESTURE_LAYER)
1996 printf("xx=<%f> yy=<%f> A=<%f>\n", xx, yy, A);
1998 a = (Evas_Coord) ((gap / 2) * sin(A));
1999 b = (Evas_Coord) ((gap / 2) * cos(A));
2000 *x = (Evas_Coord) ((x2 > x1) ? (x1 + b) : (x2 + b));
2001 *y = (Evas_Coord) ((y2 > y1) ? (y1 + a) : (y2 + a));
2006 { /* horiz line, take half width */
2007 #if defined(DEBUG_GESTURE_LAYER)
2008 printf("==== HORIZ ====\n");
2010 *x = (Evas_Coord) (xx / 2);
2011 *y = (Evas_Coord) (y1);
2015 { /* vert line, take half width */
2016 #if defined(DEBUG_GESTURE_LAYER)
2017 printf("==== VERT ====\n");
2019 *x = (Evas_Coord) (x1);
2020 *y = (Evas_Coord) (yy / 2);
2023 /* END - Compute zoom center point */
2025 return (Evas_Coord) gap;
2031 * This function is used for computing zoom value.
2033 * @param st Pointer to zoom data based on user input.
2034 * @param x1 first finger x location.
2035 * @param y1 first finger y location.
2036 * @param x2 second finger x location.
2037 * @param y2 second finger y location.
2038 * @param factor zoom-factor, used to determine how fast zoom works.
2040 * @return zoom value, when 1.0 means no zoom, 0.5 half size...
2042 * @ingroup Elm_Gesture_Layer
2044 /* FIXME change float to double */
2046 compute_zoom(Zoom_Type *st, Evas_Coord x1, Evas_Coord y1, unsigned int tm1,
2047 Evas_Coord x2, Evas_Coord y2, unsigned int tm2, double zoom_finger_factor)
2050 Evas_Coord diam = get_finger_gap_length(x1, y1, x2, y2,
2051 &st->info.x, &st->info.y);
2053 st->info.radius = diam / 2;
2057 st->zoom_base = diam;
2058 return st->info.zoom;
2061 if (st->zoom_distance_tolerance)
2062 { /* zoom tolerance <> ZERO, means zoom action NOT started yet */
2063 if (diam < (st->zoom_base - st->zoom_distance_tolerance))
2064 { /* avoid jump with zoom value when break tolerance */
2065 st->zoom_base -= st->zoom_distance_tolerance;
2066 st->zoom_distance_tolerance = 0;
2069 if (diam > (st->zoom_base + st->zoom_distance_tolerance))
2070 { /* avoid jump with zoom value when break tolerance */
2071 st->zoom_base += st->zoom_distance_tolerance;
2072 st->zoom_distance_tolerance = 0;
2078 /* We use factor only on the difference between gap-base */
2079 /* if gap=120, base=100, we get ((120-100)/100)=0.2*factor */
2080 rt = ((1.0) + ((((float) diam - (float) st->zoom_base) /
2081 (float) st->zoom_base) * zoom_finger_factor));
2084 /* Momentum: zoom per second: (NOT YET SUPPORTED) */
2085 st->info.momentum = (((rt - 1.0) * 1000) / (tm2 - tm1));
2096 * This function handles zoom with mouse wheel.
2097 * thats a combination of wheel + CTRL key.
2098 * @param obj The gesture-layer object.
2099 * @param event_info Original input event pointer.
2100 * @param event_type Type of original input event.
2101 * @param g_type what Gesture we are testing.
2103 * @ingroup Elm_Gesture_Layer
2106 _zoom_with_wheel_test(Evas_Object *obj, void *event_info,
2107 Evas_Callback_Type event_type, Elm_Gesture_Types g_type)
2109 Widget_Data *wd = elm_widget_data_get(obj);
2111 if (!wd->gesture[g_type]) return;
2113 Gesture_Info *gesture_zoom = wd->gesture[g_type];
2114 Zoom_Type *st = gesture_zoom->data;
2115 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2117 { /* Allocated once on first time, used for zoom intermediate data */
2118 st = calloc(1, sizeof(Zoom_Type));
2119 gesture_zoom->data = st;
2120 _zoom_test_reset(gesture_zoom);
2125 case EVAS_CALLBACK_KEY_UP:
2127 Evas_Event_Key_Up *p = event_info;
2128 if ((!strcmp(p->keyname, "Control_L")) ||
2129 (!strcmp(p->keyname, "Control_R")))
2130 { /* Test if we ended a zoom gesture when releasing CTRL */
2131 if ((st->zoom_wheel) &&
2132 ((gesture_zoom->state == ELM_GESTURE_STATE_START) ||
2133 (gesture_zoom->state == ELM_GESTURE_STATE_MOVE)))
2134 { /* User released CTRL after zooming */
2135 ev_flag = _set_state(gesture_zoom,
2136 ELM_GESTURE_STATE_END, &st->info, EINA_FALSE);
2137 consume_event(wd, event_info, event_type, ev_flag);
2145 case EVAS_CALLBACK_MOUSE_WHEEL:
2148 Elm_Gesture_State s;
2149 if (!evas_key_modifier_is_set(
2150 ((Evas_Event_Mouse_Wheel *) event_info)->modifiers,
2152 { /* if using wheel witout CTRL after starting zoom */
2153 if ((st->zoom_wheel) &&
2154 ((gesture_zoom->state == ELM_GESTURE_STATE_START) ||
2155 (gesture_zoom->state == ELM_GESTURE_STATE_MOVE)))
2157 ev_flag = _set_state(gesture_zoom,
2158 ELM_GESTURE_STATE_END, &st->info, EINA_FALSE);
2159 consume_event(wd, event_info, event_type, ev_flag);
2164 return; /* Ignore mouse-wheel without control */
2167 /* Using mouse wheel with CTRL for zoom */
2168 if (st->zoom_wheel || (st->zoom_distance_tolerance == 0))
2169 { /* when (zoom_wheel == NULL) and (zoom_distance_tolerance == 0)
2170 we continue a zoom gesture */
2172 s = ELM_GESTURE_STATE_MOVE;
2175 { /* On first wheel event, report START */
2176 Evas_Modifier_Mask mask = evas_key_modifier_mask_get(
2177 evas_object_evas_get(wd->target), "Control");
2179 s = ELM_GESTURE_STATE_START;
2180 if (!evas_object_key_grab(wd->target, "Control_L", mask, 0, EINA_FALSE))
2181 ERR("Failed to Grabbed CTRL_L");
2182 if (!evas_object_key_grab(wd->target, "Control_R", mask, 0, EINA_FALSE))
2183 ERR("Failed to Grabbed CTRL_R");
2186 st->zoom_distance_tolerance = 0; /* Cancel tolerance */
2187 st->zoom_wheel = (Evas_Event_Mouse_Wheel *) event_info;
2188 st->info.x = st->zoom_wheel->canvas.x;
2189 st->info.y = st->zoom_wheel->canvas.y;
2191 if (st->zoom_wheel->z < 0) /* zoom in */
2192 st->info.zoom += (wd->zoom_finger_factor * wd->zoom_wheel_factor);
2194 if (st->zoom_wheel->z > 0) /* zoom out */
2195 st->info.zoom -= (wd->zoom_finger_factor * wd->zoom_wheel_factor);
2197 if (st->info.zoom < 0.0)
2198 st->info.zoom = 0.0;
2200 ev_flag = _set_state(gesture_zoom, s, &st->info, force);
2201 consume_event(wd, event_info, event_type, ev_flag);
2213 * This function is used to test zoom gesture.
2214 * user may combine zoom, rotation together.
2215 * so its possible that both will be detected from input.
2216 * (both are two-finger movement-oriented gestures)
2218 * @param obj The gesture-layer object.
2219 * @param event_info Pointer to recent input event.
2220 * @param event_type Recent input event type.
2221 * @param g_type what Gesture we are testing.
2223 * @ingroup Elm_Gesture_Layer
2226 _zoom_test(Evas_Object *obj, Pointer_Event *pe, void *event_info,
2227 Evas_Callback_Type event_type, Elm_Gesture_Types g_type)
2231 Widget_Data *wd = elm_widget_data_get(obj);
2233 if (!wd->gesture[g_type]) return;
2235 Gesture_Info *gesture_zoom = wd->gesture[g_type];
2236 Zoom_Type *st = gesture_zoom->data;
2239 { /* Allocated once on first time, used for zoom data */
2240 st = calloc(1, sizeof(Zoom_Type));
2241 gesture_zoom->data = st;
2242 _zoom_test_reset(gesture_zoom);
2245 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2248 case EVAS_CALLBACK_MOUSE_DOWN:
2249 consume_event(wd, event_info, event_type, ev_flag);
2250 memcpy(&st->zoom_st, pe, sizeof(Pointer_Event));
2254 case EVAS_CALLBACK_MOUSE_MOVE:
2255 consume_event(wd, event_info, event_type, ev_flag);
2256 if (!st->zoom_st.timestamp)
2257 return; /* we got move event before down event.Ignore it */
2259 consume_event(wd, event_info, event_type, ev_flag);
2260 memcpy(&st->zoom_mv, pe, sizeof(Pointer_Event));
2262 /* We match this point to previous multi-move or multi-down event */
2263 if (st->zoom_mv1.timestamp)
2265 st->info.zoom = compute_zoom(st,
2266 st->zoom_mv.x, st->zoom_mv.y, st->zoom_mv.timestamp,
2267 st->zoom_mv1.x, st->zoom_mv1.y, st->zoom_mv1.timestamp,
2268 wd->zoom_finger_factor);
2272 if (st->zoom_st1.timestamp)
2274 st->info.zoom = compute_zoom(st,
2275 st->zoom_mv.x, st->zoom_mv.y, st->zoom_mv.timestamp,
2276 st->zoom_st1.x, st->zoom_st1.y, st->zoom_st1.timestamp,
2277 wd->zoom_finger_factor);
2283 case EVAS_CALLBACK_MULTI_MOVE:
2284 if (!st->zoom_st1.timestamp)
2285 return; /* We get move event before down event.Ignore it */
2287 consume_event(wd, event_info, event_type, ev_flag);
2288 if (st->zoom_mv1.timestamp)
2290 if (st->zoom_mv1.device !=
2291 ((Evas_Event_Multi_Move *) event_info)->device)
2292 { /* A third finger on screen, abort zoom */
2293 ev_flag = _set_state(gesture_zoom,
2294 ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE);
2295 consume_event(wd, event_info, event_type, ev_flag);
2301 memcpy(&st->zoom_mv1, pe, sizeof(Pointer_Event));
2303 /* Match this point to previous mouse-move or mouse-down event */
2304 if (st->zoom_mv.timestamp)
2306 st->info.zoom = compute_zoom(st,
2307 st->zoom_mv1.x, st->zoom_mv1.y, st->zoom_mv1.timestamp,
2308 st->zoom_mv.x, st->zoom_mv.y, st->zoom_mv.timestamp,
2309 wd->zoom_finger_factor);
2313 if (st->zoom_st.timestamp)
2315 st->info.zoom = compute_zoom(st,
2316 st->zoom_mv1.x, st->zoom_mv1.y, st->zoom_mv1.timestamp,
2317 st->zoom_st.x, st->zoom_st.y, st->zoom_st.timestamp,
2318 wd->zoom_finger_factor);
2324 case EVAS_CALLBACK_MULTI_DOWN:
2325 consume_event(wd, event_info, event_type, ev_flag);
2326 memcpy(&st->zoom_st1, pe, sizeof(Pointer_Event));
2329 case EVAS_CALLBACK_MOUSE_UP:
2330 case EVAS_CALLBACK_MULTI_UP:
2331 /* Reset timestamp of finger-up.This is used later
2332 by _zoom_test_reset() to retain finger-down data */
2333 consume_event(wd, event_info, event_type, ev_flag);
2334 if (((st->zoom_wheel) || (st->zoom_base)) &&
2335 (st->zoom_distance_tolerance == 0))
2337 ev_flag = _set_state(gesture_zoom, ELM_GESTURE_STATE_END,
2338 &st->info, EINA_FALSE);
2339 consume_event(wd, event_info, event_type, ev_flag);
2344 /* if we got here not a ZOOM */
2345 if (gesture_zoom->state != ELM_GESTURE_STATE_UNDEFINED)
2346 { /* Must be != undefined, if gesture started */
2347 ev_flag = _set_state(gesture_zoom,
2348 ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE);
2349 consume_event(wd, event_info, event_type, ev_flag);
2352 _zoom_test_reset(gesture_zoom);
2361 if (!st->zoom_distance_tolerance)
2362 if ((event_type == EVAS_CALLBACK_MOUSE_MOVE) ||
2363 (event_type == EVAS_CALLBACK_MULTI_MOVE))
2365 { /* Zoom broke tolerance, report move */
2366 double d = st->info.zoom - st->next_step;
2370 if (d >= wd->zoom_step)
2371 { /* Report move in steps */
2372 st->next_step = st->info.zoom;
2374 ev_flag = _set_state(gesture_zoom, ELM_GESTURE_STATE_MOVE,
2375 &st->info, EINA_TRUE);
2376 consume_event(wd, event_info, event_type, ev_flag);
2383 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
2384 (event_type == EVAS_CALLBACK_MULTI_DOWN))
2385 { /* report zoom start finger location is zoom-center temporarly */
2386 /* Zoom may have started with mouse-wheel, don't report START */
2387 if ((st->zoom_st.timestamp) && (st->zoom_st1.timestamp))
2388 { /* Set zoom-base after BOTH down events were recorded */
2389 /* Compute length of line between fingers on zoom start */
2390 st->info.zoom = 1.0;
2391 st->zoom_base = get_finger_gap_length(st->zoom_st1.x,
2392 st->zoom_st1.y, st->zoom_st.x, st->zoom_st.y,
2393 &st->info.x, &st->info.y);
2395 st->info.radius = st->zoom_base / 2;
2397 if ((gesture_zoom->state != ELM_GESTURE_STATE_START) &&
2398 (gesture_zoom->state != ELM_GESTURE_STATE_MOVE))
2399 { /* Report START only when two fingers touching */
2400 ev_flag = _set_state(gesture_zoom,
2401 ELM_GESTURE_STATE_START, &st->info, EINA_FALSE);
2402 consume_event(wd, event_info, event_type, ev_flag);
2411 _get_rotate_properties(Rotate_Type *st,
2412 Evas_Coord x1, Evas_Coord y1, unsigned int tm1,
2413 Evas_Coord x2, Evas_Coord y2, unsigned int tm2,
2416 st->info.radius = get_finger_gap_length(x1, y1, x2, y2,
2417 &st->info.x, &st->info.y) / 2;
2419 *angle = get_angle(x1, y1, x2, y2);
2420 #if 0 /* (NOT YET SUPPORTED) */
2421 if (angle == &st->info.angle)
2422 { /* Compute momentum: TODO: bug when breaking 0, 360 values */
2423 st->info.momentum = (((*angle) - st->info.base_angle) /
2424 (fabs(tm2 - tm1))) * 1000;
2427 st->info.momentum = 0;
2437 * This function is used to test rotation gesture.
2438 * user may combine zoom, rotation together.
2439 * so its possible that both will be detected from input.
2440 * (both are two-finger movement-oriented gestures)
2442 * @param obj The gesture-layer object.
2443 * @param event_info Pointer to recent input event.
2444 * @param event_type Recent input event type.
2445 * @param g_type what Gesture we are testing.
2447 * @ingroup Elm_Gesture_Layer
2450 _rotate_test(Evas_Object *obj, Pointer_Event *pe, void *event_info,
2451 Evas_Callback_Type event_type, Elm_Gesture_Types g_type)
2456 Widget_Data *wd = elm_widget_data_get(obj);
2458 if (!wd->gesture[g_type]) return;
2460 Gesture_Info *gesture = wd->gesture[g_type];
2461 Rotate_Type *st = gesture->data;
2466 { /* Allocated once on first time */
2467 st = calloc(1, sizeof(Rotate_Type));
2469 _rotate_test_reset(gesture);
2473 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2477 case EVAS_CALLBACK_MOUSE_DOWN:
2478 consume_event(wd, event_info, event_type, ev_flag);
2479 memcpy(&st->rotate_st, pe, sizeof(Pointer_Event));
2483 case EVAS_CALLBACK_MOUSE_MOVE:
2484 if (!st->rotate_st.timestamp)
2485 break; /* We got move event before down event.Ignore it */
2487 consume_event(wd, event_info, event_type, ev_flag);
2488 memcpy(&st->rotate_mv, pe, sizeof(Pointer_Event));
2490 /* Match this point to previous multi-move or multi-down event */
2491 if (st->rotate_mv1.timestamp)
2492 { /* Compute rotation angle and report to user */
2493 _get_rotate_properties(st,
2494 st->rotate_mv.x, st->rotate_mv.y, st->rotate_mv.timestamp,
2495 st->rotate_mv1.x, st->rotate_mv1.y, st->rotate_mv1.timestamp,
2500 if (st->rotate_st1.timestamp)
2501 { /* Compute rotation angle and report to user */
2502 _get_rotate_properties(st,
2503 st->rotate_mv.x, st->rotate_mv.y, st->rotate_mv.timestamp,
2504 st->rotate_st1.x, st->rotate_st1.y, st->rotate_st1.timestamp,
2511 case EVAS_CALLBACK_MULTI_MOVE:
2512 if (!st->rotate_st1.timestamp)
2513 break; /* We got move event before down event.Ignore it */
2515 consume_event(wd, event_info, event_type, ev_flag);
2516 if (st->rotate_mv1.timestamp)
2518 if (st->rotate_mv1.device !=
2519 ((Evas_Event_Multi_Move *) event_info)->device)
2520 { /* A third finger on screen, abort rotate */
2521 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
2522 &st->info, EINA_FALSE);
2523 consume_event(wd, event_info, event_type, ev_flag);
2529 memcpy(&st->rotate_mv1, pe, sizeof(Pointer_Event));
2531 /* Match this point to previous mouse-move or mouse-down event */
2532 if (st->rotate_mv.timestamp)
2533 { /* Compute rotation angle and report to user */
2534 _get_rotate_properties(st,
2535 st->rotate_mv.x, st->rotate_mv.y, st->rotate_mv.timestamp,
2536 st->rotate_mv1.x, st->rotate_mv1.y, st->rotate_mv1.timestamp,
2541 if (st->rotate_st.timestamp)
2542 { /* Compute rotation angle and report to user */
2543 _get_rotate_properties(st,
2544 st->rotate_st.x, st->rotate_st.y, st->rotate_st.timestamp,
2545 st->rotate_mv1.x, st->rotate_mv1.y, st->rotate_mv1.timestamp,
2552 case EVAS_CALLBACK_MULTI_DOWN:
2553 consume_event(wd, event_info, event_type, ev_flag);
2554 memcpy(&st->rotate_st1, pe, sizeof(Pointer_Event));
2555 _get_rotate_properties(st,
2556 st->rotate_st.x, st->rotate_st.y, st->rotate_st.timestamp,
2557 st->rotate_st1.x, st->rotate_st1.y, st->rotate_st1.timestamp,
2561 case EVAS_CALLBACK_MOUSE_UP:
2562 case EVAS_CALLBACK_MULTI_UP:
2563 consume_event(wd, event_info, event_type, ev_flag);
2564 /* Reset timestamp of finger-up.This is used later
2565 by rotate_test_reset() to retain finger-down data */
2566 if (st->rotate_angular_tolerance < 0)
2568 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END,
2569 &st->info, EINA_FALSE);
2570 consume_event(wd, event_info, event_type, ev_flag);
2575 if (gesture->state != ELM_GESTURE_STATE_UNDEFINED)
2576 { /* Must be != undefined, if gesture started */
2577 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
2578 &st->info, EINA_FALSE);
2579 consume_event(wd, event_info, event_type, ev_flag);
2582 _rotate_test_reset(gesture);
2589 if ((event_type == EVAS_CALLBACK_MOUSE_MOVE) ||
2590 (event_type == EVAS_CALLBACK_MULTI_MOVE))
2591 { /* Report MOVE or ABORT for *MOVE event */
2592 if (rotation_broke_tolerance(st))
2593 { /* Rotation broke tolerance, report move */
2594 double d = st->info.angle - st->next_step;
2598 if (d >= wd->rotate_step)
2599 { /* Report move in steps */
2600 st->next_step = st->info.angle;
2602 ev_flag = _set_state(gesture,
2603 ELM_GESTURE_STATE_MOVE, &st->info, EINA_TRUE);
2604 consume_event(wd, event_info, event_type, ev_flag);
2611 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
2612 (event_type == EVAS_CALLBACK_MULTI_DOWN))
2614 if ((st->rotate_st.timestamp) && (st->rotate_st1.timestamp))
2615 { /* two-fingers on touch screen - report rotate start */
2616 /* Set base angle, then report start. */
2617 _get_rotate_properties(st,
2618 st->rotate_st.x, st->rotate_st.y, st->rotate_st.timestamp,
2619 st->rotate_st1.x, st->rotate_st1.y, st->rotate_st1.timestamp,
2620 &st->info.base_angle);
2622 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
2623 &st->info, EINA_FALSE);
2624 consume_event(wd, event_info, event_type, ev_flag);
2634 * This function is used to save input events in an abstract struct
2635 * to be used later by getsure-testing functions.
2637 * @param data The gesture-layer object.
2638 * @param event_info Pointer to recent input event.
2639 * @param event_type Recent input event type.
2640 * @param pe The abstract data-struct (output).
2642 * @ingroup Elm_Gesture_Layer
2645 _make_pointer_event(void *data, void *event_info,
2646 Evas_Callback_Type event_type, Pointer_Event *pe)
2648 Widget_Data *wd = elm_widget_data_get(data);
2649 if (!wd) return EINA_FALSE;
2653 case EVAS_CALLBACK_MOUSE_DOWN:
2654 pe->x = ((Evas_Event_Mouse_Down *) event_info)->canvas.x;
2655 pe->y = ((Evas_Event_Mouse_Down *) event_info)->canvas.y;
2656 pe->timestamp = ((Evas_Event_Mouse_Down *) event_info)->timestamp;
2657 pe->device = ELM_MOUSE_DEVICE;
2660 case EVAS_CALLBACK_MOUSE_UP:
2661 pe->x = ((Evas_Event_Mouse_Up *) event_info)->canvas.x;
2662 pe->y = ((Evas_Event_Mouse_Up *) event_info)->canvas.y;
2663 pe->timestamp = ((Evas_Event_Mouse_Up *) event_info)->timestamp;
2664 pe->device = ELM_MOUSE_DEVICE;
2667 case EVAS_CALLBACK_MOUSE_MOVE:
2668 pe->x = ((Evas_Event_Mouse_Move *) event_info)->cur.canvas.x;
2669 pe->y = ((Evas_Event_Mouse_Move *) event_info)->cur.canvas.y;
2670 pe->timestamp = ((Evas_Event_Mouse_Move *) event_info)->timestamp;
2671 pe->device = ELM_MOUSE_DEVICE;
2674 case EVAS_CALLBACK_MULTI_DOWN:
2675 pe->x = ((Evas_Event_Multi_Down *) event_info)->canvas.x;
2676 pe->y = ((Evas_Event_Multi_Down *) event_info)->canvas.y;
2677 pe->timestamp = ((Evas_Event_Multi_Down *) event_info)->timestamp;
2678 pe->device = ((Evas_Event_Multi_Down *) event_info)->device;
2681 case EVAS_CALLBACK_MULTI_UP:
2682 pe->x = ((Evas_Event_Multi_Up *) event_info)->canvas.x;
2683 pe->y = ((Evas_Event_Multi_Up *) event_info)->canvas.y;
2684 pe->timestamp = ((Evas_Event_Multi_Up *) event_info)->timestamp;
2685 pe->device = ((Evas_Event_Multi_Up *) event_info)->device;
2688 case EVAS_CALLBACK_MULTI_MOVE:
2689 pe->x = ((Evas_Event_Multi_Move *) event_info)->cur.canvas.x;
2690 pe->y = ((Evas_Event_Multi_Move *) event_info)->cur.canvas.y;
2691 pe->timestamp = ((Evas_Event_Multi_Move *) event_info)->timestamp;
2692 pe->device = ((Evas_Event_Multi_Move *) event_info)->device;
2699 pe->event_type = event_type;
2706 * This function the core-function where input handling is done.
2707 * Here we get user input and stream it to gesture testing.
2708 * We notify user about any gestures with new state:
2710 * START - gesture started.
2711 * MOVE - gesture is ongoing.
2712 * END - gesture was completed.
2713 * ABORT - gesture was aborted after START, MOVE (will NOT be completed)
2715 * We also check if a gesture was detected, then reset event history
2716 * If no gestures were found we reset gesture test flag
2717 * after streaming event-history to widget.
2718 * (stream to the widget all events not consumed as a gesture)
2720 * @param data The gesture-layer object.
2721 * @param event_info Pointer to recent input event.
2722 * @param event_type Recent input event type.
2724 * @ingroup Elm_Gesture_Layer
2727 _event_process(void *data, Evas_Object *obj __UNUSED__,
2728 void *event_info, Evas_Callback_Type event_type)
2731 Pointer_Event *pe = NULL;
2732 Widget_Data *wd = elm_widget_data_get(data);
2735 /* Start testing candidate gesture from here */
2736 if (_make_pointer_event(data, event_info, event_type, &_pe))
2739 if (IS_TESTED(ELM_GESTURE_N_TAPS))
2740 _dbl_click_test(data, pe, event_info, event_type,
2741 ELM_GESTURE_N_TAPS, 1);
2743 if (IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS))
2744 _dbl_click_test(data, pe, event_info, event_type,
2745 ELM_GESTURE_N_DOUBLE_TAPS, 2);
2747 if (IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS))
2748 _dbl_click_test(data, pe, event_info, event_type,
2749 ELM_GESTURE_N_TRIPLE_TAPS, 3);
2751 if (IS_TESTED(ELM_GESTURE_MOMENTUM))
2752 _momentum_test(data, pe, event_info, event_type,
2753 ELM_GESTURE_MOMENTUM);
2755 if (IS_TESTED(ELM_GESTURE_N_LINES))
2756 _n_line_test(data, pe, event_info, event_type, ELM_GESTURE_N_LINES);
2758 if (IS_TESTED(ELM_GESTURE_N_FLICKS))
2759 _n_line_test(data, pe, event_info, event_type, ELM_GESTURE_N_FLICKS);
2761 if (IS_TESTED(ELM_GESTURE_ZOOM))
2762 _zoom_test(data, pe, event_info, event_type, ELM_GESTURE_ZOOM);
2764 if (IS_TESTED(ELM_GESTURE_ZOOM))
2765 _zoom_with_wheel_test(data, event_info, event_type, ELM_GESTURE_ZOOM);
2767 if (IS_TESTED(ELM_GESTURE_ROTATE))
2768 _rotate_test(data, pe, event_info, event_type, ELM_GESTURE_ROTATE);
2770 if (_get_event_flag(event_info, event_type) & EVAS_EVENT_FLAG_ON_HOLD)
2771 _event_history_add(data, event_info, event_type);
2772 else if ((event_type == EVAS_CALLBACK_MOUSE_UP) ||
2773 (event_type == EVAS_CALLBACK_MULTI_UP))
2775 Eina_List *pending = _device_is_pending(wd->pending, event_info, event_type);
2778 consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_ON_HOLD);
2779 _event_history_add(data, event_info, event_type);
2783 /* we maintain list of touched devices*/
2784 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
2785 (event_type == EVAS_CALLBACK_MULTI_DOWN))
2787 wd->touched = _add_touched_device(wd->touched, pe);
2789 else if ((event_type == EVAS_CALLBACK_MOUSE_UP) ||
2790 (event_type == EVAS_CALLBACK_MULTI_UP))
2792 wd->touched = _remove_touched_device(wd->touched, pe);
2795 /* Report current states and clear history if needed */
2796 _clear_if_finished(data);
2801 * For all _mouse_* / multi_* functions wethen send this event to
2802 * _event_process function.
2804 * @param data The gesture-layer object.
2805 * @param event_info Pointer to recent input event.
2807 * @ingroup Elm_Gesture_Layer
2810 _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
2813 Widget_Data *wd = elm_widget_data_get(data);
2815 if (((Evas_Event_Mouse_Down *) event_info)->button != 1)
2816 return; /* We only process left-click at the moment */
2818 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_DOWN);
2822 _mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
2825 Widget_Data *wd = elm_widget_data_get(data);
2828 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_MOVE);
2832 _key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
2835 Widget_Data *wd = elm_widget_data_get(data);
2838 _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_DOWN);
2842 _key_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
2845 Widget_Data *wd = elm_widget_data_get(data);
2848 _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_UP);
2852 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
2855 Widget_Data *wd = elm_widget_data_get(data);
2858 if (((Evas_Event_Mouse_Up *) event_info)->button != 1)
2859 return; /* We only process left-click at the moment */
2861 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_UP);
2865 _mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
2868 Widget_Data *wd = elm_widget_data_get(data);
2871 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_WHEEL);
2875 _multi_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
2878 Widget_Data *wd = elm_widget_data_get(data);
2881 _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_DOWN);
2885 _multi_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
2888 Widget_Data *wd = elm_widget_data_get(data);
2891 _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_MOVE);
2895 _multi_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
2898 Widget_Data *wd = elm_widget_data_get(data);
2901 _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_UP);
2905 elm_gesture_layer_hold_events_get(Evas_Object *obj)
2907 Widget_Data *wd = elm_widget_data_get(obj);
2908 if (!wd) return EINA_FALSE;
2910 return !wd->repeat_events;
2914 elm_gesture_layer_hold_events_set(Evas_Object *obj, Eina_Bool r)
2916 Widget_Data *wd = elm_widget_data_get(obj);
2919 wd->repeat_events = !r;
2923 elm_gesture_layer_zoom_step_set(Evas_Object *obj, double s)
2925 Widget_Data *wd = elm_widget_data_get(obj);
2935 elm_gesture_layer_rotate_step_set(Evas_Object *obj, double s)
2937 Widget_Data *wd = elm_widget_data_get(obj);
2943 wd->rotate_step = s;
2947 elm_gesture_layer_attach(Evas_Object *obj, Evas_Object *t)
2949 Widget_Data *wd = elm_widget_data_get(obj);
2950 if (!wd) return EINA_FALSE;
2955 /* if was attached before, unregister callbacks first */
2957 _unregister_callbacks(obj);
2961 _register_callbacks(obj);
2966 elm_gesture_layer_cb_set(Evas_Object *obj, Elm_Gesture_Types idx,
2967 Elm_Gesture_State cb_type, Elm_Gesture_Event_Cb cb, void *data)
2969 Widget_Data *wd = elm_widget_data_get(obj);
2972 if (!wd->gesture[idx])
2973 wd->gesture[idx] = calloc(1, sizeof(Gesture_Info));
2975 Gesture_Info *p = wd->gesture[idx];
2978 p->fn[cb_type].cb = cb;
2979 p->fn[cb_type].user_data = data;
2980 p->state = ELM_GESTURE_STATE_UNDEFINED;
2985 _disable_hook(Evas_Object *obj)
2987 if (elm_widget_disabled_get(obj))
2988 _unregister_callbacks(obj);
2990 _register_callbacks(obj);
2994 elm_gesture_layer_add(Evas_Object *parent)
3000 EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
3002 wd = ELM_NEW(Widget_Data);
3003 e = evas_object_evas_get(parent);
3004 if (!e) return NULL;
3005 obj = elm_widget_add(e);
3006 ELM_SET_WIDTYPE(widtype, "gesture_layer");
3007 elm_widget_type_set(obj, "gesture_layer");
3008 elm_widget_sub_object_add(parent, obj);
3009 elm_widget_data_set(obj, wd);
3010 elm_widget_del_hook_set(obj, _del_hook);
3011 elm_widget_disable_hook_set(obj, _disable_hook);
3014 wd->line_min_length =_elm_config->glayer_line_min_length * elm_finger_size_get();
3015 wd->zoom_distance_tolerance = _elm_config->glayer_zoom_distance_tolerance * elm_finger_size_get();
3016 wd->line_distance_tolerance = _elm_config->glayer_line_distance_tolerance * elm_finger_size_get();
3017 wd->zoom_finger_factor = _elm_config->glayer_zoom_finger_factor;
3018 wd->zoom_wheel_factor = _elm_config->glayer_zoom_wheel_factor; /* mouse wheel zoom steps */
3019 wd->rotate_angular_tolerance = _elm_config->glayer_rotate_angular_tolerance;
3020 wd->line_angular_tolerance = _elm_config->glayer_line_angular_tolerance;
3021 wd->flick_time_limit_ms = _elm_config->glayer_flick_time_limit_ms;
3022 wd->repeat_events = EINA_TRUE;
3024 #if defined(DEBUG_GESTURE_LAYER)
3025 printf("size of Gestures = <%d>\n", sizeof(wd->gesture));
3026 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", 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);
3028 memset(wd->gesture, 0, sizeof(wd->gesture));