handle gestures inside screen reader
authorLukasz Stanislawski <l.stanislaws@samsung.com>
Sat, 23 May 2015 14:14:06 +0000 (16:14 +0200)
committerLukasz Stanislawski <l.stanislaws@samsung.com>
Fri, 5 Jun 2015 08:14:36 +0000 (10:14 +0200)
This patch implements gesture recognition layer inside screen reader.
Implementation is based on following X-org features:
- ability to grab device using XInput2 XIGrabDevice
- ability to resend events using XSendEvent function.

Change-Id: Ida875db43fc96b07b1f2095edd00fc59a8a2264c
Signed-off-by: Lukasz Stanislawski <l.stanislaws@gmail.com>
include/gesture_tracker.h [deleted file]
include/screen_reader_gestures.h [new file with mode: 0644]
src/gesture_tracker.c [deleted file]
src/main.c
src/navigator.c
src/screen_reader.c
src/screen_reader_gestures.c [new file with mode: 0644]

diff --git a/include/gesture_tracker.h b/include/gesture_tracker.h
deleted file mode 100644 (file)
index 5ae3ea9..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#include <eldbus-1/Eldbus.h>
-#include <atspi/atspi.h>
-
-/**
- * @brief Accessibility gestures
- */
-enum _Gesture {
-     ONE_FINGER_HOVER,
-     TWO_FINGERS_HOVER,
-     ONE_FINGER_FLICK_LEFT,
-     ONE_FINGER_FLICK_RIGHT,
-     ONE_FINGER_FLICK_UP,
-     ONE_FINGER_FLICK_DOWN,
-     THREE_FINGERS_FLICK_LEFT,
-     THREE_FINGERS_FLICK_RIGHT,
-     THREE_FINGERS_FLICK_UP,
-     THREE_FINGERS_FLICK_DOWN,
-     ONE_FINGER_SINGLE_TAP,
-     ONE_FINGER_DOUBLE_TAP,
-     TWO_FINGERS_FLICK_UP,
-     TWO_FINGERS_FLICK_LEFT,
-     TWO_FINGERS_FLICK_RIGHT,
-     TWO_FINGERS_FLICK_DOWN,
-     GESTURES_COUNT,
-};
-typedef enum _Gesture Gesture;
-
-typedef struct {
-     Gesture type;         // Type of recognized gesture
-     int x_begin, x_end;     // (x,y) coordinates when gesture begin
-     int y_begin, y_end;     // (x,y) coordinates when gesture ends
-     int state;              // 0 - gesture begins, 1 - continues, 2 - ended
-} Gesture_Info;
-
-
-typedef void (*Gesture_Tracker_Cb) (void *data, Gesture_Info *g);
-void gesture_tracker_init(Eldbus_Connection *conn);
-void gesture_tracker_register(Gesture_Tracker_Cb cb, void *data);
-void gesture_tracker_shutdown(void);
-AtspiAccessible* _get_active_win(void);
diff --git a/include/screen_reader_gestures.h b/include/screen_reader_gestures.h
new file mode 100644 (file)
index 0000000..dae92aa
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef SCREEN_READER_GESTURES_H_
+#define SCREEN_READER_GESTURES_H
+
+#include <Ecore.h>
+#include <Ecore_X.h>
+
+/**
+ * @brief Accessibility gestures
+ */
+enum _Gesture {
+     ONE_FINGER_HOVER,
+     TWO_FINGERS_HOVER,
+     THREE_FINGERS_HOVER,
+     ONE_FINGER_FLICK_LEFT,
+     ONE_FINGER_FLICK_RIGHT,
+     ONE_FINGER_FLICK_UP,
+     ONE_FINGER_FLICK_DOWN,
+     TWO_FINGERS_FLICK_LEFT,
+     TWO_FINGERS_FLICK_RIGHT,
+     TWO_FINGERS_FLICK_UP,
+     TWO_FINGERS_FLICK_DOWN,
+     THREE_FINGERS_FLICK_LEFT,
+     THREE_FINGERS_FLICK_RIGHT,
+     THREE_FINGERS_FLICK_UP,
+     THREE_FINGERS_FLICK_DOWN,
+     ONE_FINGER_SINGLE_TAP,
+     ONE_FINGER_DOUBLE_TAP,
+     ONE_FINGER_TRIPLE_TAP,
+     TWO_FINGERS_SINGLE_TAP,
+     TWO_FINGERS_DOUBLE_TAP,
+     TWO_FINGERS_TRIPLE_TAP,
+     THREE_FINGERS_SINGLE_TAP,
+     THREE_FINGERS_DOUBLE_TAP,
+     THREE_FINGERS_TRIPLE_TAP,
+     ONE_FINGER_FLICK_LEFT_RETURN,
+     ONE_FINGER_FLICK_RIGHT_RETURN,
+     ONE_FINGER_FLICK_UP_RETURN,
+     ONE_FINGER_FLICK_DOWN_RETURN,
+     TWO_FINGERS_FLICK_LEFT_RETURN,
+     TWO_FINGERS_FLICK_RIGHT_RETURN,
+     TWO_FINGERS_FLICK_UP_RETURN,
+     TWO_FINGERS_FLICK_DOWN_RETURN,
+     THREE_FINGERS_FLICK_LEFT_RETURN,
+     THREE_FINGERS_FLICK_RIGHT_RETURN,
+     THREE_FINGERS_FLICK_UP_RETURN,
+     THREE_FINGERS_FLICK_DOWN_RETURN,
+     GESTURES_COUNT,
+};
+typedef enum _Gesture Gesture;
+
+typedef struct {
+     Gesture type;         // Type of recognized gesture
+     int x_beg, x_end;     // (x,y) coordinates when gesture begin (screen coords)
+     int y_beg, y_end;     // (x,y) coordinates when gesture ends (screen coords)
+     pid_t pid;            // pid of process on which gesture took place.
+     int state;            // 0 - begin, 1 - ongoing, 2 - ended
+} Gesture_Info;
+
+/**
+ * @brief Initialize gesture navigation profile.
+ *
+ * @return EINA_TRUE is initialization was successfull, EINA_FALSE otherwise
+ */
+Eina_Bool screen_reader_gestures_init(void);
+
+/**
+ * @brief Shutdown gesture navigation profile.
+ */
+void screen_reader_gestures_shutdown(void);
+
+Eina_Bool screen_reader_gesture_x_grab_touch_devices(Ecore_X_Window win);
+
+typedef void (*GestureCB)(void *data, Gesture_Info *info);
+
+/**
+ * @brief Registers callback on gestures
+ */
+void screen_reader_gestures_tracker_register(GestureCB cb, void *data);
+
+#endif
diff --git a/src/gesture_tracker.c b/src/gesture_tracker.c
deleted file mode 100644 (file)
index 411020d..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-#include <string.h>
-#include "gesture_tracker.h"
-#include "logger.h"
-
-#define BUS "com.samsung.EModule"
-#define INTERFACE "com.samsung.GestureNavigation"
-#define PATH "/com/samsung/GestureNavigation"
-#define SIGNAL "GestureDetected"
-
-static Eldbus_Connection *a11y_conn;
-static Eldbus_Object *object;
-static Eldbus_Proxy *man;
-static Gesture_Tracker_Cb user_cb;
-static void *user_data;
-
-static Gesture gesture_name_to_enum (const char *gesture_name)
-{
-  if(!gesture_name)
-     return GESTURES_COUNT;
-  DEBUG("Dbus incoming gesture: %s", gesture_name);
-  if(!strcmp("OneFingerHover", gesture_name))
-     return ONE_FINGER_HOVER;
-
-  if(!strcmp("TwoFingersHover", gesture_name))
-     return TWO_FINGERS_HOVER;
-
-  if(!strcmp("OneFingerFlickLeft", gesture_name))
-     return ONE_FINGER_FLICK_LEFT;
-
-  if(!strcmp("OneFingerFlickRight", gesture_name))
-     return ONE_FINGER_FLICK_RIGHT;
-
-  if(!strcmp("OneFingerFlickUp", gesture_name))
-     return ONE_FINGER_FLICK_UP;
-
-  if(!strcmp("OneFingerFlickDown", gesture_name))
-     return ONE_FINGER_FLICK_DOWN;
-
- if(!strcmp("ThreeFingersFlickLeft", gesture_name))
-     return THREE_FINGERS_FLICK_LEFT;
-
- if(!strcmp("ThreeFingersFlickRight", gesture_name))
-     return THREE_FINGERS_FLICK_RIGHT;
-
- if(!strcmp("ThreeFingersFlickUp", gesture_name))
-     return THREE_FINGERS_FLICK_UP;
-
- if(!strcmp("ThreeFingersFlickDown", gesture_name))
-     return THREE_FINGERS_FLICK_DOWN;
-
- if(!strcmp("OneFingerSingleTap", gesture_name))
-     return ONE_FINGER_SINGLE_TAP;
-
- if(!strcmp("OneFingerDoubleTap", gesture_name))
-     return ONE_FINGER_DOUBLE_TAP;
-
- if(!strcmp("TwoFingersFlickLeft", gesture_name))
-    return TWO_FINGERS_FLICK_LEFT;
-
- if(!strcmp("TwoFingersFlickRight", gesture_name))
-    return TWO_FINGERS_FLICK_RIGHT;
-
- if(!strcmp("TwoFingersFlickUp", gesture_name))
-    return TWO_FINGERS_FLICK_UP;
-
- if(!strcmp("TwoFingersFlickDown", gesture_name))
-     return TWO_FINGERS_FLICK_DOWN;
-
- return GESTURES_COUNT;
-}
-
-static void on_gesture_detected(void *context EINA_UNUSED, const Eldbus_Message *msg)
-{
-    DEBUG("START");
-    const char *gesture_name;
-    int x_s, y_s, x_e, y_e, state;
-
-    if(!eldbus_message_arguments_get(msg, "siiiiu", &gesture_name, &x_s, &y_s, &x_e, &y_e, &state))
-        ERROR("error geting arguments on_gesture_detected");
-
-    Gesture_Info g;
-    g.type = gesture_name_to_enum(gesture_name);
-    g.x_begin = x_s;
-    g.y_begin = y_s;
-    g.x_end = x_e;
-    g.y_end = y_e;
-    g.state = state;
-
-    if(user_cb)
-        user_cb(user_data, &g);
-}
-
-void gesture_tracker_init(Eldbus_Connection *conn)
-{
-    DEBUG("START");
-    a11y_conn = conn;
-    eldbus_connection_ref(conn);
-    object = eldbus_object_get(conn, BUS, PATH);
-    man = eldbus_proxy_get(object, INTERFACE);
-    eldbus_proxy_signal_handler_add(man, SIGNAL, on_gesture_detected, NULL);
-}
-
-void gesture_tracker_register(Gesture_Tracker_Cb cb, void *data)
-{
-   user_cb = cb;
-   user_data = data;
-}
-
-void gesture_tracker_shutdown(void)
-{
-    eldbus_proxy_unref(man);
-    eldbus_object_unref(object);
-    eldbus_connection_unref(a11y_conn);
-}
index e510888..1fe611d 100644 (file)
@@ -3,55 +3,30 @@
 #include <eldbus-1/Eldbus.h>
 #include "navigator.h"
 #include "window_tracker.h"
