Feature: Raw Touch Passthrough Mode

Adds the "Raw Touch Passthrough" property that activates a mode
which suppresses all mouse emulation events and passes through the
raw touch events from the evdev device.

When fingers are present when activating the raw mode, those fingers
are removed from the gesture interpretation and instead passed through
as new fingers.
When fingers are present when deactivating the raw mode, those fingers
are ignored by the gesture interpretation. That means a finger that
is present during the deactivation cannot be used for gesturing.

BUG=chromium:325285
TEST=manual testing on link

Change-Id: I4b67ab657dc4ad9705b28b9b009d6362fb14d5c7
Reviewed-on: https://chromium-review.googlesource.com/178600
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
Commit-Queue: Dennis Kempin <denniskempin@google.com>
Tested-by: Dennis Kempin <denniskempin@google.com>
diff --git a/include/cmt-properties.h b/include/cmt-properties.h
index 23f5548..fd4e37e 100644
--- a/include/cmt-properties.h
+++ b/include/cmt-properties.h
@@ -44,5 +44,6 @@
 #define CMT_PROP_SCROLL_BTN  "Scroll Buttons"
 #define CMT_PROP_SCROLL_AXES "Scroll Axes"
 #define CMT_PROP_DUMP_DEBUG_LOG "Dump Debug Log"
+#define CMT_PROP_RAW_TOUCH_PASSTHROUGH "Raw Touch Passthrough"
 
 #endif
diff --git a/src/cmt.c b/src/cmt.c
index 64ae0a1..017b412 100644
--- a/src/cmt.c
+++ b/src/cmt.c
@@ -42,6 +42,9 @@
 #define AXIS_LABEL_PROP_ABS_DBL_END_TIME   "Abs Dbl End Timestamp"
 
 #define AXIS_LABEL_PROP_ABS_FINGER_COUNT   "Abs Finger Count"
+
+#define AXIS_LABEL_PROP_ABS_TOUCH_TIMESTAMP "Touch Timestamp"
+
 /**
  * Forward declarations
  */
@@ -371,7 +374,12 @@
         AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA2,
         AXIS_LABEL_PROP_ABS_DBL_START_TIME,
         AXIS_LABEL_PROP_ABS_DBL_END_TIME,
-        AXIS_LABEL_PROP_ABS_FINGER_COUNT
+        AXIS_LABEL_PROP_ABS_FINGER_COUNT,
+        AXIS_LABEL_PROP_ABS_MT_POSITION_X,
+        AXIS_LABEL_PROP_ABS_MT_POSITION_Y,
+        AXIS_LABEL_PROP_ABS_MT_PRESSURE,
+        AXIS_LABEL_PROP_ABS_MT_TOUCH_MAJOR,
+        AXIS_LABEL_PROP_ABS_TOUCH_TIMESTAMP,
     };
     static const char* btn_names[CMT_NUM_BUTTONS] = {
         BTN_LABEL_PROP_BTN_LEFT,
@@ -380,6 +388,8 @@
         BTN_LABEL_PROP_BTN_BACK,
         BTN_LABEL_PROP_BTN_FORWARD,
     };
+    InputInfoPtr info = dev->public.devicePrivate;
+    CmtDevicePtr cmt = info->private;
 
     Atom axes_labels[CMT_NUM_AXES] = { 0 };
     Atom btn_labels[CMT_NUM_BUTTONS] = { 0 };
@@ -401,6 +411,7 @@
     for (i = 0; i < CMT_NUM_AXES; i++)
         axes_labels[i] = InitAtom(axes_names[i]);
 
