From: Takashi Iwai <tiwai@suse.de>
Subject: Add "cooltouch" option for the better support of CoolTouch device
Patch-mainline: Never
References: bnc#814831

Add a new option "CoolTouch" to evdev to change the handling of input
events from Advanced Silicon CoolTouch touchscreen device.
This device sends the events like:

 type 4 (Misc), code 4 (ScanCode), value d0042
 type 1 (Key), code 330 (Touch), value 1
 type 3 (Absolute), code 0 (X), value 1058
 type 3 (Absolute), code 1 (Y), value 2488
 type 4 (Misc), code 4 (ScanCode), value d0042
 type 1 (Key), code 330 (Touch), value 0
 type 3 (Absolute), code 45 (?), value 59
 -------------- Report Sync ------------
 type 4 (Misc), code 4 (ScanCode), value d0042
 type 1 (Key), code 330 (Touch), value 1
 type 4 (Misc), code 4 (ScanCode), value d0042
 type 1 (Key), code 330 (Touch), value 0
 type 3 (Absolute), code 45 (?), value 111
 -------------- Report Sync ------------
 type 4 (Misc), code 4 (ScanCode), value d0042
 type 1 (Key), code 330 (Touch), value 1
 type 4 (Misc), code 4 (ScanCode), value d0042
 type 1 (Key), code 330 (Touch), value 0
 type 3 (Absolute), code 45 (?), value 169
 -------------- Report Sync ------------
 ....
 -------------- Report Sync ------------
 type 3 (Absolute), code 45 (?), value 2144
 -------------- Report Sync ------------

So we can't translate BTN_TOUCH event straightforwardly as BTN_LEFT.
In this patch, when cooltouch option is set, the evdev keeps the touch
button state and sends BTN_LEFT ON only at the first touch after the
coordinate change, then filters the rest touch event, and sends the
BTN_LEFT OFF when no touch/coordinate is send in sync.

Signed-off-by: Takashi Iwai <tiwai@suse.de>

---
 src/evdev.c |   36 +++++++++++++++++++++++++++++++++++-
 src/evdev.h |    3 +++
 2 files changed, 38 insertions(+), 1 deletion(-)

--- a/src/evdev.c
+++ b/src/evdev.c
@@ -409,6 +409,9 @@ EvdevProcessEvent(InputInfoPtr pInfo, st
                 abs |= ABS_Y_VALUE;
             else
                 abs |= ABS_VALUE;
+	    if (pEvdev->is_cooltouch &&
+		(ev->code == ABS_X || ev->code == ABS_Y))
+		pEvdev->touch_ev_rec |= 2;
             break;
 
         case EV_KEY:
@@ -432,6 +435,9 @@ EvdevProcessEvent(InputInfoPtr pInfo, st
                 case BTN_TOUCH:
                     if (!(pEvdev->flags & EVDEV_TOUCHSCREEN))
                         break;
+		    pEvdev->touch_ev_rec |= 1;
+		    if (pEvdev->is_cooltouch)
+			break;
                     /* Treat BTN_TOUCH from devices that only have BTN_TOUCH as
                      * BTN_LEFT. */
                     ev->code = BTN_LEFT;
@@ -459,6 +465,23 @@ EvdevProcessEvent(InputInfoPtr pInfo, st
             break;
 
         case EV_SYN:
+	    if (pEvdev->is_cooltouch) {
+		if (!pEvdev->touch_ev_rec) {
+		    if (pEvdev->touching) {
+			button = EvdevUtilButtonEventToButtonNumber(pEvdev, BTN_LEFT);
+			xf86PostButtonEvent(pInfo->dev, 0, button, 0, 0, 0);
+			pEvdev->touching = FALSE;
+		    }
+		    break;
+		}
+
+		/* filter events with only touch on/off */
+		if (pEvdev->touch_ev_rec == 1 && pEvdev->touching) {
+		    pEvdev->touch_ev_rec = 0;
+		    break;
+		}
+	    }
+
             /* convert to relative motion for touchpads */
             if (abs && (pEvdev->flags & EVDEV_TOUCHPAD)) {
                 if (pEvdev->tool) { /* meaning, touch is active */
@@ -550,6 +573,15 @@ EvdevProcessEvent(InputInfoPtr pInfo, st
                 xf86PostMotionEventP(pInfo->dev, TRUE, 0, pEvdev->num_vals, v);
             }
 
+	    if (pEvdev->is_cooltouch) {
+		if (!pEvdev->touching && (pEvdev->touch_ev_rec & 1)) {
+		    button = EvdevUtilButtonEventToButtonNumber(pEvdev, BTN_LEFT);
+		    xf86PostButtonEvent(pInfo->dev, 0, button, 1, 0, 0);
+		    pEvdev->touching = TRUE;
+		}
+		pEvdev->touch_ev_rec = 0;
+            }
+
             memset(delta, 0, sizeof(delta));
             tmp = 0;
             abs = 0;
@@ -1584,7 +1616,8 @@ EvdevProbe(InputInfoPtr pInfo)
         pEvdev->flags |= EVDEV_ABSOLUTE_EVENTS;
         if (TestBit(ABS_PRESSURE, pEvdev->abs_bitmask) ||
             TestBit(BTN_TOUCH, pEvdev->key_bitmask)) {
-            if (num_buttons || TestBit(BTN_TOOL_FINGER, pEvdev->key_bitmask)) {
+            if ((num_buttons || TestBit(BTN_TOOL_FINGER, pEvdev->key_bitmask))
+		&& !pEvdev->is_cooltouch) {
                 xf86Msg(X_INFO, "%s: Found absolute touchpad\n", pInfo->name);
                 pEvdev->flags |= EVDEV_TOUCHPAD;
                 memset(pEvdev->old_vals, -1, sizeof(int) * pEvdev->num_vals);
@@ -1786,6 +1819,7 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr
        words, it disables rfkill and the "Macintosh mouse button emulation".
        Note that this needs a server that sets the console to RAW mode. */
     pEvdev->grabDevice = xf86CheckBoolOption(dev->commonOptions, "GrabDevice", 0);
+    pEvdev->is_cooltouch = xf86SetBoolOption(pInfo->options, "CoolTouch", FALSE);
 
     EvdevInitButtonMapping(pInfo);
 
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -102,6 +102,9 @@ typedef struct {
     BOOL swap_axes;
     BOOL invert_x;
     BOOL invert_y;
+    BOOL is_cooltouch;
+    BOOL touching;
+    int touch_ev_rec;
 
     /* XKB stuff has to be per-device rather than per-driver */
 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5
