diff --git a/.depend b/.depend
index 4107ed6c..adae40da 100644
--- a/.depend
+++ b/.depend
@@ -898,6 +898,14 @@ modules/usbmode.pic.o:\
mce-log.h\
mce.h\
+multitouch.o:\
+ multitouch.c\
+ multitouch.h\
+
+multitouch.pic.o:\
+ multitouch.c\
+ multitouch.h\
+
powerkey.o:\
powerkey.c\
builtin-gconf.h\
diff --git a/Makefile b/Makefile
index a4e0919c..107c4ba6 100644
--- a/Makefile
+++ b/Makefile
@@ -291,6 +291,7 @@ MCE_CORE += mce-command-line.c
MCE_CORE += mce-conf.c
MCE_CORE += datapipe.c
MCE_CORE += mce-modules.c
+MCE_CORE += multitouch.c
MCE_CORE += mce-io.c
MCE_CORE += mce-lib.c
MCE_CORE += evdev.c
@@ -575,6 +576,8 @@ NORMALIZE_USES_SPC =\
modules/radiostates.h\
modules/sensor-gestures.c\
modules/usbmode.c\
+ multitouch.c\
+ multitouch.h\
ofono-dbus-names.h\
powerkey.c\
powerkey.h\
diff --git a/multitouch.c b/multitouch.c
new file mode 100644
index 00000000..6191aed3
--- /dev/null
+++ b/multitouch.c
@@ -0,0 +1,557 @@
+/**
+ * @file multitouch.c
+ *
+ * Mode Control Entity - Tracking for evdev based multitouch devices
+ *
+ *
+ *
+ * Copyright (C) 2015 Jolla Ltd.
+ *
+ *
+ *
+ * @author Simo Piiroinen
+ *
+ * mce is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * mce is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mce. If not, see .
+ */
+
+#include "multitouch.h"
+
+#include
+#include
+#include
+#include
+#include
+
+/* ========================================================================= *
+ * TYPES & FUNCTIONS
+ * ========================================================================= */
+
+/* ------------------------------------------------------------------------- *
+ * TOUCH_POINT
+ * ------------------------------------------------------------------------- */
+
+/** Value to use for invalid touch point id */
+#define MT_POINT_ID_INVAL -1
+
+/** Value to use for touch point id when protocol does not provide ids */
+#define MT_POINT_ID_DUMMY 0
+
+/** Value to use for invalid touch point x & y coordinates */
+#define MT_POINT_XY_INVAL -1
+
+typedef struct mt_point_t mt_point_t;
+
+/** Data for one touch point */
+struct mt_point_t
+{
+ /** Touch point id from ABS_MT_TRACKING_ID event */
+ int mtp_id;
+
+ /** Touch point x-coordinate from ABS_MT_POSITION_X event */
+ int mtp_x;
+
+ /** Touch point y-coordinate from ABS_MT_POSITION_Y event */
+ int mtp_y;
+};
+
+static void mt_point_invalidate (mt_point_t *self);
+static int mt_point_distance2 (const mt_point_t *a, const mt_point_t *b);
+
+/* ------------------------------------------------------------------------- *
+ * TOUCH_VECTOR
+ * ------------------------------------------------------------------------- */
+
+typedef struct mt_touch_t mt_touch_t;
+
+/** Tracking data for start and end position of one touch sequence */
+struct mt_touch_t
+{
+ /* Coordinate where first finger was detected on screen */
+ mt_point_t mtt_beg_point;
+
+ /* Coordinate where last finger was lifted from screen */
+ mt_point_t mtt_end_point;
+
+ /* Timestamp for: Touch started */
+ int64_t mtt_beg_tick;
+
+ /* Timestamp for: Touch ended */
+ int64_t mtt_end_tick;
+
+ /* Maximum number of fingers seen during the touch */
+ size_t mtt_max_fingers;
+};
+
+/** Maximum jitter allowed in double tap (pixel) coordinates */
+#define MT_TOUCH_DBLTAP_DIST_MAX 100
+
+/** Maximum delay between double tap presses and releases [ms] */
+#define MT_TOUCH_DBLTAP_DELAY_MAX 500
+
+/** Minimum delay between double tap presses and releases [ms] */
+#define MT_TOUCH_DBLTAP_DELAY_MIN 1
+
+static bool mt_touch_is_single_tap(const mt_touch_t *self);
+static bool mt_touch_is_double_tap(const mt_touch_t *self, const mt_touch_t *prev);
+
+/* ------------------------------------------------------------------------- *
+ * TOUCH_STATE
+ * ------------------------------------------------------------------------- */
+
+/** Maximum number of simultaneous touch points to support */
+#define MT_STATE_POINTS_MAX 16
+
+typedef struct mt_state_t mt_state_t;
+
+/** Tracking data for one multitouch/mouse input device */
+struct mt_state_t
+{
+ /** Touch point constructed from MT protocol A events */
+ mt_point_t mts_accum;
+
+ /** Touch point constructed from mouse events (in SDK emulator) */
+ mt_point_t mts_mouse;
+
+ /** Array of touch points */
+ mt_point_t mts_point_array[MT_STATE_POINTS_MAX];
+
+ /** Index to currently constructed touch point
+ *
+ * MT protocol B uses explicit ABS_MT_SLOT events while
+ * on protocol A increment on SYN_MT_REPORT / reset on
+ * SYN_REPORT event is used. */
+ size_t mts_point_slot;
+
+ /** Number of currently active touch points */
+ size_t mts_point_count;
+
+ /** Currently tracked primary touch point */
+ mt_point_t mts_point_tracked;
+
+ /** Stats for the last 3 taps, used for double tap detection */
+ mt_touch_t mts_tap_arr[3];
+
+ /** Device type / protocol specific input event handler function */
+ void (*mts_event_handler_cb)(mt_state_t *, const struct input_event *);
+
+ /** Timestamp from latest evdev input event */
+ struct timeval mts_event_time;
+};
+
+static void mt_state_reset (mt_state_t *self);
+static bool mt_state_update (mt_state_t *self);
+
+static void mt_state_handle_event_a (mt_state_t *self, const struct input_event *ev);
+static void mt_state_handle_event_b (mt_state_t *self, const struct input_event *ev);
+
+mt_state_t *mt_state_create (bool protocol_b);
+void mt_state_delete (mt_state_t *self);
+
+bool mt_state_handle_event (mt_state_t *self, const struct input_event *ev);
+
+bool mt_state_touching (const mt_state_t *self);
+
+/* ========================================================================= *
+ * TOUCH_POINT
+ * ========================================================================= */
+
+/** Reset multi touch point to invalid state
+ *
+ * @param self Multi touch point object
+ */
+static void
+mt_point_invalidate(mt_point_t *self)
+{
+ self->mtp_id = MT_POINT_ID_INVAL;
+ self->mtp_x = MT_POINT_XY_INVAL;
+ self->mtp_y = MT_POINT_XY_INVAL;
+}
+
+/** Squared distance between two multi touch point objects
+ *
+ * @param a 1st multi touch point object
+ * @param b 2nd multi touch point object
+ *
+ * @return Distance between the two points, squared.
+ */
+static int mt_point_distance2(const mt_point_t *a, const mt_point_t *b)
+{
+ int x = b->mtp_x - a->mtp_x;
+ int y = b->mtp_y - a->mtp_y;
+ return x*x + y*y;
+}
+
+/* ========================================================================= *
+ * TOUCH_VECTOR
+ * ========================================================================= */
+
+/** Predicate for: Touch vector represents a single tap
+ *
+ * @param self Touch vector object
+ *
+ * @return true if touch vector is tap, false otherwise
+ */
+static bool mt_touch_is_single_tap(const mt_touch_t *self)
+{
+ bool is_single_tap = false;
+
+ if( !self )
+ goto EXIT;
+
+ /* A tap is done using one finger */
+ if( self->mtt_max_fingers != 1 )
+ goto EXIT;
+
+ /* Touch release must happen close to the point of initial contact */
+ int d2 = mt_point_distance2(&self->mtt_beg_point, &self->mtt_end_point);
+ if( d2 > MT_TOUCH_DBLTAP_DIST_MAX * MT_TOUCH_DBLTAP_DIST_MAX )
+ goto EXIT;
+
+ /* The touch duration must not be too short or too long */
+ int64_t t = self->mtt_end_tick - self->mtt_beg_tick;
+ if( t < MT_TOUCH_DBLTAP_DELAY_MIN || t > MT_TOUCH_DBLTAP_DELAY_MAX )
+ goto EXIT;
+
+ is_single_tap = true;
+
+EXIT:
+ return is_single_tap;
+}
+
+/** Predicate for: Two touch vectors represent a double tap
+ *
+ * @param self Current touch vector object
+ * @param prev Previous touch vector object
+ *
+ * @return true if touch vector is double tap, false otherwise
+ */
+static bool mt_touch_is_double_tap(const mt_touch_t *self, const mt_touch_t *prev)
+{
+ bool is_double_tap = false;
+
+ /* Both touch vectors must classify as single taps */
+ if( !mt_touch_is_single_tap(self) || !mt_touch_is_single_tap(prev) )
+ goto EXIT;
+
+ /* The second tap must start near to the end point of the 1st one */
+ int d2 = mt_point_distance2(&self->mtt_beg_point, &prev->mtt_end_point);
+ if( d2 > MT_TOUCH_DBLTAP_DIST_MAX * MT_TOUCH_DBLTAP_DIST_MAX )
+ goto EXIT;
+
+ /* The delay between the taps must be sufficiently small too */
+ int64_t t = self->mtt_beg_tick - prev->mtt_end_tick;
+ if( t < MT_TOUCH_DBLTAP_DELAY_MIN || t > MT_TOUCH_DBLTAP_DELAY_MAX )
+ goto EXIT;
+
+ is_double_tap = true;
+
+EXIT:
+ return is_double_tap;
+}
+
+/* ========================================================================= *
+ * TOUCH_STATE
+ * ========================================================================= */
+
+/** Reset all tracked multitouch points back to invalid state
+ *
+ * @param self Multitouch state object
+ */
+static void
+mt_state_reset(mt_state_t *self)
+{
+ mt_point_invalidate(&self->mts_accum);
+
+ for( size_t i = 0; i < MT_STATE_POINTS_MAX; ++i )
+ mt_point_invalidate(self->mts_point_array + i);
+
+ self->mts_point_slot = 0;
+}
+
+/** Update touch position tracking state
+ *
+ * @param self Multitouch state object
+ *
+ * @return true if a double tap was just detected, false otherwise
+ */
+static bool
+mt_state_update(mt_state_t *self)
+{
+ bool dbltap_seen = false;
+ size_t finger_count = 0;
+
+ /* Count fingers on screen and update position of one finger touch */
+ for( size_t i = 0; i < MT_STATE_POINTS_MAX; ++i ) {
+ if( self->mts_point_array[i].mtp_id == MT_POINT_ID_INVAL )
+ continue;
+
+ if( ++finger_count == 1 )
+ self->mts_point_tracked = self->mts_point_array[i];
+ }
+
+ /* Treat mouse device in SDK simulation as one finger */
+ if( self->mts_mouse.mtp_id != MT_POINT_ID_INVAL ) {
+ if( ++finger_count == 1 )
+ self->mts_point_tracked = self->mts_mouse;
+ }
+
+ /* Skip the rest if the number of fingers on screen does not change */
+ if( self->mts_point_count == finger_count )
+ goto EXIT;
+
+ /* Convert timestamp from input event to 1ms accurate tick counter */
+ int64_t tick = self->mts_event_time.tv_sec * 1000LL + self->mts_event_time.tv_usec / 1000;
+
+ /* When initial touch is detected, update the history buffer to reflect
+ * the current state of affairs */
+ if( self->mts_point_count == 0 ) {
+ memmove(self->mts_tap_arr+1, self->mts_tap_arr+0,
+ sizeof self->mts_tap_arr - sizeof *self->mts_tap_arr);
+
+ self->mts_tap_arr[0].mtt_max_fingers = finger_count;
+ self->mts_tap_arr[0].mtt_beg_point = self->mts_point_tracked;
+ self->mts_tap_arr[0].mtt_beg_tick = tick;
+ }
+
+ /* Maintain maximum number of fingers seen on screen */
+ if( self->mts_tap_arr[0].mtt_max_fingers < finger_count )
+ self->mts_tap_arr[0].mtt_max_fingers = finger_count;
+
+ /* Update touch end position and time */
+ self->mts_tap_arr[0].mtt_end_point = self->mts_point_tracked;
+ self->mts_tap_arr[0].mtt_end_tick = tick;
+
+ /* When final finger is lifted, check if the history buffer content
+ * looks like a double tap */
+ if( finger_count == 0 ) {
+ if( mt_touch_is_double_tap(self->mts_tap_arr+0, self->mts_tap_arr+1) ) {
+ if( ! mt_touch_is_double_tap(self->mts_tap_arr+1, self->mts_tap_arr+2) )
+ dbltap_seen = true;
+ }
+ }
+
+ self->mts_point_count = finger_count;
+
+EXIT:
+ return dbltap_seen;
+}
+
+/** Handle multitouch protocol A event stream
+ *
+ * Also used for handling event streams from mouse devices
+ *
+ * @param self Multitouch state object
+ * @param ev Input event
+ */
+static void
+mt_state_handle_event_a(mt_state_t *self, const struct input_event *ev)
+{
+ switch( ev->type ) {
+ case EV_KEY:
+ switch( ev->code ) {
+ case BTN_TOUCH:
+ if( ev->value == 0 )
+ mt_state_reset(self);
+ break;
+
+ case BTN_MOUSE:
+ if( ev->value > 0 )
+ self->mts_mouse.mtp_id = MT_POINT_ID_DUMMY;
+ else
+ self->mts_mouse.mtp_id = MT_POINT_ID_INVAL;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case EV_REL:
+ switch( ev->code ) {
+ case REL_X:
+ self->mts_mouse.mtp_x += ev->value;
+ break;
+ case REL_Y:
+ self->mts_mouse.mtp_y += ev->value;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case EV_ABS:
+ switch( ev->code ) {
+ case ABS_X:
+ self->mts_mouse.mtp_x = ev->value;
+ break;
+ case ABS_Y:
+ self->mts_mouse.mtp_y = ev->value;
+ break;
+
+ case ABS_MT_POSITION_X:
+ self->mts_accum.mtp_x = ev->value;
+ break;
+ case ABS_MT_POSITION_Y:
+ self->mts_accum.mtp_y = ev->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ self->mts_accum.mtp_id = ev->value;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case EV_SYN:
+ switch( ev->code ) {
+ case SYN_MT_REPORT:
+ if( self->mts_point_slot < MT_STATE_POINTS_MAX &&
+ self->mts_accum.mtp_x != MT_POINT_XY_INVAL &&
+ self->mts_accum.mtp_y != MT_POINT_XY_INVAL ) {
+ if( self->mts_accum.mtp_id == MT_POINT_ID_INVAL )
+ self->mts_accum.mtp_id = MT_POINT_ID_DUMMY;
+ self->mts_point_array[self->mts_point_slot++] = self->mts_accum;
+ }
+ mt_point_invalidate(&self->mts_accum);
+ break;
+
+ case SYN_REPORT:
+ for( size_t i = self->mts_point_slot; i < MT_STATE_POINTS_MAX; ++i )
+ mt_point_invalidate(self->mts_point_array + i);
+ self->mts_point_slot = 0;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/** Handle multitouch protocol B event stream
+ *
+ * @param self Multitouch state object
+ * @param ev Input event
+ */
+static void
+mt_state_handle_event_b(mt_state_t *self, const struct input_event *ev)
+{
+ switch( ev->type ) {
+ case EV_ABS:
+ switch( ev->code ) {
+ case ABS_MT_SLOT:
+ self->mts_point_slot = ev->value;
+ if( self->mts_point_slot >= MT_STATE_POINTS_MAX )
+ self->mts_point_slot = MT_STATE_POINTS_MAX - 1;
+ break;
+ case ABS_MT_TRACKING_ID:
+ self->mts_point_array[self->mts_point_slot].mtp_id = ev->value;
+ break;
+ case ABS_MT_POSITION_X:
+ self->mts_point_array[self->mts_point_slot].mtp_x = ev->value;
+ break;
+ case ABS_MT_POSITION_Y:
+ self->mts_point_array[self->mts_point_slot].mtp_y = ev->value;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/** Handle input event
+ *
+ * @param self Multitouch state object
+ * @param ev Input event
+ */
+bool
+mt_state_handle_event(mt_state_t *self, const struct input_event *ev)
+{
+ bool dbltap = false;
+
+ self->mts_event_time = ev->time;
+
+ self->mts_event_handler_cb(self, ev);
+
+ if( ev->type == EV_SYN && ev->code == SYN_REPORT )
+ dbltap = mt_state_update(self);
+
+ return dbltap;
+}
+
+/** Check if there is at least one finger on screen at the momement
+ *
+ * @param self Multitouch state object
+ *
+ * @return true if fingers are on screen, false otherwise
+ */
+bool
+mt_state_touching (const mt_state_t *self)
+{
+ return self && self->mts_point_count > 0;
+}
+
+/** Release multitouch state object
+ *
+ * @param self Multitouch state object, or NULL
+ */
+void
+mt_state_delete(mt_state_t *self)
+{
+ if( !self )
+ goto EXIT;
+
+ free(self);
+
+EXIT:
+ return;
+}
+
+/** Allocate multitouch state object
+ *
+ * @param protocol_b true if used for tracking multitouch protocol B device
+ *
+ * @return multitouch state object
+ */
+mt_state_t *
+mt_state_create(bool protocol_b)
+{
+ mt_state_t *self = calloc(1, sizeof *self);
+
+ if( !self )
+ goto EXIT;
+
+ self->mts_point_count = 0;
+
+ mt_state_reset(self);
+
+ mt_point_invalidate(&self->mts_mouse);
+
+ mt_point_invalidate(&self->mts_point_tracked);
+
+ if( protocol_b )
+ self->mts_event_handler_cb = mt_state_handle_event_b;
+ else
+ self->mts_event_handler_cb = mt_state_handle_event_a;
+
+EXIT:
+ return self;
+}
diff --git a/multitouch.h b/multitouch.h
new file mode 100644
index 00000000..b65a554d
--- /dev/null
+++ b/multitouch.h
@@ -0,0 +1,40 @@
+/**
+ * @file multitouch.h
+ *
+ * Mode Control Entity - Tracking for evdev based multitouch devices
+ *
+ *
+ *
+ * Copyright (C) 2015 Jolla Ltd.
+ *
+ *
+ *
+ * @author Simo Piiroinen
+ *
+ * mce is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * mce is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mce. If not, see .
+ */
+
+#ifndef MCE_MULTITOUCH_H_
+# define MCE_MULTITOUCH_H_
+
+# include
+# include
+
+typedef struct mt_state_t mt_state_t;
+
+mt_state_t *mt_state_create (bool protocol_b);
+void mt_state_delete (mt_state_t *self);
+bool mt_state_handle_event (mt_state_t *self, const struct input_event *ev);
+bool mt_state_touching (const mt_state_t *self);
+
+#endif /* MCE_MULTITOUCH_H_ */