diff options
Diffstat (limited to 'libraries/elementary/src/lib/elm_gesture_layer.c')
-rw-r--r-- | libraries/elementary/src/lib/elm_gesture_layer.c | 3520 |
1 files changed, 3520 insertions, 0 deletions
diff --git a/libraries/elementary/src/lib/elm_gesture_layer.c b/libraries/elementary/src/lib/elm_gesture_layer.c new file mode 100644 index 0000000..2d6dcc7 --- /dev/null +++ b/libraries/elementary/src/lib/elm_gesture_layer.c | |||
@@ -0,0 +1,3520 @@ | |||
1 | #include <Elementary.h> | ||
2 | #include "elm_priv.h" | ||
3 | /** @defgroup Elm_Gesture_Layer Gesture Layer */ | ||
4 | |||
5 | /* Some defaults */ | ||
6 | #define ELM_MOUSE_DEVICE 0 | ||
7 | /* ELM_GESTURE_NEGATIVE_ANGLE - magic number says we didn't compute this yet */ | ||
8 | #define ELM_GESTURE_NEGATIVE_ANGLE (-1.0) /* Magic number */ | ||
9 | #define ELM_GESTURE_MOMENTUM_DELAY 25 | ||
10 | #define ELM_GESTURE_MOMENTUM_TIMEOUT 50 | ||
11 | #define ELM_GESTURE_MULTI_TIMEOUT 50 | ||
12 | #define ELM_GESTURE_MINIMUM_MOMENTUM 0.001 | ||
13 | |||
14 | /* Some Trigo values */ | ||
15 | #define RAD_90DEG M_PI_2 | ||
16 | #define RAD_180DEG M_PI | ||
17 | #define RAD_270DEG (M_PI_2 * 3) | ||
18 | #define RAD_360DEG (M_PI * 2) | ||
19 | /* #define DEBUG_GESTURE_LAYER 1 */ | ||
20 | |||
21 | #define RAD2DEG(x) ((x) * 57.295779513) | ||
22 | #define DEG2RAD(x) ((x) / 57.295779513) | ||
23 | |||
24 | static void * | ||
25 | _glayer_bufdup(void *buf, size_t size) | ||
26 | { | ||
27 | void *p; | ||
28 | p = malloc(size); | ||
29 | memcpy(p, buf, size); | ||
30 | return p; | ||
31 | } | ||
32 | #define COPY_EVENT_INFO(EV) _glayer_bufdup(EV, sizeof(*EV)) | ||
33 | |||
34 | |||
35 | #define SET_TEST_BIT(P) do { \ | ||
36 | 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; \ | ||
37 | } while (0) | ||
38 | |||
39 | #define IS_TESTED(T) ((wd->gesture[T]) ? wd->gesture[T]->test : EINA_FALSE) | ||
40 | |||
41 | /** | ||
42 | * @internal | ||
43 | * | ||
44 | * @struct _Func_Data | ||
45 | * Struct holds callback information. | ||
46 | * | ||
47 | * @ingroup Elm_Gesture_Layer | ||
48 | */ | ||
49 | struct _Func_Data | ||
50 | { | ||
51 | void *user_data; /**< Holds user data to CB (like sd) */ | ||
52 | Elm_Gesture_Event_Cb cb; | ||
53 | }; | ||
54 | |||
55 | /** | ||
56 | * @internal | ||
57 | * | ||
58 | * @typedef Func_Data | ||
59 | * type for callback information | ||
60 | * | ||
61 | * @ingroup Elm_Gesture_Layer | ||
62 | */ | ||
63 | typedef struct _Func_Data Func_Data; | ||
64 | |||
65 | /** | ||
66 | * @internal | ||
67 | * | ||
68 | * @struct _Gesture_Info | ||
69 | * Struct holds gesture info | ||
70 | * | ||
71 | * @ingroup Elm_Gesture_Layer | ||
72 | */ | ||
73 | struct _Gesture_Info | ||
74 | { | ||
75 | Evas_Object *obj; | ||
76 | void *data; /**< Holds gesture intemidiate processing data */ | ||
77 | Func_Data fn[ELM_GESTURE_STATE_ABORT + 1]; /**< Callback info for states */ | ||
78 | Elm_Gesture_Type g_type; /**< gesture type */ | ||
79 | Elm_Gesture_State state; /**< gesture state */ | ||
80 | void *info; /**< Data for the state callback */ | ||
81 | Eina_Bool test; /**< if true this gesture should be tested on input */ | ||
82 | }; | ||
83 | |||
84 | /** | ||
85 | * @internal | ||
86 | * | ||
87 | * @typedef Gesture_Info | ||
88 | * Type for _Gesture_Info | ||
89 | * | ||
90 | * @ingroup Elm_Gesture_Layer | ||
91 | */ | ||
92 | typedef struct _Gesture_Info Gesture_Info; | ||
93 | |||
94 | /** | ||
95 | * @internal | ||
96 | * | ||
97 | * @struct _Event_History | ||
98 | * Struct holds event history. | ||
99 | * These events are repeated if no gesture found. | ||
100 | * | ||
101 | * @ingroup Elm_Gesture_Layer | ||
102 | */ | ||
103 | struct _Event_History | ||
104 | { | ||
105 | EINA_INLIST; | ||
106 | void *event; | ||
107 | Evas_Callback_Type event_type; | ||
108 | }; | ||
109 | |||
110 | /** | ||
111 | * @internal | ||
112 | * | ||
113 | * @typedef Event_History | ||
114 | * Type for _Event_History | ||
115 | * | ||
116 | * @ingroup Elm_Gesture_Layer | ||
117 | */ | ||
118 | typedef struct _Event_History Event_History; | ||
119 | |||
120 | /** | ||
121 | * @internal | ||
122 | * | ||
123 | * @struct _Pointer_Event | ||
124 | * Struct holds pointer-event info | ||
125 | * This is a generic pointer event structure | ||
126 | * | ||
127 | * @ingroup Elm_Gesture_Layer | ||
128 | */ | ||
129 | struct _Pointer_Event | ||
130 | { | ||
131 | Evas_Coord x, y; | ||
132 | unsigned int timestamp; | ||
133 | int device; | ||
134 | Evas_Callback_Type event_type; | ||
135 | }; | ||
136 | |||
137 | /** | ||
138 | * @internal | ||
139 | * | ||
140 | * @typedef Pointer_Event | ||
141 | * Type for generic pointer event structure | ||
142 | * | ||
143 | * @ingroup Elm_Gesture_Layer | ||
144 | */ | ||
145 | typedef struct _Pointer_Event Pointer_Event; | ||
146 | |||
147 | /* All *Type structs hold result for the user in 'info' field | ||
148 | * The rest is gesture processing intermediate data. | ||
149 | * NOTE: info field must be FIRST in the struct. | ||
150 | * This is used when reporting ABORT in event_history_clear() */ | ||
151 | struct _Taps_Type | ||
152 | { | ||
153 | Elm_Gesture_Taps_Info info; | ||
154 | unsigned int sum_x; | ||
155 | unsigned int sum_y; | ||
156 | unsigned int n_taps_needed; | ||
157 | unsigned int n_taps; | ||
158 | Eina_List *l; | ||
159 | }; | ||
160 | typedef struct _Taps_Type Taps_Type; | ||
161 | |||
162 | struct _Long_Tap_Type | ||
163 | { | ||
164 | Elm_Gesture_Taps_Info info; | ||
165 | Evas_Coord center_x; | ||
166 | Evas_Coord center_y; | ||
167 | unsigned int max_touched; | ||
168 | Ecore_Timer *timeout; /* When this expires, long tap STARTed */ | ||
169 | Eina_List *touched; | ||
170 | }; | ||
171 | typedef struct _Long_Tap_Type Long_Tap_Type; | ||
172 | |||
173 | struct _Momentum_Type | ||
174 | { /* Fields used by _line_test() */ | ||
175 | Elm_Gesture_Momentum_Info info; | ||
176 | Evas_Coord_Point line_st; | ||
177 | Evas_Coord_Point line_end; | ||
178 | unsigned int t_st_x; /* Time start on X */ | ||
179 | unsigned int t_st_y; /* Time start on Y */ | ||
180 | unsigned int t_end; /* Time end */ | ||
181 | unsigned int t_up; /* Recent up event time */ | ||
182 | int xdir, ydir; | ||
183 | }; | ||
184 | typedef struct _Momentum_Type Momentum_Type; | ||
185 | |||
186 | struct _Line_Data | ||
187 | { | ||
188 | Evas_Coord_Point line_st; | ||
189 | Evas_Coord_Point line_end; | ||
190 | Evas_Coord line_length; | ||
191 | unsigned int t_st; /* Time start */ | ||
192 | unsigned int t_end; /* Time end */ | ||
193 | int device; | ||
194 | double line_angle; /* Current angle of line */ | ||
195 | }; | ||
196 | typedef struct _Line_Data Line_Data; | ||
197 | |||
198 | struct _Line_Type | ||
199 | { /* Fields used by _line_test() */ | ||
200 | Elm_Gesture_Line_Info info; | ||
201 | Eina_List *list; /* List of Line_Data */ | ||
202 | }; | ||
203 | typedef struct _Line_Type Line_Type; | ||
204 | |||
205 | struct _Zoom_Type | ||
206 | { /* Fields used by _zoom_test() */ | ||
207 | Elm_Gesture_Zoom_Info info; | ||
208 | Pointer_Event zoom_st; | ||
209 | Pointer_Event zoom_mv; | ||
210 | Pointer_Event zoom_st1; | ||
211 | Pointer_Event zoom_mv1; | ||
212 | Evas_Event_Mouse_Wheel *zoom_wheel; | ||
213 | Evas_Coord zoom_base; /* Holds gap between fingers on zoom-start */ | ||
214 | Evas_Coord zoom_distance_tolerance; | ||
215 | unsigned int m_st_tm; /* momentum start time */ | ||
216 | unsigned int m_prev_tm; /* momentum prev time */ | ||
217 | int dir; /* Direction: 1=zoom-in, (-1)=zoom-out */ | ||
218 | double m_base; /* zoom value when momentum starts */ | ||
219 | double next_step; | ||
220 | }; | ||
221 | typedef struct _Zoom_Type Zoom_Type; | ||
222 | |||
223 | struct _Rotate_Type | ||
224 | { /* Fields used by _rotation_test() */ | ||
225 | Elm_Gesture_Rotate_Info info; | ||
226 | Pointer_Event rotate_st; | ||
227 | Pointer_Event rotate_mv; | ||
228 | Pointer_Event rotate_st1; | ||
229 | Pointer_Event rotate_mv1; | ||
230 | unsigned int prev_momentum_tm; /* timestamp of prev_momentum */ | ||
231 | double prev_momentum; /* Snapshot of momentum 0.01 sec ago */ | ||
232 | double accum_momentum; | ||
233 | double rotate_angular_tolerance; | ||
234 | double next_step; | ||
235 | }; | ||
236 | typedef struct _Rotate_Type Rotate_Type; | ||
237 | |||
238 | struct _Widget_Data | ||
239 | { | ||
240 | Evas_Object *target; /* Target Widget */ | ||
241 | Event_History *event_history_list; | ||
242 | |||
243 | int line_min_length; | ||
244 | Evas_Coord zoom_distance_tolerance; | ||
245 | Evas_Coord line_distance_tolerance; | ||
246 | double line_angular_tolerance; | ||
247 | double zoom_wheel_factor; /* mouse wheel zoom steps */ | ||
248 | double zoom_finger_factor; /* used for zoom factor */ | ||
249 | double rotate_angular_tolerance; | ||
250 | unsigned int flick_time_limit_ms; | ||
251 | double long_tap_start_timeout; | ||
252 | Eina_Bool glayer_continues_enable; | ||
253 | |||
254 | double zoom_step; | ||
255 | double rotate_step; | ||
256 | |||
257 | Gesture_Info *gesture[ELM_GESTURE_LAST]; | ||
258 | Ecore_Timer *dbl_timeout; /* When this expires, dbl click/taps ABORTed */ | ||
259 | Eina_List *pending; /* List of devices need to refeed *UP event */ | ||
260 | Eina_List *touched; /* Information of touched devices */ | ||
261 | |||
262 | Eina_Bool repeat_events : 1; | ||
263 | }; | ||
264 | typedef struct _Widget_Data Widget_Data; | ||
265 | |||
266 | static const char *widtype = NULL; | ||
267 | static void _del_hook(Evas_Object *obj); | ||
268 | |||
269 | static Eina_Bool _event_history_clear(Evas_Object *obj); | ||
270 | static void _reset_states(Widget_Data *wd); | ||
271 | static void _key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info); | ||
272 | static void _key_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info); | ||
273 | static void _zoom_with_wheel_test(Evas_Object *obj, void *event_info, Evas_Callback_Type event_type, Elm_Gesture_Type g_type); | ||
274 | static void _mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info); | ||
275 | static void _mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info); | ||
276 | static void _mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info); | ||
277 | static void _mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info); | ||
278 | |||
279 | static void _multi_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info); | ||
280 | static void _multi_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info); | ||
281 | static void _multi_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info); | ||
282 | |||
283 | /* START - Functions to manage touched-device list */ | ||
284 | /** | ||
285 | * @internal | ||
286 | * This function is used to find if device is touched | ||
287 | * | ||
288 | * @ingroup Elm_Gesture_Layer | ||
289 | */ | ||
290 | static int | ||
291 | compare_device(const void *data1, const void *data2) | ||
292 | { /* Compare the two device numbers */ | ||
293 | return (((Pointer_Event *) data1)->device -((Pointer_Event *) data2)->device); | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * @internal | ||
298 | * | ||
299 | * Remove Pointer Event from touched device list | ||
300 | * @param list Pointer to touched device list. | ||
301 | * @param Pointer_Event Pointer to PE. | ||
302 | * | ||
303 | * @ingroup Elm_Gesture_Layer | ||
304 | */ | ||
305 | static Eina_List * | ||
306 | _remove_touched_device(Eina_List *list, Pointer_Event *pe) | ||
307 | { | ||
308 | Eina_List *lst = NULL; | ||
309 | Pointer_Event *p = eina_list_search_unsorted(list, compare_device, pe); | ||
310 | if (p) | ||
311 | { | ||
312 | lst = eina_list_remove(list, p); | ||
313 | free(p); | ||
314 | return lst; | ||
315 | } | ||
316 | |||
317 | return list; | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * @internal | ||
322 | * | ||
323 | * Recoed Pointer Event in touched device list | ||
324 | * Note: This fuction allocates memory for PE event | ||
325 | * This memory is released in _remove_touched_device() | ||
326 | * @param list Pointer to touched device list. | ||
327 | * @param Pointer_Event Pointer to PE. | ||
328 | * | ||
329 | * @ingroup Elm_Gesture_Layer | ||
330 | */ | ||
331 | static Eina_List * | ||
332 | _add_touched_device(Eina_List *list, Pointer_Event *pe) | ||
333 | { | ||
334 | Pointer_Event *p = eina_list_search_unsorted(list, compare_device, pe); | ||
335 | if (p) | ||
336 | { /* We like to track device touch-position, overwrite info */ | ||
337 | memcpy(p, pe, sizeof(Pointer_Event)); | ||
338 | return list; | ||
339 | } | ||
340 | |||
341 | if ((pe->event_type == EVAS_CALLBACK_MOUSE_DOWN) || | ||
342 | (pe->event_type == EVAS_CALLBACK_MULTI_DOWN)) | ||
343 | { /* Add touched device on DOWN event only */ | ||
344 | p = malloc(sizeof(Pointer_Event)); | ||
345 | /* Freed in _remove_touched_device() */ | ||
346 | memcpy(p, pe, sizeof(Pointer_Event)); | ||
347 | return eina_list_append(list, p); | ||
348 | } | ||
349 | |||
350 | return list; | ||
351 | } | ||
352 | /* END - Functions to manage touched-device list */ | ||
353 | |||
354 | /** | ||
355 | * @internal | ||
356 | * | ||
357 | * Get event flag | ||
358 | * @param event_info pointer to event. | ||
359 | * | ||
360 | * @ingroup Elm_Gesture_Layer | ||
361 | */ | ||
362 | static Evas_Event_Flags | ||
363 | _get_event_flag(void *event_info, Evas_Callback_Type event_type) | ||
364 | { | ||
365 | switch(event_type) | ||
366 | { | ||
367 | case EVAS_CALLBACK_MOUSE_IN: | ||
368 | return ((Evas_Event_Mouse_In *) event_info)->event_flags; | ||
369 | case EVAS_CALLBACK_MOUSE_OUT: | ||
370 | return ((Evas_Event_Mouse_Out *) event_info)->event_flags; | ||
371 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
372 | return ((Evas_Event_Mouse_Down *) event_info)->event_flags; | ||
373 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
374 | return ((Evas_Event_Mouse_Move *) event_info)->event_flags; | ||
375 | case EVAS_CALLBACK_MOUSE_UP: | ||
376 | return ((Evas_Event_Mouse_Up *) event_info)->event_flags; | ||
377 | case EVAS_CALLBACK_MOUSE_WHEEL: | ||
378 | return ((Evas_Event_Mouse_Wheel *) event_info)->event_flags; | ||
379 | case EVAS_CALLBACK_MULTI_DOWN: | ||
380 | return ((Evas_Event_Multi_Down *) event_info)->event_flags; | ||
381 | case EVAS_CALLBACK_MULTI_MOVE: | ||
382 | return ((Evas_Event_Multi_Move *) event_info)->event_flags; | ||
383 | case EVAS_CALLBACK_MULTI_UP: | ||
384 | return ((Evas_Event_Multi_Up *) event_info)->event_flags; | ||
385 | case EVAS_CALLBACK_KEY_DOWN: | ||
386 | return ((Evas_Event_Key_Down *) event_info)->event_flags; | ||
387 | case EVAS_CALLBACK_KEY_UP: | ||
388 | return ((Evas_Event_Key_Up *) event_info)->event_flags; | ||
389 | default: | ||
390 | return EVAS_EVENT_FLAG_NONE; | ||
391 | } | ||
392 | } | ||
393 | |||
394 | /** | ||
395 | * @internal | ||
396 | * | ||
397 | * Sets event flag to value returned from user callback | ||
398 | * @param wd Widget Data | ||
399 | * @param event_info pointer to event. | ||
400 | * @param event_type what type was ev (mouse down, etc...) | ||
401 | * @param ev_flags event flags | ||
402 | * | ||
403 | * @ingroup Elm_Gesture_Layer | ||
404 | */ | ||
405 | static void | ||
406 | consume_event(Widget_Data *wd, void *event_info, | ||
407 | Evas_Callback_Type event_type, Evas_Event_Flags ev_flags) | ||
408 | { /* Mark EVAS_EVENT_FLAG_ON_HOLD on events that are used by gesture layer */ | ||
409 | /* ev_flags != EVAS_EVENT_FLAG_NONE means target used event and g-layer */ | ||
410 | /* should not refeed this event. */ | ||
411 | if (!event_info) | ||
412 | return; /* This happens when restarting gestures */ | ||
413 | |||
414 | if ((ev_flags) || (!wd->repeat_events)) | ||
415 | { | ||
416 | switch(event_type) | ||
417 | { | ||
418 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
419 | ((Evas_Event_Mouse_Down *) event_info)->event_flags |= ev_flags; | ||
420 | break; | ||
421 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
422 | ((Evas_Event_Mouse_Move *) event_info)->event_flags |= ev_flags; | ||
423 | break; | ||
424 | case EVAS_CALLBACK_MOUSE_UP: | ||
425 | ((Evas_Event_Mouse_Up *) event_info)->event_flags |= ev_flags; | ||
426 | break; | ||
427 | case EVAS_CALLBACK_MOUSE_WHEEL: | ||
428 | ((Evas_Event_Mouse_Wheel *) event_info)->event_flags |= ev_flags; | ||
429 | break; | ||
430 | case EVAS_CALLBACK_MULTI_DOWN: | ||
431 | ((Evas_Event_Multi_Down *) event_info)->event_flags |= ev_flags; | ||
432 | break; | ||
433 | case EVAS_CALLBACK_MULTI_MOVE: | ||
434 | ((Evas_Event_Multi_Move *) event_info)->event_flags |= ev_flags; | ||
435 | break; | ||
436 | case EVAS_CALLBACK_MULTI_UP: | ||
437 | ((Evas_Event_Multi_Up *) event_info)->event_flags |= ev_flags; | ||
438 | break; | ||
439 | case EVAS_CALLBACK_KEY_DOWN: | ||
440 | ((Evas_Event_Key_Down *) event_info)->event_flags |= ev_flags; | ||
441 | break; | ||
442 | case EVAS_CALLBACK_KEY_UP: | ||
443 | ((Evas_Event_Key_Up *) event_info)->event_flags |= ev_flags; | ||
444 | break; | ||
445 | default: | ||
446 | return; | ||
447 | } | ||
448 | } | ||
449 | } | ||
450 | |||
451 | /** | ||
452 | * @internal | ||
453 | * | ||
454 | * Report current state of a gesture by calling user callback. | ||
455 | * @param gesture what gesture state we report. | ||
456 | * @param info inforamtion for user callback | ||
457 | * | ||
458 | * @ingroup Elm_Gesture_Layer | ||
459 | */ | ||
460 | static Evas_Event_Flags | ||
461 | _report_state(Gesture_Info *gesture, void *info) | ||
462 | { /* We report current state (START, MOVE, END, ABORT), once */ | ||
463 | #if defined(DEBUG_GESTURE_LAYER) | ||
464 | printf("%s reporting gesture=<%d> state=<%d>\n" , __func__, gesture->g_type, | ||
465 | gesture->state); | ||
466 | #endif | ||
467 | if ((gesture->state != ELM_GESTURE_STATE_UNDEFINED) && | ||
468 | (gesture->fn[gesture->state].cb)) | ||
469 | { /* Fill state-info struct and send ptr to user callback */ | ||
470 | return gesture->fn[gesture->state].cb( | ||
471 | gesture->fn[gesture->state].user_data, info); | ||
472 | } | ||
473 | |||
474 | return EVAS_EVENT_FLAG_NONE; | ||
475 | } | ||
476 | |||
477 | /** | ||
478 | * @internal | ||
479 | * | ||
480 | * Update state for a given gesture. | ||
481 | * We may update gesture state to: | ||
482 | * UNDEFINED - current input did not start gesure yet. | ||
483 | * START - gesture started according to input. | ||
484 | * MOVE - gusture in progress. | ||
485 | * END - gesture completed according to input. | ||
486 | * ABORT - input does not matches gesure. | ||
487 | * note that we may move from UNDEFINED to ABORT | ||
488 | * because we may detect that gesture will not START | ||
489 | * with a given input. | ||
490 | * | ||
491 | * @param g given gesture to change state. | ||
492 | * @param s gesure new state. | ||
493 | * @param info buffer to be sent to user callback on report_state. | ||
494 | * @param force makes report_state to report the new-state even | ||
495 | * if its same as current state. Works for MOVE - gesture in progress. | ||
496 | * | ||
497 | * @ingroup Elm_Gesture_Layer | ||
498 | */ | ||
499 | static Evas_Event_Flags | ||
500 | _set_state(Gesture_Info *g, Elm_Gesture_State s, | ||
501 | void *info, Eina_Bool force) | ||
502 | { | ||
503 | Elm_Gesture_State old_state; | ||
504 | if ((g->state == s) && (!force)) | ||
505 | return EVAS_EVENT_FLAG_NONE; | ||
506 | |||
507 | old_state = g->state; | ||
508 | |||
509 | g->state = s; | ||
510 | g->info = info; /* Information for user callback */ | ||
511 | if ((g->state == ELM_GESTURE_STATE_ABORT) || | ||
512 | (g->state == ELM_GESTURE_STATE_END)) | ||
513 | g->test = EINA_FALSE; | ||
514 | |||
515 | if ((g->state != ELM_GESTURE_STATE_UNDEFINED) && | ||
516 | (!((old_state == ELM_GESTURE_STATE_UNDEFINED) && | ||
517 | (s == ELM_GESTURE_STATE_ABORT)))) | ||
518 | return _report_state(g, g->info); | ||
519 | |||
520 | return EVAS_EVENT_FLAG_NONE; | ||
521 | } | ||
522 | |||
523 | /** | ||
524 | * @internal | ||
525 | * | ||
526 | * This resets all gesture states and sets test-bit. | ||
527 | * this is used for restarting gestures to listen to input. | ||
528 | * happens after we complete a gesture or no gesture was detected. | ||
529 | * @param wd Widget data of the gesture-layer object. | ||
530 | * | ||
531 | * @ingroup Elm_Gesture_Layer | ||
532 | */ | ||
533 | static void | ||
534 | _reset_states(Widget_Data *wd) | ||
535 | { | ||
536 | int i; | ||
537 | Gesture_Info *p; | ||
538 | for (i = ELM_GESTURE_FIRST; i < ELM_GESTURE_LAST; i++) | ||
539 | { | ||
540 | p = wd->gesture[i]; | ||
541 | if (p) | ||
542 | { | ||
543 | _set_state(p, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE); | ||
544 | SET_TEST_BIT(p); | ||
545 | } | ||
546 | } | ||
547 | } | ||
548 | |||
549 | /** | ||
550 | * @internal | ||
551 | * | ||
552 | * if gesture was NOT detected AND we only have gestures in ABORT state | ||
553 | * we clear history immediately to be ready for input. | ||
554 | * | ||
555 | * @param obj The gesture-layer object. | ||
556 | * @return TRUE on event history_clear | ||
557 | * | ||
558 | * @ingroup Elm_Gesture_Layer | ||
559 | */ | ||
560 | static Eina_Bool | ||
561 | _clear_if_finished(Evas_Object *obj) | ||
562 | { | ||
563 | Widget_Data *wd = elm_widget_data_get(obj); | ||
564 | if (!wd) return EINA_FALSE; | ||
565 | int i; | ||
566 | |||
567 | /* Clear history if all we have aborted gestures */ | ||
568 | Eina_Bool reset_s = EINA_TRUE, all_undefined = EINA_TRUE; | ||
569 | for (i = ELM_GESTURE_FIRST; i < ELM_GESTURE_LAST; i++) | ||
570 | { /* If no gesture started and all we have aborted gestures, reset all */ | ||
571 | Gesture_Info *p = wd->gesture[i]; | ||
572 | if ((p) && (p->state != ELM_GESTURE_STATE_UNDEFINED)) | ||
573 | { | ||
574 | if ((p->state == ELM_GESTURE_STATE_START) || | ||
575 | (p->state == ELM_GESTURE_STATE_MOVE)) | ||
576 | reset_s = EINA_FALSE; | ||
577 | |||
578 | all_undefined = EINA_FALSE; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | if (reset_s && (!all_undefined)) | ||
583 | return _event_history_clear(obj); | ||
584 | |||
585 | return EINA_FALSE; | ||
586 | } | ||
587 | |||
588 | static Eina_Bool | ||
589 | _inside(Evas_Coord xx1, Evas_Coord yy1, Evas_Coord xx2, Evas_Coord yy2) | ||
590 | { | ||
591 | int w = _elm_config->finger_size >> 1; /* Finger size devided by 2 */ | ||
592 | if (xx1 < (xx2 - w)) | ||
593 | return EINA_FALSE; | ||
594 | |||
595 | if (xx1 > (xx2 + w)) | ||
596 | return EINA_FALSE; | ||
597 | |||
598 | if (yy1 < (yy2 - w)) | ||
599 | return EINA_FALSE; | ||
600 | |||
601 | if (yy1 > (yy2 + w)) | ||
602 | return EINA_FALSE; | ||
603 | |||
604 | return EINA_TRUE; | ||
605 | } | ||
606 | |||
607 | /* All *test_reset() funcs are called to clear | ||
608 | * gesture intermediate data. | ||
609 | * This happens when we need to reset our tests. | ||
610 | * for example when gesture is detected or all ABORTed. */ | ||
611 | static void | ||
612 | _tap_gestures_test_reset(Gesture_Info *gesture) | ||
613 | { | ||
614 | if (!gesture) | ||
615 | return; | ||
616 | |||
617 | Widget_Data *wd = elm_widget_data_get(gesture->obj); | ||
618 | wd->dbl_timeout = NULL; | ||
619 | Eina_List *data; | ||
620 | Pointer_Event *pe; | ||
621 | |||
622 | if (!gesture->data) | ||
623 | return; | ||
624 | |||
625 | EINA_LIST_FREE(((Taps_Type *) gesture->data)->l, data) | ||
626 | EINA_LIST_FREE(data, pe) | ||
627 | free(pe); | ||
628 | |||
629 | memset(gesture->data, 0, sizeof(Taps_Type)); | ||
630 | } | ||
631 | |||
632 | /* All *test_reset() funcs are called to clear | ||
633 | * gesture intermediate data. | ||
634 | * This happens when we need to reset our tests. | ||
635 | * for example when gesture is detected or all ABORTed. */ | ||
636 | static void | ||
637 | _n_long_tap_test_reset(Gesture_Info *gesture) | ||
638 | { | ||
639 | if (!gesture) | ||
640 | return; | ||
641 | |||
642 | if (!gesture->data) | ||
643 | return; | ||
644 | |||
645 | Long_Tap_Type *st = gesture->data; | ||
646 | Eina_List *l; | ||
647 | Pointer_Event *p; | ||
648 | EINA_LIST_FOREACH(st->touched, l, p) | ||
649 | free(p); | ||
650 | |||
651 | eina_list_free(st->touched); | ||
652 | if (st->timeout) | ||
653 | { | ||
654 | ecore_timer_del(st->timeout); | ||
655 | st->timeout = NULL; | ||
656 | } | ||
657 | memset(gesture->data, 0, sizeof(Long_Tap_Type)); | ||
658 | } | ||
659 | |||
660 | static void | ||
661 | _momentum_test_reset(Gesture_Info *gesture) | ||
662 | { | ||
663 | if (!gesture) | ||
664 | return; | ||
665 | |||
666 | if (!gesture->data) | ||
667 | return; | ||
668 | |||
669 | memset(gesture->data, 0, sizeof(Momentum_Type)); | ||
670 | } | ||
671 | |||
672 | static void | ||
673 | _line_data_reset(Line_Data *st) | ||
674 | { | ||
675 | if (!st) | ||
676 | return; | ||
677 | |||
678 | memset(st, 0, sizeof(Line_Data)); | ||
679 | st->line_angle = ELM_GESTURE_NEGATIVE_ANGLE; | ||
680 | } | ||
681 | |||
682 | static void | ||
683 | _line_test_reset(Gesture_Info *gesture) | ||
684 | { | ||
685 | if (!gesture) | ||
686 | return; | ||
687 | |||
688 | if (!gesture->data) | ||
689 | return; | ||
690 | |||
691 | Line_Type *st = gesture->data; | ||
692 | Eina_List *list = st->list; | ||
693 | Eina_List *l; | ||
694 | Line_Data *t_line; | ||
695 | EINA_LIST_FOREACH(list, l, t_line) | ||
696 | free(t_line); | ||
697 | |||
698 | eina_list_free(list); | ||
699 | st->list = NULL; | ||
700 | } | ||
701 | |||
702 | static void | ||
703 | _zoom_test_reset(Gesture_Info *gesture) | ||
704 | { | ||
705 | if (!gesture) | ||
706 | return; | ||
707 | |||
708 | if (!gesture->data) | ||
709 | return; | ||
710 | |||
711 | Widget_Data *wd = elm_widget_data_get(gesture->obj); | ||
712 | Zoom_Type *st = gesture->data; | ||
713 | Evas_Modifier_Mask mask = evas_key_modifier_mask_get( | ||
714 | evas_object_evas_get(wd->target), "Control"); | ||
715 | evas_object_key_ungrab(wd->target, "Control_L", mask, 0); | ||
716 | evas_object_key_ungrab(wd->target, "Control_R", mask, 0); | ||
717 | |||
718 | memset(st, 0, sizeof(Zoom_Type)); | ||
719 | st->zoom_distance_tolerance = wd->zoom_distance_tolerance; | ||
720 | st->info.zoom = 1.0; | ||
721 | } | ||
722 | |||
723 | static void | ||
724 | _rotate_test_reset(Gesture_Info *gesture) | ||
725 | { | ||
726 | if (!gesture) | ||
727 | return; | ||
728 | |||
729 | if (!gesture->data) | ||
730 | return; | ||
731 | |||
732 | Widget_Data *wd = elm_widget_data_get(gesture->obj); | ||
733 | Rotate_Type *st = gesture->data; | ||
734 | |||
735 | memset(st, 0, sizeof(Rotate_Type)); | ||
736 | st->info.base_angle = ELM_GESTURE_NEGATIVE_ANGLE; | ||
737 | st->rotate_angular_tolerance = wd->rotate_angular_tolerance; | ||
738 | } | ||
739 | |||
740 | |||
741 | /** | ||
742 | * @internal | ||
743 | * | ||
744 | * We register callbacks when gesture layer is attached to an object | ||
745 | * or when its enabled after disable. | ||
746 | * | ||
747 | * @param obj The gesture-layer object. | ||
748 | * | ||
749 | * @ingroup Elm_Gesture_Layer | ||
750 | */ | ||
751 | static void | ||
752 | _register_callbacks(Evas_Object *obj) | ||
753 | { | ||
754 | Widget_Data *wd = elm_widget_data_get(obj); | ||
755 | if (!wd) return; | ||
756 | |||
757 | if (wd->target) | ||
758 | { | ||
759 | evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_DOWN, | ||
760 | _mouse_down, obj); | ||
761 | evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_MOVE, | ||
762 | _mouse_move, obj); | ||
763 | evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_UP, | ||
764 | _mouse_up, obj); | ||
765 | |||
766 | evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOUSE_WHEEL, | ||
767 | _mouse_wheel, obj); | ||
768 | |||
769 | evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_DOWN, | ||
770 | _multi_down, obj); | ||
771 | evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_MOVE, | ||
772 | _multi_move, obj); | ||
773 | evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MULTI_UP, | ||
774 | _multi_up, obj); | ||
775 | |||
776 | evas_object_event_callback_add(wd->target, EVAS_CALLBACK_KEY_DOWN, | ||
777 | _key_down_cb, obj); | ||
778 | evas_object_event_callback_add(wd->target, EVAS_CALLBACK_KEY_UP, | ||
779 | _key_up_cb, obj); | ||
780 | } | ||
781 | } | ||
782 | |||
783 | /** | ||
784 | * @internal | ||
785 | * | ||
786 | * We unregister callbacks when gesture layer is disabled. | ||
787 | * | ||
788 | * @param obj The gesture-layer object. | ||
789 | * | ||
790 | * @ingroup Elm_Gesture_Layer | ||
791 | */ | ||
792 | static void | ||
793 | _unregister_callbacks(Evas_Object *obj) | ||
794 | { | ||
795 | Widget_Data *wd = elm_widget_data_get(obj); | ||
796 | if (!wd) return; | ||
797 | |||
798 | if (wd->target) | ||
799 | { | ||
800 | evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_DOWN, | ||
801 | _mouse_down); | ||
802 | evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_MOVE, | ||
803 | _mouse_move); | ||
804 | evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_UP, | ||
805 | _mouse_up); | ||
806 | |||
807 | evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MOUSE_WHEEL, | ||
808 | _mouse_wheel); | ||
809 | |||
810 | evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_DOWN, | ||
811 | _multi_down); | ||
812 | |||
813 | evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_MOVE, | ||
814 | _multi_move); | ||
815 | |||
816 | evas_object_event_callback_del(wd->target, EVAS_CALLBACK_MULTI_UP, | ||
817 | _multi_up); | ||
818 | |||
819 | evas_object_event_callback_del(wd->target, EVAS_CALLBACK_KEY_DOWN, | ||
820 | _key_down_cb); | ||
821 | evas_object_event_callback_del(wd->target, EVAS_CALLBACK_KEY_UP, | ||
822 | _key_up_cb); | ||
823 | } | ||
824 | } | ||
825 | |||
826 | /* START - Event history list handling functions */ | ||
827 | /** | ||
828 | * @internal | ||
829 | * This function is used to find if device number | ||
830 | * is found in a list of devices. | ||
831 | * The list contains devices for refeeding *UP event | ||
832 | * | ||
833 | * @ingroup Elm_Gesture_Layer | ||
834 | */ | ||
835 | static int | ||
836 | device_in_pending_list(const void *data1, const void *data2) | ||
837 | { /* Compare the two device numbers */ | ||
838 | return (((intptr_t) data1) - ((intptr_t) data2)); | ||
839 | } | ||
840 | |||
841 | /** | ||
842 | * @internal | ||
843 | * | ||
844 | * This functions adds device to refeed-pending device list | ||
845 | * @ingroup Elm_Gesture_Layer | ||
846 | */ | ||
847 | static Eina_List * | ||
848 | _add_device_pending(Eina_List *list, void *event, Evas_Callback_Type event_type) | ||
849 | { | ||
850 | int device = ELM_MOUSE_DEVICE; | ||
851 | switch(event_type) | ||
852 | { | ||
853 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
854 | break; | ||
855 | case EVAS_CALLBACK_MULTI_DOWN: | ||
856 | device = ((Evas_Event_Multi_Down *) event)->device; | ||
857 | break; | ||
858 | default: | ||
859 | return list; | ||
860 | } | ||
861 | |||
862 | if (!eina_list_search_unsorted_list(list, device_in_pending_list, | ||
863 | (void *)(intptr_t)device)) | ||
864 | { | ||
865 | return eina_list_append(list, (void *)(intptr_t)device); | ||
866 | } | ||
867 | |||
868 | return list; | ||
869 | } | ||
870 | |||
871 | /** | ||
872 | * @internal | ||
873 | * | ||
874 | * This functions returns pending-device node | ||
875 | * @ingroup Elm_Gesture_Layer | ||
876 | */ | ||
877 | static Eina_List * | ||
878 | _device_is_pending(Eina_List *list, void *event, Evas_Callback_Type event_type) | ||
879 | { | ||
880 | int device = ELM_MOUSE_DEVICE; | ||
881 | switch(event_type) | ||
882 | { | ||
883 | case EVAS_CALLBACK_MOUSE_UP: | ||
884 | break; | ||
885 | case EVAS_CALLBACK_MULTI_UP: | ||
886 | device = ((Evas_Event_Multi_Up *) event)->device; | ||
887 | break; | ||
888 | default: | ||
889 | return NULL; | ||
890 | } | ||
891 | |||
892 | return eina_list_search_unsorted_list(list, device_in_pending_list, | ||
893 | (void *)(intptr_t)device); | ||
894 | } | ||
895 | |||
896 | /** | ||
897 | * @internal | ||
898 | * | ||
899 | * This function reports ABORT to all none-detected gestures | ||
900 | * Then resets test bits for all desired gesures | ||
901 | * and clears input-events history. | ||
902 | * note: if no gesture was detected, events from history list | ||
903 | * are streamed to the widget because it's unused by layer. | ||
904 | * user may cancel refeed of events by setting repeat events. | ||
905 | * | ||
906 | * @param obj The gesture-layer object. | ||
907 | * | ||
908 | * @ingroup Elm_Gesture_Layer | ||
909 | */ | ||
910 | static Eina_Bool | ||
911 | _event_history_clear(Evas_Object *obj) | ||
912 | { | ||
913 | Widget_Data *wd = elm_widget_data_get(obj); | ||
914 | if (!wd) return EINA_FALSE; | ||
915 | |||
916 | int i; | ||
917 | Gesture_Info *p; | ||
918 | Evas *e = evas_object_evas_get(obj); | ||
919 | Eina_Bool gesture_found = EINA_FALSE; | ||
920 | for (i = ELM_GESTURE_FIRST; i < ELM_GESTURE_LAST; i++) | ||
921 | { | ||
922 | p = wd->gesture[i]; | ||
923 | if (p) | ||
924 | { | ||
925 | if (p->state == ELM_GESTURE_STATE_END) | ||
926 | gesture_found = EINA_TRUE; | ||
927 | else | ||
928 | { /* Report ABORT to all gestures that still not finished */ | ||
929 | _set_state(p, ELM_GESTURE_STATE_ABORT, wd->gesture[i]->info, | ||
930 | EINA_FALSE); | ||
931 | } | ||
932 | } | ||
933 | } | ||
934 | |||
935 | _reset_states(wd); /* we are ready to start testing for gestures again */ | ||
936 | |||
937 | /* Clear all gestures intermediate data */ | ||
938 | if (IS_TESTED(ELM_GESTURE_N_LONG_TAPS)) | ||
939 | { /* We do not clear a long-tap gesture if fingers still on surface */ | ||
940 | /* and gesture timer still pending to test gesture state */ | ||
941 | Long_Tap_Type *st = wd->gesture[ELM_GESTURE_N_LONG_TAPS]->data; | ||
942 | if ((st) && /* st not allocated if clear occurs before 1st input */ | ||
943 | ((!eina_list_count(st->touched)) || (!st->timeout))) | ||
944 | _n_long_tap_test_reset(wd->gesture[ELM_GESTURE_N_LONG_TAPS]); | ||
945 | } | ||
946 | |||
947 | if (wd->dbl_timeout) | ||
948 | { | ||
949 | ecore_timer_del(wd->dbl_timeout); | ||
950 | wd->dbl_timeout = NULL; | ||
951 | } | ||
952 | |||
953 | _tap_gestures_test_reset(wd->gesture[ELM_GESTURE_N_TAPS]); | ||
954 | _tap_gestures_test_reset(wd->gesture[ELM_GESTURE_N_DOUBLE_TAPS]); | ||
955 | _tap_gestures_test_reset(wd->gesture[ELM_GESTURE_N_TRIPLE_TAPS]); | ||
956 | _momentum_test_reset(wd->gesture[ELM_GESTURE_MOMENTUM]); | ||
957 | _line_test_reset(wd->gesture[ELM_GESTURE_N_LINES]); | ||
958 | _line_test_reset(wd->gesture[ELM_GESTURE_N_FLICKS]); | ||
959 | _zoom_test_reset(wd->gesture[ELM_GESTURE_ZOOM]); | ||
960 | _rotate_test_reset(wd->gesture[ELM_GESTURE_ROTATE]); | ||
961 | |||
962 | /* Disable gesture layer so refeeded events won't be consumed by it */ | ||
963 | _unregister_callbacks(obj); | ||
964 | while (wd->event_history_list) | ||
965 | { | ||
966 | Event_History *t; | ||
967 | t = wd->event_history_list; | ||
968 | Eina_List *pending = _device_is_pending(wd->pending, | ||
969 | wd->event_history_list->event, | ||
970 | wd->event_history_list->event_type); | ||
971 | |||
972 | /* Refeed events if no gesture matched input */ | ||
973 | if (pending || ((!gesture_found) && (!wd->repeat_events))) | ||
974 | { | ||
975 | evas_event_refeed_event(e, wd->event_history_list->event, | ||
976 | wd->event_history_list->event_type); | ||
977 | |||
978 | if (pending) | ||
979 | { | ||
980 | wd->pending = eina_list_remove_list(wd->pending, pending); | ||
981 | } | ||
982 | else | ||
983 | { | ||
984 | wd->pending = _add_device_pending(wd->pending, | ||
985 | wd->event_history_list->event, | ||
986 | wd->event_history_list->event_type); | ||
987 | } | ||
988 | } | ||
989 | |||
990 | free(wd->event_history_list->event); | ||
991 | wd->event_history_list = (Event_History *) eina_inlist_remove( | ||
992 | EINA_INLIST_GET(wd->event_history_list), | ||
993 | EINA_INLIST_GET(wd->event_history_list)); | ||
994 | free(t); | ||
995 | } | ||
996 | _register_callbacks(obj); | ||
997 | return EINA_TRUE; | ||
998 | } | ||
999 | |||
1000 | /** | ||
1001 | * @internal | ||
1002 | * | ||
1003 | * This function copies input events. | ||
1004 | * We copy event info before adding it to history. | ||
1005 | * The memory is freed when we clear history. | ||
1006 | * | ||
1007 | * @param event the event to copy | ||
1008 | * @param event_type event type to copy | ||
1009 | * | ||
1010 | * @ingroup Elm_Gesture_Layer | ||
1011 | */ | ||
1012 | static void * | ||
1013 | _copy_event_info(void *event, Evas_Callback_Type event_type) | ||
1014 | { | ||
1015 | switch(event_type) | ||
1016 | { | ||
1017 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
1018 | return COPY_EVENT_INFO((Evas_Event_Mouse_Down *) event); | ||
1019 | break; | ||
1020 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
1021 | return COPY_EVENT_INFO((Evas_Event_Mouse_Move *) event); | ||
1022 | break; | ||
1023 | case EVAS_CALLBACK_MOUSE_UP: | ||
1024 | return COPY_EVENT_INFO((Evas_Event_Mouse_Up *) event); | ||
1025 | break; | ||
1026 | case EVAS_CALLBACK_MOUSE_WHEEL: | ||
1027 | return COPY_EVENT_INFO((Evas_Event_Mouse_Wheel *) event); | ||
1028 | break; | ||
1029 | case EVAS_CALLBACK_MULTI_DOWN: | ||
1030 | return COPY_EVENT_INFO((Evas_Event_Multi_Down *) event); | ||
1031 | break; | ||
1032 | case EVAS_CALLBACK_MULTI_MOVE: | ||
1033 | return COPY_EVENT_INFO((Evas_Event_Multi_Move *) event); | ||
1034 | break; | ||
1035 | case EVAS_CALLBACK_MULTI_UP: | ||
1036 | return COPY_EVENT_INFO((Evas_Event_Multi_Up *) event); | ||
1037 | break; | ||
1038 | case EVAS_CALLBACK_KEY_DOWN: | ||
1039 | return COPY_EVENT_INFO((Evas_Event_Key_Down *) event); | ||
1040 | break; | ||
1041 | case EVAS_CALLBACK_KEY_UP: | ||
1042 | return COPY_EVENT_INFO((Evas_Event_Key_Up *) event); | ||
1043 | break; | ||
1044 | default: | ||
1045 | return NULL; | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1049 | static Eina_Bool | ||
1050 | _event_history_add(Evas_Object *obj, void *event, Evas_Callback_Type event_type) | ||
1051 | { | ||
1052 | Widget_Data *wd = elm_widget_data_get(obj); | ||
1053 | Event_History *ev; | ||
1054 | if (!wd) return EINA_FALSE; | ||
1055 | |||
1056 | ev = malloc(sizeof(Event_History)); | ||
1057 | ev->event = _copy_event_info(event, event_type); /* Freed on event_history_clear */ | ||
1058 | ev->event_type = event_type; | ||
1059 | wd->event_history_list = (Event_History *) eina_inlist_append( | ||
1060 | EINA_INLIST_GET(wd->event_history_list), EINA_INLIST_GET(ev)); | ||
1061 | |||
1062 | return EINA_TRUE; | ||
1063 | } | ||
1064 | /* END - Event history list handling functions */ | ||
1065 | |||
1066 | static void | ||
1067 | _del_hook(Evas_Object *obj) | ||
1068 | { | ||
1069 | Widget_Data *wd = elm_widget_data_get(obj); | ||
1070 | if (!wd) return; | ||
1071 | |||
1072 | _event_history_clear(obj); | ||
1073 | eina_list_free(wd->pending); | ||
1074 | |||
1075 | Pointer_Event *data; | ||
1076 | EINA_LIST_FREE(wd->touched, data) | ||
1077 | free(data); | ||
1078 | |||
1079 | if (!elm_widget_disabled_get(obj)) | ||
1080 | _unregister_callbacks(obj); | ||
1081 | |||
1082 | /* Free all gestures internal data structures */ | ||
1083 | int i; | ||
1084 | for (i = 0; i < ELM_GESTURE_LAST; i++) | ||
1085 | if (wd->gesture[i]) | ||
1086 | { | ||
1087 | if (wd->gesture[i]->data) | ||
1088 | free(wd->gesture[i]->data); | ||
1089 | |||
1090 | free(wd->gesture[i]); | ||
1091 | } | ||
1092 | |||
1093 | free(wd); | ||
1094 | } | ||
1095 | |||
1096 | static int | ||
1097 | compare_match_fingers(const void *data1, const void *data2) | ||
1098 | { /* Compare coords of first item in list to cur coords */ | ||
1099 | const Pointer_Event *pe1 = eina_list_data_get(data1); | ||
1100 | const Pointer_Event *pe2 = data2; | ||
1101 | |||
1102 | if (_inside(pe1->x, pe1->y, pe2->x, pe2->y)) | ||
1103 | return 0; | ||
1104 | else if (pe1->x < pe2->x) | ||
1105 | return -1; | ||
1106 | else | ||
1107 | { | ||
1108 | if (pe1->x == pe2->x) | ||
1109 | return pe1->y - pe2->y; | ||
1110 | else | ||
1111 | return 1; | ||
1112 | } | ||
1113 | } | ||
1114 | |||
1115 | static int | ||
1116 | compare_pe_device(const void *data1, const void *data2) | ||
1117 | { /* Compare device of first item in list to our pe device */ | ||
1118 | const Pointer_Event *pe1 = eina_list_data_get(data1); | ||
1119 | const Pointer_Event *pe2 = data2; | ||
1120 | |||
1121 | /* Only match if last was a down event */ | ||
1122 | if ((pe1->event_type != EVAS_CALLBACK_MULTI_DOWN) && | ||
1123 | (pe1->event_type != EVAS_CALLBACK_MOUSE_DOWN)) | ||
1124 | return 1; | ||
1125 | |||
1126 | if (pe1->device == pe2->device) | ||
1127 | return 0; | ||
1128 | else if (pe1->device < pe2->device) | ||
1129 | return -1; | ||
1130 | else | ||
1131 | return 1; | ||
1132 | } | ||
1133 | |||
1134 | static Eina_List* | ||
1135 | _record_pointer_event(Taps_Type *st, Eina_List *pe_list, Pointer_Event *pe, | ||
1136 | Widget_Data *wd, void *event_info, Evas_Callback_Type event_type) | ||
1137 | { /* Keep copy of pe and record it in list */ | ||
1138 | Pointer_Event *p = malloc(sizeof(Pointer_Event)); | ||
1139 | memcpy(p, pe, sizeof(Pointer_Event)); | ||
1140 | consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_NONE); | ||
1141 | |||
1142 | st->sum_x += pe->x; | ||
1143 | st->sum_y += pe->y; | ||
1144 | st->n_taps++; | ||
1145 | |||
1146 | /* This will also update middle-point to report to user later */ | ||
1147 | st->info.x = st->sum_x / st->n_taps; | ||
1148 | st->info.y = st->sum_y / st->n_taps; | ||
1149 | st->info.timestamp = pe->timestamp; | ||
1150 | |||
1151 | if (!pe_list) | ||
1152 | { | ||
1153 | pe_list = eina_list_append(pe_list, p); | ||
1154 | st->l = eina_list_append(st->l, pe_list); | ||
1155 | } | ||
1156 | else | ||
1157 | pe_list = eina_list_append(pe_list, p); | ||
1158 | |||
1159 | return pe_list; | ||
1160 | } | ||
1161 | |||
1162 | /** | ||
1163 | * @internal | ||
1164 | * | ||
1165 | * This function sets state a tap-gesture to END or ABORT | ||
1166 | * | ||
1167 | * @param data gesture info pointer | ||
1168 | * | ||
1169 | * @ingroup Elm_Gesture_Layer | ||
1170 | */ | ||
1171 | static void | ||
1172 | _tap_gesture_finish(void *data) | ||
1173 | { /* This function will test each tap gesture when timer expires */ | ||
1174 | Gesture_Info *gesture = data; | ||
1175 | Elm_Gesture_State s = ELM_GESTURE_STATE_END; | ||
1176 | /* Here we check if taps-gesture was completed successfuly */ | ||
1177 | /* Count how many taps were recieved on each device then */ | ||
1178 | /* determine if it matches n_taps_needed defined on START */ | ||
1179 | Taps_Type *st = gesture->data; | ||
1180 | Eina_List *l; | ||
1181 | Eina_List *pe_list; | ||
1182 | EINA_LIST_FOREACH(st->l, l, pe_list) | ||
1183 | { | ||
1184 | if (eina_list_count(pe_list) != st->n_taps_needed) | ||
1185 | { /* No match taps number on device, ABORT */ | ||
1186 | s = ELM_GESTURE_STATE_ABORT; | ||
1187 | break; | ||
1188 | } | ||
1189 | } | ||
1190 | |||
1191 | st->info.n = eina_list_count(st->l); | ||
1192 | _set_state(gesture, s, gesture->info, EINA_FALSE); | ||
1193 | _tap_gestures_test_reset(gesture); | ||
1194 | } | ||
1195 | |||
1196 | /** | ||
1197 | * @internal | ||
1198 | * | ||
1199 | * when this timer expires we finish tap gestures. | ||
1200 | * | ||
1201 | * @param data The gesture-layer object. | ||
1202 | * @return cancles callback for this timer. | ||
1203 | * | ||
1204 | * @ingroup Elm_Gesture_Layer | ||
1205 | */ | ||
1206 | static Eina_Bool | ||
1207 | _multi_tap_timeout(void *data) | ||
1208 | { | ||
1209 | Widget_Data *wd = elm_widget_data_get(data); | ||
1210 | if (!wd) return EINA_FALSE; | ||
1211 | |||
1212 | if (IS_TESTED(ELM_GESTURE_N_TAPS)) | ||
1213 | _tap_gesture_finish(wd->gesture[ELM_GESTURE_N_TAPS]); | ||
1214 | |||
1215 | if (IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS)) | ||
1216 | _tap_gesture_finish(wd->gesture[ELM_GESTURE_N_DOUBLE_TAPS]); | ||
1217 | |||
1218 | if (IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS)) | ||
1219 | _tap_gesture_finish(wd->gesture[ELM_GESTURE_N_TRIPLE_TAPS]); | ||
1220 | |||
1221 | _clear_if_finished(data); | ||
1222 | wd->dbl_timeout = NULL; | ||
1223 | return ECORE_CALLBACK_CANCEL; | ||
1224 | } | ||
1225 | |||
1226 | /** | ||
1227 | * @internal | ||
1228 | * | ||
1229 | * when this timer expires we START long tap gesture | ||
1230 | * | ||
1231 | * @param data The gesture-layer object. | ||
1232 | * @return cancles callback for this timer. | ||
1233 | * | ||
1234 | * @ingroup Elm_Gesture_Layer | ||
1235 | */ | ||
1236 | static Eina_Bool | ||
1237 | _long_tap_timeout(void *data) | ||
1238 | { | ||
1239 | Gesture_Info *gesture = data; | ||
1240 | Long_Tap_Type *st = gesture->data; | ||
1241 | st->timeout = NULL; | ||
1242 | |||
1243 | _set_state(gesture, ELM_GESTURE_STATE_START, | ||
1244 | gesture->data, EINA_FALSE); | ||
1245 | |||
1246 | return ECORE_CALLBACK_CANCEL; | ||
1247 | } | ||
1248 | |||
1249 | |||
1250 | /** | ||
1251 | * @internal | ||
1252 | * | ||
1253 | * This function checks if a tap gesture should start | ||
1254 | * | ||
1255 | * @param wd Gesture Layer Widget Data. | ||
1256 | * @param pe The recent input event as stored in pe struct. | ||
1257 | * @param event_info Original input event pointer. | ||
1258 | * @param event_type Type of original input event. | ||
1259 | * @param gesture what gesture is tested | ||
1260 | * @param how many taps for this gesture (1, 2 or 3) | ||
1261 | * | ||
1262 | * @return Flag to determine if we need to set a timer for finish | ||
1263 | * | ||
1264 | * @ingroup Elm_Gesture_Layer | ||
1265 | */ | ||
1266 | static Eina_Bool | ||
1267 | _tap_gesture_start(Widget_Data *wd, Pointer_Event *pe, | ||
1268 | void *event_info, Evas_Callback_Type event_type, | ||
1269 | Gesture_Info *gesture, int taps) | ||
1270 | { /* Here we fill Tap struct */ | ||
1271 | Taps_Type *st = gesture->data; | ||
1272 | if (!st) | ||
1273 | { /* Allocated once on first time */ | ||
1274 | st = calloc(1, sizeof(Taps_Type)); | ||
1275 | gesture->data = st; | ||
1276 | _tap_gestures_test_reset(gesture); | ||
1277 | } | ||
1278 | |||
1279 | Eina_List *pe_list = NULL; | ||
1280 | Pointer_Event *pe_down = NULL; | ||
1281 | Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE; | ||
1282 | switch (pe->event_type) | ||
1283 | { | ||
1284 | case EVAS_CALLBACK_MULTI_DOWN: | ||
1285 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
1286 | /* Check if got tap on same cord was tapped before */ | ||
1287 | pe_list = eina_list_search_unsorted(st->l, compare_match_fingers, pe); | ||
1288 | |||
1289 | if ((!pe_list) && | ||
1290 | eina_list_search_unsorted(st->l, compare_pe_device, pe)) | ||
1291 | { /* This device was touched in other cord before completion */ | ||
1292 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, | ||
1293 | &st->info, EINA_FALSE); | ||
1294 | consume_event(wd, event_info, event_type, ev_flag); | ||
1295 | |||
1296 | return EINA_FALSE; | ||
1297 | } | ||
1298 | |||
1299 | pe_list = _record_pointer_event(st, pe_list, pe, wd, event_info, event_type); | ||
1300 | if ((pe->device == 0) && (eina_list_count(pe_list) == 1)) | ||
1301 | { /* This is the first mouse down we got */ | ||
1302 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START, | ||
1303 | &st->info, EINA_FALSE); | ||
1304 | consume_event(wd, event_info, event_type, ev_flag); | ||
1305 | |||
1306 | st->n_taps_needed = taps * 2; /* count DOWN and UP */ | ||
1307 | |||
1308 | return EINA_TRUE; | ||
1309 | } | ||
1310 | |||
1311 | break; | ||
1312 | |||
1313 | case EVAS_CALLBACK_MULTI_UP: | ||
1314 | case EVAS_CALLBACK_MOUSE_UP: | ||
1315 | pe_list = eina_list_search_unsorted(st->l, compare_pe_device, pe); | ||
1316 | if (!pe_list) | ||
1317 | return EINA_FALSE; | ||
1318 | |||
1319 | pe_list = _record_pointer_event(st, pe_list, pe, wd, event_info, event_type); | ||
1320 | break; | ||
1321 | |||
1322 | case EVAS_CALLBACK_MULTI_MOVE: | ||
1323 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
1324 | /* Get first event in first list, this has to be a Mouse Down event */ | ||
1325 | /* and verify that user didn't move out of this area before next tap */ | ||
1326 | pe_list = eina_list_search_unsorted(st->l, compare_pe_device, pe); | ||
1327 | if (pe_list) | ||
1328 | { | ||
1329 | pe_down = eina_list_data_get(pe_list); | ||
1330 | if (!_inside(pe_down->x, pe_down->y, pe->x, pe->y)) | ||
1331 | { | ||
1332 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, | ||
1333 | &st->info, EINA_FALSE); | ||
1334 | consume_event(wd, event_info, event_type, ev_flag); | ||
1335 | } | ||
1336 | } | ||
1337 | break; | ||
1338 | |||
1339 | default: | ||
1340 | return EINA_FALSE; | ||
1341 | } | ||
1342 | |||
1343 | return EINA_FALSE; | ||
1344 | } | ||
1345 | |||
1346 | |||
1347 | /** | ||
1348 | * @internal | ||
1349 | * | ||
1350 | * This function checks all click/tap and double/triple taps | ||
1351 | * | ||
1352 | * @param obj The gesture-layer object. | ||
1353 | * @param pe The recent input event as stored in pe struct. | ||
1354 | * @param event_info Original input event pointer. | ||
1355 | * @param event_type Type of original input event. | ||
1356 | * | ||
1357 | * @ingroup Elm_Gesture_Layer | ||
1358 | */ | ||
1359 | static void | ||
1360 | _tap_gestures_test(Evas_Object *obj, Pointer_Event *pe, | ||
1361 | void *event_info, Evas_Callback_Type event_type) | ||
1362 | { /* Here we fill Recent_Taps struct and fire-up click/tap timers */ | ||
1363 | Eina_Bool need_timer = EINA_FALSE; | ||
1364 | Widget_Data *wd = elm_widget_data_get(obj); | ||
1365 | if (!wd) return; | ||
1366 | |||
1367 | if (!pe) /* this happens when unhandled event arrived */ | ||
1368 | return; /* see _make_pointer_event function */ | ||
1369 | |||
1370 | if (IS_TESTED(ELM_GESTURE_N_TAPS)) | ||
1371 | need_timer |= _tap_gesture_start(wd, pe, event_info, event_type, | ||
1372 | wd->gesture[ELM_GESTURE_N_TAPS], 1); | ||
1373 | |||
1374 | if (IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS)) | ||
1375 | need_timer |= _tap_gesture_start(wd, pe, event_info, event_type, | ||
1376 | wd->gesture[ELM_GESTURE_N_DOUBLE_TAPS], 2); | ||
1377 | |||
1378 | if (IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS)) | ||
1379 | need_timer |= _tap_gesture_start(wd, pe, event_info, event_type, | ||
1380 | wd->gesture[ELM_GESTURE_N_TRIPLE_TAPS], 3); | ||
1381 | |||
1382 | if ((need_timer) && (!wd->dbl_timeout)) | ||
1383 | { /* Set a timer to finish these gestures */ | ||
1384 | wd->dbl_timeout = ecore_timer_add(0.4, _multi_tap_timeout, | ||
1385 | obj); | ||
1386 | } | ||
1387 | } | ||
1388 | |||
1389 | /** | ||
1390 | * @internal | ||
1391 | * | ||
1392 | * This function computes center-point for long-tap gesture | ||
1393 | * | ||
1394 | * @param st Long Tap gesture info pointer | ||
1395 | * @param pe The recent input event as stored in pe struct. | ||
1396 | * | ||
1397 | * @ingroup Elm_Gesture_Layer | ||
1398 | */ | ||
1399 | static void | ||
1400 | _compute_taps_center(Long_Tap_Type *st, | ||
1401 | Evas_Coord *x_out, Evas_Coord *y_out, Pointer_Event *pe) | ||
1402 | { | ||
1403 | if (!eina_list_count(st->touched)) | ||
1404 | return; | ||
1405 | |||
1406 | Eina_List *l; | ||
1407 | Pointer_Event *p; | ||
1408 | Evas_Coord x = 0, y = 0; | ||
1409 | EINA_LIST_FOREACH(st->touched, l, p) | ||
1410 | { /* Accumulate all then take avarage */ | ||
1411 | if (p->device == pe->device) | ||
1412 | { /* This will take care of values coming from MOVE event */ | ||
1413 | x += pe->x; | ||
1414 | y += pe->y; | ||
1415 | } | ||
1416 | else | ||
1417 | { | ||
1418 | x += p->x; | ||
1419 | y += p->y; | ||
1420 | } | ||
1421 | } | ||
1422 | |||
1423 | *x_out = x / eina_list_count(st->touched); | ||
1424 | *y_out = y / eina_list_count(st->touched); | ||
1425 | } | ||
1426 | |||
1427 | /** | ||
1428 | * @internal | ||
1429 | * | ||
1430 | * This function checks N long-tap gesture. | ||
1431 | * | ||
1432 | * @param obj The gesture-layer object. | ||
1433 | * @param pe The recent input event as stored in pe struct. | ||
1434 | * @param event_info Original input event pointer. | ||
1435 | * @param event_type Type of original input event. | ||
1436 | * @param g_type what Gesture we are testing. | ||
1437 | * @param taps How many click/taps we test for. | ||
1438 | * | ||
1439 | * @ingroup Elm_Gesture_Layer | ||
1440 | */ | ||
1441 | static void | ||
1442 | _n_long_tap_test(Evas_Object *obj, Pointer_Event *pe, | ||
1443 | void *event_info, Evas_Callback_Type event_type, | ||
1444 | Elm_Gesture_Type g_type) | ||
1445 | { /* Here we fill Recent_Taps struct and fire-up click/tap timers */ | ||
1446 | Widget_Data *wd = elm_widget_data_get(obj); | ||
1447 | if (!wd) return; | ||
1448 | |||
1449 | if (!pe) /* this happens when unhandled event arrived */ | ||
1450 | return; /* see _make_pointer_event function */ | ||
1451 | Gesture_Info *gesture = wd->gesture[g_type]; | ||
1452 | if (!gesture) return; | ||
1453 | |||
1454 | Long_Tap_Type *st = gesture->data; | ||
1455 | if (!st) | ||
1456 | { /* Allocated once on first time */ | ||
1457 | st = calloc(1, sizeof(Long_Tap_Type)); | ||
1458 | gesture->data = st; | ||
1459 | _n_long_tap_test_reset(gesture); | ||
1460 | } | ||
1461 | |||
1462 | Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE; | ||
1463 | switch (pe->event_type) | ||
1464 | { | ||
1465 | case EVAS_CALLBACK_MULTI_DOWN: | ||
1466 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
1467 | st->touched = _add_touched_device(st->touched, pe); | ||
1468 | st->info.n = eina_list_count(st->touched); | ||
1469 | if (st->info.n > st->max_touched) | ||
1470 | st->max_touched = st->info.n; | ||
1471 | else | ||
1472 | { /* User removed finger from touch, then put back - ABORT */ | ||
1473 | if ((gesture->state == ELM_GESTURE_STATE_START) || | ||
1474 | (gesture->state == ELM_GESTURE_STATE_MOVE)) | ||
1475 | { | ||
1476 | ev_flag =_set_state(gesture, ELM_GESTURE_STATE_ABORT, | ||
1477 | &st->info, EINA_FALSE); | ||
1478 | consume_event(wd, event_info, event_type, ev_flag); | ||
1479 | } | ||
1480 | } | ||
1481 | |||
1482 | if ((pe->device == 0) && (eina_list_count(st->touched) == 1)) | ||
1483 | { /* This is the first mouse down we got */ | ||
1484 | st->info.timestamp = pe->timestamp; | ||
1485 | |||
1486 | /* To test long tap */ | ||
1487 | /* When this timer expires, gesture STARTED */ | ||
1488 | if (!st->timeout) | ||
1489 | st->timeout = ecore_timer_add(wd->long_tap_start_timeout, | ||
1490 | _long_tap_timeout, gesture); | ||
1491 | } | ||
1492 | |||
1493 | consume_event(wd, event_info, event_type, ev_flag); | ||
1494 | _compute_taps_center(st, &st->info.x, &st->info.y, pe); | ||
1495 | st->center_x = st->info.x; | ||
1496 | st->center_y = st->info.y; | ||
1497 | break; | ||
1498 | |||
1499 | case EVAS_CALLBACK_MULTI_UP: | ||
1500 | case EVAS_CALLBACK_MOUSE_UP: | ||
1501 | st->touched = _remove_touched_device(st->touched, pe); | ||
1502 | _compute_taps_center(st, &st->center_x, &st->center_y, pe); | ||
1503 | if (st->info.n && | ||
1504 | ((gesture->state == ELM_GESTURE_STATE_START) || | ||
1505 | (gesture->state == ELM_GESTURE_STATE_MOVE))) | ||
1506 | { /* Report END only for gesture that STARTed */ | ||
1507 | if (eina_list_count(st->touched) == 0) | ||
1508 | { /* Report END only at last release event */ | ||
1509 | ev_flag =_set_state(gesture, ELM_GESTURE_STATE_END, | ||
1510 | &st->info, EINA_FALSE); | ||
1511 | consume_event(wd, event_info, event_type, ev_flag); | ||
1512 | } | ||
1513 | } | ||
1514 | else | ||
1515 | { /* Stop test, user lifts finger before long-start */ | ||
1516 | if (st->timeout) ecore_timer_del(st->timeout); | ||
1517 | st->timeout = NULL; | ||
1518 | ev_flag =_set_state(gesture, ELM_GESTURE_STATE_ABORT, | ||
1519 | &st->info, EINA_FALSE); | ||
1520 | consume_event(wd, event_info, event_type, ev_flag); | ||
1521 | } | ||
1522 | |||
1523 | break; | ||
1524 | |||
1525 | case EVAS_CALLBACK_MULTI_MOVE: | ||
1526 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
1527 | if (st->info.n && | ||
1528 | ((gesture->state == ELM_GESTURE_STATE_START) || | ||
1529 | (gesture->state == ELM_GESTURE_STATE_MOVE))) | ||
1530 | { /* Report MOVE only if STARTED */ | ||
1531 | Evas_Coord x = 0; | ||
1532 | Evas_Coord y = 0; | ||
1533 | Elm_Gesture_State state_to_report = ELM_GESTURE_STATE_MOVE; | ||
1534 | |||
1535 | _compute_taps_center(st, &x, &y, pe); | ||
1536 | /* ABORT if user moved fingers out of tap area */ | ||
1537 | #if defined(DEBUG_GESTURE_LAYER) | ||
1538 | printf("%s x,y=(%d,%d) st->info.x,st->info.y=(%d,%d)\n",__func__,x,y,st->info.x,st->info.y); | ||
1539 | #endif | ||
1540 | if (!_inside(x, y, st->center_x, st->center_y)) | ||
1541 | state_to_report = ELM_GESTURE_STATE_ABORT; | ||
1542 | |||
1543 | /* Report MOVE if gesture started */ | ||
1544 | ev_flag = _set_state(gesture, state_to_report, | ||
1545 | &st->info, EINA_TRUE); | ||
1546 | consume_event(wd, event_info, event_type, ev_flag); | ||
1547 | } | ||
1548 | break; | ||
1549 | |||
1550 | default: | ||
1551 | return; | ||
1552 | } | ||
1553 | } | ||
1554 | |||
1555 | /** | ||
1556 | * @internal | ||
1557 | * | ||
1558 | * This function computes momentum for MOMENTUM, LINE and FLICK gestures | ||
1559 | * This momentum value will be sent to widget when gesture is completed. | ||
1560 | * | ||
1561 | * @param momentum pointer to buffer where we record momentum value. | ||
1562 | * @param x1 x coord where user started gesture. | ||
1563 | * @param y1 y coord where user started gesture. | ||
1564 | * @param x2 x coord where user completed gesture. | ||
1565 | * @param y2 y coord where user completed gesture. | ||
1566 | * @param t1x timestamp for X, when user started gesture. | ||
1567 | * @param t1y timestamp for Y, when user started gesture. | ||
1568 | * @param t2 timestamp when user completed gesture. | ||
1569 | * | ||
1570 | * @ingroup Elm_Gesture_Layer | ||
1571 | */ | ||
1572 | static void | ||
1573 | _set_momentum(Elm_Gesture_Momentum_Info *momentum, | ||
1574 | Evas_Coord xx1, Evas_Coord yy1, | ||
1575 | Evas_Coord xx2, Evas_Coord yy2, | ||
1576 | unsigned int t1x, unsigned int t1y, unsigned int t2) | ||
1577 | { | ||
1578 | Evas_Coord velx = 0, vely = 0, vel; | ||
1579 | Evas_Coord dx = xx2 - xx1; | ||
1580 | Evas_Coord dy = yy2 - yy1; | ||
1581 | int dtx = t2 - t1x; | ||
1582 | int dty = t2 - t1y; | ||
1583 | if (dtx > 0) | ||
1584 | velx = (dx * 1000) / dtx; | ||
1585 | |||
1586 | if (dty > 0) | ||
1587 | vely = (dy * 1000) / dty; | ||
1588 | |||
1589 | vel = sqrt((velx * velx) + (vely * vely)); | ||
1590 | |||
1591 | if ((_elm_config->thumbscroll_friction > 0.0) && | ||
1592 | (vel > _elm_config->thumbscroll_momentum_threshold)) | ||
1593 | { /* report momentum */ | ||
1594 | momentum->mx = velx; | ||
1595 | momentum->my = vely; | ||
1596 | } | ||
1597 | else | ||
1598 | { | ||
1599 | momentum->mx = 0; | ||
1600 | momentum->my = 0; | ||
1601 | } | ||
1602 | } | ||
1603 | |||
1604 | /** | ||
1605 | * @internal | ||
1606 | * | ||
1607 | * This function is used for computing rotation angle (DEG). | ||
1608 | * | ||
1609 | * @param x1 first finger x location. | ||
1610 | * @param y1 first finger y location. | ||
1611 | * @param x2 second finger x location. | ||
1612 | * @param y2 second finger y location. | ||
1613 | * | ||
1614 | * @return angle of the line between (x1,y1), (x2,y2) in Deg. | ||
1615 | * Angles now are given in DEG, not RAD. | ||
1616 | * ZERO angle at 12-oclock, growing clockwise. | ||
1617 | * | ||
1618 | * @ingroup Elm_Gesture_Layer | ||
1619 | */ | ||
1620 | static double | ||
1621 | get_angle(Evas_Coord xx1, Evas_Coord yy1, Evas_Coord xx2, Evas_Coord yy2) | ||
1622 | { | ||
1623 | double a, xx, yy, rt = (-1); | ||
1624 | xx = fabs(xx2 - xx1); | ||
1625 | yy = fabs(yy2 - yy1); | ||
1626 | |||
1627 | if (((int)xx) && ((int)yy)) | ||
1628 | { | ||
1629 | rt = a = RAD2DEG(atan(yy / xx)); | ||
1630 | if (xx1 < xx2) | ||
1631 | { | ||
1632 | if (yy1 < yy2) rt = 360 - a; | ||
1633 | else rt = a; | ||
1634 | } | ||
1635 | else | ||
1636 | { | ||
1637 | if (yy1 < yy2) rt = 180 + a; | ||
1638 | else rt = 180 - a; | ||
1639 | } | ||
1640 | } | ||
1641 | |||
1642 | if (rt < 0) | ||
1643 | { /* Do this only if rt is not set */ | ||
1644 | if (((int)xx)) | ||
1645 | { /* Horizontal line */ | ||
1646 | if (xx2 < xx1) rt = 180; | ||
1647 | else rt = 0.0; | ||
1648 | } | ||
1649 | else | ||
1650 | { /* Vertical line */ | ||
1651 | if (yy2 < yy1) rt = 90; | ||
1652 | else rt = 270; | ||
1653 | } | ||
1654 | } | ||
1655 | |||
1656 | /* Now we want to change from: | ||
1657 | * 90 0 | ||
1658 | * original circle 180 0 We want: 270 90 | ||
1659 | * 270 180 | ||
1660 | */ | ||
1661 | rt = 450 - rt; | ||
1662 | if (rt >= 360) rt -= 360; | ||
1663 | |||
1664 | return rt; | ||
1665 | } | ||
1666 | |||
1667 | /** | ||
1668 | * @internal | ||
1669 | * | ||
1670 | * This function is used for computing the magnitude and direction | ||
1671 | * of vector between two points. | ||
1672 | * | ||
1673 | * @param x1 first finger x location. | ||
1674 | * @param y1 first finger y location. | ||
1675 | * @param x2 second finger x location. | ||
1676 | * @param y2 second finger y location. | ||
1677 | * @param l length computed (output) | ||
1678 | * @param a angle computed (output) | ||
1679 | * | ||
1680 | * @ingroup Elm_Gesture_Layer | ||
1681 | */ | ||
1682 | static void | ||
1683 | get_vector(Evas_Coord xx1, Evas_Coord yy1, Evas_Coord xx2, Evas_Coord yy2, | ||
1684 | Evas_Coord *l, double *a) | ||
1685 | { | ||
1686 | Evas_Coord xx, yy; | ||
1687 | xx = xx2 - xx1; | ||
1688 | yy = yy2 - yy1; | ||
1689 | *l = (Evas_Coord) sqrt((xx * xx) + (yy * yy)); | ||
1690 | *a = get_angle(xx1, yy1, xx2, yy2); | ||
1691 | } | ||
1692 | |||
1693 | static int | ||
1694 | _get_direction(Evas_Coord xx1, Evas_Coord xx2) | ||
1695 | { | ||
1696 | if (xx2 < xx1) return -1; | ||
1697 | if (xx2 > xx1) return 1; | ||
1698 | return 0; | ||
1699 | } | ||
1700 | /** | ||
1701 | * @internal | ||
1702 | * | ||
1703 | * This function tests momentum gesture. | ||
1704 | * @param obj The gesture-layer object. | ||
1705 | * @param pe The recent input event as stored in pe struct. | ||
1706 | * @param event_info recent input event. | ||
1707 | * @param event_type recent event type. | ||
1708 | * @param g_type what Gesture we are testing. | ||
1709 | * | ||
1710 | * @ingroup Elm_Gesture_Layer | ||
1711 | */ | ||
1712 | static void | ||
1713 | _momentum_test(Evas_Object *obj, Pointer_Event *pe, | ||
1714 | void *event_info, Evas_Callback_Type event_type, | ||
1715 | Elm_Gesture_Type g_type) | ||
1716 | { | ||
1717 | Widget_Data *wd = elm_widget_data_get(obj); | ||
1718 | if (!wd) return; | ||
1719 | Gesture_Info *gesture = wd->gesture[g_type]; | ||
1720 | if (!gesture ) return; | ||
1721 | |||
1722 | /* When continues enable = TRUE a gesture may START on MOVE event */ | ||
1723 | /* We don't allow this to happen with the if-statement below. */ | ||
1724 | /* When continues enable = FALSE a gesture may START on DOWN only */ | ||
1725 | /* Therefor it would NOT start on MOVE event. */ | ||
1726 | /* NOTE that touched list is updated AFTER this function returns */ | ||
1727 | /* so (count == 0) when we get here on first touch on surface. */ | ||
1728 | if ((wd->glayer_continues_enable) && (!eina_list_count(wd->touched))) | ||
1729 | return; /* Got move on mouse-over move */ | ||
1730 | |||
1731 | Momentum_Type *st = gesture->data; | ||
1732 | Elm_Gesture_State state_to_report = ELM_GESTURE_STATE_MOVE; | ||
1733 | if (!st) | ||
1734 | { /* Allocated once on first time */ | ||
1735 | st = calloc(1, sizeof(Momentum_Type)); | ||
1736 | gesture->data = st; | ||
1737 | _momentum_test_reset(gesture); | ||
1738 | } | ||
1739 | |||
1740 | if (!pe) | ||
1741 | return; | ||
1742 | |||
1743 | /* First make avarage of all touched devices to determine center point */ | ||
1744 | Eina_List *l; | ||
1745 | Pointer_Event *p; | ||
1746 | Pointer_Event pe_local = *pe; /* Copy pe event info to local */ | ||
1747 | unsigned int cnt = 1; /* We start counter counting current pe event */ | ||
1748 | EINA_LIST_FOREACH(wd->touched, l, p) | ||
1749 | if (p->device != pe_local.device) | ||
1750 | { | ||
1751 | pe_local.x += p->x; | ||
1752 | pe_local.y += p->y; | ||
1753 | cnt++; | ||
1754 | } | ||
1755 | |||
1756 | |||
1757 | /* Compute avarage to get center point */ | ||
1758 | pe_local.x /= cnt; | ||
1759 | pe_local.y /= cnt; | ||
1760 | |||
1761 | /* If user added finger - reset gesture */ | ||
1762 | if ((st->info.n) && (st->info.n < cnt)) | ||
1763 | state_to_report = ELM_GESTURE_STATE_ABORT; | ||
1764 | |||
1765 | |||
1766 | if (st->info.n < cnt) | ||
1767 | st->info.n = cnt; | ||
1768 | |||
1769 | Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE; | ||
1770 | switch (event_type) | ||
1771 | { | ||
1772 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
1773 | case EVAS_CALLBACK_MULTI_DOWN: | ||
1774 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
1775 | case EVAS_CALLBACK_MULTI_MOVE: | ||
1776 | if (!st->t_st_x) | ||
1777 | { | ||
1778 | if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) || | ||
1779 | (event_type == EVAS_CALLBACK_MULTI_DOWN) || | ||
1780 | (wd->glayer_continues_enable)) /* start also on MOVE */ | ||
1781 | { /* We start on MOVE when cont-enabled only */ | ||
1782 | st->line_st.x = st->line_end.x = pe_local.x; | ||
1783 | st->line_st.y = st->line_end.y = pe_local.y; | ||
1784 | st->t_st_x = st->t_st_y = st->t_end = pe_local.timestamp; | ||
1785 | st->xdir = st->ydir = 0; | ||
1786 | st->info.x2 = st->info.x1 = pe_local.x; | ||
1787 | st->info.y2 = st->info.y1 = pe_local.y; | ||
1788 | st->info.tx = st->info.ty = pe_local.timestamp; | ||
1789 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START, | ||
1790 | &st->info, EINA_FALSE); | ||
1791 | consume_event(wd, event_info, event_type, ev_flag); | ||
1792 | } | ||
1793 | |||
1794 | return; | ||
1795 | } | ||
1796 | |||
1797 | |||
1798 | if (st->t_up) | ||
1799 | { | ||
1800 | Eina_Bool force = EINA_TRUE; /* for move state */ | ||
1801 | if ((st->t_up + ELM_GESTURE_MULTI_TIMEOUT) < pe_local.timestamp) | ||
1802 | { /* ABORT if got DOWN or MOVE event after UP+timeout */ | ||
1803 | state_to_report = ELM_GESTURE_STATE_ABORT; | ||
1804 | force = EINA_FALSE; | ||
1805 | } | ||
1806 | |||
1807 | /* We report state but don't compute momentum now */ | ||
1808 | ev_flag = _set_state(gesture, state_to_report, &st->info, | ||
1809 | force); | ||
1810 | consume_event(wd, event_info, event_type, ev_flag); | ||
1811 | return; /* Stop computing when user remove finger */ | ||
1812 | } | ||
1813 | |||
1814 | if ((pe_local.timestamp - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->t_end) | ||
1815 | { /* Too long of a wait, reset all values */ | ||
1816 | st->line_st.x = pe_local.x; | ||
1817 | st->line_st.y = pe_local.y; | ||
1818 | st->t_st_y = st->t_st_x = pe_local.timestamp; | ||
1819 | st->info.tx = st->t_st_x; | ||
1820 | st->info.ty = st->t_st_y; | ||
1821 | st->xdir = st->ydir = 0; | ||
1822 | } | ||
1823 | else | ||
1824 | { | ||
1825 | int xdir, ydir; | ||
1826 | xdir = _get_direction(st->line_end.x, pe_local.x); | ||
1827 | ydir = _get_direction(st->line_end.y, pe_local.y); | ||
1828 | if (xdir && (xdir != st->xdir)) | ||
1829 | { | ||
1830 | st->line_st.x = st->line_end.x; | ||
1831 | st->info.tx = st->t_st_x = st->t_end; | ||
1832 | st->xdir = xdir; | ||
1833 | } | ||
1834 | |||
1835 | if (ydir && (ydir != st->ydir)) | ||
1836 | { | ||
1837 | st->line_st.y = st->line_end.y; | ||
1838 | st->info.ty = st->t_st_y = st->t_end; | ||
1839 | st->ydir = ydir; | ||
1840 | } | ||
1841 | } | ||
1842 | |||
1843 | st->info.x2 = st->line_end.x = pe_local.x; | ||
1844 | st->info.y2 = st->line_end.y = pe_local.y; | ||
1845 | st->t_end = pe_local.timestamp; | ||
1846 | _set_momentum(&st->info, st->line_st.x, st->line_st.y, | ||
1847 | pe_local.x, pe_local.y, st->t_st_x, st->t_st_y, | ||
1848 | pe_local.timestamp); | ||
1849 | |||
1850 | ev_flag = _set_state(gesture, state_to_report, &st->info, | ||
1851 | EINA_TRUE); | ||
1852 | consume_event(wd, event_info, event_type, ev_flag); | ||
1853 | break; | ||
1854 | |||
1855 | |||
1856 | case EVAS_CALLBACK_MOUSE_UP: | ||
1857 | case EVAS_CALLBACK_MULTI_UP: | ||
1858 | st->t_up = pe_local.timestamp; /* Record recent up event time */ | ||
1859 | if ((cnt > 1) || /* Ignore if more fingers touch surface */ | ||
1860 | (!st->t_st_x)) /* IGNORE if info was cleared, long press,move */ | ||
1861 | return; | ||
1862 | |||
1863 | if ((pe_local.timestamp - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->t_end) | ||
1864 | { /* Too long of a wait, reset all values */ | ||
1865 | st->line_st.x = pe_local.x; | ||
1866 | st->line_st.y = pe_local.y; | ||
1867 | st->t_st_y = st->t_st_x = pe_local.timestamp; | ||
1868 | st->xdir = st->ydir = 0; | ||
1869 | } | ||
1870 | |||
1871 | st->info.x2 = pe_local.x; | ||
1872 | st->info.y2 = pe_local.y; | ||
1873 | st->line_end.x = pe_local.x; | ||
1874 | st->line_end.y = pe_local.y; | ||
1875 | st->t_end = pe_local.timestamp; | ||
1876 | |||
1877 | if ((fabs(st->info.mx) > ELM_GESTURE_MINIMUM_MOMENTUM) || | ||
1878 | (fabs(st->info.my) > ELM_GESTURE_MINIMUM_MOMENTUM)) | ||
1879 | state_to_report = ELM_GESTURE_STATE_END; | ||
1880 | else | ||
1881 | state_to_report = ELM_GESTURE_STATE_ABORT; | ||
1882 | |||
1883 | ev_flag = _set_state(gesture, state_to_report, &st->info, | ||
1884 | EINA_FALSE); | ||
1885 | consume_event(wd, event_info, event_type, ev_flag); | ||
1886 | return; | ||
1887 | |||
1888 | default: | ||
1889 | return; | ||
1890 | } | ||
1891 | } | ||
1892 | |||
1893 | static int | ||
1894 | compare_line_device(const void *data1, const void *data2) | ||
1895 | { /* Compare device component of line struct */ | ||
1896 | const Line_Data *ln1 = data1; | ||
1897 | const int *device = data2; | ||
1898 | |||
1899 | if (ln1->t_st) /* Compare only with lines that started */ | ||
1900 | return (ln1->device - (*device)); | ||
1901 | |||
1902 | return (-1); | ||
1903 | } | ||
1904 | |||
1905 | /** | ||
1906 | * @internal | ||
1907 | * | ||
1908 | * This function construct line struct from input. | ||
1909 | * @param info pointer to store line momentum. | ||
1910 | * @param st line info to store input data. | ||
1911 | * @param pe The recent input event as stored in pe struct. | ||
1912 | * | ||
1913 | * @ingroup Elm_Gesture_Layer | ||
1914 | */ | ||
1915 | static Eina_Bool | ||
1916 | _single_line_process(Elm_Gesture_Line_Info *info, Line_Data *st, | ||
1917 | Pointer_Event *pe, Evas_Callback_Type event_type) | ||
1918 | { /* Record events and set momentum for line pointed by st */ | ||
1919 | if (!pe) | ||
1920 | return EINA_FALSE; | ||
1921 | |||
1922 | switch (event_type) | ||
1923 | { | ||
1924 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
1925 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
1926 | case EVAS_CALLBACK_MULTI_DOWN: | ||
1927 | case EVAS_CALLBACK_MULTI_MOVE: | ||
1928 | if (!st->t_st) | ||
1929 | { /* This happens only when line starts */ | ||
1930 | st->line_st.x = pe->x; | ||
1931 | st->line_st.y = pe->y; | ||
1932 | st->t_st = pe->timestamp; | ||
1933 | st->device = pe->device; | ||
1934 | info->momentum.x1 = pe->x; | ||
1935 | info->momentum.y1 = pe->y; | ||
1936 | info->momentum.tx = pe->timestamp; | ||
1937 | info->momentum.ty = pe->timestamp; | ||
1938 | |||
1939 | return EINA_TRUE; | ||
1940 | } | ||
1941 | |||
1942 | break; | ||
1943 | |||
1944 | case EVAS_CALLBACK_MOUSE_UP: | ||
1945 | case EVAS_CALLBACK_MULTI_UP: | ||
1946 | /* IGNORE if line info was cleared, like long press, move */ | ||
1947 | if (!st->t_st) | ||
1948 | return EINA_FALSE; | ||
1949 | |||
1950 | st->line_end.x = pe->x; | ||
1951 | st->line_end.y = pe->y; | ||
1952 | st->t_end = pe->timestamp; | ||
1953 | break; | ||
1954 | |||
1955 | default: | ||
1956 | return EINA_FALSE; | ||
1957 | } | ||
1958 | |||
1959 | if (!st->t_st) | ||
1960 | { | ||
1961 | _line_data_reset(st); | ||
1962 | return EINA_FALSE; | ||
1963 | } | ||
1964 | |||
1965 | info->momentum.x2 = pe->x; | ||
1966 | info->momentum.y2 = pe->y; | ||
1967 | _set_momentum(&info->momentum, st->line_st.x, st->line_st.y, pe->x, pe->y, | ||
1968 | st->t_st, st->t_st, pe->timestamp); | ||
1969 | |||
1970 | return EINA_TRUE; | ||
1971 | } | ||
1972 | |||
1973 | /** | ||
1974 | * @internal | ||
1975 | * | ||
1976 | * This function test for (n) line gesture. | ||
1977 | * @param obj The gesture-layer object. | ||
1978 | * @param pe The recent input event as stored in pe struct. | ||
1979 | * @param event_info Original input event pointer. | ||
1980 | * @param event_type Type of original input event. | ||
1981 | * @param g_type what Gesture we are testing. | ||
1982 | * | ||
1983 | * @ingroup Elm_Gesture_Layer | ||
1984 | */ | ||
1985 | static void | ||
1986 | _n_line_test(Evas_Object *obj, Pointer_Event *pe, void *event_info, | ||
1987 | Evas_Callback_Type event_type, Elm_Gesture_Type g_type) | ||
1988 | { | ||
1989 | if (!pe) | ||
1990 | return; | ||
1991 | Widget_Data *wd = elm_widget_data_get(obj); | ||
1992 | if (!wd) return; | ||
1993 | Gesture_Info *gesture = wd->gesture[g_type]; | ||
1994 | if (!gesture ) return; | ||
1995 | |||
1996 | /* When continues enable = TRUE a gesture may START on MOVE event */ | ||
1997 | /* We don't allow this to happen with the if-statement below. */ | ||
1998 | /* When continues enable = FALSE a gesture may START on DOWN only */ | ||
1999 | /* Therefor it would NOT start on MOVE event. */ | ||
2000 | /* NOTE that touched list is updated AFTER this function returns */ | ||
2001 | /* so (count == 0) when we get here on first touch on surface. */ | ||
2002 | if ((wd->glayer_continues_enable) && (!eina_list_count(wd->touched))) | ||
2003 | return; /* Got move on mouse-over move */ | ||
2004 | |||
2005 | Line_Type *st = gesture->data; | ||
2006 | if (!st) | ||
2007 | { | ||
2008 | st = calloc(1, sizeof(Line_Type)); | ||
2009 | gesture->data = st; | ||
2010 | } | ||
2011 | |||
2012 | Line_Data *line = NULL; | ||
2013 | Eina_List *list = st->list; | ||
2014 | unsigned cnt = eina_list_count(list); | ||
2015 | |||
2016 | if (cnt) | ||
2017 | { /* list is not empty, locate this device on list */ | ||
2018 | line = (Line_Data *) eina_list_search_unsorted(st->list, | ||
2019 | compare_line_device, &pe->device); | ||
2020 | } | ||
2021 | |||
2022 | if (!line) | ||
2023 | { /* List is empty or device not found, new line-struct on START only */ | ||
2024 | if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) || | ||
2025 | (event_type == EVAS_CALLBACK_MULTI_DOWN) || | ||
2026 | ((wd->glayer_continues_enable) && /* START on MOVE also */ | ||
2027 | ((event_type == EVAS_CALLBACK_MOUSE_MOVE) || | ||
2028 | (event_type == EVAS_CALLBACK_MULTI_MOVE)))) | ||
2029 | { /* Allocate new item on START only */ | ||
2030 | line = calloc(1, sizeof(Line_Data)); | ||
2031 | _line_data_reset(line); | ||
2032 | list = eina_list_append(list, line); | ||
2033 | st->list = list; | ||
2034 | } | ||
2035 | } | ||
2036 | |||
2037 | if (!line) /* This may happen on MOVE that comes before DOWN */ | ||
2038 | return; /* No line-struct to work with, can't continue testing */ | ||
2039 | |||
2040 | if (_single_line_process(&st->info, line, pe, event_type)) /* update st with input */ | ||
2041 | consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_NONE); | ||
2042 | |||
2043 | /* Get direction and magnitude of the line */ | ||
2044 | double angle; | ||
2045 | get_vector(line->line_st.x, line->line_st.y, pe->x, pe->y, | ||
2046 | &line->line_length, &angle); | ||
2047 | |||
2048 | /* These are used later to compare lines length */ | ||
2049 | Evas_Coord shortest_line_len = line->line_length; | ||
2050 | Evas_Coord longest_line_len = line->line_length; | ||
2051 | Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE; | ||
2052 | |||
2053 | /* Now update line-state */ | ||
2054 | if (line->t_st) | ||
2055 | { /* Analyze line only if line started */ | ||
2056 | if (line->line_angle >= 0.0) | ||
2057 | { /* if line direction was set, we test if broke tolerance */ | ||
2058 | double a = fabs(angle - line->line_angle); | ||
2059 | |||
2060 | double d = (tan(DEG2RAD(a))) * line->line_length; /* Distance from line */ | ||
2061 | #if defined(DEBUG_GESTURE_LAYER) | ||
2062 | printf("%s a=<%f> d=<%f>\n", __func__, a, d); | ||
2063 | #endif | ||
2064 | if ((d > wd->line_distance_tolerance) || (a > wd->line_angular_tolerance)) | ||
2065 | { /* Broke tolerance: abort line and start a new one */ | ||
2066 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, | ||
2067 | &st->info, EINA_FALSE); | ||
2068 | consume_event(wd, event_info, event_type, ev_flag); | ||
2069 | return; | ||
2070 | } | ||
2071 | |||
2072 | if (wd->glayer_continues_enable) | ||
2073 | { /* We may finish line if momentum is zero */ | ||
2074 | /* This is for continues-gesture */ | ||
2075 | if ((!st->info.momentum.mx) && (!st->info.momentum.my)) | ||
2076 | { /* Finish line on zero momentum for continues gesture */ | ||
2077 | line->line_end.x = pe->x; | ||
2078 | line->line_end.y = pe->y; | ||
2079 | line->t_end = pe->timestamp; | ||
2080 | } | ||
2081 | } | ||
2082 | } | ||
2083 | else | ||
2084 | { /* Record the line angle as it broke minimum length for line */ | ||
2085 | if (line->line_length >= wd->line_min_length) | ||
2086 | st->info.angle = line->line_angle = angle; | ||
2087 | } | ||
2088 | |||
2089 | |||
2090 | if (line->t_end) | ||
2091 | { | ||
2092 | if (line->line_angle < 0.0) | ||
2093 | { /* it's not a line, too short more close to a tap */ | ||
2094 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, | ||
2095 | &st->info, EINA_FALSE); | ||
2096 | consume_event(wd, event_info, event_type, ev_flag); | ||
2097 | return; | ||
2098 | } | ||
2099 | } | ||
2100 | } | ||
2101 | |||
2102 | /* Count how many lines already started / ended */ | ||
2103 | int started = 0; | ||
2104 | int ended = 0; | ||
2105 | unsigned int tm_start = pe->timestamp; | ||
2106 | unsigned int tm_end = pe->timestamp; | ||
2107 | Eina_List *l; | ||
2108 | Line_Data *t_line; | ||
2109 | double base_angle = ELM_GESTURE_NEGATIVE_ANGLE; | ||
2110 | Eina_Bool lines_parallel = EINA_TRUE; | ||
2111 | EINA_LIST_FOREACH(list, l, t_line) | ||
2112 | { | ||
2113 | if (base_angle < 0) | ||
2114 | base_angle = t_line->line_angle; | ||
2115 | else | ||
2116 | { | ||
2117 | if (t_line->line_angle >= 0) | ||
2118 | { /* Compare angle only with lines with direction defined */ | ||
2119 | if (fabs(base_angle - t_line->line_angle) > | ||
2120 | wd->line_angular_tolerance) | ||
2121 | lines_parallel = EINA_FALSE; | ||
2122 | } | ||
2123 | } | ||
2124 | |||
2125 | if (t_line->line_length) | ||
2126 | { /* update only if this line is used */ | ||
2127 | if (shortest_line_len > t_line->line_length) | ||
2128 | shortest_line_len = t_line->line_length; | ||
2129 | |||
2130 | if (longest_line_len < t_line->line_length) | ||
2131 | longest_line_len = t_line->line_length; | ||
2132 | } | ||
2133 | |||
2134 | if (t_line->t_st) | ||
2135 | { | ||
2136 | started++; | ||
2137 | if (t_line->t_st < tm_start) | ||
2138 | tm_start = t_line->t_st; | ||
2139 | } | ||
2140 | |||
2141 | if (t_line->t_end) | ||
2142 | { | ||
2143 | ended++; | ||
2144 | if (t_line->t_end < tm_end) | ||
2145 | tm_end = t_line->t_end; | ||
2146 | } | ||
2147 | } | ||
2148 | |||
2149 | st->info.momentum.n = started; | ||
2150 | |||
2151 | |||
2152 | if (ended && | ||
2153 | ((event_type == EVAS_CALLBACK_MOUSE_DOWN) || | ||
2154 | (event_type == EVAS_CALLBACK_MULTI_DOWN))) | ||
2155 | { /* user lift one finger then starts again without line-end - ABORT */ | ||
2156 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info, | ||
2157 | EINA_FALSE); | ||
2158 | consume_event(wd, event_info, event_type, ev_flag); | ||
2159 | return; | ||
2160 | } | ||
2161 | |||
2162 | if (!lines_parallel) | ||
2163 | { /* Lines are NOT at same direction, abort this gesture */ | ||
2164 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info, | ||
2165 | EINA_FALSE); | ||
2166 | consume_event(wd, event_info, event_type, ev_flag); | ||
2167 | return; | ||
2168 | } | ||
2169 | |||
2170 | |||
2171 | /* We report ABORT if lines length are NOT matching when fingers are up */ | ||
2172 | if ((longest_line_len - shortest_line_len) > (_elm_config->finger_size * 2)) | ||
2173 | { | ||
2174 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info, | ||
2175 | EINA_FALSE); | ||
2176 | consume_event(wd, event_info, event_type, ev_flag); | ||
2177 | return; | ||
2178 | } | ||
2179 | |||
2180 | if ((g_type == ELM_GESTURE_N_FLICKS) && ((tm_end - tm_start) > wd->flick_time_limit_ms)) | ||
2181 | { /* We consider FLICK as a fast line.ABORT if take too long to finish */ | ||
2182 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, &st->info, | ||
2183 | EINA_FALSE); | ||
2184 | consume_event(wd, event_info, event_type, ev_flag); | ||
2185 | return; | ||
2186 | } | ||
2187 | |||
2188 | switch (event_type) | ||
2189 | { | ||
2190 | case EVAS_CALLBACK_MOUSE_UP: | ||
2191 | case EVAS_CALLBACK_MULTI_UP: | ||
2192 | if ((started) && (started == ended)) | ||
2193 | { | ||
2194 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END, | ||
2195 | &st->info, EINA_FALSE); | ||
2196 | consume_event(wd, event_info, event_type, ev_flag); | ||
2197 | } | ||
2198 | |||
2199 | return; | ||
2200 | |||
2201 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
2202 | case EVAS_CALLBACK_MULTI_DOWN: | ||
2203 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
2204 | case EVAS_CALLBACK_MULTI_MOVE: | ||
2205 | if (started) | ||
2206 | { | ||
2207 | if (wd->glayer_continues_enable && (started == ended)) | ||
2208 | { /* For continues gesture */ | ||
2209 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END, | ||
2210 | &st->info, EINA_FALSE); | ||
2211 | consume_event(wd, event_info, event_type, ev_flag); | ||
2212 | } | ||
2213 | else | ||
2214 | { /* When continues, may START on MOVE event too */ | ||
2215 | Elm_Gesture_State s = ELM_GESTURE_STATE_MOVE; | ||
2216 | |||
2217 | /* This happens when: on n > 1 lines then one finger up */ | ||
2218 | /* caused abort, then put finger down. */ | ||
2219 | /* This will stop line from starting again. */ | ||
2220 | /* Number of lines, MUST match touched-device in list */ | ||
2221 | if ((!wd->glayer_continues_enable) && | ||
2222 | (eina_list_count(st->list) < eina_list_count(wd->touched))) | ||
2223 | s = ELM_GESTURE_STATE_ABORT; | ||
2224 | |||
2225 | if (gesture->state == ELM_GESTURE_STATE_UNDEFINED) | ||
2226 | s = ELM_GESTURE_STATE_START; | ||
2227 | |||
2228 | ev_flag = _set_state(gesture, s, &st->info, EINA_TRUE); | ||
2229 | consume_event(wd, event_info, event_type, ev_flag); | ||
2230 | } | ||
2231 | } | ||
2232 | break; | ||
2233 | |||
2234 | default: | ||
2235 | return; /* Unhandeld event type */ | ||
2236 | } | ||
2237 | } | ||
2238 | |||
2239 | /** | ||
2240 | * @internal | ||
2241 | * | ||
2242 | * This function is used to check if rotation gesture started. | ||
2243 | * @param st Contains current rotation values from user input. | ||
2244 | * @return TRUE/FALSE if we need to set rotation START. | ||
2245 | * | ||
2246 | * @ingroup Elm_Gesture_Layer | ||
2247 | */ | ||
2248 | static Eina_Bool | ||
2249 | rotation_broke_tolerance(Rotate_Type *st) | ||
2250 | { | ||
2251 | if (st->info.base_angle < 0) | ||
2252 | return EINA_FALSE; /* Angle has to be computed first */ | ||
2253 | |||
2254 | if (st->rotate_angular_tolerance < 0) | ||
2255 | return EINA_TRUE; | ||
2256 | |||
2257 | double low = st->info.base_angle - st->rotate_angular_tolerance; | ||
2258 | double high = st->info.base_angle + st->rotate_angular_tolerance; | ||
2259 | double t = st->info.angle; | ||
2260 | |||
2261 | if (low < 0) | ||
2262 | { | ||
2263 | low += 180; | ||
2264 | high += 180; | ||
2265 | |||
2266 | if (t < 180) | ||
2267 | t += 180; | ||
2268 | else | ||
2269 | t -= 180; | ||
2270 | } | ||
2271 | |||
2272 | if (high > 360) | ||
2273 | { | ||
2274 | low -= 180; | ||
2275 | high -= 180; | ||
2276 | |||
2277 | if (t < 180) | ||
2278 | t += 180; | ||
2279 | else | ||
2280 | t -= 180; | ||
2281 | } | ||
2282 | |||
2283 | #if defined(DEBUG_GESTURE_LAYER) | ||
2284 | printf("%s angle=<%f> low=<%f> high=<%f>\n", __func__, t, low, high); | ||
2285 | #endif | ||
2286 | if ((t < low) || (t > high)) | ||
2287 | { /* This marks that roation action has started */ | ||
2288 | st->rotate_angular_tolerance = ELM_GESTURE_NEGATIVE_ANGLE; | ||
2289 | st->info.base_angle = st->info.angle; /* Avoid jump in angle value */ | ||
2290 | return EINA_TRUE; | ||
2291 | } | ||
2292 | |||
2293 | return EINA_FALSE; | ||
2294 | } | ||
2295 | |||
2296 | /** | ||
2297 | * @internal | ||
2298 | * | ||
2299 | * This function is used for computing the gap between fingers. | ||
2300 | * It returns the length and center point between fingers. | ||
2301 | * | ||
2302 | * @param x1 first finger x location. | ||
2303 | * @param y1 first finger y location. | ||
2304 | * @param x2 second finger x location. | ||
2305 | * @param y2 second finger y location. | ||
2306 | * @param x Gets center point x cord (output) | ||
2307 | * @param y Gets center point y cord (output) | ||
2308 | * | ||
2309 | * @return length of the line between (x1,y1), (x2,y2) in pixels. | ||
2310 | * | ||
2311 | * @ingroup Elm_Gesture_Layer | ||
2312 | */ | ||
2313 | static Evas_Coord | ||
2314 | get_finger_gap_length(Evas_Coord xx1, Evas_Coord yy1, | ||
2315 | Evas_Coord xx2, Evas_Coord yy2, | ||
2316 | Evas_Coord *x, Evas_Coord *y) | ||
2317 | { | ||
2318 | double a, b, xx, yy, gap; | ||
2319 | xx = fabs(xx2 - xx1); | ||
2320 | yy = fabs(yy2 - yy1); | ||
2321 | gap = sqrt((xx * xx) + (yy * yy)); | ||
2322 | |||
2323 | /* START - Compute zoom center point */ | ||
2324 | /* The triangle defined as follows: | ||
2325 | * B | ||
2326 | * / | | ||
2327 | * / | | ||
2328 | * gap / | a | ||
2329 | * / | | ||
2330 | * A-----C | ||
2331 | * b | ||
2332 | * http://en.wikipedia.org/wiki/Trigonometric_functions | ||
2333 | *************************************/ | ||
2334 | if (((int)xx) && ((int)yy)) | ||
2335 | { | ||
2336 | double A = atan((yy / xx)); | ||
2337 | #if defined(DEBUG_GESTURE_LAYER) | ||
2338 | printf("xx=<%f> yy=<%f> A=<%f>\n", xx, yy, A); | ||
2339 | #endif | ||
2340 | a = (Evas_Coord) ((gap / 2) * sin(A)); | ||
2341 | b = (Evas_Coord) ((gap / 2) * cos(A)); | ||
2342 | *x = (Evas_Coord) ((xx2 > xx1) ? (xx1 + b) : (xx2 + b)); | ||
2343 | *y = (Evas_Coord) ((yy2 > yy1) ? (yy1 + a) : (yy2 + a)); | ||
2344 | } | ||
2345 | else | ||
2346 | { | ||
2347 | if ((int)xx) | ||
2348 | { /* horiz line, take half width */ | ||
2349 | #if defined(DEBUG_GESTURE_LAYER) | ||
2350 | printf("==== HORIZ ====\n"); | ||
2351 | #endif | ||
2352 | *x = (Evas_Coord) ((xx1 + xx2) / 2); | ||
2353 | *y = (Evas_Coord) (yy1); | ||
2354 | } | ||
2355 | |||
2356 | if ((int)yy) | ||
2357 | { /* vert line, take half width */ | ||
2358 | #if defined(DEBUG_GESTURE_LAYER) | ||
2359 | printf("==== VERT ====\n"); | ||
2360 | #endif | ||
2361 | *x = (Evas_Coord) (xx1); | ||
2362 | *y = (Evas_Coord) ((yy1 + yy2) / 2); | ||
2363 | } | ||
2364 | } | ||
2365 | /* END - Compute zoom center point */ | ||
2366 | |||
2367 | return (Evas_Coord) gap; | ||
2368 | } | ||
2369 | |||
2370 | /** | ||
2371 | * @internal | ||
2372 | * | ||
2373 | * This function is used for computing zoom value. | ||
2374 | * | ||
2375 | * @param st Pointer to zoom data based on user input. | ||
2376 | * @param tm_end Recent input event timestamp. | ||
2377 | * @param zoom_val Current computed zoom value. | ||
2378 | * | ||
2379 | * @return zoom momentum | ||
2380 | * | ||
2381 | * @ingroup Elm_Gesture_Layer | ||
2382 | */ | ||
2383 | static double | ||
2384 | _zoom_momentum_get(Zoom_Type *st, unsigned int tm_end, double zoom_val) | ||
2385 | { | ||
2386 | unsigned int tm_total; | ||
2387 | if (!st->m_st_tm) | ||
2388 | { /* Init, and we don't start computing momentum yet */ | ||
2389 | st->m_st_tm = st->m_prev_tm = tm_end; | ||
2390 | st->m_base = zoom_val; | ||
2391 | return 0.0; | ||
2392 | } | ||
2393 | |||
2394 | if ((tm_end - ELM_GESTURE_MOMENTUM_DELAY) < st->m_st_tm) | ||
2395 | return 0.0; /* we don't start to compute momentum yet */ | ||
2396 | |||
2397 | if (st->dir) | ||
2398 | { /* if direction was already defined, check if changed */ | ||
2399 | if (((st->dir < 0) && (zoom_val > st->info.zoom)) || | ||
2400 | ((st->dir > 0) && (zoom_val < st->info.zoom))) | ||
2401 | { /* Direction changed, reset momentum */ | ||
2402 | st->m_st_tm = 0; | ||
2403 | st->dir = (-st->dir); | ||
2404 | return 0.0; | ||
2405 | } | ||
2406 | } | ||
2407 | else | ||
2408 | st->dir = (zoom_val > st->info.zoom) ? 1 : -1; /* init */ | ||
2409 | |||
2410 | if ((tm_end - ELM_GESTURE_MOMENTUM_TIMEOUT) > st->m_prev_tm) | ||
2411 | { | ||
2412 | st->m_st_tm = 0; /* Rest momentum when waiting too long */ | ||
2413 | return 0.0; | ||
2414 | } | ||
2415 | |||
2416 | st->m_prev_tm = tm_end; | ||
2417 | tm_total = tm_end - st->m_st_tm; | ||
2418 | |||
2419 | if (tm_total) | ||
2420 | return ((zoom_val - st->m_base) * 1000) / tm_total; | ||
2421 | else | ||
2422 | return 0.0; | ||
2423 | } | ||
2424 | |||
2425 | /** | ||
2426 | * @internal | ||
2427 | * | ||
2428 | * This function is used for computing zoom value. | ||
2429 | * | ||
2430 | * @param st Pointer to zoom data based on user input. | ||
2431 | * @param x1 first finger x location. | ||
2432 | * @param y1 first finger y location. | ||
2433 | * @param x2 second finger x location. | ||
2434 | * @param y2 second finger y location. | ||
2435 | * @param factor zoom-factor, used to determine how fast zoom works. | ||
2436 | * | ||
2437 | * @return zoom value, when 1.0 means no zoom, 0.5 half size... | ||
2438 | * | ||
2439 | * @ingroup Elm_Gesture_Layer | ||
2440 | */ | ||
2441 | static double | ||
2442 | compute_zoom(Zoom_Type *st, | ||
2443 | Evas_Coord xx1, Evas_Coord yy1, | ||
2444 | Evas_Coord xx2, Evas_Coord yy2, | ||
2445 | double zoom_finger_factor) | ||
2446 | { | ||
2447 | double rt = 1.0; | ||
2448 | unsigned int tm_end = (st->zoom_mv.timestamp > st->zoom_mv1.timestamp) ? | ||
2449 | st->zoom_mv.timestamp : st->zoom_mv1.timestamp; | ||
2450 | |||
2451 | Evas_Coord diam = get_finger_gap_length(xx1, yy1, xx2, yy2, | ||
2452 | &st->info.x, &st->info.y); | ||
2453 | |||
2454 | st->info.radius = diam / 2; | ||
2455 | |||
2456 | if (!st->zoom_base) | ||
2457 | { | ||
2458 | st->zoom_base = diam; | ||
2459 | return st->info.zoom; | ||
2460 | } | ||
2461 | |||
2462 | if (st->zoom_distance_tolerance) | ||
2463 | { /* zoom tolerance <> ZERO, means zoom action NOT started yet */ | ||
2464 | if (diam < (st->zoom_base - st->zoom_distance_tolerance)) | ||
2465 | { /* avoid jump with zoom value when break tolerance */ | ||
2466 | st->zoom_base -= st->zoom_distance_tolerance; | ||
2467 | st->zoom_distance_tolerance = 0; | ||
2468 | } | ||
2469 | |||
2470 | if (diam > (st->zoom_base + st->zoom_distance_tolerance)) | ||
2471 | { /* avoid jump with zoom value when break tolerance */ | ||
2472 | st->zoom_base += st->zoom_distance_tolerance; | ||
2473 | st->zoom_distance_tolerance = 0; | ||
2474 | } | ||
2475 | |||
2476 | return rt; | ||
2477 | } | ||
2478 | |||
2479 | /* We use factor only on the difference between gap-base */ | ||
2480 | /* if gap=120, base=100, we get ((120-100)/100)=0.2*factor */ | ||
2481 | rt = ((1.0) + ((((float) diam - (float) st->zoom_base) / | ||
2482 | (float) st->zoom_base) * zoom_finger_factor)); | ||
2483 | |||
2484 | /* Momentum: zoom per second: */ | ||
2485 | st->info.momentum = _zoom_momentum_get(st, tm_end, rt); | ||
2486 | |||
2487 | return rt; | ||
2488 | } | ||
2489 | |||
2490 | /** | ||
2491 | * @internal | ||
2492 | * | ||
2493 | * This function handles zoom with mouse wheel. | ||
2494 | * thats a combination of wheel + CTRL key. | ||
2495 | * @param obj The gesture-layer object. | ||
2496 | * @param event_info Original input event pointer. | ||
2497 | * @param event_type Type of original input event. | ||
2498 | * @param g_type what Gesture we are testing. | ||
2499 | * | ||
2500 | * @ingroup Elm_Gesture_Layer | ||
2501 | */ | ||
2502 | static void | ||
2503 | _zoom_with_wheel_test(Evas_Object *obj, void *event_info, | ||
2504 | Evas_Callback_Type event_type, Elm_Gesture_Type g_type) | ||
2505 | { | ||
2506 | Widget_Data *wd = elm_widget_data_get(obj); | ||
2507 | if (!wd) return; | ||
2508 | if (!wd->gesture[g_type]) return; | ||
2509 | |||
2510 | Gesture_Info *gesture_zoom = wd->gesture[g_type]; | ||
2511 | Zoom_Type *st = gesture_zoom->data; | ||
2512 | Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE; | ||
2513 | if (!st) | ||
2514 | { /* Allocated once on first time, used for zoom intermediate data */ | ||
2515 | st = calloc(1, sizeof(Zoom_Type)); | ||
2516 | gesture_zoom->data = st; | ||
2517 | _zoom_test_reset(gesture_zoom); | ||
2518 | } | ||
2519 | |||
2520 | switch (event_type) | ||
2521 | { | ||
2522 | case EVAS_CALLBACK_KEY_UP: | ||
2523 | { | ||
2524 | Evas_Event_Key_Up *p = event_info; | ||
2525 | if ((!strcmp(p->keyname, "Control_L")) || | ||
2526 | (!strcmp(p->keyname, "Control_R"))) | ||
2527 | { /* Test if we ended a zoom gesture when releasing CTRL */ | ||
2528 | if ((st->zoom_wheel) && | ||
2529 | ((gesture_zoom->state == ELM_GESTURE_STATE_START) || | ||
2530 | (gesture_zoom->state == ELM_GESTURE_STATE_MOVE))) | ||
2531 | { /* User released CTRL after zooming */ | ||
2532 | st->info.momentum = _zoom_momentum_get(st, | ||
2533 | p->timestamp, st->info.zoom); | ||
2534 | |||
2535 | ev_flag = _set_state(gesture_zoom, | ||
2536 | ELM_GESTURE_STATE_END, &st->info, EINA_FALSE); | ||
2537 | consume_event(wd, event_info, event_type, ev_flag); | ||
2538 | |||
2539 | return; | ||
2540 | } | ||
2541 | } | ||
2542 | break; | ||
2543 | } | ||
2544 | |||
2545 | case EVAS_CALLBACK_MOUSE_WHEEL: | ||
2546 | { | ||
2547 | Eina_Bool force; | ||
2548 | Elm_Gesture_State s; | ||
2549 | if (!evas_key_modifier_is_set( | ||
2550 | ((Evas_Event_Mouse_Wheel *) event_info)->modifiers, | ||
2551 | "Control")) | ||
2552 | { /* if using wheel witout CTRL after starting zoom */ | ||
2553 | if ((st->zoom_wheel) && | ||
2554 | ((gesture_zoom->state == ELM_GESTURE_STATE_START) || | ||
2555 | (gesture_zoom->state == ELM_GESTURE_STATE_MOVE))) | ||
2556 | { | ||
2557 | ev_flag = _set_state(gesture_zoom, | ||
2558 | ELM_GESTURE_STATE_END, &st->info, EINA_FALSE); | ||
2559 | consume_event(wd, event_info, event_type, ev_flag); | ||
2560 | |||
2561 | return; | ||
2562 | } | ||
2563 | else | ||
2564 | return; /* Ignore mouse-wheel without control */ | ||
2565 | } | ||
2566 | |||
2567 | /* Using mouse wheel with CTRL for zoom */ | ||
2568 | if (st->zoom_wheel || (st->zoom_distance_tolerance == 0)) | ||
2569 | { /* (zoom_wheel == NULL) and (zoom_distance_tolerance == 0) | ||
2570 | we continue a zoom gesture */ | ||
2571 | force = EINA_TRUE; | ||
2572 | s = ELM_GESTURE_STATE_MOVE; | ||
2573 | } | ||
2574 | else | ||
2575 | { /* On first wheel event, report START */ | ||
2576 | Evas_Modifier_Mask mask = evas_key_modifier_mask_get( | ||
2577 | evas_object_evas_get(wd->target), "Control"); | ||
2578 | force = EINA_FALSE; | ||
2579 | s = ELM_GESTURE_STATE_START; | ||
2580 | if (!evas_object_key_grab(wd->target, "Control_L", mask, 0, EINA_FALSE)) | ||
2581 | ERR("Failed to Grabbed CTRL_L"); | ||
2582 | if (!evas_object_key_grab(wd->target, "Control_R", mask, 0, EINA_FALSE)) | ||
2583 | ERR("Failed to Grabbed CTRL_R"); | ||
2584 | } | ||
2585 | |||
2586 | st->zoom_distance_tolerance = 0; /* Cancel tolerance */ | ||
2587 | st->zoom_wheel = (Evas_Event_Mouse_Wheel *) event_info; | ||
2588 | st->info.x = st->zoom_wheel->canvas.x; | ||
2589 | st->info.y = st->zoom_wheel->canvas.y; | ||
2590 | |||
2591 | if (st->zoom_wheel->z < 0) /* zoom in */ | ||
2592 | st->info.zoom += (wd->zoom_finger_factor * wd->zoom_wheel_factor); | ||
2593 | |||
2594 | if (st->zoom_wheel->z > 0) /* zoom out */ | ||
2595 | st->info.zoom -= (wd->zoom_finger_factor * wd->zoom_wheel_factor); | ||
2596 | |||
2597 | if (st->info.zoom < 0.0) | ||
2598 | st->info.zoom = 0.0; | ||
2599 | |||
2600 | st->info.momentum = _zoom_momentum_get(st, | ||
2601 | st->zoom_wheel->timestamp, st->info.zoom); | ||
2602 | |||
2603 | ev_flag = _set_state(gesture_zoom, s, &st->info, force); | ||
2604 | consume_event(wd, event_info, event_type, ev_flag); | ||
2605 | break; | ||
2606 | } | ||
2607 | |||
2608 | default: | ||
2609 | return; | ||
2610 | } | ||
2611 | } | ||
2612 | |||
2613 | /** | ||
2614 | * @internal | ||
2615 | * | ||
2616 | * This function is used to test zoom gesture. | ||
2617 | * user may combine zoom, rotation together. | ||
2618 | * so its possible that both will be detected from input. | ||
2619 | * (both are two-finger movement-oriented gestures) | ||
2620 | * | ||
2621 | * @param obj The gesture-layer object. | ||
2622 | * @param event_info Pointer to recent input event. | ||
2623 | * @param event_type Recent input event type. | ||
2624 | * @param g_type what Gesture we are testing. | ||
2625 | * | ||
2626 | * @ingroup Elm_Gesture_Layer | ||
2627 | */ | ||
2628 | static void | ||
2629 | _zoom_test(Evas_Object *obj, Pointer_Event *pe, void *event_info, | ||
2630 | Evas_Callback_Type event_type, Elm_Gesture_Type g_type) | ||
2631 | { | ||
2632 | if (!pe) | ||
2633 | return; | ||
2634 | Widget_Data *wd = elm_widget_data_get(obj); | ||
2635 | if (!wd) return; | ||
2636 | if (!wd->gesture[g_type]) return; | ||
2637 | |||
2638 | Gesture_Info *gesture_zoom = wd->gesture[g_type]; | ||
2639 | Zoom_Type *st = gesture_zoom->data; | ||
2640 | |||
2641 | if (!st) | ||
2642 | { /* Allocated once on first time, used for zoom data */ | ||
2643 | st = calloc(1, sizeof(Zoom_Type)); | ||
2644 | gesture_zoom->data = st; | ||
2645 | _zoom_test_reset(gesture_zoom); | ||
2646 | } | ||
2647 | |||
2648 | |||
2649 | /* Start - new zoom testing, letting all fingers start */ | ||
2650 | Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE; | ||
2651 | switch (event_type) | ||
2652 | { | ||
2653 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
2654 | case EVAS_CALLBACK_MULTI_MOVE: | ||
2655 | /* if non-continues mode and gesture NOT started, ignore MOVE */ | ||
2656 | if ((!wd->glayer_continues_enable) && | ||
2657 | (!st->zoom_st.timestamp)) | ||
2658 | return; | ||
2659 | |||
2660 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
2661 | case EVAS_CALLBACK_MULTI_DOWN: | ||
2662 | { /* Here we take care of zoom-start and zoom move */ | ||
2663 | Eina_List *l; | ||
2664 | Pointer_Event *p; | ||
2665 | |||
2666 | if (eina_list_count(wd->touched) > 2) | ||
2667 | { /* Process zoom only when 2 fingers on surface */ | ||
2668 | ev_flag = _set_state(gesture_zoom, | ||
2669 | ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE); | ||
2670 | consume_event(wd, event_info, event_type, ev_flag); | ||
2671 | |||
2672 | return; | ||
2673 | } | ||
2674 | |||
2675 | if (!st->zoom_st.timestamp) | ||
2676 | { /* Now scan touched-devices list and find other finger */ | ||
2677 | EINA_LIST_FOREACH(wd->touched, l, p) | ||
2678 | { /* Device of other finger <> pe device */ | ||
2679 | if (p->device != pe->device) | ||
2680 | break; | ||
2681 | } | ||
2682 | |||
2683 | if (!p) /* Single finger on touch */ | ||
2684 | return; | ||
2685 | |||
2686 | /* Record down fingers */ | ||
2687 | consume_event(wd, event_info, event_type, ev_flag); | ||
2688 | memcpy(&st->zoom_st, pe, sizeof(Pointer_Event)); | ||
2689 | memcpy(&st->zoom_st1, p, sizeof(Pointer_Event)); | ||
2690 | |||
2691 | /* Set mv field as well to be ready for MOVE events */ | ||
2692 | memcpy(&st->zoom_mv, pe, sizeof(Pointer_Event)); | ||
2693 | memcpy(&st->zoom_mv1, p, sizeof(Pointer_Event)); | ||
2694 | |||
2695 | /* Here we have zoom_st, zoom_st1 set, report START */ | ||
2696 | /* Set zoom-base after BOTH down events recorded */ | ||
2697 | /* Compute length of line between fingers zoom start */ | ||
2698 | st->info.zoom = 1.0; | ||
2699 | st->zoom_base = get_finger_gap_length(st->zoom_st1.x, | ||
2700 | st->zoom_st1.y, st->zoom_st.x, st->zoom_st.y, | ||
2701 | &st->info.x, &st->info.y); | ||
2702 | |||
2703 | st->info.radius = st->zoom_base / 2; | ||
2704 | |||
2705 | if ((gesture_zoom->state != ELM_GESTURE_STATE_START) && | ||
2706 | (gesture_zoom->state != ELM_GESTURE_STATE_MOVE)) | ||
2707 | { /* zoom started with mouse-wheel, don't report twice */ | ||
2708 | ev_flag = _set_state(gesture_zoom, | ||
2709 | ELM_GESTURE_STATE_START, &st->info, EINA_FALSE); | ||
2710 | consume_event(wd, event_info, event_type, ev_flag); | ||
2711 | } | ||
2712 | |||
2713 | return; /* Zoom started */ | ||
2714 | } /* End of ZOOM_START handling */ | ||
2715 | |||
2716 | |||
2717 | /* if we got here, we have (exacally) two fingers on surfce */ | ||
2718 | /* we also after START, report MOVE */ | ||
2719 | /* First detect which finger moved */ | ||
2720 | if (pe->device == st->zoom_mv.device) | ||
2721 | memcpy(&st->zoom_mv, pe, sizeof(Pointer_Event)); | ||
2722 | else if (pe->device == st->zoom_mv1.device) | ||
2723 | memcpy(&st->zoom_mv1, pe, sizeof(Pointer_Event)); | ||
2724 | |||
2725 | /* Compute change in zoom as fingers move */ | ||
2726 | st->info.zoom = compute_zoom(st, | ||
2727 | st->zoom_mv.x, st->zoom_mv.y, | ||
2728 | st->zoom_mv1.x, st->zoom_mv1.y, | ||
2729 | wd->zoom_finger_factor); | ||
2730 | |||
2731 | if (!st->zoom_distance_tolerance) | ||
2732 | { /* Zoom broke tolerance, report move */ | ||
2733 | double d = st->info.zoom - st->next_step; | ||
2734 | if (d < 0.0) | ||
2735 | d = (-d); | ||
2736 | |||
2737 | if (d >= wd->zoom_step) | ||
2738 | { /* Report move in steps */ | ||
2739 | st->next_step = st->info.zoom; | ||
2740 | |||
2741 | ev_flag = _set_state(gesture_zoom, | ||
2742 | ELM_GESTURE_STATE_MOVE, | ||
2743 | &st->info, EINA_TRUE); | ||
2744 | consume_event(wd, event_info, event_type, ev_flag); | ||
2745 | } | ||
2746 | } /* End of ZOOM_MOVE handling */ | ||
2747 | |||
2748 | return; | ||
2749 | } | ||
2750 | |||
2751 | case EVAS_CALLBACK_MOUSE_UP: | ||
2752 | case EVAS_CALLBACK_MULTI_UP: | ||
2753 | /* Reset timestamp of finger-up.This is used later | ||
2754 | by _zoom_test_reset() to retain finger-down data */ | ||
2755 | consume_event(wd, event_info, event_type, ev_flag); | ||
2756 | if (((st->zoom_wheel) || (st->zoom_base)) && | ||
2757 | (st->zoom_distance_tolerance == 0)) | ||
2758 | { | ||
2759 | ev_flag = _set_state(gesture_zoom, ELM_GESTURE_STATE_END, | ||
2760 | &st->info, EINA_FALSE); | ||
2761 | consume_event(wd, event_info, event_type, ev_flag); | ||
2762 | |||
2763 | return; | ||
2764 | } | ||
2765 | |||
2766 | /* if we got here not a ZOOM */ | ||
2767 | if (gesture_zoom->state != ELM_GESTURE_STATE_UNDEFINED) | ||
2768 | { /* Must be != undefined, if gesture started */ | ||
2769 | ev_flag = _set_state(gesture_zoom, | ||
2770 | ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE); | ||
2771 | consume_event(wd, event_info, event_type, ev_flag); | ||
2772 | } | ||
2773 | |||
2774 | _zoom_test_reset(gesture_zoom); | ||
2775 | |||
2776 | return; | ||
2777 | |||
2778 | default: | ||
2779 | return; | ||
2780 | } | ||
2781 | } | ||
2782 | |||
2783 | static void | ||
2784 | _get_rotate_properties(Rotate_Type *st, | ||
2785 | Evas_Coord xx1, Evas_Coord yy1, | ||
2786 | Evas_Coord xx2, Evas_Coord yy2, | ||
2787 | double *angle) | ||
2788 | { /* FIXME: Fix momentum computation, it's wrong */ | ||
2789 | double prev_angle = *angle; | ||
2790 | st->info.radius = get_finger_gap_length(xx1, yy1, xx2, yy2, | ||
2791 | &st->info.x, &st->info.y) / 2; | ||
2792 | |||
2793 | *angle = get_angle(xx1, yy1, xx2, yy2); | ||
2794 | |||
2795 | if (angle == &st->info.angle) | ||
2796 | { /* Fingers are moving, compute momentum */ | ||
2797 | unsigned int tm_start = | ||
2798 | (st->rotate_st.timestamp > st->rotate_st1.timestamp) | ||
2799 | ? st->rotate_st.timestamp : st->rotate_st1.timestamp; | ||
2800 | unsigned int tm_end = | ||
2801 | (st->rotate_mv.timestamp > st->rotate_mv1.timestamp) | ||
2802 | ? st->rotate_mv.timestamp : st->rotate_mv1.timestamp; | ||
2803 | |||
2804 | unsigned int tm_total = tm_end - tm_start; | ||
2805 | if (tm_total) | ||
2806 | { /* Momentum computed as: | ||
2807 | accumulated roation angle (deg) divided by time */ | ||
2808 | double m = 0;; | ||
2809 | if (((prev_angle < 90) && ((*angle) > 270)) || | ||
2810 | ((prev_angle > 270) && ((*angle) < 90))) | ||
2811 | { /* We circle passing ZERO point */ | ||
2812 | prev_angle = (*angle); | ||
2813 | } | ||
2814 | else m = prev_angle - (*angle); | ||
2815 | |||
2816 | st->accum_momentum += m; | ||
2817 | |||
2818 | if ((tm_end - st->prev_momentum_tm) < 100) | ||
2819 | st->prev_momentum += m; | ||
2820 | else | ||
2821 | { | ||
2822 | if (fabs(st->prev_momentum) < 0.002) | ||
2823 | st->accum_momentum = 0.0; /* reset momentum */ | ||
2824 | |||
2825 | st->prev_momentum = 0.0; /* Start again */ | ||
2826 | } | ||
2827 | |||
2828 | st->prev_momentum_tm = tm_end; | ||
2829 | st->info.momentum = (st->accum_momentum * 1000) / tm_total; | ||
2830 | } | ||
2831 | } | ||
2832 | else | ||
2833 | st->info.momentum = 0; | ||
2834 | } | ||
2835 | |||
2836 | /** | ||
2837 | * @internal | ||
2838 | * | ||
2839 | * This function is used to test rotation gesture. | ||
2840 | * user may combine zoom, rotation together. | ||
2841 | * so its possible that both will be detected from input. | ||
2842 | * (both are two-finger movement-oriented gestures) | ||
2843 | * | ||
2844 | * @param obj The gesture-layer object. | ||
2845 | * @param event_info Pointer to recent input event. | ||
2846 | * @param event_type Recent input event type. | ||
2847 | * @param g_type what Gesture we are testing. | ||
2848 | * | ||
2849 | * @ingroup Elm_Gesture_Layer | ||
2850 | */ | ||
2851 | static void | ||
2852 | _rotate_test(Evas_Object *obj, Pointer_Event *pe, void *event_info, | ||
2853 | Evas_Callback_Type event_type, Elm_Gesture_Type g_type) | ||
2854 | { | ||
2855 | if (!pe) | ||
2856 | return; | ||
2857 | |||
2858 | Widget_Data *wd = elm_widget_data_get(obj); | ||
2859 | if (!wd) return; | ||
2860 | if (!wd->gesture[g_type]) return; | ||
2861 | |||
2862 | Gesture_Info *gesture = wd->gesture[g_type]; | ||
2863 | Rotate_Type *st; | ||
2864 | if (gesture) | ||
2865 | { | ||
2866 | st = gesture->data; | ||
2867 | if (!st) | ||
2868 | { /* Allocated once on first time */ | ||
2869 | st = calloc(1, sizeof(Rotate_Type)); | ||
2870 | gesture->data = st; | ||
2871 | _rotate_test_reset(gesture); | ||
2872 | } | ||
2873 | } | ||
2874 | |||
2875 | Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE; | ||
2876 | switch (event_type) | ||
2877 | { | ||
2878 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
2879 | case EVAS_CALLBACK_MULTI_MOVE: | ||
2880 | /* if non-continues mode and gesture NOT started, ignore MOVE */ | ||
2881 | if ((!wd->glayer_continues_enable) && | ||
2882 | (!st->rotate_st.timestamp)) | ||
2883 | return; | ||
2884 | |||
2885 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
2886 | case EVAS_CALLBACK_MULTI_DOWN: | ||
2887 | { /* Here we take care of rotate-start and rotate move */ | ||
2888 | Eina_List *l; | ||
2889 | Pointer_Event *p; | ||
2890 | |||
2891 | if (eina_list_count(wd->touched) > 2) | ||
2892 | { /* Process rotate only when 2 fingers on surface */ | ||
2893 | ev_flag = _set_state(gesture, | ||
2894 | ELM_GESTURE_STATE_ABORT, &st->info, EINA_FALSE); | ||
2895 | consume_event(wd, event_info, event_type, ev_flag); | ||
2896 | |||
2897 | return; | ||
2898 | } | ||
2899 | |||
2900 | if (!st->rotate_st.timestamp) | ||
2901 | { /* Now scan touched-devices list and find other finger */ | ||
2902 | EINA_LIST_FOREACH(wd->touched, l, p) | ||
2903 | { /* Device of other finger <> pe device */ | ||
2904 | if (p->device != pe->device) | ||
2905 | break; | ||
2906 | } | ||
2907 | |||
2908 | if (!p) | ||
2909 | return; /* Single finger on touch */ | ||
2910 | |||
2911 | /* Record down fingers */ | ||
2912 | consume_event(wd, event_info, event_type, ev_flag); | ||
2913 | memcpy(&st->rotate_st, pe, sizeof(Pointer_Event)); | ||
2914 | memcpy(&st->rotate_st1, p, sizeof(Pointer_Event)); | ||
2915 | |||
2916 | /* Set mv field as well to be ready for MOVE events */ | ||
2917 | memcpy(&st->rotate_mv, pe, sizeof(Pointer_Event)); | ||
2918 | memcpy(&st->rotate_mv1, p, sizeof(Pointer_Event)); | ||
2919 | |||
2920 | /* Here we have rotate_st, rotate_st1 set, report START */ | ||
2921 | /* Set rotate-base after BOTH down events recorded */ | ||
2922 | /* Compute length of line between fingers rotate start */ | ||
2923 | _get_rotate_properties(st, | ||
2924 | st->rotate_st.x, st->rotate_st.y, | ||
2925 | st->rotate_st1.x, st->rotate_st1.y, | ||
2926 | &st->info.base_angle); | ||
2927 | |||
2928 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_START, | ||
2929 | &st->info, EINA_FALSE); | ||
2930 | consume_event(wd, event_info, event_type, ev_flag); | ||
2931 | |||
2932 | return; /* Rotate started */ | ||
2933 | } /* End of ROTATE_START handling */ | ||
2934 | |||
2935 | |||
2936 | /* if we got here, we have (exacally) two fingers on surfce */ | ||
2937 | /* we also after START, report MOVE */ | ||
2938 | /* First detect which finger moved */ | ||
2939 | if (pe->device == st->rotate_mv.device) | ||
2940 | memcpy(&st->rotate_mv, pe, sizeof(Pointer_Event)); | ||
2941 | else if (pe->device == st->rotate_mv1.device) | ||
2942 | memcpy(&st->rotate_mv1, pe, sizeof(Pointer_Event)); | ||
2943 | |||
2944 | /* Compute change in rotate as fingers move */ | ||
2945 | _get_rotate_properties(st, | ||
2946 | st->rotate_mv.x, st->rotate_mv.y, | ||
2947 | st->rotate_mv1.x, st->rotate_mv1.y, | ||
2948 | &st->info.angle); | ||
2949 | |||
2950 | if (rotation_broke_tolerance(st)) | ||
2951 | { /* Rotation broke tolerance, report move */ | ||
2952 | double d = st->info.angle - st->next_step; | ||
2953 | if (d < 0) | ||
2954 | d = (-d); | ||
2955 | |||
2956 | if (d >= wd->rotate_step) | ||
2957 | { /* Report move in steps */ | ||
2958 | st->next_step = st->info.angle; | ||
2959 | |||
2960 | ev_flag = _set_state(gesture, | ||
2961 | ELM_GESTURE_STATE_MOVE, &st->info, EINA_TRUE); | ||
2962 | consume_event(wd, event_info, event_type, ev_flag); | ||
2963 | } | ||
2964 | } /* End of ROTATE_MOVE handling */ | ||
2965 | |||
2966 | return; | ||
2967 | } | ||
2968 | |||
2969 | case EVAS_CALLBACK_MOUSE_UP: | ||
2970 | case EVAS_CALLBACK_MULTI_UP: | ||
2971 | consume_event(wd, event_info, event_type, ev_flag); | ||
2972 | /* Reset timestamp of finger-up.This is used later | ||
2973 | by rotate_test_reset() to retain finger-down data */ | ||
2974 | if (st->rotate_angular_tolerance < 0) | ||
2975 | { | ||
2976 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_END, | ||
2977 | &st->info, EINA_FALSE); | ||
2978 | consume_event(wd, event_info, event_type, ev_flag); | ||
2979 | |||
2980 | return; | ||
2981 | } | ||
2982 | |||
2983 | if (gesture->state != ELM_GESTURE_STATE_UNDEFINED) | ||
2984 | { /* Must be != undefined, if gesture started */ | ||
2985 | ev_flag = _set_state(gesture, ELM_GESTURE_STATE_ABORT, | ||
2986 | &st->info, EINA_FALSE); | ||
2987 | consume_event(wd, event_info, event_type, ev_flag); | ||
2988 | } | ||
2989 | |||
2990 | _rotate_test_reset(gesture); | ||
2991 | return; | ||
2992 | |||
2993 | default: | ||
2994 | return; | ||
2995 | } | ||
2996 | } | ||
2997 | |||
2998 | /** | ||
2999 | * @internal | ||
3000 | * | ||
3001 | * This function is used to save input events in an abstract struct | ||
3002 | * to be used later by getsure-testing functions. | ||
3003 | * | ||
3004 | * @param data The gesture-layer object. | ||
3005 | * @param event_info Pointer to recent input event. | ||
3006 | * @param event_type Recent input event type. | ||
3007 | * @param pe The abstract data-struct (output). | ||
3008 | * | ||
3009 | * @ingroup Elm_Gesture_Layer | ||
3010 | */ | ||
3011 | static Eina_Bool | ||
3012 | _make_pointer_event(void *data, void *event_info, | ||
3013 | Evas_Callback_Type event_type, Pointer_Event *pe) | ||
3014 | { | ||
3015 | Widget_Data *wd = elm_widget_data_get(data); | ||
3016 | if (!wd) return EINA_FALSE; | ||
3017 | |||
3018 | memset(pe, '\0', sizeof(*pe)); | ||
3019 | switch (event_type) | ||
3020 | { | ||
3021 | case EVAS_CALLBACK_MOUSE_DOWN: | ||
3022 | pe->x = ((Evas_Event_Mouse_Down *) event_info)->canvas.x; | ||
3023 | pe->y = ((Evas_Event_Mouse_Down *) event_info)->canvas.y; | ||
3024 | pe->timestamp = ((Evas_Event_Mouse_Down *) event_info)->timestamp; | ||
3025 | pe->device = ELM_MOUSE_DEVICE; | ||
3026 | break; | ||
3027 | |||
3028 | case EVAS_CALLBACK_MOUSE_UP: | ||
3029 | pe->x = ((Evas_Event_Mouse_Up *) event_info)->canvas.x; | ||
3030 | pe->y = ((Evas_Event_Mouse_Up *) event_info)->canvas.y; | ||
3031 | pe->timestamp = ((Evas_Event_Mouse_Up *) event_info)->timestamp; | ||
3032 | pe->device = ELM_MOUSE_DEVICE; | ||
3033 | break; | ||
3034 | |||
3035 | case EVAS_CALLBACK_MOUSE_MOVE: | ||
3036 | pe->x = ((Evas_Event_Mouse_Move *) event_info)->cur.canvas.x; | ||
3037 | pe->y = ((Evas_Event_Mouse_Move *) event_info)->cur.canvas.y; | ||
3038 | pe->timestamp = ((Evas_Event_Mouse_Move *) event_info)->timestamp; | ||
3039 | pe->device = ELM_MOUSE_DEVICE; | ||
3040 | break; | ||
3041 | |||
3042 | case EVAS_CALLBACK_MULTI_DOWN: | ||
3043 | pe->x = ((Evas_Event_Multi_Down *) event_info)->canvas.x; | ||
3044 | pe->y = ((Evas_Event_Multi_Down *) event_info)->canvas.y; | ||
3045 | pe->timestamp = ((Evas_Event_Multi_Down *) event_info)->timestamp; | ||
3046 | pe->device = ((Evas_Event_Multi_Down *) event_info)->device; | ||
3047 | break; | ||
3048 | |||
3049 | case EVAS_CALLBACK_MULTI_UP: | ||
3050 | pe->x = ((Evas_Event_Multi_Up *) event_info)->canvas.x; | ||
3051 | pe->y = ((Evas_Event_Multi_Up *) event_info)->canvas.y; | ||
3052 | pe->timestamp = ((Evas_Event_Multi_Up *) event_info)->timestamp; | ||
3053 | pe->device = ((Evas_Event_Multi_Up *) event_info)->device; | ||
3054 | break; | ||
3055 | |||
3056 | case EVAS_CALLBACK_MULTI_MOVE: | ||
3057 | pe->x = ((Evas_Event_Multi_Move *) event_info)->cur.canvas.x; | ||
3058 | pe->y = ((Evas_Event_Multi_Move *) event_info)->cur.canvas.y; | ||
3059 | pe->timestamp = ((Evas_Event_Multi_Move *) event_info)->timestamp; | ||
3060 | pe->device = ((Evas_Event_Multi_Move *) event_info)->device; | ||
3061 | break; | ||
3062 | |||
3063 | default: | ||
3064 | return EINA_FALSE; | ||
3065 | } | ||
3066 | |||
3067 | pe->event_type = event_type; | ||
3068 | return EINA_TRUE; | ||
3069 | } | ||
3070 | |||
3071 | /** | ||
3072 | * @internal | ||
3073 | * | ||
3074 | * This function restartes line, flick, zoom and rotate gestures | ||
3075 | * when gesture-layer continues-gestures enabled. | ||
3076 | * Example of continues-gesture: | ||
3077 | * When doing a line, user stops moving finger but keeps fingers on touch. | ||
3078 | * This will cause line-end, then as user continues moving his finger | ||
3079 | * it re-starts line gesture. | ||
3080 | * When continue mode is disabled, user has to lift finger from touch | ||
3081 | * to end a gesture. Them touch-again to start a new one. | ||
3082 | * | ||
3083 | * @param data The gesture-layer object. | ||
3084 | * @param wd gesture layer widget data. | ||
3085 | * @param states_reset flag that marks gestures were reset in history clear. | ||
3086 | * | ||
3087 | * @ingroup Elm_Gesture_Layer | ||
3088 | */ | ||
3089 | static void | ||
3090 | continues_gestures_restart(void *data, Eina_Bool states_reset) | ||
3091 | { | ||
3092 | Widget_Data *wd = elm_widget_data_get(data); | ||
3093 | if (!wd) return; | ||
3094 | |||
3095 | /* Run through events to restart gestures */ | ||
3096 | Gesture_Info *g; | ||
3097 | Eina_Bool n_momentum, n_lines, n_flicks, zoom, rotate; | ||
3098 | /* We turn-on flag for finished, aborted, not-started gestures */ | ||
3099 | g = wd->gesture[ELM_GESTURE_MOMENTUM]; | ||
3100 | n_momentum = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START) | ||
3101 | && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE; | ||
3102 | if (n_momentum) | ||
3103 | { | ||
3104 | _momentum_test_reset(wd->gesture[ELM_GESTURE_MOMENTUM]); | ||
3105 | _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE); | ||
3106 | SET_TEST_BIT(g); | ||
3107 | } | ||
3108 | |||
3109 | g = wd->gesture[ELM_GESTURE_N_LINES]; | ||
3110 | n_lines = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START) | ||
3111 | && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE; | ||
3112 | if (n_lines) | ||
3113 | { | ||
3114 | _line_test_reset(wd->gesture[ELM_GESTURE_N_LINES]); | ||
3115 | _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE); | ||
3116 | SET_TEST_BIT(g); | ||
3117 | } | ||
3118 | |||
3119 | g = wd->gesture[ELM_GESTURE_N_FLICKS]; | ||
3120 | n_flicks = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START) | ||
3121 | && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE; | ||
3122 | if (n_flicks) | ||
3123 | { | ||
3124 | _line_test_reset(wd->gesture[ELM_GESTURE_N_FLICKS]); | ||
3125 | _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE); | ||
3126 | SET_TEST_BIT(g); | ||
3127 | } | ||
3128 | |||
3129 | g = wd->gesture[ELM_GESTURE_ZOOM]; | ||
3130 | zoom = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START) | ||
3131 | && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE; | ||
3132 | if (zoom) | ||
3133 | { | ||
3134 | _zoom_test_reset(wd->gesture[ELM_GESTURE_ZOOM]); | ||
3135 | _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE); | ||
3136 | SET_TEST_BIT(g); | ||
3137 | } | ||
3138 | |||
3139 | g = wd->gesture[ELM_GESTURE_ROTATE]; | ||
3140 | rotate = (g) ? ((states_reset) | ((g->state != ELM_GESTURE_STATE_START) | ||
3141 | && (g->state != ELM_GESTURE_STATE_MOVE))) : EINA_FALSE; | ||
3142 | if (rotate) | ||
3143 | { | ||
3144 | _rotate_test_reset(wd->gesture[ELM_GESTURE_ROTATE]); | ||
3145 | _set_state(g, ELM_GESTURE_STATE_UNDEFINED, NULL, EINA_FALSE); | ||
3146 | SET_TEST_BIT(g); | ||
3147 | } | ||
3148 | } | ||
3149 | |||
3150 | /** | ||
3151 | * @internal | ||
3152 | * | ||
3153 | * This function the core-function where input handling is done. | ||
3154 | * Here we get user input and stream it to gesture testing. | ||
3155 | * We notify user about any gestures with new state: | ||
3156 | * Valid states are: | ||
3157 | * START - gesture started. | ||
3158 | * MOVE - gesture is ongoing. | ||
3159 | * END - gesture was completed. | ||
3160 | * ABORT - gesture was aborted after START, MOVE (will NOT be completed) | ||
3161 | * | ||
3162 | * We also check if a gesture was detected, then reset event history | ||
3163 | * If no gestures were found we reset gesture test flag | ||
3164 | * after streaming event-history to widget. | ||
3165 | * (stream to the widget all events not consumed as a gesture) | ||
3166 | * | ||
3167 | * @param data The gesture-layer object. | ||
3168 | * @param event_info Pointer to recent input event. | ||
3169 | * @param event_type Recent input event type. | ||
3170 | * | ||
3171 | * @ingroup Elm_Gesture_Layer | ||
3172 | */ | ||
3173 | static void | ||
3174 | _event_process(void *data, Evas_Object *obj __UNUSED__, | ||
3175 | void *event_info, Evas_Callback_Type event_type) | ||
3176 | { | ||
3177 | Pointer_Event _pe; | ||
3178 | Pointer_Event *pe = NULL; | ||
3179 | Widget_Data *wd = elm_widget_data_get(data); | ||
3180 | |||
3181 | #if defined(DEBUG_GESTURE_LAYER) | ||
3182 | int i; | ||
3183 | Gesture_Info *g; | ||
3184 | printf("Gesture | State | is tested\n"); | ||
3185 | for (i = ELM_GESTURE_N_TAPS; i < ELM_GESTURE_LAST; i++) | ||
3186 | { | ||
3187 | g = wd->gesture[i]; | ||
3188 | if (g) | ||
3189 | printf(" %d %d %d\n", i, g->state, g->test); | ||
3190 | } | ||
3191 | #endif | ||
3192 | |||
3193 | /* Start testing candidate gesture from here */ | ||
3194 | if (_make_pointer_event(data, event_info, event_type, &_pe)) | ||
3195 | pe = &_pe; | ||
3196 | |||
3197 | if (IS_TESTED(ELM_GESTURE_N_LONG_TAPS)) | ||
3198 | _n_long_tap_test(data, pe, event_info, event_type, | ||
3199 | ELM_GESTURE_N_LONG_TAPS); | ||
3200 | |||
3201 | /* This takes care of single, double and tripple tap */ | ||
3202 | _tap_gestures_test(data, pe, event_info, event_type); | ||
3203 | |||
3204 | if (IS_TESTED(ELM_GESTURE_MOMENTUM)) | ||
3205 | _momentum_test(data, pe, event_info, event_type, | ||
3206 | ELM_GESTURE_MOMENTUM); | ||
3207 | |||
3208 | if (IS_TESTED(ELM_GESTURE_N_LINES)) | ||
3209 | _n_line_test(data, pe, event_info, event_type, ELM_GESTURE_N_LINES); | ||
3210 | |||
3211 | if (IS_TESTED(ELM_GESTURE_N_FLICKS)) | ||
3212 | _n_line_test(data, pe, event_info, event_type, ELM_GESTURE_N_FLICKS); | ||
3213 | |||
3214 | if (_elm_config->glayer_zoom_finger_enable && IS_TESTED(ELM_GESTURE_ZOOM)) | ||
3215 | _zoom_test(data, pe, event_info, event_type, ELM_GESTURE_ZOOM); | ||
3216 | |||
3217 | if (IS_TESTED(ELM_GESTURE_ZOOM)) | ||
3218 | _zoom_with_wheel_test(data, event_info, event_type, ELM_GESTURE_ZOOM); | ||
3219 | |||
3220 | if (_elm_config->glayer_rotate_finger_enable && IS_TESTED(ELM_GESTURE_ROTATE)) | ||
3221 | _rotate_test(data, pe, event_info, event_type, ELM_GESTURE_ROTATE); | ||
3222 | |||
3223 | if (_get_event_flag(event_info, event_type) & EVAS_EVENT_FLAG_ON_HOLD) | ||
3224 | _event_history_add(data, event_info, event_type); | ||
3225 | else if ((event_type == EVAS_CALLBACK_MOUSE_UP) || | ||
3226 | (event_type == EVAS_CALLBACK_MULTI_UP)) | ||
3227 | { | ||
3228 | Eina_List *pending = _device_is_pending(wd->pending, event_info, event_type); | ||
3229 | if (pending) | ||
3230 | { | ||
3231 | consume_event(wd, event_info, event_type, EVAS_EVENT_FLAG_ON_HOLD); | ||
3232 | _event_history_add(data, event_info, event_type); | ||
3233 | } | ||
3234 | } | ||
3235 | |||
3236 | /* we maintain list of touched devices */ | ||
3237 | /* We also use move to track current device x.y pos */ | ||
3238 | if ((event_type == EVAS_CALLBACK_MOUSE_DOWN) || | ||
3239 | (event_type == EVAS_CALLBACK_MULTI_DOWN) || | ||
3240 | (event_type == EVAS_CALLBACK_MOUSE_MOVE) || | ||
3241 | (event_type == EVAS_CALLBACK_MULTI_MOVE)) | ||
3242 | { | ||
3243 | wd->touched = _add_touched_device(wd->touched, pe); | ||
3244 | } | ||
3245 | else if ((event_type == EVAS_CALLBACK_MOUSE_UP) || | ||
3246 | (event_type == EVAS_CALLBACK_MULTI_UP)) | ||
3247 | { | ||
3248 | wd->touched = _remove_touched_device(wd->touched, pe); | ||
3249 | } | ||
3250 | |||
3251 | /* Report current states and clear history if needed */ | ||
3252 | Eina_Bool states_reset = _clear_if_finished(data); | ||
3253 | if (wd->glayer_continues_enable) | ||
3254 | continues_gestures_restart(data, states_reset); | ||
3255 | } | ||
3256 | |||
3257 | |||
3258 | /** | ||
3259 | * For all _mouse_* / multi_* functions wethen send this event to | ||
3260 | * _event_process function. | ||
3261 | * | ||
3262 | * @param data The gesture-layer object. | ||
3263 | * @param event_info Pointer to recent input event. | ||
3264 | * | ||
3265 | * @ingroup Elm_Gesture_Layer | ||
3266 | */ | ||
3267 | static void | ||
3268 | _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, | ||
3269 | void *event_info) | ||
3270 | { | ||
3271 | Widget_Data *wd = elm_widget_data_get(data); | ||
3272 | if (!wd) return; | ||
3273 | if (((Evas_Event_Mouse_Down *) event_info)->button != 1) | ||
3274 | return; /* We only process left-click at the moment */ | ||
3275 | |||
3276 | _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_DOWN); | ||
3277 | } | ||
3278 | |||
3279 | static void | ||
3280 | _mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, | ||
3281 | void *event_info) | ||
3282 | { | ||
3283 | Widget_Data *wd = elm_widget_data_get(data); | ||
3284 | if (!wd) return; | ||
3285 | |||
3286 | _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_MOVE); | ||
3287 | } | ||
3288 | |||
3289 | static void | ||
3290 | _key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, | ||
3291 | void *event_info) | ||
3292 | { | ||
3293 | Widget_Data *wd = elm_widget_data_get(data); | ||
3294 | if (!wd) return; | ||
3295 | |||
3296 | _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_DOWN); | ||
3297 | } | ||
3298 | |||
3299 | static void | ||
3300 | _key_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, | ||
3301 | void *event_info) | ||
3302 | { | ||
3303 | Widget_Data *wd = elm_widget_data_get(data); | ||
3304 | if (!wd) return; | ||
3305 | |||
3306 | _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_UP); | ||
3307 | } | ||
3308 | |||
3309 | static void | ||
3310 | _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, | ||
3311 | void *event_info) | ||
3312 | { | ||
3313 | Widget_Data *wd = elm_widget_data_get(data); | ||
3314 | if (!wd) return; | ||
3315 | |||
3316 | if (((Evas_Event_Mouse_Up *) event_info)->button != 1) | ||
3317 | return; /* We only process left-click at the moment */ | ||
3318 | |||
3319 | _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_UP); | ||
3320 | } | ||
3321 | |||
3322 | static void | ||
3323 | _mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, | ||
3324 | void *event_info) | ||
3325 | { | ||
3326 | Widget_Data *wd = elm_widget_data_get(data); | ||
3327 | if (!wd) return; | ||
3328 | |||
3329 | _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_WHEEL); | ||
3330 | } | ||
3331 | |||
3332 | static void | ||
3333 | _multi_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, | ||
3334 | void *event_info) | ||
3335 | { | ||
3336 | Widget_Data *wd = elm_widget_data_get(data); | ||
3337 | if (!wd) return; | ||
3338 | |||
3339 | _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_DOWN); | ||
3340 | } | ||
3341 | |||
3342 | static void | ||
3343 | _multi_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, | ||
3344 | void *event_info) | ||
3345 | { | ||
3346 | Widget_Data *wd = elm_widget_data_get(data); | ||
3347 | if (!wd) return; | ||
3348 | |||
3349 | _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_MOVE); | ||
3350 | } | ||
3351 | |||
3352 | static void | ||
3353 | _multi_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, | ||
3354 | void *event_info) | ||
3355 | { | ||
3356 | Widget_Data *wd = elm_widget_data_get(data); | ||
3357 | if (!wd) return; | ||
3358 | |||
3359 | _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_UP); | ||
3360 | } | ||
3361 | |||
3362 | EAPI Eina_Bool | ||
3363 | elm_gesture_layer_hold_events_get(const Evas_Object *obj) | ||
3364 | { | ||
3365 | ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE; | ||
3366 | |||
3367 | Widget_Data *wd = elm_widget_data_get(obj); | ||
3368 | if (!wd) return EINA_FALSE; | ||
3369 | |||
3370 | return !wd->repeat_events; | ||
3371 | } | ||
3372 | |||
3373 | EAPI void | ||
3374 | elm_gesture_layer_hold_events_set(Evas_Object *obj, Eina_Bool hold_events) | ||
3375 | { | ||
3376 | ELM_CHECK_WIDTYPE(obj, widtype); | ||
3377 | |||
3378 | Widget_Data *wd = elm_widget_data_get(obj); | ||
3379 | if (!wd) return; | ||
3380 | |||
3381 | wd->repeat_events = !(!!hold_events); | ||
3382 | } | ||
3383 | |||
3384 | EAPI double | ||
3385 | elm_gesture_layer_zoom_step_get(const Evas_Object *obj) | ||
3386 | { | ||
3387 | ELM_CHECK_WIDTYPE(obj, widtype) 0; | ||
3388 | |||
3389 | Widget_Data *wd = elm_widget_data_get(obj); | ||
3390 | if (!wd) return 0; | ||
3391 | |||
3392 | return wd->zoom_step; | ||
3393 | } | ||
3394 | |||
3395 | EAPI void | ||
3396 | elm_gesture_layer_zoom_step_set(Evas_Object *obj, double step) | ||
3397 | { | ||
3398 | ELM_CHECK_WIDTYPE(obj, widtype); | ||
3399 | |||
3400 | Widget_Data *wd = elm_widget_data_get(obj); | ||
3401 | if (!wd) return; | ||
3402 | |||
3403 | if (step < 0) return; | ||
3404 | |||
3405 | wd->zoom_step = step; | ||
3406 | } | ||
3407 | |||
3408 | EAPI double | ||
3409 | elm_gesture_layer_rotate_step_get(const Evas_Object *obj) | ||
3410 | { | ||
3411 | ELM_CHECK_WIDTYPE(obj, widtype) 0; | ||
3412 | |||
3413 | Widget_Data *wd = elm_widget_data_get(obj); | ||
3414 | if (!wd) return 0; | ||
3415 | |||
3416 | return wd->rotate_step; | ||
3417 | } | ||
3418 | |||
3419 | EAPI void | ||
3420 | elm_gesture_layer_rotate_step_set(Evas_Object *obj, double step) | ||
3421 | { | ||
3422 | ELM_CHECK_WIDTYPE(obj, widtype); | ||
3423 | |||
3424 | Widget_Data *wd = elm_widget_data_get(obj); | ||
3425 | if (!wd) return; | ||
3426 | |||
3427 | if (step < 0) return; | ||
3428 | |||
3429 | wd->rotate_step = step; | ||
3430 | } | ||
3431 | |||
3432 | EAPI Eina_Bool | ||
3433 | elm_gesture_layer_attach(Evas_Object *obj, Evas_Object *target) | ||
3434 | { | ||
3435 | ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE; | ||
3436 | |||
3437 | Widget_Data *wd = elm_widget_data_get(obj); | ||
3438 | if (!wd) return EINA_FALSE; | ||
3439 | |||
3440 | if (!target) return EINA_FALSE; | ||
3441 | |||
3442 | /* if was attached before, unregister callbacks first */ | ||
3443 | if (wd->target) | ||
3444 | _unregister_callbacks(obj); | ||
3445 | |||
3446 | wd->target = target; | ||
3447 | |||
3448 | _register_callbacks(obj); | ||
3449 | return EINA_TRUE; | ||
3450 | } | ||
3451 | |||
3452 | EAPI void | ||
3453 | elm_gesture_layer_cb_set(Evas_Object *obj, Elm_Gesture_Type idx, | ||
3454 | Elm_Gesture_State cb_type, Elm_Gesture_Event_Cb cb, void *data) | ||
3455 | { | ||
3456 | ELM_CHECK_WIDTYPE(obj, widtype); | ||
3457 | |||
3458 | Widget_Data *wd = elm_widget_data_get(obj); | ||
3459 | Gesture_Info *p; | ||
3460 | if (!wd) return; | ||
3461 | |||
3462 | if (!wd->gesture[idx]) | ||
3463 | wd->gesture[idx] = calloc(1, sizeof(Gesture_Info)); | ||
3464 | if (!wd->gesture[idx]) return; | ||
3465 | |||
3466 | p = wd->gesture[idx]; | ||
3467 | p->obj = obj; | ||
3468 | p->g_type = idx; | ||
3469 | p->fn[cb_type].cb = cb; | ||
3470 | p->fn[cb_type].user_data = data; | ||
3471 | p->state = ELM_GESTURE_STATE_UNDEFINED; | ||
3472 | SET_TEST_BIT(p); | ||
3473 | } | ||
3474 | |||
3475 | static void | ||
3476 | _disable_hook(Evas_Object *obj) | ||
3477 | { | ||
3478 | if (elm_widget_disabled_get(obj)) | ||
3479 | _unregister_callbacks(obj); | ||
3480 | else | ||
3481 | _register_callbacks(obj); | ||
3482 | } | ||
3483 | |||
3484 | EAPI Evas_Object * | ||
3485 | elm_gesture_layer_add(Evas_Object *parent) | ||
3486 | { | ||
3487 | Evas_Object *obj; | ||
3488 | Evas *e; | ||
3489 | Widget_Data *wd; | ||
3490 | |||
3491 | ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL); | ||
3492 | |||
3493 | ELM_SET_WIDTYPE(widtype, "gesture_layer"); | ||
3494 | elm_widget_type_set(obj, "gesture_layer"); | ||
3495 | elm_widget_sub_object_add(parent, obj); | ||
3496 | elm_widget_data_set(obj, wd); | ||
3497 | elm_widget_del_hook_set(obj, _del_hook); | ||
3498 | elm_widget_disable_hook_set(obj, _disable_hook); | ||
3499 | |||
3500 | wd->target = NULL; | ||
3501 | wd->line_min_length =_elm_config->glayer_line_min_length * _elm_config->finger_size; | ||
3502 | wd->zoom_distance_tolerance = _elm_config->glayer_zoom_distance_tolerance * _elm_config->finger_size; | ||
3503 | wd->line_distance_tolerance = _elm_config->glayer_line_distance_tolerance * _elm_config->finger_size; | ||
3504 | wd->zoom_finger_factor = _elm_config->glayer_zoom_finger_factor; | ||
3505 | wd->zoom_wheel_factor = _elm_config->glayer_zoom_wheel_factor; /* mouse wheel zoom steps */ | ||
3506 | wd->rotate_angular_tolerance = _elm_config->glayer_rotate_angular_tolerance; | ||
3507 | wd->line_angular_tolerance = _elm_config->glayer_line_angular_tolerance; | ||
3508 | wd->flick_time_limit_ms = _elm_config->glayer_flick_time_limit_ms; | ||
3509 | wd->long_tap_start_timeout = _elm_config->glayer_long_tap_start_timeout; | ||
3510 | wd->repeat_events = EINA_TRUE; | ||
3511 | wd->glayer_continues_enable = _elm_config->glayer_continues_enable; | ||
3512 | |||
3513 | #if defined(DEBUG_GESTURE_LAYER) | ||
3514 | printf("size of Gestures = <%d>\n", sizeof(wd->gesture)); | ||
3515 | printf("initial values:\n\tzoom_finger_factor=<%f>\n\tzoom_distance_tolerance=<%d>\n\tline_min_length=<%d>\n\tline_distance_tolerance=<%d>\n\tzoom_wheel_factor=<%f>\n\trotate_angular_tolerance=<%f>\n\twd->line_angular_tolerance=<%f>\n\twd->flick_time_limit_ms=<%d>\n\twd->long_tap_start_timeout=<%f>\n\twd->zoom_step=<%f>\n\twd->rotate_step=<%f>\n\twd->glayer_continues_enable=<%d>\n ", wd->zoom_finger_factor, wd->zoom_distance_tolerance, wd->line_min_length, wd->line_distance_tolerance, wd->zoom_wheel_factor, wd->rotate_angular_tolerance, wd->line_angular_tolerance, wd->flick_time_limit_ms, wd->long_tap_start_timeout, wd->zoom_step, wd->rotate_step, wd->glayer_continues_enable); | ||
3516 | #endif | ||
3517 | memset(wd->gesture, 0, sizeof(wd->gesture)); | ||
3518 | |||
3519 | return obj; | ||
3520 | } | ||