+    /* initialize mouse emulation valuators */
     InitPointerDeviceStruct((DevicePtr)dev,
                             map,
                             CMT_NUM_BUTTONS, btn_labels,
@@ -410,11 +421,47 @@
 
     for (i = 0; i < CMT_NUM_AXES; i++) {
         int mode = (i == CMT_AXIS_X || i == CMT_AXIS_Y) ? Relative : Absolute;
+        if (i >= CMT_AXIS_MT_POSITION_X)
+            break;
         xf86InitValuatorAxisStruct(
             dev, i, axes_labels[i], -1, -1, 1, 0, 1, mode);
         xf86InitValuatorDefaults(dev, i);
     }
 
+    /* initialize raw touch valuators */
+    InitTouchClassDeviceStruct(dev, Event_Get_Slot_Count(&cmt->evdev),
+                               XIDependentTouch, CMT_NUM_MT_AXES);
+
+    for (i = 0; i < CMT_NUM_AXES; i++) {
+        int mode = (i == CMT_AXIS_X || i == CMT_AXIS_Y) ? Relative : Absolute;
+        int input_axis = 0;
+        if (i == CMT_AXIS_TOUCH_TIMESTAMP) {
+            xf86InitValuatorAxisStruct(dev, i, axes_labels[i],
+                0, INT_MAX, 1, 0, 1, Absolute);
+            continue;
+        }
+
+        if (i == CMT_AXIS_MT_POSITION_X)
+            input_axis = ABS_MT_POSITION_X;
+        else if (i == CMT_AXIS_MT_POSITION_Y)
+            input_axis = ABS_MT_POSITION_Y;
+        else if (i == CMT_AXIS_MT_PRESSURE)
+            input_axis = ABS_MT_PRESSURE;
+        else if (i == CMT_AXIS_MT_TOUCH_MAJOR)
+            input_axis = ABS_MT_TOUCH_MAJOR;
+        else
+            continue;
+        xf86InitValuatorAxisStruct(
+                dev, i, axes_labels[i],
+                cmt->evdev.info.absinfo[input_axis].minimum,
+                cmt->evdev.info.absinfo[input_axis].maximum,
+                cmt->evdev.info.absinfo[input_axis].resolution,
+                0,
+                cmt->evdev.info.absinfo[input_axis].resolution,
+                mode);
+        xf86InitValuatorDefaults(dev, i);
+    }
+
     return Success;
 }
 
diff --git a/src/cmt.h b/src/cmt.h
index 1b630b9..faa4dc2 100644
--- a/src/cmt.h
+++ b/src/cmt.h
@@ -51,10 +51,16 @@
     CMT_AXIS_METRICS_DATA2,
     CMT_AXIS_DBL_START_TIME,
     CMT_AXIS_DBL_END_TIME,
-    CMT_AXIS_FINGER_COUNT
+    CMT_AXIS_FINGER_COUNT,
+    CMT_AXIS_MT_POSITION_X,
+    CMT_AXIS_MT_POSITION_Y,
+    CMT_AXIS_MT_PRESSURE,
+    CMT_AXIS_MT_TOUCH_MAJOR,
+    CMT_AXIS_TOUCH_TIMESTAMP
 };
 
-#define CMT_NUM_AXES (CMT_AXIS_FINGER_COUNT - CMT_AXIS_X + 1)
+#define CMT_NUM_AXES (CMT_AXIS_TOUCH_TIMESTAMP - CMT_AXIS_X + 1)
+#define CMT_NUM_MT_AXES (CMT_AXIS_TOUCH_TIMESTAMP - CMT_AXIS_MT_POSITION_X + 1)
 
 /* Button numbers. */
 enum CMT_BUTTON {
diff --git a/src/gesture.c b/src/gesture.c
index 91040c3..a54b96d 100644
--- a/src/gesture.c
+++ b/src/gesture.c
@@ -54,6 +54,8 @@
 Gesture_Init(GesturePtr rec, size_t max_fingers)
 {
     rec->interpreter = NewGestureInterpreter();
+    rec->slot_states = NULL;
+
     if (!rec->interpreter)
         return !Success;
     rec->fingers = malloc(max_fingers * sizeof(struct FingerState));
@@ -87,6 +89,10 @@
         free(rec->fingers);
         rec->fingers = NULL;
     }
+    if(rec->slot_states) {
+        free(rec->slot_states);
+        rec->slot_states = NULL;
+    }
 }
 
 void
