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 _Long_Tap_Type
158 Elm_Gesture_Taps_Info info;
159 unsigned int center_x;
160 unsigned int center_y;
162 Ecore_Timer *timeout; /* When this expires, long tap STARTed */
165 typedef struct _Long_Tap_Type Long_Tap_Type;
167 struct _Momentum_Type
168 { /* Fields used by _line_test() */
169 Elm_Gesture_Momentum_Info info;
170 Evas_Coord_Point line_st;
171 Evas_Coord_Point line_end;
172 unsigned int t_st_x; /* Time start on X */
173 unsigned int t_st_y; /* Time start on Y */
174 unsigned int t_end; /* Time end */
177 typedef struct _Momentum_Type Momentum_Type;
181 Evas_Coord_Point line_st;
182 Evas_Coord_Point line_end;
183 Evas_Coord line_length;
184 unsigned int t_st; /* Time start */
185 unsigned int t_end; /* Time end */
187 double line_angle; /* Current angle of line */
189 typedef struct _Line_Data Line_Data;
192 { /* Fields used by _line_test() */
193 Elm_Gesture_Line_Info info;
194 Eina_List *list; /* List of Line_Data */
196 typedef struct _Line_Type Line_Type;
199 { /* Fields used by _zoom_test() */
200 Elm_Gesture_Zoom_Info info;
201 Pointer_Event zoom_st;
202 Pointer_Event zoom_mv;
203 Pointer_Event zoom_st1;
204 Pointer_Event zoom_mv1;
205 Evas_Event_Mouse_Wheel *zoom_wheel;
206 Evas_Coord zoom_base; /* Holds gap between fingers on zoom-start */
207 Evas_Coord zoom_distance_tolerance;
210 typedef struct _Zoom_Type Zoom_Type;
213 { /* Fields used by _rotation_test() */
214 Elm_Gesture_Rotate_Info info;
215 Pointer_Event rotate_st;
216 Pointer_Event rotate_mv;
217 Pointer_Event rotate_st1;
218 Pointer_Event rotate_mv1;
219 double rotate_angular_tolerance;
222 typedef struct _Rotate_Type Rotate_Type;
226 Evas_Object *target; /* Target Widget */
227 Event_History *event_history_list;
230 Evas_Coord zoom_distance_tolerance;
231 Evas_Coord line_distance_tolerance;
232 double line_angular_tolerance;
233 double zoom_wheel_factor; /* mouse wheel zoom steps */
234 double zoom_finger_factor; /* used for zoom factor */
235 double rotate_angular_tolerance;
236 unsigned int flick_time_limit_ms;
237 double long_tap_start_timeout;
242 Gesture_Info *gesture[ELM_GESTURE_LAST];
243 Ecore_Timer *dbl_timeout; /* When this expires, dbl click/taps ABORTed */
244 Eina_List *pending; /* List of devices need to refeed *UP event */
245 Eina_List *touched; /* Information of touched devices */
247 Eina_Bool repeat_events : 1;
249 typedef struct _Widget_Data Widget_Data;
251 static const char *widtype = NULL;
252 static void _del_hook(Evas_Object *obj);
254 static void _event_history_clear(Evas_Object *obj);
255 static void _reset_states(Widget_Data *wd);
256 static void _key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
257 static void _key_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
258 static void _zoom_with_wheel_test(Evas_Object *obj, void *event_info, Evas_Callback_Type event_type, Elm_Gesture_Types g_type);
259 static void _mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
260 static void _mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info);
261 static void _mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
262 static void _mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
264 static void _multi_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
265 static void _multi_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
266 static void _multi_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info);
268 /* START - Functions to manage touched-device list */
271 * This function is used to find if device is touched
273 * @ingroup Elm_Gesture_Layer
276 device_is_touched(const void *data1, const void *data2)
277 { /* Compare the two device numbers */
278 return (((Pointer_Event *) data1)->device -((Pointer_Event *) data2)->device);
284 * Recoed Pointer Event in touched device list
285 * Note: This fuction allocates memory for PE event
286 * This memory is released in _remove_touched_device()
287 * @param list Pointer to touched device list.
288 * @param Pointer_Event Pointer to PE.
290 * @ingroup Elm_Gesture_Layer
293 _add_touched_device(Eina_List *list, Pointer_Event *pe)
295 if (eina_list_search_unsorted_list(list, device_is_touched, pe))
298 Pointer_Event *p = malloc(sizeof(Pointer_Event ));
299 memcpy(p, pe, sizeof(Pointer_Event)); /* Freed in _remove_touched_device() */
300 return eina_list_append(list, p);
306 * Remove Pointer Event from touched device list
307 * @param list Pointer to touched device list.
308 * @param Pointer_Event Pointer to PE.
310 * @ingroup Elm_Gesture_Layer
313 _remove_touched_device(Eina_List *list, Pointer_Event *pe)
315 Pointer_Event *p = eina_list_search_unsorted(list, device_is_touched, pe);
319 return eina_list_remove(list, p);
324 /* END - Functions to manage touched-device list */
330 * @param event_info pointer to event.
332 * @ingroup Elm_Gesture_Layer
334 static Evas_Event_Flags
335 _get_event_flag(void *event_info, Evas_Callback_Type event_type)
339 case EVAS_CALLBACK_MOUSE_IN:
340 return ((Evas_Event_Mouse_In *) event_info)->event_flags;
341 case EVAS_CALLBACK_MOUSE_OUT:
342 return ((Evas_Event_Mouse_Out *) event_info)->event_flags;
343 case EVAS_CALLBACK_MOUSE_DOWN:
344 return ((Evas_Event_Mouse_Down *) event_info)->event_flags;
345 case EVAS_CALLBACK_MOUSE_MOVE:
346 return ((Evas_Event_Mouse_Move *) event_info)->event_flags;
347 case EVAS_CALLBACK_MOUSE_UP:
348 return ((Evas_Event_Mouse_Up *) event_info)->event_flags;
349 case EVAS_CALLBACK_MOUSE_WHEEL:
350 return ((Evas_Event_Mouse_Wheel *) event_info)->event_flags;
351 case EVAS_CALLBACK_MULTI_DOWN:
352 return ((Evas_Event_Multi_Down *) event_info)->event_flags;
353 case EVAS_CALLBACK_MULTI_MOVE:
354 return ((Evas_Event_Multi_Move *) event_info)->event_flags;
355 case EVAS_CALLBACK_MULTI_UP:
356 return ((Evas_Event_Multi_Up *) event_info)->event_flags;
357 case EVAS_CALLBACK_KEY_DOWN:
358 return ((Evas_Event_Key_Down *) event_info)->event_flags;
359 case EVAS_CALLBACK_KEY_UP:
360 return ((Evas_Event_Key_Up *) event_info)->event_flags;
362 return EVAS_EVENT_FLAG_NONE;
369 * Sets event flag to value returned from user callback
370 * @param wd Widget Data
371 * @param event_info pointer to event.
372 * @param event_type what type was ev (mouse down, etc...)
373 * @param ev_flags event flags
375 * @ingroup Elm_Gesture_Layer
378 consume_event(Widget_Data *wd, void *event_info,
379 Evas_Callback_Type event_type, Evas_Event_Flags ev_flags)
380 { /* Mark EVAS_EVENT_FLAG_ON_HOLD on events that are used by gesture layer */
381 /* ev_flags != EVAS_EVENT_FLAG_NONE means target used event and g-layer */
382 /* should not refeed this event. */
383 if ((ev_flags) || (!wd->repeat_events))
387 case EVAS_CALLBACK_MOUSE_DOWN:
388 ((Evas_Event_Mouse_Down *) event_info)->event_flags |= ev_flags;
390 case EVAS_CALLBACK_MOUSE_MOVE:
391 ((Evas_Event_Mouse_Move *) event_info)->event_flags |= ev_flags;
393 case EVAS_CALLBACK_MOUSE_UP:
394 ((Evas_Event_Mouse_Up *) event_info)->event_flags |= ev_flags;
396 case EVAS_CALLBACK_MOUSE_WHEEL:
397 ((Evas_Event_Mouse_Wheel *) event_info)->event_flags |= ev_flags;
399 case EVAS_CALLBACK_MULTI_DOWN:
400 ((Evas_Event_Multi_Down *) event_info)->event_flags |= ev_flags;
402 case EVAS_CALLBACK_MULTI_MOVE:
403 ((Evas_Event_Multi_Move *) event_info)->event_flags |= ev_flags;
405 case EVAS_CALLBACK_MULTI_UP:
406 ((Evas_Event_Multi_Up *) event_info)->event_flags |= ev_flags;
408 case EVAS_CALLBACK_KEY_DOWN:
409 ((Evas_Event_Key_Down *) event_info)->event_flags |= ev_flags;
411 case EVAS_CALLBACK_KEY_UP:
412 ((Evas_Event_Key_Up *) event_info)->event_flags |= ev_flags;
423 * Report current state of a gesture by calling user callback.
424 * @param gesture what gesture state we report.
425 * @param info inforamtion for user callback
427 * @ingroup Elm_Gesture_Layer
429 static Evas_Event_Flags
430 _report_state(Gesture_Info *gesture, void *info)
431 { /* We report current state (START, MOVE, END, ABORT), once */
432 #if defined(DEBUG_GESTURE_LAYER)
433 printf("%s reporting gesture=<%d> state=<%d>\n" , __func__, g_type,
436 if ((gesture->state != ELM_GESTURE_STATE_UNDEFINED) &&
437 (gesture->fn[gesture->state].cb))
438 { /* Fill state-info struct and send ptr to user callback */
439 return gesture->fn[gesture->state].cb(
440 gesture->fn[gesture->state].user_data, info);
443 return EVAS_EVENT_FLAG_NONE;
449 * Update state for a given gesture.
450 * We may update gesture state to:
451 * UNDEFINED - current input did not start gesure yet.
452 * START - gesture started according to input.
453 * MOVE - gusture in progress.
454 * END - gesture completed according to input.
455 * ABORT - input does not matches gesure.
456 * note that we may move from UNDEFINED to ABORT
457 * because we may detect that gesture will not START
458 * with a given input.
460 * @param g given gesture to change state.
461 * @param s gesure new state.
462 * @param info buffer to be sent to user callback on report_state.
463 * @param force makes report_state to report the new-state even
464 * if its same as current state. Works for MOVE - gesture in progress.
466 * @ingroup Elm_Gesture_Layer
468 static Evas_Event_Flags
469 _set_state(Gesture_Info *g, Elm_Gesture_State s,
470 void *info, Eina_Bool force)
472 Elm_Gesture_State old_state;
473 if ((g->state == s) && (!force))
474 return EVAS_EVENT_FLAG_NONE;
476 old_state = g->state;
479 g->info = info; /* Information for user callback */
480 if ((g->state == ELM_GESTURE_STATE_ABORT) ||
481 (g->state == ELM_GESTURE_STATE_END))
482 g->test = EINA_FALSE;
484 if ((g->state != ELM_GESTURE_STATE_UNDEFINED) &&
485 (!((old_state == ELM_GESTURE_STATE_UNDEFINED) &&
486 (s == ELM_GESTURE_STATE_ABORT))))
487 return _report_state(g, g->info);
489 return EVAS_EVENT_FLAG_NONE;
495 * This resets all gesture states and sets test-bit.
496 * this is used for restarting gestures to listen to input.
497 * happens after we complete a gesture or no gesture was detected.
498 * @param wd Widget data of the gesture-layer object.
500 * @ingroup Elm_Gesture_Layer
503 _reset_states(Widget_Data *wd)
507 for (i = ELM_GESTURE_FIRST; i < ELM_GESTURE_LAST; i++)
512 _set_state(p, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE);
521 * if gesture was NOT detected AND we only have gestures in ABORT state
522 * we clear history immediately to be ready for input.
524 * @param obj The gesture-layer object.
526 * @ingroup Elm_Gesture_Layer
529 _clear_if_finished(Evas_Object *obj)
531 Widget_Data *wd = elm_widget_data_get(obj);
535 /* Clear history if all we have aborted gestures */
536 Eina_Bool reset_s = EINA_TRUE, all_undefined = EINA_TRUE;
537 for (i = ELM_GESTURE_FIRST ; i < ELM_GESTURE_LAST; i++)
538 { /* If no gesture started and all we have aborted gestures, reset all */
539 Gesture_Info *p = wd->gesture[i];
540 if ((p) && (p->state != ELM_GESTURE_STATE_UNDEFINED))
542 if ((p->state == ELM_GESTURE_STATE_START) ||
543 (p->state == ELM_GESTURE_STATE_MOVE))
544 reset_s = EINA_FALSE;
546 all_undefined = EINA_FALSE;
550 // if ((!wd->touched) || (reset_s && !all_undefined))
551 /* (!wd->touched && reset_s) - don't stop zoom with mouse-wheel */
552 if (reset_s && (!eina_list_count(wd->touched) || !all_undefined))
553 _event_history_clear(obj);
557 _inside(Evas_Coord x1, Evas_Coord y1, Evas_Coord x2, Evas_Coord y2)
559 int w = elm_finger_size_get() >> 1; /* Finger size devided by 2 */
575 /* All *test_reset() funcs are called to clear
576 * gesture intermediate data.
577 * This happens when we need to reset our tests.
578 * for example when gesture is detected or all ABORTed. */
580 _dbl_click_test_reset(Gesture_Info *gesture)
585 Widget_Data *wd = elm_widget_data_get(gesture->obj);
586 if (wd->dbl_timeout) ecore_timer_del(wd->dbl_timeout);
587 wd->dbl_timeout = NULL;
594 EINA_LIST_FREE(((Taps_Type *) gesture->data)->l, data)
595 EINA_LIST_FREE(data, pe)
598 memset(gesture->data, 0, sizeof(Taps_Type));
601 /* All *test_reset() funcs are called to clear
602 * gesture intermediate data.
603 * This happens when we need to reset our tests.
604 * for example when gesture is detected or all ABORTed. */
606 _n_long_tap_test_reset(Gesture_Info *gesture)
614 Long_Tap_Type *st = gesture->data;
615 if (st->timeout) ecore_timer_del(st->timeout);
618 EINA_LIST_FOREACH(st->touched, l, p)
621 eina_list_free(st->touched);
622 memset(gesture->data, 0, sizeof(Long_Tap_Type));
626 _momentum_test_reset(Gesture_Info *gesture)
634 memset(gesture->data, 0, sizeof(Momentum_Type));
638 _line_data_reset(Line_Data *st)
643 memset(st, 0, sizeof(Line_Data));
644 st->line_angle = ELM_GESTURE_NEGATIVE_ANGLE;
648 _line_test_reset(Gesture_Info *gesture)
656 Line_Type *st = gesture->data;
657 Eina_List *list = st->list;
660 EINA_LIST_FOREACH(list, l, t_line)
663 eina_list_free(list);
668 _zoom_test_reset(Gesture_Info *gesture)
676 Widget_Data *wd = elm_widget_data_get(gesture->obj);
677 Zoom_Type *st = gesture->data;
678 Pointer_Event pe, pe1;
679 Evas_Modifier_Mask mask = evas_key_modifier_mask_get(
680 evas_object_evas_get(wd->target), "Control");
681 evas_object_key_ungrab(wd->target, "Control_L", mask, 0);
682 evas_object_key_ungrab(wd->target, "Control_R", mask, 0);
684 pe.timestamp = pe1.timestamp = 0;
686 if (eina_list_search_unsorted_list(wd->touched, device_is_touched,
688 memcpy(&pe, &st->zoom_st, sizeof(Pointer_Event));
690 if (eina_list_search_unsorted_list(wd->touched, device_is_touched,
692 memcpy(&pe1, &st->zoom_st1, sizeof(Pointer_Event));
694 memset(st, 0, sizeof(Zoom_Type));
696 /* If user released one finger only, restore down-info */
697 if (pe.timestamp && (!pe1.timestamp))
698 memcpy(&st->zoom_st, &pe, sizeof(Pointer_Event));
700 if (pe1.timestamp && (!pe.timestamp))
701 memcpy(&st->zoom_st1, &pe1, sizeof(Pointer_Event));
703 st->zoom_distance_tolerance = wd->zoom_distance_tolerance;
708 _rotate_test_reset(Gesture_Info *gesture)
716 Widget_Data *wd = elm_widget_data_get(gesture->obj);
717 Rotate_Type *st = gesture->data;
718 Pointer_Event pe, pe1;
720 pe.timestamp = pe1.timestamp = 0;
722 if (eina_list_search_unsorted_list(wd->touched, device_is_touched,
724 memcpy(&pe, &st->rotate_st, sizeof(Pointer_Event));
726 if (eina_list_search_unsorted_list(wd->touched, device_is_touched,
728 memcpy(&pe1, &st->rotate_st1, sizeof(Pointer_Event));
730 memset(st, 0, sizeof(Rotate_Type));
732 /* If user released one finger only, restore down-info */
733 if (pe.timestamp && (!pe1.timestamp))
734 memcpy(&st->rotate_st, &pe, sizeof(Pointer_Event));
736 if (pe1.timestamp && (!pe.timestamp))
737 memcpy(&st->rotate_st1, &pe1, sizeof(Pointer_Event));
740 st->info.base_angle = ELM_GESTURE_NEGATIVE_ANGLE;
741 st->rotate_angular_tolerance = wd->rotate_angular_tolerance;
748 * We register callbacks when gesture layer is attached to an object
749 * or when its enabled after disable.
751 * @param obj The gesture-layer object.
753 * @ingroup Elm_Gesture_Layer
756 _register_callbacks(Evas_Object *obj)
758 Widget_Data *wd = elm_widget_data_get(obj);
763 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_DOWN,
765 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_MOVE,
767 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_UP,
770 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_WHEEL,
773 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_DOWN,
775 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_MOVE,
777 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_UP,
780 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_KEY_DOWN,
782 evas_object_event_callback_add(wd->target, EVAS_CALLBACK_KEY_UP,
790 * We unregister callbacks when gesture layer is disabled.
792 * @param obj The gesture-layer object.
794 * @ingroup Elm_Gesture_Layer
797 _unregister_callbacks(Evas_Object *obj)
799 Widget_Data *wd = elm_widget_data_get(obj);
804 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_DOWN,
806 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_MOVE,
808 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_UP,
811 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_WHEEL,
814 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_DOWN,
817 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_MOVE,
820 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_UP,
823 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_KEY_DOWN,
825 evas_object_event_callback_del(wd->target, EVAS_CALLBACK_KEY_UP,
830 /* START - Event history list handling functions */
833 * This function is used to find if device number
834 * is found in a list of devices.
835 * The list contains devices for refeeding *UP event
837 * @ingroup Elm_Gesture_Layer
840 device_in_pending_list(const void *data1, const void *data2)
841 { /* Compare the two device numbers */
842 return (((intptr_t) data1) - ((intptr_t) data2));
848 * This functions adds device to refeed-pending device list
849 * @ingroup Elm_Gesture_Layer
852 _add_device_pending(Eina_List *list, void *event, Evas_Callback_Type event_type)
854 int device = ELM_MOUSE_DEVICE;
857 case EVAS_CALLBACK_MOUSE_DOWN:
859 case EVAS_CALLBACK_MULTI_DOWN:
860 device = ((Evas_Event_Multi_Down *) event)->device;
866 if (!eina_list_search_unsorted_list(list, device_in_pending_list,
869 return eina_list_append(list, (intptr_t*) device);
878 * This functions returns pending-device node
879 * @ingroup Elm_Gesture_Layer
882 _device_is_pending(Eina_List *list, void *event, Evas_Callback_Type event_type)
884 int device = ELM_MOUSE_DEVICE;
887 case EVAS_CALLBACK_MOUSE_UP:
889 case EVAS_CALLBACK_MULTI_UP:
890 device = ((Evas_Event_Multi_Up *) event)->device;
896 return eina_list_search_unsorted_list(list, device_in_pending_list,
897 (intptr_t *) device);
903 * This function reports ABORT to all none-detected gestures
904 * Then resets test bits for all desired gesures
905 * and clears input-events history.
906 * note: if no gesture was detected, events from history list
907 * are streamed to the widget because it's unused by layer.
908 * user may cancel refeed of events by setting repeat events.
910 * @param obj The gesture-layer object.
912 * @ingroup Elm_Gesture_Layer
915 _event_history_clear(Evas_Object *obj)
917 Widget_Data *wd = elm_widget_data_get(obj);
922 Evas *e = evas_object_evas_get(obj);
923 Eina_Bool gesture_found = EINA_FALSE;
924 for (i = ELM_GESTURE_FIRST ; i < ELM_GESTURE_LAST; i++)
929 if (p->state == ELM_GESTURE_STATE_END)
930 gesture_found = EINA_TRUE;
932 { /* Report ABORT to all gestures that still not finished */
933 _set_state(p, ELM_GESTURE_STATE_ABORT, wd->gesture[i]->info,
939 _reset_states(wd); /* we are ready to start testing for gestures again */
941 /* Clear all gestures intermediate date */
942 _n_long_tap_test_reset(wd->gesture[ELM_GESTURE_N_LONG_TAPS]);
943 _dbl_click_test_reset(wd->gesture[ELM_GESTURE_N_TAPS]);
944 _dbl_click_test_reset(wd->gesture[ELM_GESTURE_N_DOUBLE_TAPS]);
945 _dbl_click_test_reset(wd->gesture[ELM_GESTURE_N_TRIPLE_TAPS]);
946 _momentum_test_reset(wd->gesture[ELM_GESTURE_MOMENTUM]);
947 _line_test_reset(wd->gesture[ELM_GESTURE_N_LINES]);
948 _line_test_reset(wd->gesture[ELM_GESTURE_N_FLICKS]);
949 _zoom_test_reset(wd->gesture[ELM_GESTURE_ZOOM]);
950 _rotate_test_reset(wd->gesture[ELM_GESTURE_ROTATE]);
952 /* Disable gesture layer so refeeded events won't be consumed by it */
953 _unregister_callbacks(obj);
954 while (wd->event_history_list)
957 t = wd->event_history_list;
958 Eina_List *pending = _device_is_pending(wd->pending,
959 wd->event_history_list->event,
960 wd->event_history_list->event_type);
962 /* Refeed events if no gesture matched input */
963 if (pending || ((!gesture_found) && (!wd->repeat_events)))
965 evas_event_refeed_event(e, wd->event_history_list->event,
966 wd->event_history_list->event_type);
970 wd->pending = eina_list_remove_list(wd->pending, pending);
971 int device = ELM_MOUSE_DEVICE;
972 if (wd->event_history_list->event_type == EVAS_CALLBACK_MULTI_UP)
973 device = ((Evas_Event_Multi_Up *)
974 (wd->event_history_list->event))->device;
977 wd->pending = _add_device_pending(wd->pending,
978 wd->event_history_list->event,
979 wd->event_history_list->event_type);
982 free(wd->event_history_list->event);
983 wd->event_history_list = (Event_History *) eina_inlist_remove(
984 EINA_INLIST_GET(wd->event_history_list),
985 EINA_INLIST_GET(wd->event_history_list));
988 _register_callbacks(obj);
994 * This function copies input events.
995 * We copy event info before adding it to history.
996 * The memory is freed when we clear history.
998 * @param event the event to copy
999 * @param event_type event type to copy
1001 * @ingroup Elm_Gesture_Layer
1004 _copy_event_info(void *event, Evas_Callback_Type event_type)
1008 case EVAS_CALLBACK_MOUSE_DOWN:
1009 return COPY_EVENT_INFO((Evas_Event_Mouse_Down *) event);
1011 case EVAS_CALLBACK_MOUSE_MOVE:
1012 return COPY_EVENT_INFO((Evas_Event_Mouse_Move *) event);
1014 case EVAS_CALLBACK_MOUSE_UP:
1015 return COPY_EVENT_INFO((Evas_Event_Mouse_Up *) event);
1017 case EVAS_CALLBACK_MOUSE_WHEEL:
1018 return COPY_EVENT_INFO((Evas_Event_Mouse_Wheel *) event);
1020 case EVAS_CALLBACK_MULTI_DOWN:
1021 return COPY_EVENT_INFO((Evas_Event_Multi_Down *) event);
1023 case EVAS_CALLBACK_MULTI_MOVE:
1024 return COPY_EVENT_INFO((Evas_Event_Multi_Move *) event);
1026 case EVAS_CALLBACK_MULTI_UP:
1027 return COPY_EVENT_INFO((Evas_Event_Multi_Up *) event);
1029 case EVAS_CALLBACK_KEY_DOWN:
1030 return COPY_EVENT_INFO((Evas_Event_Key_Down *) event);
1032 case EVAS_CALLBACK_KEY_UP:
1033 return COPY_EVENT_INFO((Evas_Event_Key_Up *) event);
1041 _event_history_add(Evas_Object *obj, void *event, Evas_Callback_Type event_type)
1043 Widget_Data *wd = elm_widget_data_get(obj);
1045 if (!wd) return EINA_FALSE;
1047 ev = malloc(sizeof(Event_History));
1048 ev->event = _copy_event_info(event, event_type); /* Freed on event_history_clear */
1049 ev->event_type = event_type;
1050 wd->event_history_list = (Event_History *) eina_inlist_append(
1051 EINA_INLIST_GET(wd->event_history_list), EINA_INLIST_GET(ev));
1055 /* END - Event history list handling functions */
1058 _del_hook(Evas_Object *obj)
1060 Widget_Data *wd = elm_widget_data_get(obj);
1063 _event_history_clear(obj);
1064 eina_list_free(wd->pending);
1066 Pointer_Event *data;
1067 EINA_LIST_FREE(wd->touched, data)
1070 if (!elm_widget_disabled_get(obj))
1071 _unregister_callbacks(obj);
1073 /* Free all gestures internal data structures */
1075 for (i = 0; i < ELM_GESTURE_LAST; i++)
1078 if (wd->gesture[i]->data)
1079 free(wd->gesture[i]->data);
1081 free(wd->gesture[i]);
1088 compare_match_fingers(const void *data1, const void *data2)
1089 { /* Compare coords of first item in list to cur coords */
1090 const Pointer_Event *pe1 = eina_list_data_get(data1);
1091 const Pointer_Event *pe2 = data2;
1093 if (_inside(pe1->x, pe1->y, pe2->x, pe2->y))
1095 else if (pe1->x < pe2->x)
1099 if (pe1->x == pe2->x)
1100 return pe1->y - pe2->y;
1107 compare_pe_device(const void *data1, const void *data2)
1108 { /* Compare coords of first item in list to cur coords */
1109 const Pointer_Event *pe1 = eina_list_data_get(eina_list_last(data1));
1110 const Pointer_Event *pe2 = data2;
1112 /* Only match if last was a down event */
1113 if ((pe1->event_type != EVAS_CALLBACK_MULTI_DOWN) &&
1114 (pe1->event_type != EVAS_CALLBACK_MOUSE_DOWN))
1118 if (pe1->device == pe2->device)
1120 else if (pe1->device < pe2->device)
1127 _record_pointer_event(Taps_Type *st, Eina_List *pe_list, Pointer_Event *pe,
1128 Widget_Data *wd, void *event_info, Evas_Callback_Type event_type)
1129 { /* Keep copy of pe and record it in list */
1130 Pointer_Event *p = malloc(sizeof(Pointer_Event));
1131 memcpy(p, pe, sizeof(Pointer_Event));
1132 consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_NONE);
1138 /* This will also update middle-point to report to user later */
1139 st->info.x = st->sum_x / st->n_taps;
1140 st->info.y = st->sum_y / st->n_taps;
1141 st->info.timestamp = pe->timestamp;
1145 pe_list = eina_list_append(pe_list, p);
1146 st->l = eina_list_append(st->l, pe_list);
1149 pe_list = eina_list_append(pe_list, p);
1157 * when this timer expires we ABORT double click gesture.
1159 * @param data The gesture-layer object.
1160 * @return cancles callback for this timer.
1162 * @ingroup Elm_Gesture_Layer
1165 _dbl_click_timeout(void *data)
1167 Gesture_Info *gesture = data;
1168 Widget_Data *wd = elm_widget_data_get(gesture->obj);
1170 wd->dbl_timeout = NULL;
1171 _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1172 gesture->info, EINA_FALSE);
1174 _dbl_click_test_reset(gesture);
1175 _clear_if_finished(gesture->obj);
1176 return ECORE_CALLBACK_CANCEL;
1182 * when this timer expires we START long tap gesture
1184 * @param data The gesture-layer object.
1185 * @return cancles callback for this timer.
1187 * @ingroup Elm_Gesture_Layer
1190 _long_tap_timeout(void *data)
1192 Gesture_Info *gesture = data;
1193 Long_Tap_Type *st = gesture->data;
1196 _set_state(gesture, ELM_GESTURE_STATE_START,
1197 gesture->data, EINA_FALSE);
1199 return ECORE_CALLBACK_CANCEL;
1205 * This function checks all click/tap and double/triple taps
1207 * @param obj The gesture-layer object.
1208 * @param pe The recent input event as stored in pe struct.
1209 * @param event_info Original input event pointer.
1210 * @param event_type Type of original input event.
1211 * @param g_type what Gesture we are testing.
1212 * @param taps How many click/taps we test for.
1214 * @ingroup Elm_Gesture_Layer
1217 _dbl_click_test(Evas_Object *obj, Pointer_Event *pe,
1218 void *event_info, Evas_Callback_Type event_type,
1219 Elm_Gesture_Types g_type, int taps)
1220 { /* Here we fill Recent_Taps struct and fire-up click/tap timers */
1221 Widget_Data *wd = elm_widget_data_get(obj);
1224 if (!pe) /* this happens when unhandled event arrived */
1225 return; /* see _make_pointer_event function */
1227 Gesture_Info *gesture = wd->gesture[g_type];
1228 if (!gesture ) return;
1230 if ((gesture->state == ELM_GESTURE_STATE_UNDEFINED) &&
1231 eina_list_count(wd->touched))
1232 return; /* user left a finger on device, do NOT start */
1234 Taps_Type *st = gesture->data;
1236 { /* Allocated once on first time */
1237 st = calloc(1, sizeof(Taps_Type));
1239 _dbl_click_test_reset(gesture);
1242 Eina_List *pe_list = NULL;
1243 Pointer_Event *pe_down = NULL;
1244 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1245 switch (pe->event_type)
1247 case EVAS_CALLBACK_MULTI_DOWN:
1248 case EVAS_CALLBACK_MOUSE_DOWN:
1249 pe_list = eina_list_search_unsorted(st->l, compare_match_fingers, pe);
1250 pe_list = _record_pointer_event(st, pe_list, pe, wd, event_info, event_type);
1251 if ((pe->device == 0) && (eina_list_count(pe_list) == 1))
1252 { /* This is the first mouse down we got */
1253 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
1254 &st->info, EINA_FALSE);
1255 consume_event(wd, event_info, event_type, ev_flag);
1257 /* To test dbl_click/dbl_tap */
1258 /* When this timer expires, gesture ABORTed if not completed */
1259 if (!wd->dbl_timeout && (taps > 1))
1260 wd->dbl_timeout = ecore_timer_add(0.4, _dbl_click_timeout,
1267 case EVAS_CALLBACK_MULTI_UP:
1268 case EVAS_CALLBACK_MOUSE_UP:
1269 pe_list = eina_list_search_unsorted(st->l, compare_pe_device, pe);
1271 return; /* Got only first mouse_down and mouse_up */
1273 pe_list = _record_pointer_event(st, pe_list, pe, wd, event_info, event_type);
1275 if (eina_list_count(pe_list) <= (unsigned int) ((taps - 1) * 2))
1276 return; /* Got only first mouse_down and mouse_up */
1278 /* Get first event in first list, this has to be Mouse Down event */
1279 pe_down = eina_list_data_get(pe_list);
1281 if (_inside(pe_down->x, pe_down->y, pe->x, pe->y))
1287 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1288 &st->info, EINA_FALSE);
1289 consume_event(wd, event_info, event_type, ev_flag);
1293 if (st->count_ups == eina_list_count(st->l))
1295 /* Abort if we found a single click */
1296 if ((taps == 1) && (st->count_ups == 1))
1298 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1299 &st->info, EINA_FALSE);
1300 consume_event(wd, event_info, event_type, ev_flag);
1303 st->info.n = st->count_ups;
1304 ev_flag =_set_state(gesture, ELM_GESTURE_STATE_END,
1305 &st->info, EINA_FALSE);
1306 consume_event(wd, event_info, event_type, ev_flag);
1313 case EVAS_CALLBACK_MULTI_MOVE:
1314 case EVAS_CALLBACK_MOUSE_MOVE:
1315 /* Get first event in first list, this has to be a Mouse Down event */
1316 /* and verify that user didn't move out of this area before next tap */
1317 pe_list = eina_list_search_unsorted(st->l, compare_pe_device, pe);
1320 pe_down = eina_list_data_get(pe_list);
1321 if (!_inside(pe_down->x, pe_down->y, pe->x, pe->y))
1323 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1324 &st->info, EINA_FALSE);
1325 consume_event(wd, event_info, event_type, ev_flag);
1338 * This function computes center-point for long-tap gesture
1340 * @param st Long Tap gesture info pointer
1341 * @param pe The recent input event as stored in pe struct.
1343 * @ingroup Elm_Gesture_Layer
1346 _compute_taps_center(Long_Tap_Type *st, Pointer_Event *pe)
1348 if(!eina_list_count(st->touched))
1353 Evas_Coord x = 0, y = 0;
1354 EINA_LIST_FOREACH(st->touched, l, p)
1355 { /* Accumulate all then take avarage */
1356 if (p->device == pe->device)
1357 { /* This will take care of values coming from MOVE event */
1368 st->info.x = x / eina_list_count(st->touched);
1369 st->info.y = y / eina_list_count(st->touched);
1375 * This function checks N long-tap gesture.
1377 * @param obj The gesture-layer object.
1378 * @param pe The recent input event as stored in pe struct.
1379 * @param event_info Original input event pointer.
1380 * @param event_type Type of original input event.
1381 * @param g_type what Gesture we are testing.
1382 * @param taps How many click/taps we test for.
1384 * @ingroup Elm_Gesture_Layer
1387 _n_long_tap_test(Evas_Object *obj, Pointer_Event *pe,
1388 void *event_info, Evas_Callback_Type event_type,
1389 Elm_Gesture_Types g_type)
1390 { /* Here we fill Recent_Taps struct and fire-up click/tap timers */
1391 Widget_Data *wd = elm_widget_data_get(obj);
1394 if (!pe) /* this happens when unhandled event arrived */
1395 return; /* see _make_pointer_event function */
1397 Gesture_Info *gesture = wd->gesture[g_type];
1398 if (!gesture ) return;
1400 Long_Tap_Type *st = gesture->data;
1402 { /* Allocated once on first time */
1403 st = calloc(1, sizeof(Long_Tap_Type));
1405 _n_long_tap_test_reset(gesture);
1408 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1409 switch (pe->event_type)
1411 case EVAS_CALLBACK_MULTI_DOWN:
1412 case EVAS_CALLBACK_MOUSE_DOWN:
1413 if (st->info.n > eina_list_count(st->touched))
1414 { /* ABORT: user lifts finger then back before completing gesgure */
1415 if (st->timeout) ecore_timer_del(st->timeout);
1417 ev_flag =_set_state(gesture, ELM_GESTURE_STATE_ABORT,
1418 &st->info, EINA_FALSE);
1421 st->touched = _add_touched_device(st->touched, pe);
1422 st->info.n = eina_list_count(st->touched);
1423 if ((pe->device == 0) && (eina_list_count(st->touched) == 1))
1424 { /* This is the first mouse down we got */
1425 st->info.timestamp = pe->timestamp;
1427 /* To test long tap */
1428 /* When this timer expires, gesture STARTED */
1430 st->timeout = ecore_timer_add(wd->long_tap_start_timeout, _long_tap_timeout,
1434 consume_event(wd, event_info, event_type, ev_flag);
1435 _compute_taps_center(st, pe);
1438 case EVAS_CALLBACK_MULTI_UP:
1439 case EVAS_CALLBACK_MOUSE_UP:
1440 st->touched = _remove_touched_device(st->touched, pe);
1442 ((gesture->state == ELM_GESTURE_STATE_START) ||
1443 (gesture->state == ELM_GESTURE_STATE_MOVE)))
1444 { /* Report END only for gesture that STARTed */
1445 if (eina_list_count(st->touched) == 0)
1446 { /* Report END only at last release event */
1447 ev_flag =_set_state(gesture, ELM_GESTURE_STATE_END,
1448 &st->info, EINA_FALSE);
1449 consume_event(wd, event_info, event_type, ev_flag);
1454 case EVAS_CALLBACK_MULTI_MOVE:
1455 case EVAS_CALLBACK_MOUSE_MOVE:
1457 ((gesture->state == ELM_GESTURE_STATE_START) ||
1458 (gesture->state == ELM_GESTURE_STATE_MOVE)))
1459 { /* Report MOVE only if STARTED */
1460 _compute_taps_center(st, pe);
1461 /* Report MOVE if gesture started */
1462 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_MOVE,
1463 &st->info, EINA_TRUE);
1464 consume_event(wd, event_info, event_type, ev_flag);
1476 * This function computes momentum for MOMENTUM, LINE and FLICK gestures
1477 * This momentum value will be sent to widget when gesture is completed.
1479 * @param momentum pointer to buffer where we record momentum value.
1480 * @param x1 x coord where user started gesture.
1481 * @param y1 y coord where user started gesture.
1482 * @param x2 x coord where user completed gesture.
1483 * @param y2 y coord where user completed gesture.
1484 * @param t1x timestamp for X, when user started gesture.
1485 * @param t1y timestamp for Y, when user started gesture.
1486 * @param t2 timestamp when user completed gesture.
1488 * @ingroup Elm_Gesture_Layer
1491 _set_momentum(Elm_Gesture_Momentum_Info *momentum, Evas_Coord x1, Evas_Coord y1,
1492 Evas_Coord x2, Evas_Coord y2, unsigned int t1x, unsigned int t1y,
1495 Evas_Coord velx = 0, vely = 0, vel;
1496 Evas_Coord dx = x2 - x1;
1497 Evas_Coord dy = y2 - y1;
1501 velx = (dx * 1000) / dtx;
1504 vely = (dy * 1000) / dty;
1506 vel = sqrt((velx * velx) + (vely * vely));
1508 if ((_elm_config->thumbscroll_friction > 0.0) &&
1509 (vel > _elm_config->thumbscroll_momentum_threshold))
1510 { /* report momentum */
1511 momentum->mx = velx;
1512 momentum->my = vely;
1524 * This function is used for computing rotation angle (DEG).
1526 * @param x1 first finger x location.
1527 * @param y1 first finger y location.
1528 * @param x2 second finger x location.
1529 * @param y2 second finger y location.
1531 * @return angle of the line between (x1,y1), (x2,y2) in Radians.
1533 * @ingroup Elm_Gesture_Layer
1536 get_angle(Evas_Coord x1, Evas_Coord y1, Evas_Coord x2, Evas_Coord y2)
1542 if (((int) xx) && ((int) yy))
1549 return RAD_360DEG - a;
1560 return RAD_180DEG + a;
1564 return RAD_180DEG - a;
1570 { /* Horizontal line */
1595 * This function is used for computing the magnitude and direction
1596 * of vector between two points.
1598 * @param x1 first finger x location.
1599 * @param y1 first finger y location.
1600 * @param x2 second finger x location.
1601 * @param y2 second finger y location.
1602 * @param l length computed (output)
1603 * @param a angle computed (output)
1605 * @ingroup Elm_Gesture_Layer
1608 get_vector(Evas_Coord x1, Evas_Coord y1, Evas_Coord x2, Evas_Coord y2,
1609 Evas_Coord *l, double *a)
1614 *l = (Evas_Coord) sqrt(xx*xx + yy*yy);
1615 *a = get_angle(x1, y1, x2, y2);
1619 _get_direction(Evas_Coord x1, Evas_Coord x2)
1632 * This function tests momentum gesture.
1633 * @param obj The gesture-layer object.
1634 * @param pe The recent input event as stored in pe struct.
1635 * @param event_info recent input event.
1636 * @param event_type recent event type.
1637 * @param g_type what Gesture we are testing.
1639 * @ingroup Elm_Gesture_Layer
1642 _momentum_test(Evas_Object *obj, Pointer_Event *pe,
1643 void *event_info, Evas_Callback_Type event_type,
1644 Elm_Gesture_Types g_type)
1646 Widget_Data *wd = elm_widget_data_get(obj);
1648 Gesture_Info *gesture = wd->gesture[g_type];
1649 if (!gesture ) return;
1651 if ((gesture->state == ELM_GESTURE_STATE_UNDEFINED) &&
1652 eina_list_count(wd->touched))
1653 return; /* user left a finger on device, do NOT start */
1655 Momentum_Type *st = gesture->data;
1657 { /* Allocated once on first time */
1658 st = calloc(1, sizeof(Momentum_Type));
1660 _momentum_test_reset(gesture);
1666 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1667 switch (pe->event_type)
1669 case EVAS_CALLBACK_MOUSE_DOWN:
1670 st->line_st.x = st->line_end.x = pe->x;
1671 st->line_st.y = st->line_end.y = pe->y;
1672 st->t_st_x = st->t_st_y = st->t_end = pe->timestamp;
1673 st->xdir = st->ydir = 0;
1674 st->info.x2 = st->info.x1 = pe->x;
1675 st->info.y2 = st->info.y1 = pe->y;
1676 st->info.tx = st->info.ty = pe->timestamp;
1677 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
1678 &st->info, EINA_FALSE);
1679 consume_event(wd, event_info, event_type, ev_flag);
1682 case EVAS_CALLBACK_MOUSE_UP:
1683 /* IGNORE if line info was cleared, like long press, move */
1687 if ((pe->timestamp - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->t_end)
1689 /* Too long of a wait, reset all values */
1690 st->line_st.x = pe->x;
1691 st->line_st.y = pe->y;
1692 st->t_st_y = st->t_st_x = pe->timestamp;
1693 st->xdir = st->ydir = 0;
1696 st->info.x2 = pe->x;
1697 st->info.y2 = pe->y;
1698 st->line_end.x = pe->x;
1699 st->line_end.y = pe->y;
1700 st->t_end = pe->timestamp;
1702 _set_momentum(&st->info, st->line_st.x, st->line_st.y, pe->x, pe->y,
1703 st->t_st_x, st->t_st_y, pe->timestamp);
1705 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END, &st->info,
1707 consume_event(wd, event_info, event_type, ev_flag);
1711 case EVAS_CALLBACK_MOUSE_MOVE:
1712 /* IGNORE if line info was cleared, like long press, move */
1716 if ((pe->timestamp - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->t_end)
1718 /* Too long of a wait, reset all values */
1719 st->line_st.x = pe->x;
1720 st->line_st.y = pe->y;
1721 st->t_st_y = st->t_st_x = pe->timestamp;
1722 st->info.tx = st->t_st_x;
1723 st->info.ty = st->t_st_y;
1724 st->xdir = st->ydir = 0;
1729 xdir = _get_direction(st->line_end.x, pe->x);
1730 ydir = _get_direction(st->line_end.y, pe->y);
1731 if (!xdir || (xdir == (-st->xdir)))
1733 st->line_st.x = st->line_end.x;
1734 st->info.tx = st->t_st_x = st->t_end;
1738 if (!ydir || (ydir == (-st->ydir)))
1740 st->line_st.y = st->line_end.y;
1741 st->info.ty = st->t_st_y = st->t_end;
1746 st->info.x2 = st->line_end.x = pe->x;
1747 st->info.y2 = st->line_end.y = pe->y;
1748 st->t_end = pe->timestamp;
1749 _set_momentum(&st->info, st->line_st.x, st->line_st.y, pe->x, pe->y,
1750 st->t_st_x, st->t_st_y, pe->timestamp);
1751 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_MOVE, &st->info,
1753 consume_event(wd, event_info, event_type, ev_flag);
1756 case EVAS_CALLBACK_MULTI_UP:
1757 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
1759 consume_event(wd, event_info, event_type, ev_flag);
1768 compare_line_device(const void *data1, const void *data2)
1769 { /* Compare device component of line struct */
1770 const Line_Data *ln1 = data1;
1771 const int *device = data2;
1773 if (ln1->t_st) /* Compare only with lines that started */
1774 return (ln1->device - (*device));
1782 * This function construct line struct from input.
1783 * @param info pointer to store line momentum.
1784 * @param st line info to store input data.
1785 * @param pe The recent input event as stored in pe struct.
1787 * @ingroup Elm_Gesture_Layer
1790 _single_line_process(Elm_Gesture_Line_Info *info, Line_Data *st,
1792 { /* Record events and set momentum for line pointed by st */
1796 switch (pe->event_type)
1798 case EVAS_CALLBACK_MOUSE_DOWN:
1799 case EVAS_CALLBACK_MULTI_DOWN:
1800 st->line_st.x = pe->x;
1801 st->line_st.y = pe->y;
1802 st->t_st = pe->timestamp;
1803 st->device = pe->device;
1804 info->momentum.x1 = pe->x;
1805 info->momentum.y1 = pe->y;
1806 info->momentum.tx = pe->timestamp;
1807 info->momentum.ty = pe->timestamp;
1812 case EVAS_CALLBACK_MOUSE_UP:
1813 case EVAS_CALLBACK_MULTI_UP:
1814 /* IGNORE if line info was cleared, like long press, move */
1818 st->line_end.x = pe->x;
1819 st->line_end.y = pe->y;
1820 st->t_end = pe->timestamp;
1823 case EVAS_CALLBACK_MOUSE_MOVE:
1824 case EVAS_CALLBACK_MULTI_MOVE:
1825 /* IGNORE if line info was cleared, like long press, move */
1836 _line_data_reset(st);
1840 info->momentum.x2 = pe->x;
1841 info->momentum.y2 = pe->y;
1842 _set_momentum(&info->momentum, st->line_st.x, st->line_st.y, pe->x, pe->y,
1843 st->t_st, st->t_st, pe->timestamp);
1851 * This function test for (n) line gesture.
1852 * @param obj The gesture-layer object.
1853 * @param pe The recent input event as stored in pe struct.
1854 * @param event_info Original input event pointer.
1855 * @param event_type Type of original input event.
1856 * @param g_type what Gesture we are testing.
1858 * @ingroup Elm_Gesture_Layer
1861 _n_line_test(Evas_Object *obj, Pointer_Event *pe, void *event_info,
1862 Evas_Callback_Type event_type, Elm_Gesture_Types g_type)
1866 Widget_Data *wd = elm_widget_data_get(obj);
1868 Gesture_Info *gesture = wd->gesture[g_type];
1869 if (!gesture ) return;
1871 if ((gesture->state == ELM_GESTURE_STATE_UNDEFINED) &&
1872 eina_list_count(wd->touched))
1873 return; /* user left a finger on device, do NOT start */
1875 Line_Type *st = gesture->data;
1878 st = calloc(1, sizeof(Line_Type));
1882 Line_Data *line = NULL;
1883 Eina_List *list = st->list;
1884 unsigned int i, cnt = eina_list_count(list);
1887 { /* list is not empty, locate this device on list */
1888 line = (Line_Data *) eina_list_search_unsorted(st->list,
1889 compare_line_device, &pe->device);
1892 { /* Try to locate an empty-node */
1893 for (i = 0; i < cnt; i++)
1895 line = eina_list_nth(list, i);
1897 break; /* Found a free node */
1905 { /* List is empty or device not found, new line-struct on START only */
1906 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
1907 (event_type == EVAS_CALLBACK_MULTI_DOWN))
1908 { /* Allocate new item on START */
1909 line = calloc(1, sizeof(Line_Data));
1910 _line_data_reset(line);
1911 list = eina_list_append(list, line);
1916 if (!line) /* This may happen on MOVE that comes before DOWN */
1917 return; /* No line-struct to work with, can't continue testing */
1920 if (_single_line_process(&st->info, line, pe)) /* update st with input */
1921 consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_NONE);
1923 /* Get direction and magnitude of the line */
1925 get_vector(line->line_st.x, line->line_st.y, pe->x, pe->y,
1926 &line->line_length, &angle);
1928 /* These are used later to compare lines length */
1929 Evas_Coord shortest_line_len = line->line_length;
1930 Evas_Coord longest_line_len = line->line_length;
1931 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
1933 /* Now update line-state */
1935 { /* Analyze line only if line started */
1936 if (line->line_angle >= 0.0)
1937 { /* if line direction was set, we test if broke tolerance */
1938 double a = fabs(angle - line->line_angle);
1940 double d = (tan(a)) * line->line_length; /* Distance from line */
1941 #if defined(DEBUG_GESTURE_LAYER)
1942 printf("%s a=<%f> d=<%f>\n", __func__, (a * 57.295779513), d);
1944 if ((d > wd->line_distance_tolerance) || (a > wd->line_angular_tolerance))
1945 { /* Broke tolerance: abort line and start a new one */
1946 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1947 &st->info, EINA_FALSE);
1948 consume_event(wd, event_info, event_type, ev_flag);
1953 { /* Record the line angle as it broke minimum length for line */
1954 if (line->line_length >= wd->line_min_length)
1955 st->info.angle = line->line_angle = angle;
1960 if (line->line_angle < 0.0)
1961 { /* it's not a line, too short more close to a tap */
1962 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
1963 &st->info, EINA_FALSE);
1964 consume_event(wd, event_info, event_type, ev_flag);
1970 /* Count how many lines already started / ended */
1973 unsigned int tm_start = pe->timestamp;
1974 unsigned int tm_end = pe->timestamp;
1977 double base_angle = ELM_GESTURE_NEGATIVE_ANGLE;
1978 Eina_Bool lines_parallel = EINA_TRUE;
1979 EINA_LIST_FOREACH(list, l, t_line)
1982 base_angle = t_line->line_angle;
1985 if (t_line->line_angle >= 0)
1986 { /* Compare angle only with lines with direction defined */
1987 if (fabs(base_angle - t_line->line_angle) >
1988 wd->line_angular_tolerance)
1989 lines_parallel = EINA_FALSE;
1993 if (t_line->line_length)
1994 { /* update only if this line is used */
1995 if (shortest_line_len > t_line->line_length)
1996 shortest_line_len = t_line->line_length;
1998 if (longest_line_len < t_line->line_length)
1999 longest_line_len = t_line->line_length;
2005 if (t_line->t_st < tm_start)
2006 tm_start = t_line->t_st;
2012 if (t_line->t_end < tm_end)
2013 tm_end = t_line->t_end;
2017 st->info.n = started;
2021 ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
2022 (event_type == EVAS_CALLBACK_MULTI_DOWN)))
2023 { /* user lift one finger then starts again without line-end - ABORT */
2024 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
2026 consume_event(wd, event_info, event_type, ev_flag);
2030 if (!lines_parallel)
2031 { /* Lines are NOT at same direction, abort this gesture */
2032 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
2034 consume_event(wd, event_info, event_type, ev_flag);
2039 /* We report ABORT if lines length are NOT matching when fingers are up */
2040 if ((longest_line_len - shortest_line_len) > (elm_finger_size_get()*2))
2042 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
2044 consume_event(wd, event_info, event_type, ev_flag);
2048 if ((g_type == ELM_GESTURE_N_FLICKS) && ((tm_end - tm_start) > wd->flick_time_limit_ms))
2049 { /* We consider FLICK as a fast line.ABORT if take too long to finish */
2050 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info,
2052 consume_event(wd, event_info, event_type, ev_flag);
2058 case EVAS_CALLBACK_MOUSE_UP:
2059 case EVAS_CALLBACK_MULTI_UP:
2060 if ((started) && (started == ended))
2062 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END,
2063 &st->info, EINA_FALSE);
2064 consume_event(wd, event_info, event_type, ev_flag);
2069 case EVAS_CALLBACK_MOUSE_DOWN:
2070 case EVAS_CALLBACK_MULTI_DOWN:
2073 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
2074 &st->info, EINA_TRUE);
2075 consume_event(wd, event_info, event_type, ev_flag);
2080 case EVAS_CALLBACK_MOUSE_MOVE:
2081 case EVAS_CALLBACK_MULTI_MOVE:
2084 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_MOVE,
2085 &st->info, EINA_TRUE);
2086 consume_event(wd, event_info, event_type, ev_flag);
2092 return; /* Unhandeld event type */
2099 * This function is used to check if rotation gesture started.
2100 * @param st Contains current rotation values from user input.
2101 * @return TRUE/FALSE if we need to set rotation START.
2103 * @ingroup Elm_Gesture_Layer
2106 rotation_broke_tolerance(Rotate_Type *st)
2108 if (st->info.base_angle < 0)
2109 return EINA_FALSE; /* Angle has to be computed first */
2111 if (st->rotate_angular_tolerance < 0)
2114 double low = st->info.base_angle - st->rotate_angular_tolerance;
2115 double high = st->info.base_angle + st->rotate_angular_tolerance;
2116 double t = st->info.angle;
2129 if (high > RAD_360DEG)
2140 #if defined(DEBUG_GESTURE_LAYER)
2141 printf("%s angle=<%d> low=<%d> high=<%d>\n", __func__, t, low, high);
2143 if ((t < low) || (t > high))
2144 { /* This marks that roation action has started */
2145 st->rotate_angular_tolerance = ELM_GESTURE_NEGATIVE_ANGLE;
2146 st->info.base_angle = st->info.angle; /* Avoid jump in angle value */
2156 * This function is used for computing the gap between fingers.
2157 * It returns the length and center point between fingers.
2159 * @param x1 first finger x location.
2160 * @param y1 first finger y location.
2161 * @param x2 second finger x location.
2162 * @param y2 second finger y location.
2163 * @param x Gets center point x cord (output)
2164 * @param y Gets center point y cord (output)
2166 * @return length of the line between (x1,y1), (x2,y2) in pixels.
2168 * @ingroup Elm_Gesture_Layer
2171 get_finger_gap_length(Evas_Coord x1, Evas_Coord y1, Evas_Coord x2,
2172 Evas_Coord y2, Evas_Coord *x, Evas_Coord *y)
2174 double a, b, xx, yy, gap;
2177 gap = sqrt(xx*xx + yy*yy);
2179 /* START - Compute zoom center point */
2180 /* The triangle defined as follows:
2188 * http://en.wikipedia.org/wiki/Trigonometric_functions
2189 *************************************/
2190 if (((int) xx) && ((int) yy))
2192 double A = atan((yy / xx));
2193 #if defined(DEBUG_GESTURE_LAYER)
2194 printf("xx=<%f> yy=<%f> A=<%f>\n", xx, yy, A);
2196 a = (Evas_Coord) ((gap / 2) * sin(A));
2197 b = (Evas_Coord) ((gap / 2) * cos(A));
2198 *x = (Evas_Coord) ((x2 > x1) ? (x1 + b) : (x2 + b));
2199 *y = (Evas_Coord) ((y2 > y1) ? (y1 + a) : (y2 + a));
2204 { /* horiz line, take half width */
2205 #if defined(DEBUG_GESTURE_LAYER)
2206 printf("==== HORIZ ====\n");
2208 *x = (Evas_Coord) (xx / 2);
2209 *y = (Evas_Coord) (y1);
2213 { /* vert line, take half width */
2214 #if defined(DEBUG_GESTURE_LAYER)
2215 printf("==== VERT ====\n");
2217 *x = (Evas_Coord) (x1);
2218 *y = (Evas_Coord) (yy / 2);
2221 /* END - Compute zoom center point */
2223 return (Evas_Coord) gap;
2229 * This function is used for computing zoom value.
2231 * @param st Pointer to zoom data based on user input.
2232 * @param x1 first finger x location.
2233 * @param y1 first finger y location.
2234 * @param x2 second finger x location.
2235 * @param y2 second finger y location.
2236 * @param factor zoom-factor, used to determine how fast zoom works.
2238 * @return zoom value, when 1.0 means no zoom, 0.5 half size...
2240 * @ingroup Elm_Gesture_Layer
2242 /* FIXME change float to double */
2244 compute_zoom(Zoom_Type *st, Evas_Coord x1, Evas_Coord y1, unsigned int tm1,
2245 Evas_Coord x2, Evas_Coord y2, unsigned int tm2, double zoom_finger_factor)
2248 Evas_Coord diam = get_finger_gap_length(x1, y1, x2, y2,
2249 &st->info.x, &st->info.y);
2251 st->info.radius = diam / 2;
2255 st->zoom_base = diam;
2256 return st->info.zoom;
2259 if (st->zoom_distance_tolerance)
2260 { /* zoom tolerance <> ZERO, means zoom action NOT started yet */
2261 if (diam < (st->zoom_base - st->zoom_distance_tolerance))
2262 { /* avoid jump with zoom value when break tolerance */
2263 st->zoom_base -= st->zoom_distance_tolerance;
2264 st->zoom_distance_tolerance = 0;
2267 if (diam > (st->zoom_base + st->zoom_distance_tolerance))
2268 { /* avoid jump with zoom value when break tolerance */
2269 st->zoom_base += st->zoom_distance_tolerance;
2270 st->zoom_distance_tolerance = 0;
2276 /* We use factor only on the difference between gap-base */
2277 /* if gap=120, base=100, we get ((120-100)/100)=0.2*factor */
2278 rt = ((1.0) + ((((float) diam - (float) st->zoom_base) /
2279 (float) st->zoom_base) * zoom_finger_factor));
2282 /* Momentum: zoom per second: (NOT YET SUPPORTED) */
2283 st->info.momentum = (((rt - 1.0) * 1000) / (tm2 - tm1));
2294 * This function handles zoom with mouse wheel.
2295 * thats a combination of wheel + CTRL key.
2296 * @param obj The gesture-layer object.
2297 * @param event_info Original input event pointer.
2298 * @param event_type Type of original input event.
2299 * @param g_type what Gesture we are testing.
2301 * @ingroup Elm_Gesture_Layer
2304 _zoom_with_wheel_test(Evas_Object *obj, void *event_info,
2305 Evas_Callback_Type event_type, Elm_Gesture_Types g_type)
2307 Widget_Data *wd = elm_widget_data_get(obj);
2309 if (!wd->gesture[g_type]) return;
2311 Gesture_Info *gesture_zoom = wd->gesture[g_type];
2312 Zoom_Type *st = gesture_zoom->data;
2313 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2315 { /* Allocated once on first time, used for zoom intermediate data */
2316 st = calloc(1, sizeof(Zoom_Type));
2317 gesture_zoom->data = st;
2318 _zoom_test_reset(gesture_zoom);
2323 case EVAS_CALLBACK_KEY_UP:
2325 Evas_Event_Key_Up *p = event_info;
2326 if ((!strcmp(p->keyname, "Control_L")) ||
2327 (!strcmp(p->keyname, "Control_R")))
2328 { /* Test if we ended a zoom gesture when releasing CTRL */
2329 if ((st->zoom_wheel) &&
2330 ((gesture_zoom->state == ELM_GESTURE_STATE_START) ||
2331 (gesture_zoom->state == ELM_GESTURE_STATE_MOVE)))
2332 { /* User released CTRL after zooming */
2333 ev_flag = _set_state(gesture_zoom,
2334 ELM_GESTURE_STATE_END, &st->info, EINA_FALSE);
2335 consume_event(wd, event_info, event_type, ev_flag);
2343 case EVAS_CALLBACK_MOUSE_WHEEL:
2346 Elm_Gesture_State s;
2347 if (!evas_key_modifier_is_set(
2348 ((Evas_Event_Mouse_Wheel *) event_info)->modifiers,
2350 { /* if using wheel witout CTRL after starting zoom */
2351 if ((st->zoom_wheel) &&
2352 ((gesture_zoom->state == ELM_GESTURE_STATE_START) ||
2353 (gesture_zoom->state == ELM_GESTURE_STATE_MOVE)))
2355 ev_flag = _set_state(gesture_zoom,
2356 ELM_GESTURE_STATE_END, &st->info, EINA_FALSE);
2357 consume_event(wd, event_info, event_type, ev_flag);
2362 return; /* Ignore mouse-wheel without control */
2365 /* Using mouse wheel with CTRL for zoom */
2366 if (st->zoom_wheel || (st->zoom_distance_tolerance == 0))
2367 { /* when (zoom_wheel == NULL) and (zoom_distance_tolerance == 0)
2368 we continue a zoom gesture */
2370 s = ELM_GESTURE_STATE_MOVE;
2373 { /* On first wheel event, report START */
2374 Evas_Modifier_Mask mask = evas_key_modifier_mask_get(
2375 evas_object_evas_get(wd->target), "Control");
2377 s = ELM_GESTURE_STATE_START;
2378 if (!evas_object_key_grab(wd->target, "Control_L", mask, 0, EINA_FALSE))
2379 ERR("Failed to Grabbed CTRL_L");
2380 if (!evas_object_key_grab(wd->target, "Control_R", mask, 0, EINA_FALSE))
2381 ERR("Failed to Grabbed CTRL_R");
2384 st->zoom_distance_tolerance = 0; /* Cancel tolerance */
2385 st->zoom_wheel = (Evas_Event_Mouse_Wheel *) event_info;
2386 st->info.x = st->zoom_wheel->canvas.x;
2387 st->info.y = st->zoom_wheel->canvas.y;
2389 if (st->zoom_wheel->z < 0) /* zoom in */
2390 st->info.zoom += (wd->zoom_finger_factor * wd->zoom_wheel_factor);
2392 if (st->zoom_wheel->z > 0) /* zoom out */
2393 st->info.zoom -= (wd->zoom_finger_factor * wd->zoom_wheel_factor);
2395 if (st->info.zoom < 0.0)
2396 st->info.zoom = 0.0;
2398 ev_flag = _set_state(gesture_zoom, s, &st->info, force);
2399 consume_event(wd, event_info, event_type, ev_flag);
2411 * This function is used to test zoom gesture.
2412 * user may combine zoom, rotation together.
2413 * so its possible that both will be detected from input.
2414 * (both are two-finger movement-oriented gestures)
2416 * @param obj The gesture-layer object.
2417 * @param event_info Pointer to recent input event.
2418 * @param event_type Recent input event type.
2419 * @param g_type what Gesture we are testing.
2421 * @ingroup Elm_Gesture_Layer
2424 _zoom_test(Evas_Object *obj, Pointer_Event *pe, void *event_info,
2425 Evas_Callback_Type event_type, Elm_Gesture_Types g_type)
2429 Widget_Data *wd = elm_widget_data_get(obj);
2431 if (!wd->gesture[g_type]) return;
2433 Gesture_Info *gesture_zoom = wd->gesture[g_type];
2434 Zoom_Type *st = gesture_zoom->data;
2437 { /* Allocated once on first time, used for zoom data */
2438 st = calloc(1, sizeof(Zoom_Type));
2439 gesture_zoom->data = st;
2440 _zoom_test_reset(gesture_zoom);
2443 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2446 case EVAS_CALLBACK_MOUSE_DOWN:
2447 consume_event(wd, event_info, event_type, ev_flag);
2448 memcpy(&st->zoom_st, pe, sizeof(Pointer_Event));
2452 case EVAS_CALLBACK_MOUSE_MOVE:
2453 consume_event(wd, event_info, event_type, ev_flag);
2454 if (!st->zoom_st.timestamp)
2455 return; /* we got move event before down event.Ignore it */
2457 consume_event(wd, event_info, event_type, ev_flag);
2458 memcpy(&st->zoom_mv, pe, sizeof(Pointer_Event));
2460 /* We match this point to previous multi-move or multi-down event */
2461 if (st->zoom_mv1.timestamp)
2463 st->info.zoom = compute_zoom(st,
2464 st->zoom_mv.x, st->zoom_mv.y, st->zoom_mv.timestamp,
2465 st->zoom_mv1.x, st->zoom_mv1.y, st->zoom_mv1.timestamp,
2466 wd->zoom_finger_factor);
2470 if (st->zoom_st1.timestamp)
2472 st->info.zoom = compute_zoom(st,
2473 st->zoom_mv.x, st->zoom_mv.y, st->zoom_mv.timestamp,
2474 st->zoom_st1.x, st->zoom_st1.y, st->zoom_st1.timestamp,
2475 wd->zoom_finger_factor);
2481 case EVAS_CALLBACK_MULTI_MOVE:
2482 if (!st->zoom_st1.timestamp)
2483 return; /* We get move event before down event.Ignore it */
2485 consume_event(wd, event_info, event_type, ev_flag);
2486 if (st->zoom_mv1.timestamp)
2488 if (st->zoom_mv1.device !=
2489 ((Evas_Event_Multi_Move *) event_info)->device)
2490 { /* A third finger on screen, abort zoom */
2491 ev_flag = _set_state(gesture_zoom,
2492 ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE);
2493 consume_event(wd, event_info, event_type, ev_flag);
2499 memcpy(&st->zoom_mv1, pe, sizeof(Pointer_Event));
2501 /* Match this point to previous mouse-move or mouse-down event */
2502 if (st->zoom_mv.timestamp)
2504 st->info.zoom = compute_zoom(st,
2505 st->zoom_mv1.x, st->zoom_mv1.y, st->zoom_mv1.timestamp,
2506 st->zoom_mv.x, st->zoom_mv.y, st->zoom_mv.timestamp,
2507 wd->zoom_finger_factor);
2511 if (st->zoom_st.timestamp)
2513 st->info.zoom = compute_zoom(st,
2514 st->zoom_mv1.x, st->zoom_mv1.y, st->zoom_mv1.timestamp,
2515 st->zoom_st.x, st->zoom_st.y, st->zoom_st.timestamp,
2516 wd->zoom_finger_factor);
2522 case EVAS_CALLBACK_MULTI_DOWN:
2523 consume_event(wd, event_info, event_type, ev_flag);
2524 memcpy(&st->zoom_st1, pe, sizeof(Pointer_Event));
2527 case EVAS_CALLBACK_MOUSE_UP:
2528 case EVAS_CALLBACK_MULTI_UP:
2529 /* Reset timestamp of finger-up.This is used later
2530 by _zoom_test_reset() to retain finger-down data */
2531 consume_event(wd, event_info, event_type, ev_flag);
2532 if (((st->zoom_wheel) || (st->zoom_base)) &&
2533 (st->zoom_distance_tolerance == 0))
2535 ev_flag = _set_state(gesture_zoom, ELM_GESTURE_STATE_END,
2536 &st->info, EINA_FALSE);
2537 consume_event(wd, event_info, event_type, ev_flag);
2542 /* if we got here not a ZOOM */
2543 if (gesture_zoom->state != ELM_GESTURE_STATE_UNDEFINED)
2544 { /* Must be != undefined, if gesture started */
2545 ev_flag = _set_state(gesture_zoom,
2546 ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE);
2547 consume_event(wd, event_info, event_type, ev_flag);
2550 _zoom_test_reset(gesture_zoom);
2559 if (!st->zoom_distance_tolerance)
2560 if ((event_type == EVAS_CALLBACK_MOUSE_MOVE) ||
2561 (event_type == EVAS_CALLBACK_MULTI_MOVE))
2563 { /* Zoom broke tolerance, report move */
2564 double d = st->info.zoom - st->next_step;
2568 if (d >= wd->zoom_step)
2569 { /* Report move in steps */
2570 st->next_step = st->info.zoom;
2572 ev_flag = _set_state(gesture_zoom, ELM_GESTURE_STATE_MOVE,
2573 &st->info, EINA_TRUE);
2574 consume_event(wd, event_info, event_type, ev_flag);
2581 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
2582 (event_type == EVAS_CALLBACK_MULTI_DOWN))
2583 { /* report zoom start finger location is zoom-center temporarly */
2584 /* Zoom may have started with mouse-wheel, don't report START */
2585 if ((st->zoom_st.timestamp) && (st->zoom_st1.timestamp))
2586 { /* Set zoom-base after BOTH down events were recorded */
2587 /* Compute length of line between fingers on zoom start */
2588 st->info.zoom = 1.0;
2589 st->zoom_base = get_finger_gap_length(st->zoom_st1.x,
2590 st->zoom_st1.y, st->zoom_st.x, st->zoom_st.y,
2591 &st->info.x, &st->info.y);
2593 st->info.radius = st->zoom_base / 2;
2595 if ((gesture_zoom->state != ELM_GESTURE_STATE_START) &&
2596 (gesture_zoom->state != ELM_GESTURE_STATE_MOVE))
2597 { /* Report START only when two fingers touching */
2598 ev_flag = _set_state(gesture_zoom,
2599 ELM_GESTURE_STATE_START, &st->info, EINA_FALSE);
2600 consume_event(wd, event_info, event_type, ev_flag);
2609 _get_rotate_properties(Rotate_Type *st,
2610 Evas_Coord x1, Evas_Coord y1, unsigned int tm1,
2611 Evas_Coord x2, Evas_Coord y2, unsigned int tm2,
2614 st->info.radius = get_finger_gap_length(x1, y1, x2, y2,
2615 &st->info.x, &st->info.y) / 2;
2617 *angle = get_angle(x1, y1, x2, y2);
2618 #if 0 /* (NOT YET SUPPORTED) */
2619 if (angle == &st->info.angle)
2620 { /* Compute momentum: TODO: bug when breaking 0, 360 values */
2621 st->info.momentum = (((*angle) - st->info.base_angle) /
2622 (fabs(tm2 - tm1))) * 1000;
2625 st->info.momentum = 0;
2635 * This function is used to test rotation gesture.
2636 * user may combine zoom, rotation together.
2637 * so its possible that both will be detected from input.
2638 * (both are two-finger movement-oriented gestures)
2640 * @param obj The gesture-layer object.
2641 * @param event_info Pointer to recent input event.
2642 * @param event_type Recent input event type.
2643 * @param g_type what Gesture we are testing.
2645 * @ingroup Elm_Gesture_Layer
2648 _rotate_test(Evas_Object *obj, Pointer_Event *pe, void *event_info,
2649 Evas_Callback_Type event_type, Elm_Gesture_Types g_type)
2654 Widget_Data *wd = elm_widget_data_get(obj);
2656 if (!wd->gesture[g_type]) return;
2658 Gesture_Info *gesture = wd->gesture[g_type];
2659 Rotate_Type *st = gesture->data;
2664 { /* Allocated once on first time */
2665 st = calloc(1, sizeof(Rotate_Type));
2667 _rotate_test_reset(gesture);
2671 Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
2675 case EVAS_CALLBACK_MOUSE_DOWN:
2676 consume_event(wd, event_info, event_type, ev_flag);
2677 memcpy(&st->rotate_st, pe, sizeof(Pointer_Event));
2681 case EVAS_CALLBACK_MOUSE_MOVE:
2682 if (!st->rotate_st.timestamp)
2683 break; /* We got move event before down event.Ignore it */
2685 consume_event(wd, event_info, event_type, ev_flag);
2686 memcpy(&st->rotate_mv, pe, sizeof(Pointer_Event));
2688 /* Match this point to previous multi-move or multi-down event */
2689 if (st->rotate_mv1.timestamp)
2690 { /* Compute rotation angle and report to user */
2691 _get_rotate_properties(st,
2692 st->rotate_mv.x, st->rotate_mv.y, st->rotate_mv.timestamp,
2693 st->rotate_mv1.x, st->rotate_mv1.y, st->rotate_mv1.timestamp,
2698 if (st->rotate_st1.timestamp)
2699 { /* Compute rotation angle and report to user */
2700 _get_rotate_properties(st,
2701 st->rotate_mv.x, st->rotate_mv.y, st->rotate_mv.timestamp,
2702 st->rotate_st1.x, st->rotate_st1.y, st->rotate_st1.timestamp,
2709 case EVAS_CALLBACK_MULTI_MOVE:
2710 if (!st->rotate_st1.timestamp)
2711 break; /* We got move event before down event.Ignore it */
2713 consume_event(wd, event_info, event_type, ev_flag);
2714 if (st->rotate_mv1.timestamp)
2716 if (st->rotate_mv1.device !=
2717 ((Evas_Event_Multi_Move *) event_info)->device)
2718 { /* A third finger on screen, abort rotate */
2719 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
2720 &st->info, EINA_FALSE);
2721 consume_event(wd, event_info, event_type, ev_flag);
2727 memcpy(&st->rotate_mv1, pe, sizeof(Pointer_Event));
2729 /* Match this point to previous mouse-move or mouse-down event */
2730 if (st->rotate_mv.timestamp)
2731 { /* Compute rotation angle and report to user */
2732 _get_rotate_properties(st,
2733 st->rotate_mv.x, st->rotate_mv.y, st->rotate_mv.timestamp,
2734 st->rotate_mv1.x, st->rotate_mv1.y, st->rotate_mv1.timestamp,
2739 if (st->rotate_st.timestamp)
2740 { /* Compute rotation angle and report to user */
2741 _get_rotate_properties(st,
2742 st->rotate_st.x, st->rotate_st.y, st->rotate_st.timestamp,
2743 st->rotate_mv1.x, st->rotate_mv1.y, st->rotate_mv1.timestamp,
2750 case EVAS_CALLBACK_MULTI_DOWN:
2751 consume_event(wd, event_info, event_type, ev_flag);
2752 memcpy(&st->rotate_st1, pe, sizeof(Pointer_Event));
2753 _get_rotate_properties(st,
2754 st->rotate_st.x, st->rotate_st.y, st->rotate_st.timestamp,
2755 st->rotate_st1.x, st->rotate_st1.y, st->rotate_st1.timestamp,
2759 case EVAS_CALLBACK_MOUSE_UP:
2760 case EVAS_CALLBACK_MULTI_UP:
2761 consume_event(wd, event_info, event_type, ev_flag);
2762 /* Reset timestamp of finger-up.This is used later
2763 by rotate_test_reset() to retain finger-down data */
2764 if (st->rotate_angular_tolerance < 0)
2766 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END,
2767 &st->info, EINA_FALSE);
2768 consume_event(wd, event_info, event_type, ev_flag);
2773 if (gesture->state != ELM_GESTURE_STATE_UNDEFINED)
2774 { /* Must be != undefined, if gesture started */
2775 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT,
2776 &st->info, EINA_FALSE);
2777 consume_event(wd, event_info, event_type, ev_flag);
2780 _rotate_test_reset(gesture);
2787 if ((event_type == EVAS_CALLBACK_MOUSE_MOVE) ||
2788 (event_type == EVAS_CALLBACK_MULTI_MOVE))
2789 { /* Report MOVE or ABORT for *MOVE event */
2790 if (rotation_broke_tolerance(st))
2791 { /* Rotation broke tolerance, report move */
2792 double d = st->info.angle - st->next_step;
2796 if (d >= wd->rotate_step)
2797 { /* Report move in steps */
2798 st->next_step = st->info.angle;
2800 ev_flag = _set_state(gesture,
2801 ELM_GESTURE_STATE_MOVE, &st->info, EINA_TRUE);
2802 consume_event(wd, event_info, event_type, ev_flag);
2809 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
2810 (event_type == EVAS_CALLBACK_MULTI_DOWN))
2812 if ((st->rotate_st.timestamp) && (st->rotate_st1.timestamp))
2813 { /* two-fingers on touch screen - report rotate start */
2814 /* Set base angle, then report start. */
2815 _get_rotate_properties(st,
2816 st->rotate_st.x, st->rotate_st.y, st->rotate_st.timestamp,
2817 st->rotate_st1.x, st->rotate_st1.y, st->rotate_st1.timestamp,
2818 &st->info.base_angle);
2820 ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START,
2821 &st->info, EINA_FALSE);
2822 consume_event(wd, event_info, event_type, ev_flag);
2832 * This function is used to save input events in an abstract struct
2833 * to be used later by getsure-testing functions.
2835 * @param data The gesture-layer object.
2836 * @param event_info Pointer to recent input event.
2837 * @param event_type Recent input event type.
2838 * @param pe The abstract data-struct (output).
2840 * @ingroup Elm_Gesture_Layer
2843 _make_pointer_event(void *data, void *event_info,
2844 Evas_Callback_Type event_type, Pointer_Event *pe)
2846 Widget_Data *wd = elm_widget_data_get(data);
2847 if (!wd) return EINA_FALSE;
2851 case EVAS_CALLBACK_MOUSE_DOWN:
2852 pe->x = ((Evas_Event_Mouse_Down *) event_info)->canvas.x;
2853 pe->y = ((Evas_Event_Mouse_Down *) event_info)->canvas.y;
2854 pe->timestamp = ((Evas_Event_Mouse_Down *) event_info)->timestamp;
2855 pe->device = ELM_MOUSE_DEVICE;
2858 case EVAS_CALLBACK_MOUSE_UP:
2859 pe->x = ((Evas_Event_Mouse_Up *) event_info)->canvas.x;
2860 pe->y = ((Evas_Event_Mouse_Up *) event_info)->canvas.y;
2861 pe->timestamp = ((Evas_Event_Mouse_Up *) event_info)->timestamp;
2862 pe->device = ELM_MOUSE_DEVICE;
2865 case EVAS_CALLBACK_MOUSE_MOVE:
2866 pe->x = ((Evas_Event_Mouse_Move *) event_info)->cur.canvas.x;
2867 pe->y = ((Evas_Event_Mouse_Move *) event_info)->cur.canvas.y;
2868 pe->timestamp = ((Evas_Event_Mouse_Move *) event_info)->timestamp;
2869 pe->device = ELM_MOUSE_DEVICE;
2872 case EVAS_CALLBACK_MULTI_DOWN:
2873 pe->x = ((Evas_Event_Multi_Down *) event_info)->canvas.x;
2874 pe->y = ((Evas_Event_Multi_Down *) event_info)->canvas.y;
2875 pe->timestamp = ((Evas_Event_Multi_Down *) event_info)->timestamp;
2876 pe->device = ((Evas_Event_Multi_Down *) event_info)->device;
2879 case EVAS_CALLBACK_MULTI_UP:
2880 pe->x = ((Evas_Event_Multi_Up *) event_info)->canvas.x;
2881 pe->y = ((Evas_Event_Multi_Up *) event_info)->canvas.y;
2882 pe->timestamp = ((Evas_Event_Multi_Up *) event_info)->timestamp;
2883 pe->device = ((Evas_Event_Multi_Up *) event_info)->device;
2886 case EVAS_CALLBACK_MULTI_MOVE:
2887 pe->x = ((Evas_Event_Multi_Move *) event_info)->cur.canvas.x;
2888 pe->y = ((Evas_Event_Multi_Move *) event_info)->cur.canvas.y;
2889 pe->timestamp = ((Evas_Event_Multi_Move *) event_info)->timestamp;
2890 pe->device = ((Evas_Event_Multi_Move *) event_info)->device;
2897 pe->event_type = event_type;
2904 * This function the core-function where input handling is done.
2905 * Here we get user input and stream it to gesture testing.
2906 * We notify user about any gestures with new state:
2908 * START - gesture started.
2909 * MOVE - gesture is ongoing.
2910 * END - gesture was completed.
2911 * ABORT - gesture was aborted after START, MOVE (will NOT be completed)
2913 * We also check if a gesture was detected, then reset event history
2914 * If no gestures were found we reset gesture test flag
2915 * after streaming event-history to widget.
2916 * (stream to the widget all events not consumed as a gesture)
2918 * @param data The gesture-layer object.
2919 * @param event_info Pointer to recent input event.
2920 * @param event_type Recent input event type.
2922 * @ingroup Elm_Gesture_Layer
2925 _event_process(void *data, Evas_Object *obj __UNUSED__,
2926 void *event_info, Evas_Callback_Type event_type)
2929 Pointer_Event *pe = NULL;
2930 Widget_Data *wd = elm_widget_data_get(data);
2933 /* Start testing candidate gesture from here */
2934 if (_make_pointer_event(data, event_info, event_type, &_pe))
2937 if (IS_TESTED(ELM_GESTURE_N_LONG_TAPS))
2938 _n_long_tap_test(data, pe, event_info, event_type,
2939 ELM_GESTURE_N_LONG_TAPS);
2941 if (IS_TESTED(ELM_GESTURE_N_TAPS))
2942 _dbl_click_test(data, pe, event_info, event_type,
2943 ELM_GESTURE_N_TAPS, 1);
2945 if (IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS))
2946 _dbl_click_test(data, pe, event_info, event_type,
2947 ELM_GESTURE_N_DOUBLE_TAPS, 2);
2949 if (IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS))
2950 _dbl_click_test(data, pe, event_info, event_type,
2951 ELM_GESTURE_N_TRIPLE_TAPS, 3);
2953 if (IS_TESTED(ELM_GESTURE_MOMENTUM))
2954 _momentum_test(data, pe, event_info, event_type,
2955 ELM_GESTURE_MOMENTUM);
2957 if (IS_TESTED(ELM_GESTURE_N_LINES))
2958 _n_line_test(data, pe, event_info, event_type, ELM_GESTURE_N_LINES);
2960 if (IS_TESTED(ELM_GESTURE_N_FLICKS))
2961 _n_line_test(data, pe, event_info, event_type, ELM_GESTURE_N_FLICKS);
2963 if (IS_TESTED(ELM_GESTURE_ZOOM))
2964 _zoom_test(data, pe, event_info, event_type, ELM_GESTURE_ZOOM);
2966 if (IS_TESTED(ELM_GESTURE_ZOOM))
2967 _zoom_with_wheel_test(data, event_info, event_type, ELM_GESTURE_ZOOM);
2969 if (IS_TESTED(ELM_GESTURE_ROTATE))
2970 _rotate_test(data, pe, event_info, event_type, ELM_GESTURE_ROTATE);
2972 if (_get_event_flag(event_info, event_type) & EVAS_EVENT_FLAG_ON_HOLD)
2973 _event_history_add(data, event_info, event_type);
2974 else if ((event_type == EVAS_CALLBACK_MOUSE_UP) ||
2975 (event_type == EVAS_CALLBACK_MULTI_UP))
2977 Eina_List *pending = _device_is_pending(wd->pending, event_info, event_type);
2980 consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_ON_HOLD);
2981 _event_history_add(data, event_info, event_type);
2985 /* we maintain list of touched devices*/
2986 if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
2987 (event_type == EVAS_CALLBACK_MULTI_DOWN))
2989 wd->touched = _add_touched_device(wd->touched, pe);
2991 else if ((event_type == EVAS_CALLBACK_MOUSE_UP) ||
2992 (event_type == EVAS_CALLBACK_MULTI_UP))
2994 wd->touched = _remove_touched_device(wd->touched, pe);
2997 /* Report current states and clear history if needed */
2998 _clear_if_finished(data);
3003 * For all _mouse_* / multi_* functions wethen send this event to
3004 * _event_process function.
3006 * @param data The gesture-layer object.
3007 * @param event_info Pointer to recent input event.
3009 * @ingroup Elm_Gesture_Layer
3012 _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3015 Widget_Data *wd = elm_widget_data_get(data);
3017 if (((Evas_Event_Mouse_Down *) event_info)->button != 1)
3018 return; /* We only process left-click at the moment */
3020 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_DOWN);
3024 _mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3027 Widget_Data *wd = elm_widget_data_get(data);
3030 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_MOVE);
3034 _key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3037 Widget_Data *wd = elm_widget_data_get(data);
3040 _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_DOWN);
3044 _key_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3047 Widget_Data *wd = elm_widget_data_get(data);
3050 _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_UP);
3054 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3057 Widget_Data *wd = elm_widget_data_get(data);
3060 if (((Evas_Event_Mouse_Up *) event_info)->button != 1)
3061 return; /* We only process left-click at the moment */
3063 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_UP);
3067 _mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3070 Widget_Data *wd = elm_widget_data_get(data);
3073 _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_WHEEL);
3077 _multi_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3080 Widget_Data *wd = elm_widget_data_get(data);
3083 _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_DOWN);
3087 _multi_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3090 Widget_Data *wd = elm_widget_data_get(data);
3093 _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_MOVE);
3097 _multi_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
3100 Widget_Data *wd = elm_widget_data_get(data);
3103 _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_UP);
3107 elm_gesture_layer_hold_events_get(Evas_Object *obj)
3109 Widget_Data *wd = elm_widget_data_get(obj);
3110 if (!wd) return EINA_FALSE;
3112 return !wd->repeat_events;
3116 elm_gesture_layer_hold_events_set(Evas_Object *obj, Eina_Bool r)
3118 Widget_Data *wd = elm_widget_data_get(obj);
3121 wd->repeat_events = !r;
3125 elm_gesture_layer_zoom_step_set(Evas_Object *obj, double s)
3127 Widget_Data *wd = elm_widget_data_get(obj);
3137 elm_gesture_layer_rotate_step_set(Evas_Object *obj, double s)
3139 Widget_Data *wd = elm_widget_data_get(obj);
3145 wd->rotate_step = s;
3149 elm_gesture_layer_attach(Evas_Object *obj, Evas_Object *t)
3151 Widget_Data *wd = elm_widget_data_get(obj);
3152 if (!wd) return EINA_FALSE;
3157 /* if was attached before, unregister callbacks first */
3159 _unregister_callbacks(obj);
3163 _register_callbacks(obj);
3168 elm_gesture_layer_cb_set(Evas_Object *obj, Elm_Gesture_Types idx,
3169 Elm_Gesture_State cb_type, Elm_Gesture_Event_Cb cb, void *data)
3171 Widget_Data *wd = elm_widget_data_get(obj);
3174 if (!wd->gesture[idx])
3175 wd->gesture[idx] = calloc(1, sizeof(Gesture_Info));
3177 Gesture_Info *p = wd->gesture[idx];
3180 p->fn[cb_type].cb = cb;
3181 p->fn[cb_type].user_data = data;
3182 p->state = ELM_GESTURE_STATE_UNDEFINED;
3187 _disable_hook(Evas_Object *obj)
3189 if (elm_widget_disabled_get(obj))
3190 _unregister_callbacks(obj);
3192 _register_callbacks(obj);
3196 elm_gesture_layer_add(Evas_Object *parent)
3202 EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
3204 wd = ELM_NEW(Widget_Data);
3205 e = evas_object_evas_get(parent);
3206 if (!e) return NULL;
3207 obj = elm_widget_add(e);
3208 ELM_SET_WIDTYPE(widtype, "gesture_layer");
3209 elm_widget_type_set(obj, "gesture_layer");
3210 elm_widget_sub_object_add(parent, obj);
3211 elm_widget_data_set(obj, wd);
3212 elm_widget_del_hook_set(obj, _del_hook);
3213 elm_widget_disable_hook_set(obj, _disable_hook);
3216 wd->line_min_length =_elm_config->glayer_line_min_length * elm_finger_size_get();
3217 wd->zoom_distance_tolerance = _elm_config->glayer_zoom_distance_tolerance * elm_finger_size_get();
3218 wd->line_distance_tolerance = _elm_config->glayer_line_distance_tolerance * elm_finger_size_get();
3219 wd->zoom_finger_factor = _elm_config->glayer_zoom_finger_factor;
3220 wd->zoom_wheel_factor = _elm_config->glayer_zoom_wheel_factor; /* mouse wheel zoom steps */
3221 wd->rotate_angular_tolerance = _elm_config->glayer_rotate_angular_tolerance;
3222 wd->line_angular_tolerance = _elm_config->glayer_line_angular_tolerance;
3223 wd->flick_time_limit_ms = _elm_config->glayer_flick_time_limit_ms;
3224 wd->long_tap_start_timeout = _elm_config->glayer_long_tap_start_timeout;
3225 wd->repeat_events = EINA_TRUE;
3227 #if defined(DEBUG_GESTURE_LAYER)
3228 printf("size of Gestures = <%d>\n", sizeof(wd->gesture));
3229 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>\nwd->long_tap_start_timeout=<%f>\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);
3231 memset(wd->gesture, 0, sizeof(wd->gesture));