This patch adds the control of the embedded LED on the top-left corner
of new Synaptics devices.  For LED control, it requires the patch to
Linux synaptics input driver,
        https://patchwork.kernel.org/patch/92434/

When evdev reports the presense of LED and LED_MUTE bits, the driver
assumes it supports the embeded LED control.  This corresponds to the
touchpad-off state.  When touchpad is disabled, LED is turned on.

For linking between the touchpad state and the LED state, a new callback
UpdateHardware is introduced.  Only eventcomm.c supports this (naturally).

A new feature for the LED-equipped device is that user can double-tap
on the LED to toggle the touchpad state on the fly.  This is also linked
with the touchpad-off state.

There is a new parameter for controlling the LED double-tap behavior, too.
It specifies the double-tap time.  Passing zero disables the double-tap
feature.

Signed-off-by: Takashi Iwai <ti...@suse.de>
---
 include/synaptics-properties.h |    3 ++
 man/synaptics.man              |   13 +++++++
 src/eventcomm.c                |   39 +++++++++++++++++++++-
 src/properties.c               |   26 ++++++++++++++
 src/synaptics.c                |   73 ++++++++++++++++++++++++++++++++++++++-
 src/synapticsstr.h             |    6 +++
 src/synproto.h                 |    1 +
 tools/synclient.c              |    1 +
 8 files changed, 159 insertions(+), 3 deletions(-)

diff --git a/include/synaptics-properties.h b/include/synaptics-properties.h
index c77afd3..56c3e1d 100644
--- a/include/synaptics-properties.h
+++ b/include/synaptics-properties.h
@@ -161,4 +161,7 @@
 /* 8 bit (BOOL) */
 #define SYNAPTICS_PROP_TOUCH_BUTTON_SENSE "Synaptics Touch Button Sense"
 
+/* 32 bit */
+#define SYNAPTICS_PROP_LED_DOUBLE_TAP "Synaptics LED Double Tap"
+
 #endif /* _SYNAPTICS_PROPERTIES_H_ */
diff --git a/man/synaptics.man b/man/synaptics.man
index f92ea0c..baa7e8f 100644
--- a/man/synaptics.man
+++ b/man/synaptics.man
@@ -530,6 +530,19 @@ clicking the button.  On the other hand, this reduces the 
available
 space on the touchpad.  The default is on.
 Property: "Synaptics Touch Button Sense"
 .
+.TP
+.BI "Option \*qLEDDoubleTap\*q \*q" integer \*q
+.
+The double-tap time for toggling the touchpad-control on the top-left
+corner LED.
+.
+Some devices have an LED on the top-left corner to indicate the
+touchpad state.  User can double-tap on the LED to toggle the touchpad
+state.  This option controls the double-tap time in milli-seconds.
+When it's zero, the LED-tapping feature is disabled.  The default
+value is 400.
+Property: "Synaptics LED Double Tap"
+.
 .LP
 A tap event happens when the finger is touched and released in a time
 interval shorter than MaxTapTime, and the touch and release
diff --git a/src/eventcomm.c b/src/eventcomm.c
index ca99f3a..292e64e 100644
--- a/src/eventcomm.c
+++ b/src/eventcomm.c
@@ -179,6 +179,39 @@ static void event_query_clickpad(LocalDevicePtr local)
     }
 }
 