@@ -98,6 +104,7 @@
     EventStatePtr evstate = &cmt->evstate;
     struct HardwareProperties hwprops;
     EvdevPtr evdev = &cmt->evdev;
+    int i;
 
     /* Store the device for which to generate gestures */
     rec->dev = dev;
@@ -126,6 +133,14 @@
             rec->interpreter,
             Gesture_Device_Class(cmt->evdev.info.evdev_class));
     GestureInterpreterSetHardwareProperties(rec->interpreter, &hwprops);
+
+    rec->slot_states = malloc(sizeof(int) * evstate->slot_count);
+    if (!rec->slot_states) {
+        ERR(info, "BadAlloc: rec->slot_states");
+        return;
+    }
+    for (i = 0; i < evstate->slot_count; ++i)
+        rec->slot_states[i] = SLOT_STATUS_FREE;
 }
 
 void
@@ -161,23 +176,88 @@
     InputInfoPtr info = dev->public.devicePrivate;
     CmtDevicePtr cmt = info->private;
     EvdevPtr evdev = &cmt->evdev;
+    ValuatorMask* mask = rec->mask;
     int i;
     MtSlotPtr slot;
     struct HardwareState hwstate = { 0 };
     int current_finger;
+    bool has_gesture_fingers = false;
 
-    if (!rec->interpreter)
+    if (!rec->interpreter || ! rec->slot_states)
         return;
 
     /* zero initialize all FingerStates to clear out previous state. */
     memset(rec->fingers, 0,
            Event_Get_Slot_Count(evdev) * sizeof(struct FingerState));
 
