--- /dev/null
+#include "e.h"
+#include "e_mod_private.h"
+#include "e_mod_config.h"
+
+#define HISTORY_MAX 8
+#define LONGPRESS_TIMEOUT 0.4
+
+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
+{
+ E_Zone *zone; /**< E_Zone on wich accessible layer is implemented */
+ 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;
+
+int E_EVENT_ATSPI_GESTURE_DETECTED;
+
+static Eina_List *covers;
+static Eina_List *handlers;
+
+static void _covers_init(void);
+static void _covers_shutdown(void);
+static void _hover_event_emit(Cover *cov, int state);
+
+static void
+_gesture_info_free(void *data, void *info)
+{
+ free(data);
+}
+
+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;
+
+ ecore_event_add(E_EVENT_ATSPI_GESTURE_DETECTED, info, _gesture_info_free, NULL);
+}
+
+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 > 2))
+ {
+ 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++;
+ if (cov->flick_gesture.n_fingers < 3) /* n_fingers == 3 makes out of bounds write */
+ {
+ 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;
+ }
+ _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;
+ cov->hover_gesture.longpressed = EINA_TRUE;
+ if (cov->hover_gesture.timer)
+ ecore_timer_del(cov->hover_gesture.timer);
+ cov->hover_gesture.timer = NULL;
+ _hover_event_emit(cov, 0);
+ }
+ // 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
+_hover_event_emit(Cover *cov, int state)
+{
+ DEBUG("Emit hover event");
+ 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:
+ _event_emit(ONE_FINGER_HOVER, ax, ay, ax, ay, state);
+ break;
+ case 2:
+ _event_emit(TWO_FINGERS_HOVER, ax, ay, ax, ay, state);
+ 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
+ {
+ int finger = _tap_gesture_finger_check(cov, ev->root.x, ev->root.y);
+
+ 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;
+ }
+
+ int dx = ev->root.x - cov->tap_gesture_data.x_org[0];
+ int dy = ev->root.y - cov->tap_gesture_data.y_org[0];
+
+ 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(_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;
+ }
+}
+
+static Eina_Bool
+_cb_mouse_down(void *data EINA_UNUSED,
+ int type EINA_UNUSED,
+ void *event)
+{
+ Ecore_Event_Mouse_Button *ev = event;
+ Eina_List *l;
+ Cover *cov;
+
+ EINA_LIST_FOREACH(covers, l, cov)
+ {
+ if (ev->window == cov->win)
+ {
+ 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;
+ }
+ }
+
+ 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;
+ Eina_List *l;
+ Cover *cov;
+
+ EINA_LIST_FOREACH(covers, l, cov)
+ {
+ if (ev->window == cov->win)
+ {
+ 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;
+ }
+ }
+ 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;
+ Eina_List *l;
+ Cover *cov;
+
+ EINA_LIST_FOREACH(covers, l, cov)
+ {
+ if (ev->window == cov->win)
+ {
+ 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;
+ }
+ }
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_zone_add(void *data EINA_UNUSED,
+ int type EINA_UNUSED,
+ void *event EINA_UNUSED)
+{
+ _covers_shutdown();
+ _covers_init();
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_zone_del(void *data EINA_UNUSED,
+ int type EINA_UNUSED,
+ void *event EINA_UNUSED)
+{
+ _covers_shutdown();
+ _covers_init();
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_zone_move_resize(void *data EINA_UNUSED,
+ int type EINA_UNUSED,
+ void *event EINA_UNUSED)
+{
+ _covers_shutdown();
+ _covers_init();
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+
+static void
+_events_init(void)
+{
+#define HANDLER_APPEND(event, cb) \
+ handlers = eina_list_append( \
+ handlers, ecore_event_handler_add(event, cb, NULL));
+ HANDLER_APPEND(ECORE_EVENT_MOUSE_BUTTON_DOWN, _cb_mouse_down);
+ HANDLER_APPEND(ECORE_EVENT_MOUSE_BUTTON_UP, _cb_mouse_up);
+ HANDLER_APPEND(ECORE_EVENT_MOUSE_MOVE, _cb_mouse_move);
+ HANDLER_APPEND(E_EVENT_ZONE_ADD, _cb_zone_add);
+ HANDLER_APPEND(E_EVENT_ZONE_DEL, _cb_zone_del);
+ HANDLER_APPEND(E_EVENT_ZONE_MOVE_RESIZE, _cb_zone_move_resize);
+#undef APPEND_HANDLER
+
+ if (!E_EVENT_ATSPI_GESTURE_DETECTED)
+ E_EVENT_ATSPI_GESTURE_DETECTED = ecore_event_type_new();
+}
+
+static void
+_events_shutdown(void)
+{
+ E_FREE_LIST(handlers, ecore_event_handler_del);
+}
+
+static Cover *
+_cover_new(E_Zone *zone)
+{
+ Cover *cov;
+
+ cov = E_NEW(Cover, 1);
+ if (!cov) return NULL;
+ cov->zone = zone;
+
+ cov->win = ecore_x_window_input_new(zone->container->manager->root,
+ zone->container->x + zone->x,
+ zone->container->y + zone->y,
+ zone->w, zone->h);
+ ecore_x_input_multi_select(cov->win);
+
+ ecore_x_icccm_title_set(cov->win, "atspi-service-input");
+ ecore_x_netwm_name_set(cov->win, "atspi-service-input");
+
+ ecore_x_window_ignore_set(cov->win, 1);
+ ecore_x_window_configure(cov->win,
+ ECORE_X_WINDOW_CONFIGURE_MASK_SIBLING |
+ ECORE_X_WINDOW_CONFIGURE_MASK_STACK_MODE,
+ 0, 0, 0, 0, 0,
+ zone->container->layers[8].win,
+ ECORE_X_WINDOW_STACK_ABOVE);
+ ecore_x_window_show(cov->win);
+ ecore_x_window_raise(cov->win);
+
+ return cov;
+}
+
+static void
+_covers_init(void)
+{
+ Eina_List *l, *l2, *l3;
+ E_Manager *man;
+
+ EINA_LIST_FOREACH(e_manager_list(), l, man)
+ {
+ E_Container *con;
+ EINA_LIST_FOREACH(man->containers, l2, con)
+ {
+ E_Zone *zone;
+ EINA_LIST_FOREACH(con->zones, l3, zone)
+ {
+ Cover *cov = _cover_new(zone);
+ if (cov) covers = eina_list_append(covers, cov);
+ }
+ }
+ }
+ DEBUG("Created %d covers.", eina_list_count(covers));
+}
+
+static void
+_covers_shutdown(void)
+{
+ Cover *cov;
+
+ EINA_LIST_FREE(covers, cov)
+ {
+ ecore_x_window_ignore_set(cov->win, 0);
+ ecore_x_window_free(cov->win);
+ if (cov->tap_gesture_data.timer)
+ ecore_timer_del(cov->tap_gesture_data.timer);
+ if (cov->hover_gesture.timer)
+ ecore_timer_del(cov->hover_gesture.timer);
+ free(cov);
+ }
+}
+
+int _e_mod_atspi_gestures_init(void)
+{
+ DEBUG("gesture init");
+ ecore_x_event_mask_set(ecore_x_window_root_first_get(),
+ ECORE_X_EVENT_MASK_WINDOW_CONFIGURE);
+ ecore_x_event_mask_set(ecore_x_window_root_first_get(),
+ ECORE_X_EVENT_MASK_WINDOW_PROPERTY);
+
+ _covers_init();
+ _events_init();
+
+ return 0;
+}
+
+int _e_mod_atspi_gestures_shutdown(void)
+{
+ DEBUG("gesture shutdown");
+
+ _covers_shutdown();
+ _events_shutdown();
+
+ return 0;
+}
--- /dev/null
+#include "e.h"
+#include "e_mod_main.h"
+#include "e_mod_private.h"
+#include "e_mod_config.h"
+
+#include <vconf.h>
+
+#define E_A11Y_SERVICE_BUS_NAME "com.samsung.EModule"
+#define E_A11Y_SERVICE_NAVI_IFC_NAME "com.samsung.GestureNavigation"
+#define E_A11Y_SERVICE_NAVI_OBJ_PATH "/com/samsung/GestureNavigation"
+#define E_A11Y_SERVICE_TRACKER_IFC_NAME "com.samsung.WindowTracker"
+#define E_A11Y_SERVICE_TRACKER_OBJ_PATH "/com/samsung/WindowTracker"
+
+#define VCONF_ATSPI_KEY "db/setting/accessibility/atspi"
+#define E_ATSPI_BUS_TIMEOUT 4000
+
+int _eina_log_dom;
+
+static int g_enabled;
+static int g_gesture_navi;
+static Eina_List *handlers;
+static const char *_a11y_bus_address;
+static E_DBus_Connection *_a11y_conn;
+static E_DBus_Object *_dbus_obj;
+static E_DBus_Object *_wt_dbus_obj;
+static DBusPendingCall *_bus_request;
+
+/* module setup */
+EAPI E_Module_Api e_modapi =
+{
+ E_MODULE_API_VERSION,
+ "E17 ATSPI Service"
+};
+
+int _a11y_bus_register(void);
+static const char *_gesture_enum_to_string(Gesture g);
+static void _events_shutdown();
+static void _events_init();
+
+static void _on_get_a11y_address(void *data, DBusMessage *msg, DBusError *err)
+{
+ const char *address = NULL;
+
+ _bus_request = NULL;
+
+ if (dbus_error_is_set(err))
+ {
+ ERROR("Dbus error occured: %s", err->message);
+ }
+ else if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR)
+ {
+ DBusError err2;
+ dbus_error_init(&err2);
+ if (dbus_message_get_args(msg, &err2, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID))
+ {
+ eina_stringshare_replace(&_a11y_bus_address, address);
+ DEBUG("AT-SPI bus address: %s", address);
+ _a11y_bus_register();
+ }
+ }
+ else
+ ERROR("GetAddress method call failed: %s", dbus_message_get_error_name(msg));
+}
+
+/**
+ * @brief Get accessibility bus address
+ */
+static int
+_fetch_a11y_bus_address(void)
+{
+ E_DBus_Connection *conn = NULL;
+ DBusMessage *msg = NULL;
+
+ if (_bus_request)
+ return 0;
+
+ conn = e_dbus_bus_get(DBUS_BUS_SESSION);
+ if (!conn)
+ {
+ ERROR("unable to get system bus");
+ goto fail;
+ }
+
+ msg = dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
+ "org.a11y.Bus", "GetAddress");
+ if (!msg)
+ {
+ ERROR("DBus message allocation failed");
+ goto fail;
+ }
+
+ _bus_request = e_dbus_message_send(conn, msg, _on_get_a11y_address,
+ E_ATSPI_BUS_TIMEOUT, NULL);
+ dbus_message_unref(msg);
+
+ return 0;
+fail:
+ if (msg) dbus_message_unref(msg);
+ return -1;
+}
+
+static DBusMessage *_on_get_active_window(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+
+ Ecore_X_Window win = _e_mod_atspi_window_tracker_top_window_get();
+ pid_t pid = _e_mod_atspi_window_tracker_top_window_pid_get();
+
+ if (pid == -1)
+ {
+ ERROR("Invalid PID");
+ return dbus_message_new_error(msg, "org.freedesktop.", "Invalid");
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ {
+ ERROR("Unable to create return message.");
+ return NULL;
+ }
+
+ if (!dbus_message_append_args(reply, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INT32, &win,
+ DBUS_TYPE_INVALID))
+ {
+ ERROR("Appending replay args faild.");
+ dbus_message_unref(reply);
+ return NULL;
+ }
+
+ INFO("GetActiveWindow method called");
+ return reply;
+}
+
+static DBusMessage *_on_get_supported_gestures(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, iter2;
+ Gesture g;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) return NULL;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ goto fail;
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &iter2))
+ goto fail;
+
+ for (g = ONE_FINGER_FLICK_LEFT; g < GESTURES_COUNT; g++)
+ {
+ if (!dbus_message_iter_append_basic(&iter2, DBUS_TYPE_STRING, _gesture_enum_to_string(g)))
+ goto fail;
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &iter2))
+ goto fail;
+
+ DEBUG("Get supported gestures method called");
+ return reply;
+
+fail:
+ dbus_message_unref(reply);
+ return NULL;
+}
+
+static void _propety_get(E_DBus_Object *obj, const char *property, int *type, void **value)
+{
+ DEBUG("Get property request");
+ if (property && !strcmp(property, "AccessibilityGestureNavigation"))
+ {
+ if (type) *type = DBUS_TYPE_BOOLEAN;
+ if (value) *value = &g_gesture_navi;
+ }
+ else
+ if (type) *type = DBUS_TYPE_INVALID;
+}
+
+static int _propety_set(E_DBus_Object *obj, const char *property, int type, void *value)
+{
+ DEBUG("Set property request");
+ if (property && !strcmp(property, "AccessibilityGestureNavigation"))
+ {
+ if (value && !g_gesture_navi)
+ {
+ _e_mod_atspi_gestures_init();
+ g_gesture_navi = EINA_TRUE;
+ }
+ else if (g_gesture_navi)
+ {
+ _e_mod_atspi_gestures_shutdown();
+ g_gesture_navi = EINA_TRUE;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static void
+_on_name_cb(void *data, DBusMessage *msg, DBusError *err)
+{
+ DEBUG("On_name_cb");
+}
+
+int _a11y_bus_register(void)
+{
+ DBusConnection *conn;
+ E_DBus_Interface *ifc, *wt_ifc;
+ DBusError err;
+
+ if (_a11y_conn) return 0;
+ conn = dbus_connection_open_private(_a11y_bus_address, NULL);
+ if (!conn)
+ {
+ ERROR("Unable to connect with %s", _a11y_bus_address);
+ return -1;
+ }
+
+ dbus_error_init(&err);
+ if (dbus_bus_register(conn, &err) != TRUE)
+ {
+ ERROR("Registering on AT-SPI bus failed.");
+ dbus_connection_close(conn);
+ dbus_connection_unref(conn);
+ return -1;
+ }
+
+ _a11y_conn = e_dbus_connection_setup(conn);
+ if (!_a11y_conn)
+ {
+ ERROR("Unable to integrate with ecore_main_loop");
+ dbus_connection_close(conn);
+ dbus_connection_unref(conn);
+ return -1;
+ }
+
+ e_dbus_request_name(_a11y_conn, E_A11Y_SERVICE_BUS_NAME,
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, _on_name_cb, NULL);
+ ifc = e_dbus_interface_new(E_A11Y_SERVICE_NAVI_IFC_NAME);
+ wt_ifc = e_dbus_interface_new(E_A11Y_SERVICE_TRACKER_IFC_NAME);
+
+ _dbus_obj = e_dbus_object_add(_a11y_conn, E_A11Y_SERVICE_NAVI_OBJ_PATH, NULL);
+ _wt_dbus_obj = e_dbus_object_add(_a11y_conn, E_A11Y_SERVICE_TRACKER_OBJ_PATH, NULL);
+
+ e_dbus_interface_method_add(ifc, "GetSupportedGestures", NULL, "as", _on_get_supported_gestures);
+ e_dbus_interface_signal_add(ifc, "GestureDetected", "siii");
+
+ e_dbus_interface_method_add(wt_ifc, "GetActiveWindow", NULL, "ii", _on_get_active_window);
+ e_dbus_interface_signal_add(wt_ifc, "ActiveWindowChanged", "ii");
+
+ e_dbus_object_interface_attach(_dbus_obj, ifc);
+ e_dbus_object_interface_attach(_wt_dbus_obj, wt_ifc);
+
+ e_dbus_object_property_get_cb_set(_dbus_obj, _propety_get);
+ e_dbus_object_property_set_cb_set(_dbus_obj, _propety_set);
+
+ DEBUG("AT-SPI dbus service initialized.");
+ return 0;
+}
+
+int _a11y_bus_unregister(void)
+{
+ if (!_a11y_conn) return 0;
+
+ e_dbus_release_name(_a11y_conn, E_A11Y_SERVICE_BUS_NAME, NULL, NULL);
+
+ if (_dbus_obj) e_dbus_object_free(_dbus_obj);
+ _dbus_obj = NULL;
+ if (_wt_dbus_obj) e_dbus_object_free(_wt_dbus_obj);
+ _wt_dbus_obj = NULL;
+
+ e_dbus_connection_close(_a11y_conn);
+ _a11y_conn = NULL;
+
+ return 0;
+}
+
+int _e_mod_atspi_dbus_init(void)
+{
+ DEBUG("dbus init");
+
+ e_dbus_init();
+
+ return _fetch_a11y_bus_address();
+}
+
+int _e_mod_atspi_dbus_shutdown(void)
+{
+ DEBUG("dbus shutdown");
+ if (_a11y_bus_address)
+ eina_stringshare_del(_a11y_bus_address);
+ _a11y_bus_address = NULL;
+
+ if (_bus_request)
+ dbus_pending_call_cancel(_bus_request);
+ _bus_request = NULL;
+
+ _a11y_bus_unregister();
+
+ e_dbus_shutdown();
+
+ return 0;
+}
+
+static const char *_gesture_enum_to_string(Gesture g)
+{
+ switch(g)
+ {
+ case ONE_FINGER_HOVER:
+ return "OneFingerHover";
+ case TWO_FINGERS_HOVER:
+ return "TwoFingersHover";
+ case THREE_FINGERS_HOVER:
+ return "ThreeFingersHover";
+ case ONE_FINGER_FLICK_LEFT:
+ return "OneFingerFlickLeft";
+ case ONE_FINGER_FLICK_RIGHT:
+ return "OneFingerFlickRight";
+ case ONE_FINGER_FLICK_UP:
+ return "OneFingerFlickUp";
+ case ONE_FINGER_FLICK_DOWN:
+ return "OneFingerFlickDown";
+ case TWO_FINGERS_FLICK_UP:
+ return "TwoFingersFlickUp";
+ case TWO_FINGERS_FLICK_DOWN:
+ return "TwoFingersFlickDown";
+ case TWO_FINGERS_FLICK_LEFT:
+ return "TwoFingersFlickLeft";
+ case TWO_FINGERS_FLICK_RIGHT:
+ return "TwoFingersFlickRight";
+ case THREE_FINGERS_FLICK_LEFT:
+ return "ThreeFingersFlickLeft";
+ case THREE_FINGERS_FLICK_RIGHT:
+ return "ThreeFingersFlickRight";
+ case THREE_FINGERS_FLICK_UP:
+ return "ThreeFingersFlickUp";
+ case THREE_FINGERS_FLICK_DOWN:
+ return "ThreeFingersFlickDown";
+ case ONE_FINGER_SINGLE_TAP:
+ return "OneFingerSingleTap";
+ case ONE_FINGER_DOUBLE_TAP:
+ return "OneFingerDoubleTap";
+ case ONE_FINGER_TRIPLE_TAP:
+ return "OneFingerTripleTap";
+ case TWO_FINGERS_SINGLE_TAP:
+ return "TwoFingersSingleTap";
+ case TWO_FINGERS_DOUBLE_TAP:
+ return "TwoFingersDoubleTap";
+ case TWO_FINGERS_TRIPLE_TAP:
+ return "TwoFingersTripleTap";
+ case THREE_FINGERS_SINGLE_TAP:
+ return "ThreeFingersSingleTap";
+ case THREE_FINGERS_DOUBLE_TAP:
+ return "ThreeFingersDoubleTap";
+ case THREE_FINGERS_TRIPLE_TAP:
+ return "ThreeFingersTripleTap";
+ case ONE_FINGER_FLICK_LEFT_RETURN:
+ return "OneFingerFlickLeftReturn";
+ case ONE_FINGER_FLICK_RIGHT_RETURN:
+ return "OneFingerFlickRightReturn";
+ case ONE_FINGER_FLICK_UP_RETURN:
+ return "OneFingerFlickUpReturn";
+ case ONE_FINGER_FLICK_DOWN_RETURN:
+ return "OneFingerFlickDownReturn";
+ case TWO_FINGERS_FLICK_LEFT_RETURN:
+ return "TwoFingersFlickLeftReturn";
+ case TWO_FINGERS_FLICK_RIGHT_RETURN:
+ return "TwoFingersFlickRightReturn";
+ case TWO_FINGERS_FLICK_UP_RETURN:
+ return "TwoFingersFlickUpReturn";
+ case TWO_FINGERS_FLICK_DOWN_RETURN:
+ return "TwoFingersFlickDownReturn";
+ case THREE_FINGERS_FLICK_LEFT_RETURN:
+ return "ThreeFingersFlickLeftReturn";
+ case THREE_FINGERS_FLICK_RIGHT_RETURN:
+ return "ThreeFingersFlickRightReturn";
+ case THREE_FINGERS_FLICK_UP_RETURN:
+ return "ThreeFingersFlickUpReturn";
+ case THREE_FINGERS_FLICK_DOWN_RETURN:
+ return "ThreeFingersFlickDownReturn";
+ default:
+ ERROR("[atspi] dbus: unhandled gesture enum");
+ return NULL;
+ }
+}
+
+int _e_mod_atpis_dbus_windown_changed_broadcast(pid_t pid, int id)
+{
+ DBusMessage *sig;
+ if (!_a11y_conn) return -1;
+
+ sig = dbus_message_new_signal(E_A11Y_SERVICE_TRACKER_OBJ_PATH,
+ E_A11Y_SERVICE_TRACKER_IFC_NAME, "ActiveWindowChanged");
+ if (!sig) return -1;
+
+ if (!dbus_message_append_args(sig, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INT32, &id, DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref(sig);
+ return -1;
+ }
+ if (!e_dbus_message_send(_a11y_conn, sig, NULL, 0, NULL))
+ {
+ ERROR("Sending ActiveWindowChanged signal failed!");
+ dbus_message_unref(sig);
+ return -1;
+ }
+ INFO("ActiveWindowChanged Dbus signal send");
+ dbus_message_unref(sig);
+ return 0;
+}
+
+int _e_mod_atspi_dbus_broadcast(Gesture_Info *gi)
+{
+ const char *name;
+ DBusMessage *sig;
+
+ if (!_a11y_conn) return -1;
+
+ name = _gesture_enum_to_string(gi->type);
+ if (!name) return -1;
+
+ sig = dbus_message_new_signal(E_A11Y_SERVICE_NAVI_OBJ_PATH,
+ E_A11Y_SERVICE_NAVI_IFC_NAME, "GestureDetected");
+ if (!sig) return -1;
+
+ if (!dbus_message_append_args(sig, DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INT32, &gi->x_beg, DBUS_TYPE_INT32, &gi->y_beg,
+ DBUS_TYPE_INT32, &gi->x_end, DBUS_TYPE_INT32, &gi->y_end,
+ DBUS_TYPE_UINT32, &gi->state,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref(sig);
+ return -1;
+ }
+
+ if (!e_dbus_message_send(_a11y_conn, sig, NULL, 0, NULL))
+ {
+ ERROR("Sending GestureDetected signal failed!");
+ dbus_message_unref(sig);
+ return -1;
+ }
+
+ INFO("GestureDetected %s (%d %d %d %d %d)", name, gi->x_beg, gi->y_beg, gi->x_end, gi->y_end, gi->state);
+ dbus_message_unref(sig);
+ return 0;
+}
+
+static void
+_e_mod_submodules_init(void)
+{
+ INFO("Init subsystems...");
+
+ if (_e_mod_atspi_config_init())
+ goto fail;
+ if (_e_mod_atspi_gestures_init())
+ goto fail_gestures;
+ if (_e_mod_atspi_dbus_init())
+ goto fail_dbus;
+ if (_e_mod_atspi_window_tracker_init())
+ goto fail_window_tracker;
+
+ _events_init();
+ g_gesture_navi = EINA_TRUE;
+
+ return;
+
+fail_window_tracker:
+ ERROR("Window tracker submodule initialization failed.");
+ _e_mod_atspi_dbus_shutdown();
+fail_dbus:
+ ERROR("Dbus submodule initialization failed.");
+ _e_mod_atspi_gestures_shutdown();
+fail_gestures:
+ ERROR("Gestures submodule initialization failed.");
+ _e_mod_atspi_config_shutdown();
+fail:
+ ERROR("Module initialization failed.");
+}
+
+static void
+_e_mod_submodules_shutdown(void)
+{
+ INFO("Shutdown subsystems...");
+ _e_mod_atspi_config_save();
+ _e_mod_atspi_config_shutdown();
+ _e_mod_atspi_window_tracker_shutdown();
+ _e_mod_atspi_gestures_shutdown();
+ g_gesture_navi = EINA_FALSE;
+ _events_shutdown();
+ _e_mod_atspi_dbus_shutdown();
+}
+
+static Eina_Bool
+_gesture_cb(void *data,
+ int type EINA_UNUSED,
+ void *event)
+{
+ Gesture_Info *gi = event;
+
+ if (g_gesture_navi)
+ _e_mod_atspi_dbus_broadcast(gi);
+
+ return EINA_TRUE;
+}
+
+
+static Eina_Bool
+_top_window_changed_cb(void *data,
+ int type EINA_UNUSED,
+ void *event)
+{
+ Ecore_X_Window win = _e_mod_atspi_window_tracker_top_window_get();
+ pid_t pid = _e_mod_atspi_window_tracker_top_window_pid_get();
+
+ _e_mod_atpis_dbus_windown_changed_broadcast(pid, win);
+ return EINA_TRUE;
+}
+
+static void
+_events_init(void)
+{
+#define HANDLER_APPEND(event, cb) \
+ handlers = eina_list_append( \
+ handlers, ecore_event_handler_add(event, cb, NULL));
+ HANDLER_APPEND(E_EVENT_ATSPI_GESTURE_DETECTED, _gesture_cb);
+ HANDLER_APPEND(E_EVENT_ATSPI_TOP_WINDOW_CHANGED, _top_window_changed_cb);
+#undef APPEND_HANDLER
+}
+
+static void
+_events_shutdown(void)
+{
+ E_FREE_LIST(handlers, ecore_event_handler_del);
+}
+
+static void
+_vconf_atspi_accessibility_change(keynode_t *node,
+ void *data)
+{
+ INFO("vconf %s key changed\n", VCONF_ATSPI_KEY);
+#if 0
+ int val;
+ if (vconf_get_bool(VCONF_ATSPI_KEY, &val))
+ {
+ ERROR("vconf key \"atspi\" get failed\n");
+ return;
+ }
+
+ if (!!g_enabled == !!val) return;
+
+ if (val)
+ _e_mod_submodules_init();
+ else
+ _e_mod_submodules_shutdown();
+
+ g_enabled = val;
+#endif
+}
+
+
+EAPI void *
+e_modapi_init(E_Module *m)
+{
+ DEBUG("module init\n");
+
+ _e_mod_log_init();
+#if 0
+ if (vconf_notify_key_changed(VCONF_ATSPI_KEY,
+ _vconf_atspi_accessibility_change,
+ NULL))
+ ERROR("Unable to register on vconf atspi change");
+
+ if (vconf_get_bool(VCONF_ATSPI_KEY, &g_enabled))
+ {
+ ERROR("Unable to get vconf key\n");
+ return m;
+ }
+#endif
+
+ if (g_enabled)
+ _e_mod_submodules_init();
+
+ return m;
+}
+
+EAPI int
+e_modapi_shutdown(E_Module *m)
+{
+ DEBUG("module shutdown");
+
+ if (g_enabled)
+ _e_mod_atspi_dbus_shutdown();
+
+ g_enabled = 0;
+
+ _e_mod_log_shutdown();
+
+ return 1;
+}
+
+EAPI int
+e_modapi_save(E_Module *m)
+{
+ DEBUG("module saved");
+ return 1;
+}
+
+int _e_mod_log_init(void)
+{
+ if (!_eina_log_dom)
+ {
+ _eina_log_dom = eina_log_domain_register("e17-mod-tizen-screen-reader", NULL);
+ if (_eina_log_dom < 0)
+ {
+ fprintf(stderr, "Unable to register e17-mod-tizen-screen-reader log domain");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void _e_mod_log_shutdown(void)
+{
+ if (_eina_log_dom)
+ {
+ eina_log_domain_unregister(_eina_log_dom);
+ _eina_log_dom = 0;
+ }
+}