-#include "gesture_tracker.h"
 #include "logger.h"
 #include "screen_reader.h"
+#include "screen_reader_gestures.h"
 
-#define A11Y_BUS "org.a11y.Bus"
-#define A11Y_INTERFACE "org.a11y.Bus"
-#define A11Y_PATH "/org/a11y/bus"
-#define A11Y_GET_ADDRESS "GetAddress"
-
-Eldbus_Connection *a11y_conn;
-
-static void _init_modules(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
-{
-   DEBUG("START");
-   const char *a11y_bus_address = NULL;
-
-   logger_init();
-   if(!eldbus_message_arguments_get(msg, "s", &a11y_bus_address))
-      ERROR("error geting arguments _init_modules");
-   Eldbus_Connection *a11y_conn = eldbus_address_connection_get(a11y_bus_address);
-
-   gesture_tracker_init(a11y_conn);
-   navigator_init();
-}
 
 static int app_create(void *data)
 {
-    DEBUG("START");
     eldbus_init();
     elm_init(0, NULL);
 
-    Eldbus_Connection *session_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION);
-    Eldbus_Object *a11y_obj = eldbus_object_get(session_conn, A11Y_BUS, A11Y_PATH);
-    Eldbus_Proxy *manager = eldbus_proxy_get(a11y_obj, A11Y_INTERFACE);
-    eldbus_proxy_call(manager, A11Y_GET_ADDRESS, _init_modules, data, -1, "");
-
+    logger_init();
+    screen_reader_gestures_init();
     screen_reader_create_service(data);
+    navigator_init();
 
     return 0;
 }
 
 static int app_terminate(void *data)
 {
-    DEBUG("START");
     screen_reader_terminate_service(data);
 
-    eldbus_connection_unref(a11y_conn);
-    gesture_tracker_shutdown();
     navigator_shutdown();
+    screen_reader_gestures_shutdown();
     eldbus_shutdown();
     logger_shutdown();
     return 0;
@@ -59,7 +34,6 @@ static int app_terminate(void *data)
 
 int main(int argc, char **argv)
 {
-    DEBUG("Screen Reader Main Function Start");
     unsetenv("ELM_ATSPI_MODE");
 
     struct appcore_ops ops =
index 62b1589..9d332fb 100644 (file)
@@ -4,7 +4,6 @@
 #include <atspi/atspi.h>
 #include "logger.h"
 #include "navigator.h"
-#include "gesture_tracker.h"
 #include "window_tracker.h"
 #include "keyboard_tracker.h"
 #include "pivot_chooser.h"
@@ -16,6 +15,7 @@
 #include "screen_reader_system.h"
 #include "screen_reader_haptic.h"
 #include "screen_reader_tts.h"
+#include "screen_reader_gestures.h"
 
 #define QUICKPANEL_DOWN TRUE
 #define QUICKPANEL_UP FALSE
@@ -37,7 +37,6 @@ typedef struct
   int x,y;
 } last_focus_t;
 
-static last_focus_t last_focus = {-1,-1};
 static AtspiAccessible *current_obj;
 static AtspiAccessible *top_window;
 //static AtspiScrollable *scrolled_obj;
@@ -490,6 +489,7 @@ static AtspiAccessible *get_nearest_widget(AtspiAccessible* app_obj, gint x_cord
 }
 #endif
 
+#if 0
 static void _focus_widget(Gesture_Info *info)
 {
     DEBUG("START");
@@ -521,6 +521,7 @@ static void _focus_widget(Gesture_Info *info)
             info->x_begin, info->y_begin);
     DEBUG("END");
 }
