[PATCH 12/18] Add pinch gesture support
Takashi Iwai
tiwai at suse.de
Fri Oct 8 10:22:36 PDT 2010
Signed-off-by: Takashi Iwai <tiwai at suse.de>
---
include/synaptics-properties.h | 3 +
src/Makefile.am | 3 +-
src/keymap.c | 227 ++++++++++++++++++++++++++++++++++++++++
src/properties.c | 13 +++
src/synaptics.c | 105 ++++++++++++++++++
src/synapticsstr.h | 21 ++++-
tools/synclient.c | 2 +
7 files changed, 372 insertions(+), 2 deletions(-)
create mode 100644 src/keymap.c
diff --git a/include/synaptics-properties.h b/include/synaptics-properties.h
index 47f6bb1..7112bc0 100644
--- a/include/synaptics-properties.h
+++ b/include/synaptics-properties.h
@@ -170,4 +170,7 @@
/* 8 bit (BOOL), double-tap action on LED corner (on/off) */
#define SYNAPTICS_PROP_LED_DOUBLE_TAP "Synaptics LED Dobule Tap"
+/* 32 bit, 2 values, start, delta (0 disable) */
+#define SYNAPTICS_PROP_MULTI_TOUCH_PINCH "Synaptics Multi-Touch Pinch"
+
#endif /* _SYNAPTICS_PROPERTIES_H_ */
diff --git a/src/Makefile.am b/src/Makefile.am
index 980ab5e..625fea6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -35,7 +35,8 @@ AM_CFLAGS = $(XORG_CFLAGS)
alpscomm.c alpscomm.h \
ps2comm.c ps2comm.h \
synproto.h \
- properties.c
+ properties.c \
+ keymap.c
if BUILD_EVENTCOMM
@DRIVER_NAME at _drv_la_SOURCES += \
diff --git a/src/keymap.c b/src/keymap.c
new file mode 100644
index 0000000..b1e7cc6
--- /dev/null
+++ b/src/keymap.c
@@ -0,0 +1,227 @@
+/*
+ * Minimal keyboard support; mostly copied from other drivers
+ *
+ * Copyright (c) 2010 Takashi Iwai
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Red Hat
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission. Red
+ * Hat makes no representations about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xorg-server.h>
+#include <unistd.h>
+#include <misc.h>
+#include <xf86.h>
+#include <sys/shm.h>
+#include <stdio.h>
+#include <xf86_OSproc.h>
+#include <xf86Xinput.h>
+#include <exevents.h>
+
+#include <X11/keysym.h>
+#include <X11/XF86keysym.h>
+#include <xkbsrv.h>
+
+#include "synaptics.h"
+#include "synapticsstr.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+static int get_keycode(LocalDevicePtr local, int keysym)
+{
+ KeySymsPtr ksr = XkbGetCoreMap(local->dev);
+ int c;
+
+ for (c = ksr->minKeyCode; c <= ksr->maxKeyCode; c++) {
+ if (ksr->map[(c - ksr->minKeyCode) * ksr->mapWidth] == keysym)
+ break;
+ }
+ if (c > ksr->maxKeyCode)
+ c = -1;
+ free(ksr);
+ return c;
+}
+
+static int get_modifier(int keysym)
+{
+ static struct { KeySym keysym; CARD8 mask; } modifiers[] = {
+ { XK_Shift_L, ShiftMask },
+ { XK_Shift_R, ShiftMask },
+ { XK_Control_L, ControlMask },
+ { XK_Control_R, ControlMask },
+ { XK_Caps_Lock, LockMask },
+ { XK_Alt_L, Mod1Mask },
+ { XK_Alt_R, Mod1Mask },
+ { XK_Num_Lock, Mod2Mask },
+ { XK_Mode_switch, Mod3Mask },
+ { XK_Scroll_Lock, Mod5Mask },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(modifiers); i++) {
+ if (modifiers[i].keysym == keysym)
+ return modifiers[i].mask;
+ }
+ return 0;
+}
+
+static struct keysym_table {
+ const char *str;
+ int keysym;
+} keysym_ref[] = {
+ { "Control", XK_Control_L },
+ { "Shift", XK_Shift_L },
+ { "Alt", XK_Alt_L },
+ { "Mode_switch", XK_Mode_switch },
+ { "space", XK_space },
+ { "quotedbl", XK_quotedbl },
+ { "numbersign", XK_numbersign },
+ { "dollar", XK_dollar},
+ { "percent", XK_percent },
+ { "apostrophe", XK_apostrophe },
+ { "plus", XK_plus },
+ { "comma", XK_comma },
+ { "minus", XK_minus },
+ { "backslash", XK_backslash },
+ { "F1", XK_F1 },
+ { "F2", XK_F2 },
+ { "F3", XK_F3 },
+ { "F4", XK_F4 },
+ { "F5", XK_F5 },
+ { "F6", XK_F6 },
+ { "F7", XK_F7 },
+ { "F8", XK_F8 },
+ { "F9", XK_F9 },
+ { "F10", XK_F10 },
+ { "Home", XK_Home },
+ { "Up", XK_Up },
+ { "Prior", XK_Prior },
+ { "Left", XK_Left },
+ { "Begin", XK_Begin },
+ { "Right", XK_Right },
+ { "End", XK_End },
+ { "Down", XK_Down },
+ { "Next", XK_Next },
+ { "Insert", XK_Insert },
+ { "Delete", XK_Delete },
+ { "Return", XK_Return },
+ { "Print", XK_Print },
+ { "BackSpace", XK_BackSpace },
+ { "Tab", XK_Tab },
+ { NULL, NoSymbol }
+};
+
+static int string_to_keysym(const char *str)
+{
+ struct keysym_table *p;
+ for (p = keysym_ref; p->str; p++)
+ if (!strcmp(p->str, str))
+ return p->keysym;
+ if (*str < 0x80)
+ return *str; /* FIXME: this is a hack; using the first letter */
+ return NoSymbol;
+}
+
+int SynapticsParseActionStr(LocalDevicePtr local, const char *_str,
+ int *action, int max_actions)
+{
+ char *item;
+ char *str, *next;
+ int num_actions = 0;
+
+ str = xstrdup(_str);
+ if (!str)
+ return 0;
+ for (item = str; item && *item; item = next) {
+ int button, keysym, keycode;
+
+ next = strchr(item, '+');
+ if (next)
+ *next++ = 0;
+ if (!*item)
+ continue;
+ if (sscanf(item, "Button%d", &button) == 1) {
+ action[num_actions++] = (ACTION_BUTTON << 16) | button;
+ } else {
+ keysym = string_to_keysym(item);
+ if (keysym == NoSymbol) {
+ xf86Msg(X_WARNING, "%s: invalid keysym '%s'\n",
+ local->name, item);
+ continue;
+ }
+ keycode = get_keycode(local, keysym);
+ if (keycode < 0) {
+ xf86Msg(X_WARNING, "%s: can't get keycode for '%s'\n",
+ local->name, item);
+ continue;
+ }
+ if (get_modifier(keysym))
+ action[num_actions++] = (ACTION_KEYMOD << 16) | keycode;
+ else
+ action[num_actions++] = (ACTION_KEY << 16) | keycode;
+ }
+ if (num_actions >= max_actions)
+ break;
+ }
+ free(str);
+ return num_actions;
+}
+
+static void
+synaptics_kbdctrl(DeviceIntPtr device, KeybdCtrl *ctrl)
+{
+}
+
+Bool SynapticsInitKeyboard(DeviceIntPtr dev)
+{
+ return InitKeyboardDeviceStruct(dev, NULL, NULL, synaptics_kbdctrl);
+}
+
+void SynapticsSendAction(LocalDevicePtr local, int num_actions, int *action)
+{
+ int n;
+
+ for (n = 0; n < num_actions; n++) {
+ int val = action[n] & 0xffff;
+ switch ((action[n] >> 16) & 0xf) {
+ case ACTION_KEYMOD:
+ xf86PostKeyboardEvent(local->dev, val, TRUE);
+ break;
+ case ACTION_KEY:
+ xf86PostKeyboardEvent(local->dev, val, TRUE);
+ xf86PostKeyboardEvent(local->dev, val, FALSE);
+ break;
+ case ACTION_BUTTON:
+ xf86PostButtonEvent(local->dev, FALSE, val, TRUE, 0, 0);
+ xf86PostButtonEvent(local->dev, FALSE, val, FALSE, 0, 0);
+ break;
+ }
+ }
+ for (n = num_actions - 1; n >= 0; n--) {
+ int val = action[n] & 0xffff;
+ switch ((action[n] >> 16) & 0xf) {
+ case ACTION_KEYMOD:
+ xf86PostKeyboardEvent(local->dev, val, FALSE);
+ break;
+ }
+ }
+}
diff --git a/src/properties.c b/src/properties.c
index 4042edc..0f679dd 100644
--- a/src/properties.c
+++ b/src/properties.c
@@ -87,6 +87,7 @@ Atom prop_touch_button_sticky = 0;
Atom prop_led = 0;
Atom prop_led_status = 0;
Atom prop_led_double_tap = 0;
+Atom prop_multi_touch_pinch = 0;
static Atom
InitAtom(DeviceIntPtr dev, char *name, int format, int nvalues, int *values)
@@ -292,6 +293,9 @@ InitDeviceProperties(InputInfoPtr pInfo)
prop_led_double_tap = InitAtom(local->dev, SYNAPTICS_PROP_LED_DOUBLE_TAP, 8, 1, ¶->led_double_tap);
+ values[0] = para->multi_touch_pinch_start;
+ values[1] = para->multi_touch_pinch_dist;
+ prop_multi_touch_pinch = InitAtom(local->dev, SYNAPTICS_PROP_MULTI_TOUCH_PINCH, 32, 2, values);
}
int
@@ -529,6 +533,15 @@ SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
return BadMatch;
para->led_double_tap = *(CARD8*)prop->data;
+ } else if (property == prop_multi_touch_pinch)
+ {
+ INT32 *dist;
+ if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ dist = (INT32*)prop->data;
+ para->multi_touch_pinch_start = dist[0];
+ para->multi_touch_pinch_dist = dist[1];
} else if (property == prop_gestures)
{
diff --git a/src/synaptics.c b/src/synaptics.c
index 05df1c8..902db53 100644
--- a/src/synaptics.c
+++ b/src/synaptics.c
@@ -598,6 +598,8 @@ static void set_default_parameters(InputInfoPtr pInfo)
pars->touch_button_area = xf86SetIntOption(opts, "TouchButtonArea", 20);
pars->touch_button_sticky = xf86SetIntOption(opts, "TouchButtonSticky", 64);
pars->led_double_tap = xf86SetBoolOption(opts, "LEDDoubleTap", TRUE);
+ pars->multi_touch_pinch_start = xf86SetIntOption(opts, "MultiTouchPinchStart", 0);
+ pars->multi_touch_pinch_dist = xf86SetIntOption(opts, "MultiTouchPinchDelta", 0);
/* Warn about (and fix) incorrectly configured TopEdge/BottomEdge parameters */
if (pars->top_edge > pars->bottom_edge) {
@@ -751,6 +753,13 @@ SynapticsPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
CalculateScalingCoeffs(priv);
+ if (!SynapticsInitKeyboard(dev)) {
+ ErrorF("unable to init keyboard device\n");
+ return !Success;
+ }
+
+ setup_zoom_actions(local);
+
if (!alloc_param_data(pInfo))
goto SetupProc_fail;
@@ -792,6 +801,23 @@ SynapticsPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
}
+static void setup_zoom_actions(LocalDevicePtr local)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
+ char *action;
+
+ action = xf86FindOptionValue(local->options, "ZoomInAction");
+ if (!action)
+ action = "Control+Button4";
+ priv->zoom_in_num_actions =
+ SynapticsParseActionStr(local, action, priv->zoom_in_action, MAX_ACTIONS);
+ action = xf86FindOptionValue(local->options, "ZoomOutAction");
+ if (!action)
+ action = "Control+Button5";
+ priv->zoom_out_num_actions =
+ SynapticsParseActionStr(local, action, priv->zoom_out_action, MAX_ACTIONS);
+}
+
/*
* Uninitialize the device.
*/
@@ -2133,6 +2159,12 @@ stop_coasting(SynapticsPrivate *priv)
priv->scroll_packet_count = 0;
}
+static inline int
+multi_touch_distance(struct SynapticsHwState *hw)
+{
+ return sqrt(SQR(hw->x - hw->multi_touch_x) + SQR(hw->y - hw->multi_touch_y));
+}
+
static int
HandleScrolling(SynapticsPrivate *priv, struct SynapticsHwState *hw,
edge_type edge, Bool finger, struct ScrollData *sd)
@@ -2413,6 +2445,51 @@ HandleScrolling(SynapticsPrivate *priv, struct SynapticsHwState *hw,
return delay;
}
+ static void
+handle_multi_touch_pinch(SynapticsPrivate *priv, struct SynapticsHwState *hw,
+ int *zoom_in, int *zoom_out)
+{
+ SynapticsParameters *para = &priv->synpara;
+ int width = abs(priv->maxx - priv->minx);
+ int dist, dist_diff, abs_diff;
+
+ *zoom_in = *zoom_out = 0;
+
+ if (hw->multi_touch <= 1 || hw->numFingers < 2 ||
+ (priv->multi_touch_mode < MULTI_TOUCH_MODE_GESTURE &&
+ priv->multi_touch_mode != MULTI_TOUCH_MODE_START))
+ return; /* no multi-touch or in other mode */
+ if (para->multi_touch_pinch_dist <= 0 ||
+ para->multi_touch_pinch_start <= 0)
+ return; /* pinch disabled */
+
+ dist = multi_touch_distance(hw);
+ dist_diff = dist - priv->multi_touch_distance;
+ abs_diff = abs(dist_diff);
+ if (priv->prev_multi_touch_distance > 0 &&
+ abs(dist - priv->prev_multi_touch_distance) > width / 4)
+ return; /* jump? */
+
+ if (abs_diff > para->multi_touch_pinch_start) {
+ if (priv->multi_touch_mode != MULTI_TOUCH_MODE_PINCH) {
+ priv->multi_touch_mode = MULTI_TOUCH_MODE_PINCH;
+ priv->vert_scroll_twofinger_on = FALSE;
+ priv->horiz_scroll_twofinger_on = FALSE;
+ }
+ }
+ if (abs_diff > para->multi_touch_pinch_dist) {
+ int num = abs_diff / para->multi_touch_pinch_dist;
+ if (dist_diff > 0) {
+ *zoom_in += num;
+ priv->multi_touch_distance += num * para->multi_touch_pinch_dist;
+ } else {
+ *zoom_out += num;
+ priv->multi_touch_distance -= num * para->multi_touch_pinch_dist;
+ }
+ }
+ priv->prev_multi_touch_distance = dist;
+}
+
static void
handle_clickfinger(SynapticsParameters *para, struct SynapticsHwState *hw)
{
@@ -2551,6 +2628,26 @@ update_hw_button_state(const InputInfoPtr pInfo, struct SynapticsHwState *hw, in
}
}
+static void send_zoom_event(LocalDevicePtr local, int zoom_in, int zoom_out)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
+ int dir = zoom_out - zoom_in;
+ int num_actions;
+ int *action;
+
+ if (!dir)
+ return;
+ if (dir < 0) {
+ num_actions = priv->zoom_in_num_actions;
+ action = priv->zoom_in_action;
+ } else {
+ num_actions = priv->zoom_out_num_actions;
+ action = priv->zoom_out_action;
+ }
+
+ SynapticsSendAction(local, num_actions, action);
+}
+
#define SWAP(a, b) do { int _tmp = (a); (a) = (b); (b) = _tmp; } while (0)
static void swap_hw_pts(struct SynapticsHwState *hw)
{
@@ -2580,6 +2677,8 @@ update_multi_touch(SynapticsPrivate *priv, struct SynapticsHwState *hw)
priv->multi_touch_mode = clickpad_init_multi_touch_mode(priv, hw);
else
priv->multi_touch_mode = MULTI_TOUCH_MODE_START;
+ priv->multi_touch_distance = multi_touch_distance(hw);
+ priv->prev_multi_touch_distance = 0;
}
} else {
if (priv->multi_touch_mode != MULTI_TOUCH_MODE_NONE) {
@@ -2693,6 +2792,7 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw)
int delay = 1000000000;
int timeleft;
Bool inside_active_area;
+ int zoom_in = 0, zoom_out = 0;
update_multi_touch(priv, hw);
@@ -2757,6 +2857,8 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw)
if (timeleft > 0)
delay = MIN(delay, timeleft);
+ handle_multi_touch_pinch(priv, hw, &zoom_in, &zoom_out);
+
/*
* Compensate for unequal x/y resolution. This needs to be done after
* calculations that require unadjusted coordinates, for example edge
@@ -2822,6 +2924,9 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw)
xf86PostButtonEvent(pInfo->dev, FALSE, id, (buttons & (1 << (id - 1))), 0, 0);
}
+ send_zoom_event(local, zoom_in, zoom_out);
+
+
/* Process scroll events only if coordinates are
* in the Synaptics Area
*/
diff --git a/src/synapticsstr.h b/src/synapticsstr.h
index 34038ae..31756cb 100644
--- a/src/synapticsstr.h
+++ b/src/synapticsstr.h
@@ -165,6 +165,8 @@ typedef struct _SynapticsParameters
Bool has_led; /* has an embedded LED */
Bool led_status; /* Current status of LED (1=on) */
Bool led_double_tap; /* double-tap period in ms for touchpad LED control */
+ int multi_touch_pinch_start; /* multi-touch pinch sthard threshold distance */
+ int multi_touch_pinch_dist; /* multi-touch pinch delta threshold distance */
} SynapticsParameters;
@@ -173,10 +175,14 @@ enum MultiTouchMode {
MULTI_TOUCH_MODE_START,
MULTI_TOUCH_MODE_BUTTON,
MULTI_TOUCH_MODE_DRAG,
+ MULTI_TOUCH_MODE_PINCH,
+ MULTI_TOUCH_MODE_GESTURE = MULTI_TOUCH_MODE_PINCH,
MULTI_TOUCH_MODE_SCROLL,
- MULTI_TOUCH_MODE_GESTURE = MULTI_TOUCH_MODE_SCROLL,
};
+#define MAX_ACTIONS 4
+enum { ACTION_BUTTON = 1, ACTION_KEY, ACTION_KEYMOD };
+
typedef struct _SynapticsPrivateRec
{
SynapticsParameters synpara; /* Default parameter settings, read from
@@ -263,6 +269,13 @@ typedef struct _SynapticsPrivateRec
int led_tap_millis;
enum MultiTouchMode multi_touch_mode;
+ int multi_touch_distance;
+ int prev_multi_touch_distance;
+
+ int zoom_in_num_actions;
+ int zoom_in_action[MAX_ACTIONS];
+ int zoom_out_num_actions;
+ int zoom_out_action[MAX_ACTIONS];
enum TouchpadModel model; /* The detected model */
} SynapticsPrivate;
@@ -270,4 +283,10 @@ typedef struct _SynapticsPrivateRec
extern void SynapticsDefaultDimensions(InputInfoPtr pInfo);
+/* keymap.c */
+Bool SynapticsInitKeyboard(DeviceIntPtr dev);
+int SynapticsParseActionStr(LocalDevicePtr local, const char *_str,
+ int *action, int max_actions);
+void SynapticsSendAction(LocalDevicePtr local, int num_actions, int *action);
+
#endif /* _SYNAPTICSSTR_H_ */
diff --git a/tools/synclient.c b/tools/synclient.c
index b51fe42..ab737dc 100644
--- a/tools/synclient.c
+++ b/tools/synclient.c
@@ -147,6 +147,8 @@ static struct Parameter params[] = {
{"TouchButtonSticky", PT_INT, 0, 1000, SYNAPTICS_PROP_TOUCH_BUTTON_STICKY, 32, 0},
{"LEDStatus", PT_BOOL, 0, 1, SYNAPTICS_PROP_LED_STATUS, 8, 0},
{"LEDDoubleTap", PT_BOOL, 0, 1, SYNAPTICS_PROP_LED_DOUBLE_TAP, 8, 0},
+ {"MultiTouchPinchStart", PT_INT, 0, 1000, SYNAPTICS_PROP_MULTI_TOUCH_PINCH, 32, 0},
+ {"MultiTouchPinchDelta", PT_INT, 0, 4000, SYNAPTICS_PROP_MULTI_TOUCH_PINCH, 32, 1},
{ NULL, 0, 0, 0, 0 }
};
--
1.7.3.1
More information about the xorg-devel
mailing list