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, ¶->touch_button_area); prop_touch_button_sense = InitAtom(local->dev, SYNAPTICS_PROP_TOUCH_BUTTON_SENSE, 8, 1, ¶->touch_button_sense); + + prop_led_double_tap = InitAtom(local->dev, SYNAPTICS_PROP_LED_DOUBLE_TAP, 32, 1, ¶->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