+#endif
 
 static void _focus_next(void)
 {
@@ -986,7 +987,7 @@ static void on_gesture_detected(void *data, Gesture_Info *info)
    switch(info->type)
    {
       case ONE_FINGER_HOVER:
-          _focus_widget(info);
+          //_focus_widget(info);
           break;
       case TWO_FINGERS_HOVER:
 //          _widget_scroll(info);
@@ -1004,7 +1005,7 @@ static void on_gesture_detected(void *data, Gesture_Info *info)
           _value_dec_widget();
           break;
       case ONE_FINGER_SINGLE_TAP:
-          _focus_widget(info);
+          //_focus_widget(info);
           break;
       case ONE_FINGER_DOUBLE_TAP:
           _activate_widget();
@@ -1091,8 +1092,7 @@ void kb_tracker (void *data, Key k)
 void navigator_init(void)
 {
    DEBUG("START");
-   // register on gesture_getected
-   gesture_tracker_register(on_gesture_detected, NULL);
+   screen_reader_gestures_tracker_register(on_gesture_detected, NULL);
    // register on active_window
    window_tracker_init();
    window_tracker_register(on_window_activate, NULL);
index ebb3daf..b7f7a86 100644 (file)
@@ -38,11 +38,6 @@ Service_Data *get_pointer_to_service_data_struct()
 
 int screen_reader_create_service(void *data)
 {
-       int vconf_ret = vconf_set_bool("db/setting/accessibility/atspi", EINA_TRUE);
-       if(vconf_ret != 0)
-       {
-               ERROR("Gesture fail layer set to false with error:%d", vconf_ret);
-       }
        Service_Data *service_data = data;
 
        vconf_init(service_data);
@@ -65,15 +60,6 @@ int screen_reader_terminate_service(void *data)
        DEBUG("Service Terminate Callback \n");
 
        Service_Data *service_data = data;
-       vconf_ret = vconf_set_bool("db/setting/accessibility/atspi", EINA_FALSE);
-       if(vconf_ret == 0)
-       {
-               DEBUG("Gesture layer set to false");
-       }
-       else
-       {
-               DEBUG("Gesture fail layer set to false");
-       }
 
        tts_stop(service_data->tts);
        tts_unprepare(service_data->tts);
diff --git a/src/screen_reader_gestures.c b/src/screen_reader_gestures.c
new file mode 100644 (file)
index 0000000..5b837fb
--- /dev/null
@@ -0,0 +1,1128 @@
+#include "screen_reader_gestures.h"
+#include "logger.h"
+
+#include <Ecore.h>
+#include <Ecore_Input.h>
+#include <Ecore_X.h>
+
+static GestureCB _global_cb;
+static void *_global_data;
+
+struct _Gestures_Config
+{
+   // minimal required length of flick gesture (in pixels)
+   int one_finger_flick_min_length;
+   // maximal time of gesture
+   int one_finger_flick_max_time;
+   // timeout period to activate hover gesture (first longpress timeout)
+   double one_finger_hover_longpress_timeout;
+   // tap timeout - maximal ammount of time allowed between seqiential taps
+   double two_fingers_hover_longpress_timeout;
+   // tap timeout - maximal ammount of time allowed between seqiential taps
+   double one_finger_tap_timeout;
+   // tap radius(in pixels)
+   int one_finger_tap_radius;
+};
+typedef struct _Gestures_Config Gestures_Config;
+
+typedef enum {
+     FLICK_DIRECTION_UNDEFINED,
+     FLICK_DIRECTION_DOWN,
+     FLICK_DIRECTION_UP,
+     FLICK_DIRECTION_LEFT,
+     FLICK_DIRECTION_RIGHT,
+     FLICK_DIRECTION_DOWN_RETURN,
+     FLICK_DIRECTION_UP_RETURN,
+     FLICK_DIRECTION_LEFT_RETURN,
+     FLICK_DIRECTION_RIGHT_RETURN,
+} flick_direction_e;
+
+typedef enum {
+     GESTURE_NOT_STARTED = 0, // Gesture is ready to start
+     GESTURE_ONGOING,         // Gesture in progress.
+     GESTURE_FINISHED,        // Gesture finished - should be emited
+     GESTURE_ABORTED          // Gesture aborted
+} gesture_state_e;
+
+typedef enum {
+      ONE_FINGER_GESTURE = 1,
+      TWO_FINGERS_GESTURE,
+      THREE_FINGERS_GESTURE
+} gesture_type_e;
+
+struct _Cover
+{
+   Ecore_X_Window  win;  /**< Input window covering given zone */
+   unsigned int    n_taps; /**< Number of fingers touching screen */
+   unsigned int    event_time;
+
+   struct {
+        gesture_state_e state;     // current state of gesture
+        unsigned int timestamp[3]; // time of gesture;
+        int finger[3];             // finger number which initiates gesture
+        int x_org[3], y_org[3];    // coorinates of finger down event
+        int x_end[3], y_end[3];    // coorinates of finger up event
+        flick_direction_e dir;     // direction of flick
+        int n_fingers;             // number of fingers in gesture
+        int n_fingers_left;        // number of fingers in gesture
+                                   //         still touching screen
+        Eina_Bool finger_out[3];   // finger is out of the finger boundary
+        Eina_Bool return_flick[3];
+   } flick_gesture;
+
+   struct {
+        gesture_state_e state;   // currest gesture state
+        int x[2], y[2];
+        int n_fingers;
+        int finger[2];
+        unsigned int timestamp; // time of gesture;
+        unsigned int last_emission_time; // last time of gesture emission
+        Ecore_Timer *timer;
+        Eina_Bool longpressed;
+   } hover_gesture;
+
+   struct {
+        Eina_Bool started; // indicates if taps recognition process has started
+        Eina_Bool pressed; // indicates if finger is down
+        int n_taps;        // number of taps captures in sequence
+        int finger[3];        // device id of finget
+        Ecore_Timer *timer;  // sequence expiration timer
+        int x_org[3], y_org[3];    // coordinates of first tap
+        gesture_type_e tap_type;
+   } tap_gesture_data;
+};
+typedef struct _Cover Cover;
+
+Gestures_Config *_e_mod_config;
+static Ecore_X_Window scrolled_win;
+static int rx, ry;
+static Eina_List *handlers;
+static Cover *cov;
+
+static void _hover_event_emit(Cover *cov, int state);
+
+static void _event_emit(Gesture g, int x, int y, int x_e, int y_e, int state)
+{
+   Gesture_Info *info = calloc(sizeof(Gesture_Info), 1);
+   EINA_SAFETY_ON_NULL_RETURN(info);
+
+   info->type = g;
+   info->x_beg = x;
+   info->x_end = x_e;
+   info->y_beg = y;
+   info->y_end = y_e;
+   info->state = state;
+
+   if (_global_cb)
+     _global_cb(_global_data, info);
+   free(info);
+}
+
+static void
+_flick_gesture_mouse_down(Ecore_Event_Mouse_Button *ev, Cover *cov)
+{
+   if (cov->flick_gesture.state == GESTURE_NOT_STARTED)
+     {
+        cov->flick_gesture.state = GESTURE_ONGOING;
+        cov->flick_gesture.finger[0] = ev->multi.device;
+        cov->flick_gesture.x_org[0] = ev->root.x;
+        cov->flick_gesture.y_org[0] = ev->root.y;
+        cov->flick_gesture.timestamp[0] = ev->timestamp;
+        cov->flick_gesture.n_fingers = 1;
+        cov->flick_gesture.n_fingers_left = 1;
+        cov->flick_gesture.dir = FLICK_DIRECTION_UNDEFINED;
+        cov->flick_gesture.finger_out[0] = EINA_FALSE;
+        cov->flick_gesture.return_flick[0] = EINA_FALSE;
+     }
+   else if (cov->flick_gesture.state == GESTURE_ONGOING)
+     {
+        // abort gesture if too many fingers touched screen
+        if ((cov->n_taps > 3) || (cov->flick_gesture.n_fingers > 3))
+          {
+             cov->flick_gesture.state = GESTURE_ABORTED;
+             return;
+          }
+
+        cov->flick_gesture.x_org[cov->flick_gesture.n_fingers] = ev->root.x;
+        cov->flick_gesture.y_org[cov->flick_gesture.n_fingers] = ev->root.y;
+        cov->flick_gesture.timestamp[cov->flick_gesture.n_fingers] = ev->timestamp;
+        cov->flick_gesture.finger[cov->flick_gesture.n_fingers] = ev->multi.device;
+        cov->flick_gesture.n_fingers++;
+        cov->flick_gesture.n_fingers_left++;
+        cov->flick_gesture.finger_out[cov->flick_gesture.n_fingers] = EINA_FALSE;
+        cov->flick_gesture.return_flick[cov->flick_gesture.n_fingers] = EINA_FALSE;
+     }
+}
+
+static Eina_Bool
+_flick_gesture_time_check(unsigned int event_time, unsigned int gesture_time)
+{
+   DEBUG("Flick time: %d", event_time - gesture_time);
+   if ((event_time - gesture_time) < _e_mod_config->one_finger_flick_max_time * 2) //Double time because of the possible of return flick
+     return EINA_TRUE;
+   else
+     return EINA_FALSE;
+}
+
+static Eina_Bool
+_flick_gesture_length_check(int x, int y, int x_org, int y_org)
+{
+   int dx = x - x_org;
+   int dy = y - y_org;
+
+   if ((dx * dx + dy * dy) > (_e_mod_config->one_finger_flick_min_length *
+                            _e_mod_config->one_finger_flick_min_length))
+     return EINA_TRUE;
+   else
+     return EINA_FALSE;
+}
+
+static flick_direction_e
+_flick_gesture_direction_get(int x, int y, int x_org, int y_org)
+{
+   int dx = x - x_org;
+   int dy = y - y_org;
+
+   if ((dy < 0) && (abs(dx) < -dy))
+     return FLICK_DIRECTION_UP;
+   if ((dy > 0) && (abs(dx) < dy))
+     return FLICK_DIRECTION_DOWN;
+   if ((dx > 0) && (dx > abs(dy)))
+     return FLICK_DIRECTION_RIGHT;
+   if ((dx < 0) && (-dx > abs(dy)))
+     return FLICK_DIRECTION_LEFT;
+
+   return FLICK_DIRECTION_UNDEFINED;
+}
+
+static void
+_flick_event_emit(Cover *cov)
+{
+   int ax, ay, axe, aye, i, type = -1;
+   ax = ay = axe = aye = 0;
+
+   for (i = 0; i < cov->flick_gesture.n_fingers; i++)
+     {
+        ax += cov->flick_gesture.x_org[i];
+        ay += cov->flick_gesture.y_org[i];
+        axe += cov->flick_gesture.x_end[i];
+        aye += cov->flick_gesture.y_end[i];
+     }
+
+   ax /= cov->flick_gesture.n_fingers;
+   ay /= cov->flick_gesture.n_fingers;
+   axe /= cov->flick_gesture.n_fingers;
+   aye /= cov->flick_gesture.n_fingers;
+
+   if (cov->flick_gesture.dir == FLICK_DIRECTION_LEFT)
+     {
+        if (cov->flick_gesture.n_fingers == 1)
+          type = ONE_FINGER_FLICK_LEFT;
+        if (cov->flick_gesture.n_fingers == 2)
+          type = TWO_FINGERS_FLICK_LEFT;
+        if (cov->flick_gesture.n_fingers == 3)
+          type = THREE_FINGERS_FLICK_LEFT;
+     }
+   else if (cov->flick_gesture.dir == FLICK_DIRECTION_RIGHT)
+     {
+        if (cov->flick_gesture.n_fingers == 1)
+          type = ONE_FINGER_FLICK_RIGHT;
+        if (cov->flick_gesture.n_fingers == 2)
+          type = TWO_FINGERS_FLICK_RIGHT;
+        if (cov->flick_gesture.n_fingers == 3)
+          type = THREE_FINGERS_FLICK_RIGHT;
+     }
+   else if (cov->flick_gesture.dir == FLICK_DIRECTION_UP)
+     {
+        if (cov->flick_gesture.n_fingers == 1)
+          type = ONE_FINGER_FLICK_UP;
+        if (cov->flick_gesture.n_fingers == 2)
+          type = TWO_FINGERS_FLICK_UP;
+        if (cov->flick_gesture.n_fingers == 3)
+          type = THREE_FINGERS_FLICK_UP;
+     }
+   else if (cov->flick_gesture.dir == FLICK_DIRECTION_DOWN)
+     {
+        if (cov->flick_gesture.n_fingers == 1)
+          type = ONE_FINGER_FLICK_DOWN;
+        if (cov->flick_gesture.n_fingers == 2)
+          type = TWO_FINGERS_FLICK_DOWN;
+        if (cov->flick_gesture.n_fingers == 3)
+          type = THREE_FINGERS_FLICK_DOWN;
+     }
+   else if (cov->flick_gesture.dir == FLICK_DIRECTION_DOWN_RETURN)
+     {
+        if (cov->flick_gesture.n_fingers == 1)
+          type = ONE_FINGER_FLICK_DOWN_RETURN;
+        if (cov->flick_gesture.n_fingers == 2)
+          type = TWO_FINGERS_FLICK_DOWN_RETURN;
+        if (cov->flick_gesture.n_fingers == 3)
+          type = THREE_FINGERS_FLICK_DOWN_RETURN;
+     }
+   else if (cov->flick_gesture.dir == FLICK_DIRECTION_UP_RETURN)
+     {
+        if (cov->flick_gesture.n_fingers == 1)
+          type = ONE_FINGER_FLICK_UP_RETURN;
+        if (cov->flick_gesture.n_fingers == 2)
+          type = TWO_FINGERS_FLICK_UP_RETURN;
+        if (cov->flick_gesture.n_fingers == 3)
+          type = THREE_FINGERS_FLICK_UP_RETURN;
+     }
+   else if (cov->flick_gesture.dir == FLICK_DIRECTION_LEFT_RETURN)
+     {
+        if (cov->flick_gesture.n_fingers == 1)
+          type = ONE_FINGER_FLICK_LEFT_RETURN;
+        if (cov->flick_gesture.n_fingers == 2)
+          type = TWO_FINGERS_FLICK_LEFT_RETURN;
+        if (cov->flick_gesture.n_fingers == 3)
+          type = THREE_FINGERS_FLICK_LEFT_RETURN;
+     }
+   else if (cov->flick_gesture.dir == FLICK_DIRECTION_RIGHT_RETURN)
+     {
+        if (cov->flick_gesture.n_fingers == 1)
+          type = ONE_FINGER_FLICK_RIGHT_RETURN;
+        if (cov->flick_gesture.n_fingers == 2)
+          type = TWO_FINGERS_FLICK_RIGHT_RETURN;
+        if (cov->flick_gesture.n_fingers == 3)
+          type = THREE_FINGERS_FLICK_RIGHT_RETURN;
+     }
+   DEBUG("FLICK GESTURE: N: %d F: %d", cov->flick_gesture.n_fingers, cov->flick_gesture.dir);
+   _event_emit(type, ax, ay, axe, aye, 2);
+}
+
+static void
+_flick_gesture_mouse_up(Ecore_Event_Mouse_Button *ev, Cover *cov)
+{
+   if (cov->flick_gesture.state == GESTURE_ONGOING)
+     {
+        int i;
+        // check if fingers match
+        for (i = 0; i < cov->flick_gesture.n_fingers; i++)
+          {
+             if (cov->flick_gesture.finger[i] == ev->multi.device)
+               break;
+          }
+        if (i == cov->flick_gesture.n_fingers)
+          {
+             DEBUG("Finger id not recognized. Gesture aborted.");
+             cov->flick_gesture.state = GESTURE_ABORTED;
+             goto end;
+          }
+
+        // check if flick for given finger is valid
+        if (!_flick_gesture_time_check(ev->timestamp,
+                                       cov->flick_gesture.timestamp[i]))
+          {
+             DEBUG("finger flick gesture timeout expired. Gesture aborted.");
+             cov->flick_gesture.state = GESTURE_ABORTED;
+             goto end;
+          }
+
+        // check minimal flick length
+        if (!_flick_gesture_length_check(ev->root.x, ev->root.y,
+                                        cov->flick_gesture.x_org[i],
+                                        cov->flick_gesture.y_org[i]))
+          {
+             if (!cov->flick_gesture.finger_out[i])
+               {
+                  DEBUG("Minimal gesture length not reached and no return flick. Gesture aborted.");
+                  cov->flick_gesture.state = GESTURE_ABORTED;
+                  goto end;
+               }
+             cov->flick_gesture.return_flick[i] = EINA_TRUE;
+          }
+
+        flick_direction_e s = cov->flick_gesture.return_flick[i] ?
+                                       cov->flick_gesture.dir :
+                                       _flick_gesture_direction_get(ev->root.x, ev->root.y,
+                                                                     cov->flick_gesture.x_org[i],
+                                                                     cov->flick_gesture.y_org[i]);
+
+        cov->flick_gesture.n_fingers_left--;
+
+        if ((cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED ||
+               cov->flick_gesture.dir > FLICK_DIRECTION_RIGHT)
+               && cov->flick_gesture.return_flick[i] == EINA_FALSE)
+         {
+            DEBUG("Flick gesture");
+            cov->flick_gesture.dir = s;
+         }
+
+        // gesture is valid only if all flicks are in same direction
+        if (cov->flick_gesture.dir != s)
+          {
+             DEBUG("Flick in different direction. Gesture aborted.");
+             cov->flick_gesture.state = GESTURE_ABORTED;
+             goto end;
+          }
+
+        cov->flick_gesture.x_end[i] = ev->root.x;
+        cov->flick_gesture.y_end[i] = ev->root.y;
+
+        if (!cov->flick_gesture.n_fingers_left)
+          {
+             _flick_event_emit(cov);
+             cov->flick_gesture.state = GESTURE_NOT_STARTED;
+          }
+     }
+
+end:
+   // if no finger is touching a screen, gesture will be reseted.
+   if ((cov->flick_gesture.state == GESTURE_ABORTED) && (cov->n_taps == 0))
+     {
+        DEBUG("Restet flick gesture");
+        cov->flick_gesture.state = GESTURE_NOT_STARTED;
+     }
+}
+
+static void
+_flick_gesture_mouse_move(Ecore_Event_Mouse_Move *ev, Cover *cov)
+{
+   if (cov->flick_gesture.state == GESTURE_ONGOING)
+      {
+         int i;
+         for(i = 0; i < cov->flick_gesture.n_fingers; ++i)
+            {
+               if (cov->flick_gesture.finger[i] == ev->multi.device)
+               break;
+            }
+         if (i == cov->flick_gesture.n_fingers)
+          {
+             if (cov->flick_gesture.n_fingers >= 3) //that is because of the EFL bug. Mouse move event before mouse down(!)
+               {
+                  ERROR("Finger id not recognized. Gesture aborted.");
+                  cov->flick_gesture.state = GESTURE_ABORTED;
+                  return;
+               }
+          }
+         if(!cov->flick_gesture.finger_out[i])
+            {
+               int dx = ev->root.x - cov->flick_gesture.x_org[i];
+               int dy = ev->root.y - cov->flick_gesture.y_org[i];
+
+               if (dx < 0) dx *= -1;
+              if (dy < 0) dy *= -1;
+
+              if (dx > _e_mod_config->one_finger_flick_min_length)
+                  {
+                     cov->flick_gesture.finger_out[i] = EINA_TRUE;
+                     if (ev->root.x > cov->flick_gesture.x_org[i])
+                        {
+                           if (cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED ||
+                                 cov->flick_gesture.dir == FLICK_DIRECTION_RIGHT_RETURN)
+                              {
+                                 cov->flick_gesture.dir = FLICK_DIRECTION_RIGHT_RETURN;
+                              }
+                           else
+                              {
+                                 ERROR("Invalid direction, abort");
+                                 cov->flick_gesture.state = GESTURE_ABORTED;
+                              }
+                        }
+                     else
+                        {
+                           if (cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED ||
+                                 cov->flick_gesture.dir == FLICK_DIRECTION_LEFT_RETURN)
+                              {
+                                 cov->flick_gesture.dir = FLICK_DIRECTION_LEFT_RETURN;
+                              }
+                           else
+                              {
+                                 ERROR("Invalid direction, abort");
+                                 cov->flick_gesture.state = GESTURE_ABORTED;
+                              }
+                        }
+                     return;
+                  }
+
+               else if (dy > _e_mod_config->one_finger_flick_min_length)
+                  {
+                     cov->flick_gesture.finger_out[i] = EINA_TRUE;
+                     if (ev->root.y > cov->flick_gesture.y_org[i])
+                        {
+                           if (cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED ||
+                                 cov->flick_gesture.dir == FLICK_DIRECTION_DOWN_RETURN)
+                              {
+                                 cov->flick_gesture.dir = FLICK_DIRECTION_DOWN_RETURN;
+                              }
+                           else
+                              {
+                                 ERROR("Invalid direction, abort");
+                                 cov->flick_gesture.state = GESTURE_ABORTED;
+                              }
+                        }
+                     else
+                        {
+                           if (cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED ||
+                                 cov->flick_gesture.dir == FLICK_DIRECTION_UP_RETURN)
+                              {
+                                 cov->flick_gesture.dir = FLICK_DIRECTION_UP_RETURN;
+                              }
+                           else
+                              {
+                                 ERROR("Invalid direction, abort");
+                                 cov->flick_gesture.state = GESTURE_ABORTED;
+                              }
+                        }
+                     return;
+                  }
+            }
+      }
+   return;
+}
+
+static Eina_Bool
+_on_hover_timeout(void *data)
+{
+   Cover *cov = data;
+   DEBUG("Hover timer expierd");
+
+   cov->hover_gesture.longpressed = EINA_TRUE;
+   cov->hover_gesture.timer = NULL;
+
+   if (cov->hover_gesture.last_emission_time == -1)
+     {
+        _hover_event_emit(cov, 0);
+        cov->hover_gesture.last_emission_time = cov->event_time;
+     }
+   return EINA_FALSE;
+}
+
+static void
+_hover_gesture_timer_reset(Cover *cov, double time)
+{
+   DEBUG("Hover timer reset");
+   cov->hover_gesture.longpressed = EINA_FALSE;
+   if (cov->hover_gesture.timer)
+     {
+        ecore_timer_reset(cov->hover_gesture.timer);
+        return;
+     }
+   cov->hover_gesture.timer = ecore_timer_add(time, _on_hover_timeout, cov);
+}
+
+static void
+_hover_gesture_mouse_down(Ecore_Event_Mouse_Button *ev, Cover *cov)
+{
+   if (cov->hover_gesture.state == GESTURE_NOT_STARTED &&
+       cov->n_taps == 1)
+     {
+        cov->hover_gesture.state = GESTURE_ONGOING;
+        cov->hover_gesture.timestamp = ev->timestamp;
+        cov->hover_gesture.last_emission_time = -1;
+        cov->hover_gesture.x[0] = ev->root.x;
+        cov->hover_gesture.y[0] = ev->root.y;
+        cov->hover_gesture.finger[0] = ev->multi.device;
+        cov->hover_gesture.n_fingers = 1;
+        _hover_gesture_timer_reset(cov, _e_mod_config->one_finger_hover_longpress_timeout);
+     }
+   if (cov->hover_gesture.state == GESTURE_ONGOING &&
+       cov->n_taps == 2)
+     {
+        if (cov->hover_gesture.longpressed)
+          {
+             _hover_event_emit(cov, 2);
+             goto abort;
+          }
+        cov->hover_gesture.timestamp = -1;
+        cov->hover_gesture.last_emission_time = -1;
+        cov->hover_gesture.x[1] = ev->root.x;
+        cov->hover_gesture.y[1] = ev->root.y;
+        cov->hover_gesture.finger[1] = ev->multi.device;
+        cov->hover_gesture.n_fingers = 2;
+        _hover_gesture_timer_reset(cov, _e_mod_config->one_finger_hover_longpress_timeout);
+     }
+   // abort gesture if more then 2 fingers touched screen
+   if ((cov->hover_gesture.state == GESTURE_ONGOING) &&
+       cov->n_taps > 2)
+     {
+        DEBUG("More then 2 finged. Abort hover gesture");
+        _hover_event_emit(cov, 2);
+        goto abort;
+     }
+   return;
+
+abort:
+   cov->hover_gesture.state = GESTURE_ABORTED;
+   if (cov->hover_gesture.timer)
+     ecore_timer_del(cov->hover_gesture.timer);
+   cov->hover_gesture.timer = NULL;
+}
+
+static void
+_hover_gesture_mouse_up(Ecore_Event_Mouse_Button *ev, Cover *cov)
+{
+   int i;
+   if (cov->hover_gesture.state == GESTURE_ONGOING)
+     {
+
+        for (i = 0; i < cov->hover_gesture.n_fingers; i++)
+          {
+             if (cov->hover_gesture.finger[i] == ev->multi.device)
+              break;
+          }
+        if (i == cov->hover_gesture.n_fingers)
+          {
+             DEBUG("Invalid finger id: %d", ev->multi.device);
+             return;
+          }
+        else
+          {
+             cov->hover_gesture.state = GESTURE_ABORTED;
+             if (cov->hover_gesture.timer)
+               ecore_timer_del(cov->hover_gesture.timer);
+             cov->hover_gesture.timer = NULL;
+             // aditionally emit event to complete sequence
+             if (cov->hover_gesture.longpressed)
+                _hover_event_emit(cov, 2);
+          }
+     }
+   // reset gesture only if user released all his fingers
+   if (cov->n_taps == 0)
+     cov->hover_gesture.state = GESTURE_NOT_STARTED;
+}
+
+static void _get_root_coords(Ecore_X_Window win, int *x, int *y)
+{
+   Ecore_X_Window root = ecore_x_window_root_first_get();
+   Ecore_X_Window parent = ecore_x_window_parent_get(win);
+   int wx, wy;
+
+   if (x) *x = 0;
+   if (y) *y = 0;
+
+   while (parent && (root != parent))
+     {
+        ecore_x_window_geometry_get(parent, &wx, &wy, NULL, NULL);
+        if (x) *x += wx;
+        if (y) *y += wy;
+        parent = ecore_x_window_parent_get(parent);
+     }
+}
+
+static void
+_start_scroll(int x, int y)
+{
+   Ecore_X_Window under = ecore_x_window_at_xy_get(x, y);
+   _get_root_coords(under, &rx, &ry);
+   DEBUG("Send down: %d %d", x - rx, y - ry);
+   ecore_x_mouse_in_send(under, x - rx, y - ry);
+   ecore_x_window_focus(under);
+   ecore_x_mouse_down_send(under, x - rx, y - ry, 1);
+   scrolled_win = under;
+}
+
+static void
+_continue_scroll(int x, int y)
+{
+   DEBUG("Send move: %d %d", x - rx, y - ry);
+   ecore_x_mouse_move_send(scrolled_win, x - rx, y - ry);
+}
+
+static void
+_end_scroll(int x, int y)
+{
+   DEBUG("Send up: %d %d", x - rx, y - ry);
+   ecore_x_mouse_up_send(scrolled_win, x - rx, y - ry, 1);
+   ecore_x_mouse_out_send(scrolled_win, x - rx, y - ry);
+}
+
+static void
+_hover_event_emit(Cover *cov, int state)
+{
+   int ax = 0, ay = 0, j;
+   for (j = 0; j < cov->hover_gesture.n_fingers; j++)
+     {
+        ax += cov->hover_gesture.x[j];
+        ay += cov->hover_gesture.y[j];
+     }
+
+   ax /= cov->hover_gesture.n_fingers;
+   ay /= cov->hover_gesture.n_fingers;
+
+   switch (cov->hover_gesture.n_fingers)
+     {
+      case 1:
+         INFO("ON FINGER HOVER");
+         _event_emit(ONE_FINGER_HOVER, ax, ay, ax, ay, state);
+         break;
+      case 2:
+         INFO("TWO FINGERS HOVER");
+         _event_emit(TWO_FINGERS_HOVER, ax, ay, ax, ay, state);
+         if (state == 0)
+           _start_scroll(ax, ay);
+         else if (state == 1)
+           _continue_scroll(ax, ay);
+         else if (state == 2)
+           _end_scroll(ax, ay);
+         break;
+      default:
+         break;
+     }
+}
+
+static void
+_hover_gesture_mouse_move(Ecore_Event_Mouse_Move *ev, Cover *cov)
+{
+   if (cov->hover_gesture.state == GESTURE_ONGOING)
+     {
+        // check fingers
+        int i;
+        if (!cov->hover_gesture.longpressed)
+          return;
+
+        for (i = 0; i < cov->hover_gesture.n_fingers; i++)
+          {
+             if (cov->hover_gesture.finger[i] == ev->multi.device)
+               break;
+          }
+        if (i == cov->hover_gesture.n_fingers)
+          {
+             DEBUG("Invalid finger id: %d", ev->multi.device);
+             return;
+          }
+          cov->hover_gesture.x[i] = ev->root.x;
+          cov->hover_gesture.y[i] = ev->root.y;
+          _hover_event_emit(cov, 1);
+      }
+}
+
+static void
+_tap_event_emit(Cover *cov)
+{
+   switch (cov->tap_gesture_data.n_taps)
+     {
+      case 1:
+         if(cov->tap_gesture_data.tap_type == ONE_FINGER_GESTURE)
+            {
+               DEBUG("ONE_FINGER_SINGLE_TAP");
+               _event_emit(ONE_FINGER_SINGLE_TAP,
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     2);
+            }
+         else if(cov->tap_gesture_data.tap_type == TWO_FINGERS_GESTURE)
+            {
+               DEBUG("TWO_FINGERS_SINGLE_TAP");
+               _event_emit(TWO_FINGERS_SINGLE_TAP,
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     cov->tap_gesture_data.x_org[1], cov->tap_gesture_data.y_org[1],
+                     2);
+            }
+         else if(cov->tap_gesture_data.tap_type == THREE_FINGERS_GESTURE)
+            {
+               DEBUG("THREE_FINGERS_SINGLE_TAP");
+               _event_emit(THREE_FINGERS_SINGLE_TAP,
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     cov->tap_gesture_data.x_org[2], cov->tap_gesture_data.y_org[2],
+                     2);
+            }
+         else
+            {
+               ERROR("Unknown tap");
+            }
+         break;
+      case 2:
+         if(cov->tap_gesture_data.tap_type == ONE_FINGER_GESTURE)
+            {
+               DEBUG("ONE_FINGER_DOUBLE_TAP");
+               _event_emit(ONE_FINGER_DOUBLE_TAP,
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     2);
+            }
+         else if(cov->tap_gesture_data.tap_type == TWO_FINGERS_GESTURE)
+            {
+               DEBUG("TWO_FINGERS_DOUBLE_TAP");
+               _event_emit(TWO_FINGERS_DOUBLE_TAP,
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     cov->tap_gesture_data.x_org[1], cov->tap_gesture_data.y_org[1],
+                     2);
+            }
+         else if(cov->tap_gesture_data.tap_type == THREE_FINGERS_GESTURE)
+            {
+               DEBUG("THREE_FINGERS_DOUBLE_TAP");
+               _event_emit(THREE_FINGERS_DOUBLE_TAP,
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     cov->tap_gesture_data.x_org[2], cov->tap_gesture_data.y_org[2],
+                     2);
+            }
+         else
+            {
+               ERROR("Unknown tap");
+            }
+         break;
+      case 3:
+         if(cov->tap_gesture_data.tap_type == ONE_FINGER_GESTURE)
+            {
+               DEBUG("ONE_FINGER_TRIPLE_TAP");
+               _event_emit(ONE_FINGER_TRIPLE_TAP,
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     2);
+            }
+         else if(cov->tap_gesture_data.tap_type == TWO_FINGERS_GESTURE)
+            {
+               DEBUG("TWO_FINGERS_TRIPLE_TAP");
+               _event_emit(TWO_FINGERS_TRIPLE_TAP,
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     cov->tap_gesture_data.x_org[1], cov->tap_gesture_data.y_org[1],
+                     2);
+            }
+         else if(cov->tap_gesture_data.tap_type == THREE_FINGERS_GESTURE)
+            {
+               DEBUG("THREE_FINGERS_TRIPLE_TAP");
+               _event_emit(THREE_FINGERS_TRIPLE_TAP,
+                     cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0],
+                     cov->tap_gesture_data.x_org[2], cov->tap_gesture_data.y_org[2],
+                     2);
+            }
+         else
+            {
+               ERROR("Unknown tap");
+            }
+         break;
+      default:
+         ERROR("Unknown tap");
+         break;
+     }
+}
+
+static Eina_Bool
+_on_tap_timer_expire(void *data)
+{
+   Cover *cov = data;
+   DEBUG("Timer expired");
+
+   if (cov->tap_gesture_data.started && !cov->tap_gesture_data.pressed)
+     _tap_event_emit(cov);
+
+   // finish gesture
+   cov->tap_gesture_data.started = EINA_FALSE;
+   cov->tap_gesture_data.timer = NULL;
+   cov->tap_gesture_data.tap_type = ONE_FINGER_GESTURE;
+   cov->tap_gesture_data.finger[0] = -1;
+   cov->tap_gesture_data.finger[1] = -1;
+   cov->tap_gesture_data.finger[2] = -1;
+
+   return EINA_FALSE;
+}
+
+static int _tap_gesture_finger_check(Cover *cov, int x, int y)
+{
+   int dx = x - cov->tap_gesture_data.x_org[0];
+   int dy = y - cov->tap_gesture_data.y_org[0];
+
+   if (cov->tap_gesture_data.finger[0] != -1 &&
+         (dx * dx + dy * dy < _e_mod_config->one_finger_tap_radius *
+         _e_mod_config->one_finger_tap_radius))
+      {
+         return 0;
+      }
+
+   dx = x - cov->tap_gesture_data.x_org[1];
+   dy = y - cov->tap_gesture_data.y_org[1];
+   if (cov->tap_gesture_data.finger[1] != -1 &&
+            (dx * dx + dy * dy < _e_mod_config->one_finger_tap_radius *
+            _e_mod_config->one_finger_tap_radius))
+      {
+         return 1;
+      }
+
+   dx = x - cov->tap_gesture_data.x_org[2];
+   dy = y - cov->tap_gesture_data.y_org[2];
+   if (cov->tap_gesture_data.finger[2] != -1 &&
+            (dx * dx + dy * dy < _e_mod_config->one_finger_tap_radius *
+            _e_mod_config->one_finger_tap_radius))
+      {
+         return 2;
+      }
+
+   return -1;
+}
+
+static void
+_tap_gestures_mouse_down(Ecore_Event_Mouse_Button *ev, Cover *cov)
+{
+   if (cov->n_taps > 4)
+      {
+         ERROR("Too many fingers");
+         return;
+      }
+
+   cov->tap_gesture_data.pressed = EINA_TRUE;
+
+   if (cov->tap_gesture_data.started == EINA_FALSE)
+      {
+         DEBUG("First finger down");
+         cov->tap_gesture_data.started = EINA_TRUE;
+         cov->tap_gesture_data.finger[0] = ev->multi.device;
+         cov->tap_gesture_data.x_org[0] = ev->root.x;
+         cov->tap_gesture_data.y_org[0] = ev->root.y;
+         cov->tap_gesture_data.finger[1] = -1;
+         cov->tap_gesture_data.finger[2] = -1;
+         cov->tap_gesture_data.n_taps = 0;
+         cov->tap_gesture_data.timer = ecore_timer_add(
+                                           _e_mod_config->one_finger_tap_timeout,
+                                           _on_tap_timer_expire, cov);
+         cov->tap_gesture_data.tap_type = ONE_FINGER_GESTURE;
+      }
+
+   else
+      {
+         if (ev->multi.device == cov->tap_gesture_data.finger[0])
+            {
+               DEBUG("First finger down");
+
+               if (_tap_gesture_finger_check(cov, ev->root.x, ev->root.y) == -1)
+                  {
+                     ERROR("Abort gesture");
+                     cov->tap_gesture_data.started = EINA_FALSE;
+                     ecore_timer_del(cov->tap_gesture_data.timer);
+                     cov->tap_gesture_data.timer = NULL;
+                     cov->tap_gesture_data.tap_type = ONE_FINGER_GESTURE;
+                     cov->tap_gesture_data.finger[0] = -1;
+                     cov->tap_gesture_data.finger[1] = -1;
+                     cov->tap_gesture_data.finger[2] = -1;
+                     _tap_gestures_mouse_down(ev, cov);
+                     return;
+                  }
+
+               cov->tap_gesture_data.x_org[0] = ev->root.x;
+               cov->tap_gesture_data.y_org[0] = ev->root.y;
+            }
+         else if (cov->tap_gesture_data.finger[1] == -1 ||
+                  cov->tap_gesture_data.finger[1] == ev->multi.device)
+            {
+               DEBUG("Second finger down");
+               cov->tap_gesture_data.finger[1] = ev->multi.device;
+
+               cov->tap_gesture_data.x_org[1] = ev->root.x;
+               cov->tap_gesture_data.y_org[1] = ev->root.y;
+               if (cov->tap_gesture_data.tap_type < TWO_FINGERS_GESTURE)
+                  cov->tap_gesture_data.tap_type = TWO_FINGERS_GESTURE;
+            }
+         else if (cov->tap_gesture_data.finger[2] == -1 ||
+                  cov->tap_gesture_data.finger[2] == ev->multi.device)
+            {
+               DEBUG("Third finger down");
+               cov->tap_gesture_data.finger[2] = ev->multi.device;
+
+               cov->tap_gesture_data.x_org[2] = ev->root.x;
+               cov->tap_gesture_data.y_org[2] = ev->root.y;
+               if (cov->tap_gesture_data.tap_type < THREE_FINGERS_GESTURE)
+                  cov->tap_gesture_data.tap_type = THREE_FINGERS_GESTURE;
+            }
+         else
+            {
+               ERROR("Unknown finger down");
+            }
+         ecore_timer_reset(cov->tap_gesture_data.timer);
+      }
+}
+
+static void
+_tap_gestures_mouse_up(Ecore_Event_Mouse_Button *ev, Cover *cov)
+{
+   if (cov->tap_gesture_data.timer)
+      {
+         cov->tap_gesture_data.pressed = EINA_FALSE;
+
+         if (ev->multi.device == cov->tap_gesture_data.finger[0])
+            {
+               DEBUG("First finger up");
+
+               int dx = ev->root.x - cov->tap_gesture_data.x_org[0];
+               int dy = ev->root.y - cov->tap_gesture_data.y_org[0];
+
+               if((dx * dx + dy * dy) < _e_mod_config->one_finger_tap_radius *
+                     _e_mod_config->one_finger_tap_radius)
+                  {
+                     if (cov->n_taps == 0)
+                        {
+                           cov->tap_gesture_data.n_taps++;
+                        }
+                  }
+               else
+                  {
+                     ERROR("Abort gesture");
+                     cov->tap_gesture_data.started = EINA_FALSE;
+                  }
+            }
+         else if (ev->multi.device == cov->tap_gesture_data.finger[1])
+            {
+               DEBUG("Second finger up");
+
+               int dx = ev->root.x - cov->tap_gesture_data.x_org[1];
+               int dy = ev->root.y - cov->tap_gesture_data.y_org[1];
+
+               if((dx * dx + dy * dy) < _e_mod_config->one_finger_tap_radius *
+                     _e_mod_config->one_finger_tap_radius)
+                  {
+                     if (cov->n_taps == 0)
+                        {
+                           cov->tap_gesture_data.n_taps++;
+                        }
+                  }
+               else
+                  {
+                     ERROR("Abort gesture");
+                     cov->tap_gesture_data.started = EINA_FALSE;
+                  }
+            }
+         else if (ev->multi.device == cov->tap_gesture_data.finger[2])
+            {
+               DEBUG("Third finger up");
+
+               int dx = ev->root.x - cov->tap_gesture_data.x_org[2];
+               int dy = ev->root.y - cov->tap_gesture_data.y_org[2];
+
+               if((dx * dx + dy * dy) < _e_mod_config->one_finger_tap_radius *
+                     _e_mod_config->one_finger_tap_radius)
+                  {
+                     if (cov->n_taps == 0)
+                        {
+                           cov->tap_gesture_data.n_taps++;
+                        }
+                  }
+               else
+                  {
+                     ERROR("Abort gesture");
+                     cov->tap_gesture_data.started = EINA_FALSE;
+                  }
+            }
+         else
+            {
+               ERROR("Unknown finger up, abort gesture");
+               cov->tap_gesture_data.started = EINA_FALSE;
+            }
+      }
+}
+
+static void
+_tap_gestures_move(Ecore_Event_Mouse_Move *ev, Cover *cov)
+{
+#if 0
+   if(_tap_gesture_finger_check(cov, ev->root.x, ev->root.y) == -1)
+   {
+       ERROR("Abort gesture");
+       cov->tap_gesture_data.started = EINA_FALSE;
+       ecore_timer_del(cov->tap_gesture_data.timer);
+       cov->tap_gesture_data.timer = NULL;
+       cov->tap_gesture_data.tap_type = ONE_FINGER_GESTURE;
+       cov->tap_gesture_data.finger[0] = -1;
+       cov->tap_gesture_data.finger[1] = -1;
+       cov->tap_gesture_data.finger[2] = -1;
+       return;
+   }
+#endif
+}
+
+static Eina_Bool
+_cb_mouse_down(void    *data EINA_UNUSED,
+               int      type EINA_UNUSED,
+               void    *event)
+{
+   Ecore_Event_Mouse_Button *ev = event;
+
+   cov->n_taps++;
+   cov->event_time = ev->timestamp;
+
+   DEBUG("mouse down: multi.device: %d, taps: %d", ev->multi.device, cov->n_taps);
+
+   _flick_gesture_mouse_down(ev, cov);
+   _hover_gesture_mouse_down(ev, cov);
+   _tap_gestures_mouse_down(ev, cov);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_mouse_up(void    *data EINA_UNUSED,
+             int      type EINA_UNUSED,
+             void    *event)
+{
+   Ecore_Event_Mouse_Button *ev = event;
+
+   cov->n_taps--;
+   cov->event_time = ev->timestamp;
+
+   DEBUG("mouse up, multi.device: %d, taps: %d", ev->multi.device, cov->n_taps);
+
+   _flick_gesture_mouse_up(ev, cov);
+   _hover_gesture_mouse_up(ev, cov);
+   _tap_gestures_mouse_up(ev, cov);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_mouse_move(void    *data EINA_UNUSED,
+               int      type EINA_UNUSED,
+               void    *event)
+{
+   Ecore_Event_Mouse_Move *ev = event;
+
+   cov->event_time = ev->timestamp;
+
+   _flick_gesture_mouse_move(ev, cov);
+   _hover_gesture_mouse_move(ev, cov);
+   _tap_gestures_move(ev, cov);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+Eina_Bool screen_reader_gestures_init(void)
+{
+   ecore_init();
+   ecore_x_init(NULL);
+
+   if (!ecore_x_pointer_grab(ecore_x_window_root_first_get()))
+     {
+        ERROR("Failed to grab pointer on: %x", ecore_x_window_root_first_get());
+        return EINA_FALSE;
+     }
+   if (!ecore_x_input_touch_devices_grab(ecore_x_window_root_first_get()))
+     {
+        ERROR("Failed to grab devices on: %x", ecore_x_window_root_first_get());
+        ecore_x_pointer_ungrab();
+        return EINA_FALSE;
+     }
+
+   ecore_x_input_multi_select(ecore_x_window_root_first_get());
+   _e_mod_config = calloc(sizeof(Gestures_Config), 1);
+   cov = calloc(sizeof(Cover), 1);
+
+   _e_mod_config->one_finger_flick_min_length = 100;
+   _e_mod_config->one_finger_flick_max_time = 400;
+   _e_mod_config->one_finger_hover_longpress_timeout = 0.81;
+   _e_mod_config->two_fingers_hover_longpress_timeout = 0.1;
+   _e_mod_config->one_finger_tap_timeout = 0.4;
+   _e_mod_config->one_finger_tap_radius = 100;
+
+   handlers = eina_list_append(NULL, ecore_event_handler_add(ECORE_EVENT_MOUSE_MOVE, _cb_mouse_move, NULL));
+   handlers = eina_list_append(handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, _cb_mouse_up, NULL));
+   handlers = eina_list_append(handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, _cb_mouse_down, NULL));
+
+   return EINA_TRUE;
+}
+
+void screen_reader_gestures_shutdown(void)
+{
+   Ecore_Event_Handler *hdlr;
+   EINA_LIST_FREE(handlers, hdlr)
+      ecore_event_handler_del(hdlr);
+
+   ecore_x_pointer_ungrab();
+   ecore_x_input_touch_devices_ungrab();
+   ecore_x_shutdown();
+   ecore_shutdown();
+   free(_e_mod_config);
+   free(cov);
+}
+
+void screen_reader_gestures_tracker_register(GestureCB cb, void *data)
+{
+   _global_cb = cb;
+   _global_data = data;
+}