+/* LED support: the kernel driver should give EV_LED and LED_MUTE bits */
+static void event_query_led(LocalDevicePtr local)
+{
+    SynapticsPrivate *priv = (SynapticsPrivate *)local->private;
+    unsigned long evbits[NBITS(EV_MAX)] = {0};
+    int rc;
+
+    priv->has_led = FALSE;
+
+    SYSCALL(rc = ioctl(local->fd, EVIOCGBIT(0, sizeof(evbits)), evbits));
+    if (TEST_BIT(EV_LED, evbits)) {
+       unsigned long ledbits[NBITS(LED_MAX)] = {0};
+       SYSCALL(rc = ioctl(local->fd, EVIOCGBIT(EV_LED, sizeof(ledbits)), 
ledbits));
+       if (TEST_BIT(LED_MUTE, ledbits)) {
+           xf86Msg(X_INFO, "%s: has LED control\n", local->name);
+           priv->has_led = TRUE;
+       }
+    }
+}
+
+static void EventUpdateHardware(LocalDevicePtr local)
+{
+    SynapticsPrivate *priv = (SynapticsPrivate *)local->private;
+    if (priv->has_led) {
+       struct input_event ev;
+       gettimeofday(&ev.time, NULL);
+       ev.type = EV_LED;
+       ev.code = LED_MUTE;
+       ev.value = priv->synpara.touchpad_off != 0;
+       (void)write(local->fd, &ev, sizeof(ev));
+    }
+}
+
 /* Query device for axis ranges */
 static void
 event_query_axis_ranges(LocalDevicePtr local)
@@ -268,6 +301,7 @@ event_query_axis_ranges(LocalDevicePtr local)
     }
 
     event_query_clickpad(local);
+    event_query_led(local);
 }
 
 static Bool
@@ -421,6 +455,8 @@ EventReadHwState(LocalDevicePtr local,
                break;
            }
            break;
+       case EV_LED:
+           return TRUE;
        }
     }
     return FALSE;
@@ -502,5 +538,6 @@ struct SynapticsProtocolOperations event_proto_operations = 
{
     EventQueryHardware,
     EventReadHwState,
     EventAutoDevProbe,
-    EventReadDevDimensions
+    EventReadDevDimensions,
+    EventUpdateHardware,
 };
diff --git a/src/properties.c b/src/properties.c
index a579479..57f11f0 100644
--- a/src/properties.c
+++ b/src/properties.c
@@ -86,6 +86,7 @@ Atom prop_resolution            = 0;
 Atom prop_area                  = 0;
 Atom prop_touch_button_area     = 0;
 Atom prop_touch_button_sense    = 0;
+Atom prop_led_double_tap        = 0;
 
 static Atom
 InitAtom(DeviceIntPtr dev, char *name, int format, int nvalues, int *values)
@@ -280,6 +281,9 @@ InitDeviceProperties(LocalDevicePtr local)
     prop_touch_button_area = InitAtom(local->dev, 
SYNAPTICS_PROP_TOUCH_BUTTON_AREA, 8, 1, &para->touch_button_area);
 
     prop_touch_button_sense = InitAtom(local->dev, 
SYNAPTICS_PROP_TOUCH_BUTTON_SENSE, 8, 1, &para->touch_button_sense);
+
+    prop_led_double_tap = InitAtom(local->dev, SYNAPTICS_PROP_LED_DOUBLE_TAP, 
32, 1, &para->led_double_tap);
+
 }
 
 int
@@ -496,6 +500,11 @@ SetProperty(DeviceIntPtr dev, Atom property, 
XIPropertyValuePtr prop,
             return BadValue;
 
         para->touchpad_off = off;
+       if (!checkonly) {
+           if (priv->proto_ops && priv->proto_ops->UpdateHardware)
+               priv->proto_ops->UpdateHardware(local);
+       }
+
 
     } else if (property == prop_guestmouse)
     {
@@ -661,10 +670,27 @@ SetProperty(DeviceIntPtr dev, Atom property, 
XIPropertyValuePtr prop,
             return BadMatch;
 
         para->touch_button_sense = *(BOOL*)prop->data;
+    } else if (property == prop_led_double_tap)
+    {
+        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
+            return BadMatch;
+
+        para->led_double_tap = *(INT32*)prop->data;
     }
 
     return Success;
 }
 
+void SynapticsToggleOffProperty(DeviceIntPtr dev, Bool off)
+{
+       uint8_t val;
+
+       if (!prop_off)
+               return;
+       val = off;
+       XIChangeDeviceProperty(dev, prop_off, XA_INTEGER, 8,
+                              PropModeReplace, 1, &val, FALSE);
+}
+
 #endif
 