+    /* clear out previous state from valuator */
+    valuator_mask_zero(mask);
+
+    if (cmt->props.raw_passthrough) {
+        for (i = 0; i < evstate->slot_count; i++) {
+            slot = &evstate->slots[i];
+
+            /* send TouchEnd for lifted fingers */
+            if (slot->tracking_id == -1) {
+                if (rec->slot_states[i] == SLOT_STATUS_RAW) {
+                    xf86PostTouchEvent(dev, i, XI_TouchEnd, 0, mask);
+                }
+                rec->slot_states[i] = SLOT_STATUS_FREE;
+                continue;
+            }
+
+            /*
+             * valuators 0 (CMT_AXIS_X) and 1 (CMT_AXIS_Y) are hardcoded into
+             * X.org as finger position, so we need to set those too.
+             */
+            valuator_mask_set_double(mask, CMT_AXIS_MT_POSITION_X,
+                                     slot->position_x);
+            valuator_mask_set_double(mask, CMT_AXIS_MT_POSITION_Y,
+                                     slot->position_y);
+            valuator_mask_set_double(mask, CMT_AXIS_MT_PRESSURE,
+                                     slot->pressure);
+            valuator_mask_set_double(mask, CMT_AXIS_MT_TOUCH_MAJOR,
+                                     slot->touch_major);
+            valuator_mask_set_double(mask, CMT_AXIS_TOUCH_TIMESTAMP,
+                                     StimeFromTimeval(tv));
+            valuator_mask_set_double(mask, CMT_AXIS_X, slot->position_x);
+            valuator_mask_set_double(mask, CMT_AXIS_Y, slot->position_y);
+
+            if (rec->slot_states[i] == SLOT_STATUS_RAW) {
+                xf86PostTouchEvent(dev, i, XI_TouchUpdate, 0, mask);
+            } else {
+                /* take over STATUS_GESTURE slots too */
+                if (rec->slot_states[i] == SLOT_STATUS_GESTURE)
+                    has_gesture_fingers = true;
+                xf86PostTouchEvent(dev, i, XI_TouchBegin, 0, mask);
+
+            }
+            rec->slot_states[i] = SLOT_STATUS_RAW;
+        }
+
+        if (has_gesture_fingers) {
+            /* push empty hardware state to clear interpreter state */
+            hwstate.timestamp = StimeFromTimeval(tv);
+            GestureInterpreterPushHardwareState(rec->interpreter, &hwstate);
+        }
+        return;
+    }
+
     current_finger = 0;
     for (i = 0; i < evstate->slot_count; i++) {
         slot = &evstate->slots[i];
-        if (slot->tracking_id == -1)
+        if (slot->tracking_id == -1) {
+            rec->slot_states[i] = SLOT_STATUS_FREE;
             continue;
+        }
+
+        if (rec->slot_states[i] == SLOT_STATUS_FREE) {
+            rec->slot_states[i] = SLOT_STATUS_GESTURE;
+        } else if (rec->slot_states[i] == SLOT_STATUS_RAW) {
+            /* ignore any fingers that are still present from raw mode */
+            continue;
+        }
+
         rec->fingers[current_finger].touch_major = (float)slot->touch_major;
         rec->fingers[current_finger].touch_minor = (float)slot->touch_minor;
         rec->fingers[current_finger].width_major = (float)slot->width_major;
@@ -254,6 +334,12 @@
     DeviceIntPtr dev = rec->dev;
     ValuatorMask* mask = rec->mask;
     InputInfoPtr info = dev->public.devicePrivate;
+    CmtDevicePtr cmt = info->private;
+
+    if (cmt->props.raw_passthrough) {
+        DBG(info, "Gesture Suppressed");
+        return;
+    }
 
     DBG(info, "Gesture Start: %f End: %f \n",
         gesture->start_time, gesture->end_time);
diff --git a/src/gesture.h b/src/gesture.h
index e63cfa4..4abeb33 100644
--- a/src/gesture.h
+++ b/src/gesture.h
@@ -17,11 +17,18 @@
 #include "libevdev/libevdev.h"
 #include "properties.h"
 
+enum SLOT_STATUS {
+    SLOT_STATUS_FREE = 0,
+    SLOT_STATUS_RAW,
+    SLOT_STATUS_GESTURE
+};
+
 typedef struct {
     GestureInterpreter* interpreter;  /* The interpreter from Gestures lib */
     DeviceIntPtr dev;
     struct FingerState *fingers;
     ValuatorMask *mask;
+    int *slot_states;  /* Leep track of slot usage between syn reports */
 } GestureRec, *GesturePtr;
 
 int Gesture_Init(GesturePtr, size_t);
diff --git a/src/properties.c b/src/properties.c
index bbdf31c..403aab4 100644
--- a/src/properties.c
+++ b/src/properties.c
@@ -14,6 +14,7 @@
 
 #include "cmt.h"
 #include "cmt-properties.h"
+#include "gesture.h"
 
 #define COMPILE_ASSERT(expr) COMPILE_ASSERT_IMPL(expr, __LINE__)
 #define COMPILE_ASSERT_JOIN(a, b) a##b
@@ -172,6 +173,12 @@
     Prop_RegisterHandlers(dev, dump_debug_log_prop, &cmt->evdev, NULL,
                           Event_Dump_Debug_Log);
 
+    PropCreate_Bool(dev,
+                    CMT_PROP_RAW_TOUCH_PASSTHROUGH,
+                    &props->raw_passthrough,
+                    1,
+                    &bool_false);
+
     return Success;
 }
 
diff --git a/src/properties.h b/src/properties.h
index ac52177..3f91ac8 100644
--- a/src/properties.h
+++ b/src/properties.h
@@ -15,14 +15,15 @@
 
 
 typedef struct {
-    int  area_left;
-    int  area_right;
-    int  area_top;
-    int  area_bottom;
-    int  res_y;
-    int  res_x;
-    int  orientation_minimum;
-    int  orientation_maximum;
+    int area_left;
+    int area_right;
+    int area_top;
+    int area_bottom;
+    int res_y;
+    int res_x;
+    int orientation_minimum;
+    int orientation_maximum;
+    int raw_passthrough;
     GesturesPropBool dump_debug_log;
 } CmtProperties, *CmtPropertiesPtr;