diff --git a/src/synaptics.c b/src/synaptics.c
index f701074..86001e2 100644
--- a/src/synaptics.c
+++ b/src/synaptics.c
@@ -134,6 +134,7 @@ static void CalculateScalingCoeffs(SynapticsPrivate *priv);
 void InitDeviceProperties(LocalDevicePtr local);
 int SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
                 BOOL checkonly);
+void SynapticsToggleOffProperty(DeviceIntPtr dev, Bool off);
 #endif
 
 InputDriverRec SYNAPTICS = {
@@ -534,6 +535,7 @@ static void set_default_parameters(LocalDevicePtr local)
     pars->resolution_vert = xf86SetIntOption(opts, "VertResolution", 
vertResolution);
     pars->touch_button_area = xf86SetIntOption(opts, "TouchButtonArea", 20);
     pars->touch_button_sense = xf86SetBoolOption(opts, "TouchButtonSense", 
FALSE);
+    pars->led_double_tap = xf86SetIntOption(opts, "LEDDoubleTap", 400);
 
     /* Warn about (and fix) incorrectly configured TopEdge/BottomEdge 
parameters */
     if (pars->top_edge > pars->bottom_edge) {
@@ -766,6 +768,11 @@ DeviceOn(DeviceIntPtr dev)
     }
 
     xf86AddEnabledDevice(local);
+
+    /* update LED */
+    if (priv->proto_ops && priv->proto_ops->UpdateHardware)
+       priv->proto_ops->UpdateHardware(local);
+
     dev->public.on = TRUE;
 
     return Success;
@@ -2054,6 +2061,60 @@ HandleClickWithFingers(SynapticsParameters *para, struct 
SynapticsHwState *hw)
     }
 }
 
+/* clicpad button toggle point:
+ * some devices have a LED at the upper-left corner, and double-tapping it
+ * toggles the touchpad enable/disable
+ */
+static int
+HandleToggleLED(LocalDevicePtr local, struct SynapticsHwState *hw, int finger)
+{
+    SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
+    SynapticsParameters *para = &priv->synpara;
+    int click_led_x = (priv->maxx - priv->minx) * 1 / 10 + priv->minx;
+    int click_led_y = priv->maxy / 8 + priv->miny;
+    int diff;
+
+    if (finger) {
+       if (hw->x >= click_led_x || hw->y >= click_led_y) {
+           /* outside the toggle area */
+           priv->led_touch_state = FALSE;
+           priv->led_tapped = FALSE;
+           return finger;
+       }
+       if (!priv->led_touch_state) {
+           /* touch start */
+           priv->led_touch_millis = hw->millis;
+           priv->led_touch_state = TRUE;
+       }
+       return 0; /* already processed; ignore this finger event */
+    }
+
+    if (!priv->led_touch_state)
+       return finger; /* nothing happened */
+
+    /* touch-released */
+    priv->led_touch_state = FALSE;
+    diff = TIME_DIFF(priv->led_touch_millis + para->tap_time, hw->millis);
+    if (diff < 0) /* non-tap? */
+       return finger;
+    if (priv->led_tapped) {
+       /* double-tapped? */
+       diff = TIME_DIFF(priv->led_tap_millis + para->led_double_tap, 
hw->millis);
+       if (diff >= 0) {
+           para->touchpad_off = !para->touchpad_off;
+           if (priv->proto_ops && priv->proto_ops->UpdateHardware)
+               priv->proto_ops->UpdateHardware(local); /* update LED */
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
+           SynapticsToggleOffProperty(local->dev, para->touchpad_off);
+#endif
+           priv->led_tapped = FALSE;
+       }
+    } else
+       priv->led_tapped = TRUE;
+    priv->led_tap_millis = hw->millis;
+    return 0; /* already processed; ignore this finger event */
+}
+
 /* clickpad event handling */
 static void
 HandleClickpad(LocalDevicePtr local, struct SynapticsHwState *hw)
@@ -2151,11 +2212,11 @@ HandleState(LocalDevicePtr local, struct 
SynapticsHwState *hw)
     }
 
     /* If touchpad is switched off, we skip the whole thing and return delay */
-    if (para->touchpad_off == 1)
+    if (para->touchpad_off == 1 && !(priv->has_led && para->led_double_tap))
        return delay;
 
     /* Clickpad handling for button area */
-    if (priv->is_clickpad && para->touch_button_area > 0)
+    if (para->touchpad_off != 1 && priv->is_clickpad && 
para->touch_button_area > 0)
        HandleClickpad(local, hw);
 
     /* Treat the first two multi buttons as up/down for now. */
@@ -2213,6 +2274,14 @@ HandleState(LocalDevicePtr local, struct 
SynapticsHwState *hw)
 
     finger = SynapticsDetectFinger(priv, hw);
 
+    if (priv->has_led && para->led_double_tap) {
+       finger = HandleToggleLED(local, hw, finger);
+       if (para->touchpad_off == 1) {
+           priv->finger_state = finger;
+           return delay;
+       }
+    }
+
     /* tap and drag detection */
     timeleft = HandleTapProcessing(priv, hw, edge, finger, inside_active_area);
     if (timeleft > 0)
diff --git a/src/synapticsstr.h b/src/synapticsstr.h
index 54afea6..afa7cc3 100644
--- a/src/synapticsstr.h
+++ b/src/synapticsstr.h
@@ -162,6 +162,7 @@ typedef struct _SynapticsParameters
     int area_left_edge, area_right_edge, area_top_edge, area_bottom_edge; /* 
area coordinates absolute */
     int touch_button_area;                  /* touch button (clickpad) area in 
percent */
     Bool touch_button_sense;                /* touch button (clickpad) area 
sensing */
+    int led_double_tap;                            /* double-tap period in ms 
for touchpad LED control */
 } SynapticsParameters;
 
 
@@ -236,6 +237,11 @@ typedef struct _SynapticsPrivateRec
     Bool has_pressure;                 /* device reports pressure */
     Bool is_clickpad;                  /* is Clickpad device (one-button) */
     struct SynapticsHwState prev_hw;   /* previous h/w state (for clickpad) */
+    Bool has_led;                      /* has LED indicator */
+    Bool led_touch_state;
+    Bool led_tapped;
+    int led_touch_millis;
+    int led_tap_millis;
 
     enum TouchpadModel model;          /* The detected model */
 } SynapticsPrivate;
diff --git a/src/synproto.h b/src/synproto.h
index f7be819..1611a12 100644
--- a/src/synproto.h
+++ b/src/synproto.h
@@ -97,6 +97,7 @@ struct SynapticsProtocolOperations {
                        struct CommData *comm, struct SynapticsHwState *hwRet);
     Bool (*AutoDevProbe)(LocalDevicePtr local);
     void (*ReadDevDimensions)(LocalDevicePtr local);
+    void (*UpdateHardware)(LocalDevicePtr local);
 };
 
 extern struct SynapticsProtocolOperations psaux_proto_operations;
diff --git a/tools/synclient.c b/tools/synclient.c
index c4a458b..7ba88cd 100644
--- a/tools/synclient.c
+++ b/tools/synclient.c
@@ -145,6 +145,7 @@ static struct Parameter params[] = {
     {"AreaBottomEdge",        PT_INT,    0, 10000, SYNAPTICS_PROP_AREA,        
32,     3},
     {"TouchButtonArea",       PT_INT,    0, 100,   
SYNAPTICS_PROP_TOUCH_BUTTON_AREA,   8,      0},
     {"TouchButtonSense",      PT_BOOL,   0, 1,     
SYNAPTICS_PROP_TOUCH_BUTTON_SENSE,  8,      0},
+    {"LEDDoubleTap",          PT_INT,    0, 1000,  
SYNAPTICS_PROP_LED_DOUBLE_TAP,      32,     0},
     { NULL, 0, 0, 0, 0 }
 };
 
-- 
1.7.0.4

_______________________________________________
xorg-devel@lists.x.org: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

Reply via email to