mirror of
https://github.com/X11Libre/xf86-input-synaptics.git
synced 2026-04-14 11:54:16 +00:00
1697 lines
47 KiB
C
1697 lines
47 KiB
C
/*
|
|
* Copyright 2003 Jörg Bösner <ich@joerg-boesner.de>
|
|
* patch for switching the touchpad off (for example, when a
|
|
* USB mouse is connected)
|
|
*
|
|
* Copyright 2003 Hartwig Felger <hgfelger@hgfelger.de>
|
|
* patch to make the horizontal wheel replacement buttons work.
|
|
*
|
|
* Copyright 2002 Peter Osterlund <petero2@telia.com>
|
|
* patches for fast scrolling, palm detection, edge motion,
|
|
* horizontal scrolling
|
|
*
|
|
* Copyright 2002 S. Lehner <sam_x@bluemail.ch>
|
|
* for newer Firmware (5.8) protocol changes for 3rd to 6th button
|
|
*
|
|
* Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
|
|
* start merging tpconfig and gpm code to an xfree input module
|
|
* adding some changes and extensions (ex. 3rd and 4th button)
|
|
*
|
|
* Copyright (c) 1999 Henry Davies <hdavies@ameritech.net> for the
|
|
* absolute to relative translation code (from the gpm source)
|
|
* and some other ideas
|
|
*
|
|
* Synaptics Passthrough Support
|
|
* Copyright (c) 2002 Linuxcare Inc. David Kennedy <dkennedy@linuxcare.com>
|
|
* adapted to version 0.12.1
|
|
* Copyright (c) 2003 Fred Hucht <fred@thp.Uni-Duisburg.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*
|
|
* Trademarks are the property of their respective owners.
|
|
*
|
|
*/
|
|
|
|
|
|
/*****************************************************************************
|
|
* Standard Headers
|
|
****************************************************************************/
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <misc.h>
|
|
#include <xf86.h>
|
|
#define NEED_XF86_TYPES
|
|
#include <xf86_ansic.h>
|
|
#include <xf86_OSproc.h>
|
|
#include <xf86Xinput.h>
|
|
#include <xisb.h>
|
|
#include <exevents.h> /* Needed for InitValuator/Proximity stuff */
|
|
#include "mipointer.h"
|
|
#ifdef XFREE_4_0_3
|
|
#include <xf86Optrec.h> /* needed for Options */
|
|
#endif
|
|
|
|
|
|
/*****************************************************************************
|
|
* Local Headers
|
|
****************************************************************************/
|
|
#define SYNAPTICS_PRIVATE
|
|
#include "synaptics.h"
|
|
#include "ps2comm.h"
|
|
|
|
/*****************************************************************************
|
|
* Variables without includable headers
|
|
****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Local Variables and Types
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
|
|
* section 2.3.2, which says that they should be valid regardless of the
|
|
* actual size of the sensor.
|
|
*/
|
|
#define XMIN_NOMINAL 1472
|
|
#define XMAX_NOMINAL 5472
|
|
#define YMIN_NOMINAL 1408
|
|
#define YMAX_NOMINAL 4448
|
|
|
|
#define MAX_UNSYNC_PACKETS 10 /* i.e. 10 to 60 bytes */
|
|
|
|
typedef enum {
|
|
BOTTOM_EDGE = 1,
|
|
TOP_EDGE = 2,
|
|
LEFT_EDGE = 4,
|
|
RIGHT_EDGE = 8,
|
|
LEFT_BOTTOM_EDGE = BOTTOM_EDGE | LEFT_EDGE,
|
|
RIGHT_BOTTOM_EDGE = BOTTOM_EDGE | RIGHT_EDGE,
|
|
RIGHT_TOP_EDGE = TOP_EDGE | RIGHT_EDGE,
|
|
LEFT_TOP_EDGE = TOP_EDGE | LEFT_EDGE
|
|
} edge_type;
|
|
|
|
#define MAX(a, b) (((a)>(b))?(a):(b))
|
|
#define MIN(a, b) (((a)<(b))?(a):(b))
|
|
#define TIME_DIFF(a, b) ((long)((a)-(b)))
|
|
#define SYSCALL(call) while (((call) == -1) && (errno == EINTR))
|
|
|
|
/* for auto-dev: */
|
|
#define DEV_INPUT_EVENT "/dev/input"
|
|
#define EVENT_DEV_NAME "event"
|
|
|
|
#define VERSION "0.12.2"
|
|
|
|
/*****************************************************************************
|
|
* Forward declaration
|
|
****************************************************************************/
|
|
static InputInfoPtr
|
|
SynapticsPreInit(InputDriverPtr drv, IDevPtr dev, int flags);
|
|
|
|
|
|
InputDriverRec SYNAPTICS = {
|
|
1,
|
|
"synaptics",
|
|
NULL,
|
|
SynapticsPreInit,
|
|
/*SynapticsUnInit*/ NULL,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
#ifdef XFree86LOADER
|
|
|
|
static XF86ModuleVersionInfo VersionRec = {
|
|
"synaptics",
|
|
MODULEVENDORSTRING,
|
|
MODINFOSTRING1,
|
|
MODINFOSTRING2,
|
|
XF86_VERSION_CURRENT,
|
|
1, 0, 0,
|
|
ABI_CLASS_XINPUT,
|
|
ABI_XINPUT_VERSION,
|
|
MOD_CLASS_XINPUT,
|
|
{0, 0, 0, 0} /* signature, to be patched into the file by
|
|
* a tool */
|
|
};
|
|
|
|
|
|
static pointer
|
|
SetupProc(pointer module, pointer options, int *errmaj, int *errmin)
|
|
{
|
|
xf86AddInputDriver(&SYNAPTICS, module, 0);
|
|
return module;
|
|
}
|
|
|
|
XF86ModuleData synapticsModuleData = {&VersionRec, &SetupProc, NULL };
|
|
|
|
#endif /* XFree86LOADER */
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function Definitions
|
|
****************************************************************************/
|
|
|
|
static void
|
|
SetDeviceAndProtocol(LocalDevicePtr local)
|
|
{
|
|
char *str_par;
|
|
SynapticsPrivate *priv = local->private;
|
|
|
|
priv->proto = SYN_PROTO_PSAUX;
|
|
str_par = xf86FindOptionValue(local->options, "Protocol");
|
|
if (str_par && !strcmp(str_par, "event")) {
|
|
priv->proto = SYN_PROTO_EVENT;
|
|
} else if (str_par && !strcmp(str_par, "psaux")) {
|
|
/* Already set up */
|
|
} else { /* default to auto-dev */
|
|
/* We are trying to find the right eventX Device, or fall back to
|
|
the psaux Protocol and the given Device from XF86Config */
|
|
int fd = -1;
|
|
int i;
|
|
for (i = 0; ; i++) {
|
|
char fname[64];
|
|
struct input_id id;
|
|
int ret;
|
|
|
|
sprintf(fname, "%s/%s%d", DEV_INPUT_EVENT, EVENT_DEV_NAME, i);
|
|
SYSCALL(fd = open(fname, O_RDONLY));
|
|
if (fd < 0) {
|
|
if (errno == ENOENT) {
|
|
ErrorF("%s no synaptics event device found\n", local->name);
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
SYSCALL(ret = ioctl(fd, EVIOCGID, &id));
|
|
SYSCALL(close(fd));
|
|
if (ret >= 0) {
|
|
if ((id.bustype == BUS_I8042) &&
|
|
(id.vendor == 0x0002) &&
|
|
(id.product == PSMOUSE_SYNAPTICS)) {
|
|
priv->proto = SYN_PROTO_EVENT;
|
|
xf86Msg(X_PROBED, "%s auto-dev sets Synaptics Device to %s\n",
|
|
local->name, fname);
|
|
xf86ReplaceStrOption(local->options, "Device", fname);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* called by the module loader for initialization
|
|
*/
|
|
static InputInfoPtr
|
|
SynapticsPreInit(InputDriverPtr drv, IDevPtr dev, int flags)
|
|
{
|
|
LocalDevicePtr local;
|
|
SynapticsPrivate *priv;
|
|
#ifdef XFREE_4_0_3
|
|
XF86OptionPtr optList;
|
|
#else
|
|
pointer optList;
|
|
#endif
|
|
char *str_par;
|
|
int shmid;
|
|
unsigned long now;
|
|
|
|
/* allocate memory for SynaticsPrivateRec */
|
|
priv = xcalloc(1, sizeof(SynapticsPrivate));
|
|
if (!priv)
|
|
return NULL;
|
|
|
|
/* Allocate a new InputInfoRec and add it to the head xf86InputDevs. */
|
|
local = xf86AllocateInput(drv, 0);
|
|
if (!local) {
|
|
xfree(priv);
|
|
return NULL;
|
|
}
|
|
|
|
/* initialize the InputInfoRec */
|
|
local->name = dev->identifier;
|
|
local->type_name = XI_MOUSE; /* XI_TOUCHPAD and KDE killed the X Server at startup ? */
|
|
local->device_control = DeviceControl;
|
|
local->read_input = ReadInput;
|
|
local->control_proc = ControlProc;
|
|
local->close_proc = CloseProc;
|
|
local->switch_mode = SwitchMode;
|
|
local->conversion_proc = ConvertProc;
|
|
local->reverse_conversion_proc = NULL;
|
|
local->dev = NULL;
|
|
local->private = priv;
|
|
local->private_flags = 0;
|
|
local->flags = XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS;
|
|
local->conf_idev = dev;
|
|
local->motion_history_proc = xf86GetMotionEvents;
|
|
local->history_size = 0;
|
|
local->always_core_feedback = 0;
|
|
|
|
xf86Msg(X_INFO, "Synaptics touchpad driver version %s\n", VERSION);
|
|
|
|
xf86CollectInputOptions(local, NULL, NULL);
|
|
|
|
xf86OptionListReport(local->options);
|
|
|
|
SetDeviceAndProtocol(local);
|
|
|
|
/* open the touchpad device */
|
|
local->fd = xf86OpenSerial(local->options);
|
|
if (local->fd == -1) {
|
|
ErrorF("Synaptics driver unable to open device\n");
|
|
goto SetupProc_fail;
|
|
}
|
|
xf86ErrorFVerb( 6, "port opened successfully\n" );
|
|
|
|
/* initialize variables */
|
|
priv->timer = NULL;
|
|
priv->repeatButtons = 0;
|
|
priv->nextRepeat = 0;
|
|
now = GetTimeInMillis();
|
|
priv->count_packet_finger = 0;
|
|
priv->tapping_millis = now;
|
|
priv->button_delay_millis = now;
|
|
priv->touch_on.millis = now;
|
|
priv->hasGuest = FALSE;
|
|
|
|
/* install shared memory or normal memory for parameter */
|
|
priv->shm_config = FALSE;
|
|
if (xf86SetBoolOption(local->options, "SHMConfig", FALSE)) {
|
|
if ((shmid = xf86shmget(SHM_SYNAPTICS, 0, 0)) != -1)
|
|
xf86shmctl(shmid, XF86IPC_RMID, NULL);
|
|
if ((shmid = xf86shmget(SHM_SYNAPTICS, sizeof(SynapticsSHM),
|
|
0777 | XF86IPC_CREAT)) == -1) {
|
|
xf86Msg(X_ERROR, "%s error shmget\n", local->name);
|
|
goto SetupProc_fail;
|
|
}
|
|
else if ((priv->synpara = (SynapticsSHM*) xf86shmat(shmid, NULL, 0)) == NULL) {
|
|
xf86Msg(X_ERROR, "%s error shmat\n", local->name);
|
|
goto SetupProc_fail;
|
|
}
|
|
priv->shm_config = TRUE;
|
|
} else {
|
|
priv->synpara = xcalloc(1, sizeof(SynapticsSHM));
|
|
if (!priv->synpara)
|
|
goto SetupProc_fail;
|
|
}
|
|
|
|
/* read the parameter */
|
|
priv->synpara->left_edge = xf86SetIntOption(local->options, "LeftEdge", 1900);
|
|
priv->synpara->right_edge = xf86SetIntOption(local->options, "RightEdge", 5400);
|
|
priv->synpara->top_edge = xf86SetIntOption(local->options, "TopEdge", 1900);
|
|
priv->synpara->bottom_edge = xf86SetIntOption(local->options, "BottomEdge", 4000);
|
|
priv->synpara->finger_low = xf86SetIntOption(local->options, "FingerLow", 25);
|
|
priv->synpara->finger_high = xf86SetIntOption(local->options, "FingerHigh", 30);
|
|
priv->synpara->tap_time = xf86SetIntOption(local->options, "MaxTapTime", 180);
|
|
priv->synpara->tap_move = xf86SetIntOption(local->options, "MaxTapMove", 220);
|
|
priv->synpara->emulate_mid_button_time = xf86SetIntOption(local->options,
|
|
"EmulateMidButtonTime", 75);
|
|
priv->synpara->scroll_dist_vert = xf86SetIntOption(local->options, "VertScrollDelta", 100);
|
|
priv->synpara->scroll_dist_horiz = xf86SetIntOption(local->options, "HorizScrollDelta", 100);
|
|
priv->synpara->edge_motion_speed = xf86SetIntOption(local->options, "EdgeMotionSpeed", 40);
|
|
priv->synpara->repeater = xf86SetStrOption(local->options, "Repeater", NULL);
|
|
priv->synpara->updown_button_scrolling = xf86SetBoolOption(local->options, "UpDownScrolling", TRUE);
|
|
priv->synpara->touchpad_off = xf86SetBoolOption(local->options, "TouchpadOff", FALSE);
|
|
priv->synpara->locked_drags = xf86SetBoolOption(local->options, "LockedDrags", FALSE);
|
|
priv->synpara->tap_action[RT_TAP] = xf86SetIntOption(local->options, "RTCornerButton", 2);
|
|
priv->synpara->tap_action[RB_TAP] = xf86SetIntOption(local->options, "RBCornerButton", 3);
|
|
priv->synpara->tap_action[LT_TAP] = xf86SetIntOption(local->options, "LTCornerButton", 0);
|
|
priv->synpara->tap_action[LB_TAP] = xf86SetIntOption(local->options, "LBCornerButton", 0);
|
|
priv->synpara->tap_action[F1_TAP] = xf86SetIntOption(local->options, "TapButton1", 1);
|
|
priv->synpara->tap_action[F2_TAP] = xf86SetIntOption(local->options, "TapButton2", 2);
|
|
priv->synpara->tap_action[F3_TAP] = xf86SetIntOption(local->options, "TapButton3", 3);
|
|
|
|
str_par = xf86FindOptionValue(local->options, "MinSpeed");
|
|
if ((!str_par) || (xf86sscanf(str_par, "%lf", &priv->synpara->min_speed) != 1))
|
|
priv->synpara->min_speed=0.02;
|
|
str_par = xf86FindOptionValue(local->options, "MaxSpeed");
|
|
if ((!str_par) || (xf86sscanf(str_par, "%lf", &priv->synpara->max_speed) != 1))
|
|
priv->synpara->max_speed=0.18;
|
|
str_par = xf86FindOptionValue(local->options, "AccelFactor");
|
|
if ((!str_par) || (xf86sscanf(str_par, "%lf", &priv->synpara->accl) != 1))
|
|
priv->synpara->accl=0.0015;
|
|
|
|
/* Warn about (and fix) incorrectly configured TopEdge/BottomEdge parameters */
|
|
if (priv->synpara->top_edge > priv->synpara->bottom_edge) {
|
|
int tmp = priv->synpara->top_edge;
|
|
priv->synpara->top_edge = priv->synpara->bottom_edge;
|
|
priv->synpara->bottom_edge = tmp;
|
|
xf86Msg(X_WARNING, "%s: TopEdge is bigger than BottomEdge. Fixing.\n",
|
|
local->name);
|
|
}
|
|
|
|
priv->buffer = XisbNew(local->fd, 200);
|
|
DBG(9, XisbTrace(priv->buffer, 1));
|
|
|
|
if (priv->synpara->repeater) {
|
|
/* create repeater fifo */
|
|
if ((xf86mknod(priv->synpara->repeater, 666, XF86_S_IFIFO) != 0) &&
|
|
(xf86errno != xf86_EEXIST)) {
|
|
xf86Msg(X_ERROR, "%s can't create repeater fifo\n", local->name);
|
|
xf86free(priv->synpara->repeater);
|
|
priv->synpara->repeater = NULL;
|
|
priv->fifofd = -1;
|
|
} else {
|
|
/* open the repeater fifo */
|
|
optList = xf86NewOption("Device", priv->synpara->repeater);
|
|
if ((priv->fifofd = xf86OpenSerial(optList)) == -1) {
|
|
xf86Msg(X_ERROR, "%s repeater device open failed\n", local->name);
|
|
xf86free(priv->synpara->repeater);
|
|
priv->synpara->repeater = NULL;
|
|
priv->fifofd = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (QueryHardware(local) != Success) {
|
|
xf86Msg(X_ERROR, "%s Unable to query/initialize Synaptics hardware.\n", local->name);
|
|
goto SetupProc_fail;
|
|
}
|
|
|
|
local->history_size = xf86SetIntOption( local->options, "HistorySize", 0 );
|
|
|
|
xf86ProcessCommonOptions(local, local->options);
|
|
local->flags |= XI86_CONFIGURED;
|
|
|
|
if (local->fd != -1) {
|
|
xf86RemoveEnabledDevice(local);
|
|
if (priv->buffer) {
|
|
XisbFree(priv->buffer);
|
|
priv->buffer = NULL;
|
|
}
|
|
xf86CloseSerial(local->fd);
|
|
}
|
|
local->fd = -1;
|
|
return local;
|
|
|
|
SetupProc_fail:
|
|
if (local->fd >= 0) {
|
|
RemoveEnabledDevice(local->fd);
|
|
xf86CloseSerial(local->fd);
|
|
local->fd = -1;
|
|
}
|
|
|
|
if (priv->buffer)
|
|
XisbFree(priv->buffer);
|
|
if (priv->synpara) {
|
|
if (priv->shm_config) {
|
|
if ((shmid = xf86shmget(SHM_SYNAPTICS, 0, 0)) != -1)
|
|
xf86shmctl(shmid, XF86IPC_RMID, NULL);
|
|
} else {
|
|
xfree(priv->synpara);
|
|
}
|
|
}
|
|
/* Freeing priv makes the X server crash. Don't know why.
|
|
xfree(priv);
|
|
*/
|
|
return local;
|
|
}
|
|
|
|
/*
|
|
* Alter the control parameters for the mouse. Note that all special
|
|
* protocol values are handled by dix.
|
|
*/
|
|
static void
|
|
SynapticsCtrl(DeviceIntPtr device, PtrCtrl *ctrl)
|
|
{
|
|
DBG(3, ErrorF("SynapticsCtrl called.\n"));
|
|
/*
|
|
pInfo = device->public.devicePrivate;
|
|
pMse = pInfo->private;
|
|
|
|
pMse->num = ctrl->num;
|
|
pMse->den = ctrl->den;
|
|
pMse->threshold = ctrl->threshold;
|
|
*/
|
|
}
|
|
|
|
static Bool
|
|
DeviceControl(DeviceIntPtr dev, int mode)
|
|
{
|
|
Bool RetValue;
|
|
|
|
switch (mode) {
|
|
case DEVICE_INIT:
|
|
DeviceInit(dev);
|
|
RetValue = Success;
|
|
break;
|
|
case DEVICE_ON:
|
|
RetValue = DeviceOn( dev );
|
|
break;
|
|
case DEVICE_OFF:
|
|
RetValue = DeviceOff( dev );
|
|
break;
|
|
case DEVICE_CLOSE:
|
|
{
|
|
int shmid;
|
|
LocalDevicePtr local = (LocalDevicePtr) dev->public.devicePrivate;
|
|
SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
|
|
RetValue = DeviceOff( dev );
|
|
if (priv->shm_config)
|
|
if ((shmid = xf86shmget(SHM_SYNAPTICS, 0, 0)) != -1)
|
|
xf86shmctl(shmid, XF86IPC_RMID, NULL);
|
|
}
|
|
break;
|
|
default:
|
|
RetValue = BadValue;
|
|
}
|
|
|
|
return RetValue;
|
|
}
|
|
|
|
static Bool
|
|
DeviceOn(DeviceIntPtr dev)
|
|
{
|
|
LocalDevicePtr local = (LocalDevicePtr) dev->public.devicePrivate;
|
|
SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
|
|
|
|
DBG(3, ErrorF("Synaptics DeviceOn called\n"));
|
|
|
|
local->fd = xf86OpenSerial(local->options);
|
|
if (local->fd == -1) {
|
|
xf86Msg(X_WARNING, "%s: cannot open input device\n", local->name);
|
|
return !Success;
|
|
}
|
|
|
|
/* Try to grab the event device so that data doesn't leak to /dev/input/mice */
|
|
if (priv->proto == SYN_PROTO_EVENT) {
|
|
int ret;
|
|
SYSCALL(ret = ioctl(local->fd, EVIOCGRAB, (pointer)1));
|
|
if (ret < 0) {
|
|
xf86Msg(X_WARNING, "%s can't grab event device\n",
|
|
local->name, errno);
|
|
}
|
|
}
|
|
|
|
priv->buffer = XisbNew(local->fd, 64);
|
|
if (!priv->buffer) {
|
|
xf86CloseSerial(local->fd);
|
|
local->fd = -1;
|
|
return !Success;
|
|
}
|
|
|
|
xf86FlushInput(local->fd);
|
|
|
|
/* reinit the pad */
|
|
QueryHardware(local);
|
|
xf86AddEnabledDevice(local);
|
|
dev->public.on = TRUE;
|
|
|
|
return Success;
|
|
}
|
|
|
|
static Bool
|
|
DeviceOff(DeviceIntPtr dev)
|
|
{
|
|
LocalDevicePtr local = (LocalDevicePtr) dev->public.devicePrivate;
|
|
SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
|
|
|
|
DBG(3, ErrorF("Synaptics DeviceOff called\n"));
|
|
|
|
if (local->fd != -1) {
|
|
xf86RemoveEnabledDevice(local);
|
|
if (priv->proto == SYN_PROTO_PSAUX)
|
|
synaptics_set_mode(local->fd, 0);
|
|
if (priv->buffer) {
|
|
XisbFree(priv->buffer);
|
|
priv->buffer = NULL;
|
|
}
|
|
xf86CloseSerial(local->fd);
|
|
}
|
|
dev->public.on = FALSE;
|
|
return Success;
|
|
}
|
|
|
|
static Bool
|
|
DeviceInit(DeviceIntPtr dev)
|
|
{
|
|
LocalDevicePtr local = (LocalDevicePtr) dev->public.devicePrivate;
|
|
unsigned char map[] = {0, 1, 2, 3, 4, 5, 6, 7};
|
|
|
|
DBG(3, ErrorF("Synaptics DeviceInit called\n"));
|
|
|
|
dev->public.on = FALSE;
|
|
|
|
InitPointerDeviceStruct((DevicePtr)dev, map,
|
|
7,
|
|
miPointerGetMotionEvents, SynapticsCtrl,
|
|
miPointerGetMotionBufferSize());
|
|
|
|
/* X valuator */
|
|
xf86InitValuatorAxisStruct(dev, 0, 0, -1, 1, 0, 1);
|
|
xf86InitValuatorDefaults(dev, 0);
|
|
/* Y valuator */
|
|
xf86InitValuatorAxisStruct(dev, 1, 0, -1, 1, 0, 1);
|
|
xf86InitValuatorDefaults(dev, 1);
|
|
|
|
xf86MotionHistoryAllocate(local);
|
|
|
|
return Success;
|
|
}
|
|
|
|
static int
|
|
move_distance(int dx, int dy)
|
|
{
|
|
return xf86sqrt((dx * dx) + (dy * dy));
|
|
}
|
|
|
|
static edge_type
|
|
edge_detection(SynapticsPrivate *priv, int x, int y)
|
|
{
|
|
edge_type edge = 0;
|
|
|
|
if (x > priv->synpara->right_edge)
|
|
edge |= RIGHT_EDGE;
|
|
else if (x < priv->synpara->left_edge)
|
|
edge |= LEFT_EDGE;
|
|
|
|
if (y < priv->synpara->top_edge)
|
|
edge |= TOP_EDGE;
|
|
else if (y > priv->synpara->bottom_edge)
|
|
edge |= BOTTOM_EDGE;
|
|
|
|
return edge;
|
|
}
|
|
|
|
static CARD32
|
|
timerFunc(OsTimerPtr timer, CARD32 now, pointer arg)
|
|
{
|
|
LocalDevicePtr local = (LocalDevicePtr) (arg);
|
|
SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
|
|
struct SynapticsHwState hw;
|
|
int delay;
|
|
int sigstate;
|
|
CARD32 wakeUpTime;
|
|
|
|
sigstate = xf86BlockSIGIO();
|
|
|
|
hw = priv->hwState;
|
|
hw.guest_dx = hw.guest_dy = 0;
|
|
hw.millis = now;
|
|
delay = HandleState(local, &hw);
|
|
|
|
/*
|
|
* Workaround for wraparound bug in the TimerSet function. This bug is already
|
|
* fixed in CVS, but this driver needs to work with XFree86 versions 4.2.x and
|
|
* 4.3.x too.
|
|
*/
|
|
wakeUpTime = now + delay;
|
|
if (wakeUpTime <= now)
|
|
wakeUpTime = 0xffffffffL;
|
|
|
|
priv->timer = TimerSet(priv->timer, TimerAbsolute, wakeUpTime, timerFunc, local);
|
|
|
|
xf86UnblockSIGIO(sigstate);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define MOVE_HIST(a) (priv->move_hist[((priv->count_packet_finger-(a))%SYNAPTICS_MOVE_HISTORY)])
|
|
|
|
static int clamp(int val, int min, int max)
|
|
{
|
|
if (val < min)
|
|
return min;
|
|
else if (val < max)
|
|
return val;
|
|
else
|
|
return max;
|
|
}
|
|
|
|
|
|
/*
|
|
* called for each full received packet from the touchpad
|
|
*/
|
|
static void
|
|
ReadInput(LocalDevicePtr local)
|
|
{
|
|
SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
|
|
struct SynapticsHwState hw;
|
|
int delay = 0;
|
|
Bool newDelay = FALSE;
|
|
|
|
while (SynapticsGetHwState(local, priv, &hw) == Success) {
|
|
hw.millis = GetTimeInMillis();
|
|
delay = HandleState(local, &hw);
|
|
newDelay = TRUE;
|
|
}
|
|
|
|
if (newDelay)
|
|
priv->timer = TimerSet(priv->timer, 0, delay, timerFunc, local);
|
|
}
|
|
|
|
static int
|
|
HandleMidButtonEmulation(SynapticsPrivate *priv, struct SynapticsHwState *hw, long *delay)
|
|
{
|
|
SynapticsSHM *para = priv->synpara;
|
|
Bool done = FALSE;
|
|
long timeleft;
|
|
int mid = 0;
|
|
|
|
while (!done) {
|
|
switch (priv->mid_emu_state) {
|
|
case MBE_OFF:
|
|
priv->button_delay_millis = hw->millis;
|
|
if (hw->left) {
|
|
priv->mid_emu_state = MBE_LEFT;
|
|
} else if (hw->right) {
|
|
priv->mid_emu_state = MBE_RIGHT;
|
|
} else {
|
|
done = TRUE;
|
|
}
|
|
break;
|
|
case MBE_LEFT:
|
|
timeleft = TIME_DIFF(priv->button_delay_millis + para->emulate_mid_button_time,
|
|
hw->millis);
|
|
if (timeleft > 0)
|
|
*delay = MIN(*delay, timeleft);
|
|
if (!hw->left || (timeleft <= 0)) {
|
|
hw->left = TRUE;
|
|
priv->mid_emu_state = MBE_TIMEOUT;
|
|
done = TRUE;
|
|
} else if (hw->right) {
|
|
priv->mid_emu_state = MBE_MID;
|
|
} else {
|
|
hw->left = FALSE;
|
|
done = TRUE;
|
|
}
|
|
break;
|
|
case MBE_RIGHT:
|
|
timeleft = TIME_DIFF(priv->button_delay_millis + para->emulate_mid_button_time,
|
|
hw->millis);
|
|
if (timeleft > 0)
|
|
*delay = MIN(*delay, timeleft);
|
|
if (!hw->right || (timeleft <= 0)) {
|
|
hw->right = TRUE;
|
|
priv->mid_emu_state = MBE_TIMEOUT;
|
|
done = TRUE;
|
|
} else if (hw->left) {
|
|
priv->mid_emu_state = MBE_MID;
|
|
} else {
|
|
hw->right = FALSE;
|
|
done = TRUE;
|
|
}
|
|
break;
|
|
case MBE_MID:
|
|
if (!hw->left && !hw->right) {
|
|
priv->mid_emu_state = MBE_OFF;
|
|
} else {
|
|
mid = TRUE;
|
|
hw->left = hw->right = FALSE;
|
|
done = TRUE;
|
|
}
|
|
break;
|
|
case MBE_TIMEOUT:
|
|
if (!hw->left && !hw->right) {
|
|
priv->mid_emu_state = MBE_OFF;
|
|
} else {
|
|
done = TRUE;
|
|
}
|
|
}
|
|
}
|
|
return mid;
|
|
}
|
|
|
|
static int
|
|
SynapticsDetectFinger(SynapticsPrivate *priv, struct SynapticsHwState *hw)
|
|
{
|
|
SynapticsSHM *para = priv->synpara;
|
|
int finger;
|
|
|
|
/* finger detection thru pressure and threshold */
|
|
finger = (((hw->z > para->finger_high) && !priv->finger_flag) ||
|
|
((hw->z > para->finger_low) && priv->finger_flag));
|
|
|
|
/* palm detection */
|
|
if (finger) {
|
|
if ((hw->z > 200) && (hw->fingerWidth > 10))
|
|
priv->palm = TRUE;
|
|
} else {
|
|
priv->palm = FALSE;
|
|
}
|
|
if (hw->x == 0)
|
|
priv->avg_width = 0;
|
|
else
|
|
priv->avg_width += (hw->fingerWidth - priv->avg_width + 1) / 2;
|
|
if (finger && !priv->finger_flag) {
|
|
int safe_width = MAX(hw->fingerWidth, priv->avg_width);
|
|
if (hw->numFingers > 1)
|
|
finger = TRUE; /* more than one finger -> not a palm */
|
|
else if ((safe_width < 6) && (priv->prev_z < para->finger_high))
|
|
finger = TRUE; /* thin finger, distinct touch -> not a palm */
|
|
else if ((safe_width < 7) && (priv->prev_z < para->finger_high / 2))
|
|
finger = TRUE; /* thin finger, distinct touch -> not a palm */
|
|
else if (hw->z > priv->prev_z + 1) /* z not stable, may be a palm */
|
|
finger = FALSE;
|
|
else if (hw->z < priv->prev_z - 5) /* z not stable, may be a palm */
|
|
finger = FALSE;
|
|
else if (hw->z > 200) /* z too large -> probably palm */
|
|
finger = FALSE;
|
|
else if (hw->fingerWidth > 10) /* finger width too large -> probably palm */
|
|
finger = FALSE;
|
|
}
|
|
priv->prev_z = hw->z;
|
|
|
|
return finger;
|
|
}
|
|
|
|
static void
|
|
ReportTap(SynapticsPrivate *priv, TapEvent tap)
|
|
{
|
|
int button = priv->synpara->tap_action[tap];
|
|
switch (button) {
|
|
case 1:
|
|
priv->tap_left = TRUE;
|
|
break;
|
|
case 2:
|
|
priv->tap_mid = TRUE;
|
|
break;
|
|
case 3:
|
|
priv->tap_right = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* React on changes in the hardware state. This function is called every time
|
|
* the hardware state changes. The return value is used to specify how many
|
|
* milliseconds to wait before calling the function again if no state change
|
|
* occurs.
|
|
*/
|
|
static int
|
|
HandleState(LocalDevicePtr local, struct SynapticsHwState* hw)
|
|
{
|
|
SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
|
|
SynapticsSHM *para = priv->synpara;
|
|
Bool finger;
|
|
int dist, dx, dy, buttons, id;
|
|
edge_type edge;
|
|
Bool mid;
|
|
double speed, integral;
|
|
int change;
|
|
int scroll_up, scroll_down, scroll_left, scroll_right;
|
|
int double_click;
|
|
long delay = 1000000000;
|
|
long timeleft;
|
|
int i;
|
|
|
|
dx = dy = 0;
|
|
|
|
/* update hardware state in shared memory */
|
|
para->x = hw->x;
|
|
para->y = hw->y;
|
|
para->z = hw->z;
|
|
para->numFingers = hw->numFingers;
|
|
para->fingerWidth = hw->fingerWidth;
|
|
para->left = hw->left;
|
|
para->right = hw->right;
|
|
para->up = hw->up;
|
|
para->down = hw->down;
|
|
for (i = 0; i < 8; i++)
|
|
para->multi[i] = hw->multi[i];
|
|
para->guest_left = hw->guest_left;
|
|
para->guest_mid = hw->guest_mid;
|
|
para->guest_right = hw->guest_right;
|
|
para->guest_dx = hw->guest_dx;
|
|
para->guest_dy = hw->guest_dy;
|
|
|
|
/* If touchpad is switched off, we skip the whole thing and return delay */
|
|
if (para->touchpad_off == TRUE)
|
|
return delay;
|
|
|
|
/* Treat the first two multi buttons as up/down for now. */
|
|
hw->up |= hw->multi[0];
|
|
hw->down |= hw->multi[1];
|
|
|
|
/* 3rd button emulation */
|
|
mid = HandleMidButtonEmulation(priv, hw, &delay);
|
|
|
|
/* Up/Down button scrolling or middle/double click */
|
|
double_click = FALSE;
|
|
if (!para->updown_button_scrolling) {
|
|
if (hw->down) { /* map down button to middle button */
|
|
mid = TRUE;
|
|
}
|
|
|
|
if (hw->up) { /* up button generates double click */
|
|
if (!priv->prev_up)
|
|
double_click = TRUE;
|
|
}
|
|
priv->prev_up = hw->up;
|
|
|
|
/* reset up/down button events */
|
|
hw->up = hw->down = FALSE;
|
|
}
|
|
|
|
edge = edge_detection(priv, hw->x, hw->y);
|
|
|
|
finger = SynapticsDetectFinger(priv, hw);
|
|
|
|
/* tap and drag detection */
|
|
if (priv->palm) {
|
|
/* Palm detected, skip tap/drag processing */
|
|
} else if (finger && !priv->finger_flag) { /* touched */
|
|
DBG(7, ErrorF("touched - x:%d, y:%d millis:%lu\n", hw->x, hw->y, hw->millis));
|
|
if (priv->tap) {
|
|
DBG(7, ErrorF("drag detected - tap time:%lu\n", priv->tapping_millis));
|
|
priv->drag = TRUE; /* drag gesture */
|
|
}
|
|
priv->touch_on.x = hw->x;
|
|
priv->touch_on.y = hw->y;
|
|
priv->touch_on.millis = hw->millis;
|
|
} else if (!finger && priv->finger_flag) { /* untouched */
|
|
DBG(7, ErrorF("untouched - x:%d, y:%d millis:%lu finger:%d\n",
|
|
hw->x, hw->y, hw->millis, priv->finger_count));
|
|
/* check if
|
|
1. the tap is in tap_time
|
|
2. the max movement is in tap_move or more than one finger are tapped */
|
|
timeleft = TIME_DIFF(priv->touch_on.millis + para->tap_time, hw->millis);
|
|
if (timeleft > 0 &&
|
|
(((abs(hw->x - priv->touch_on.x) < para->tap_move) &&
|
|
(abs(hw->y - priv->touch_on.y) < para->tap_move)) ||
|
|
priv->finger_count)) {
|
|
if (priv->drag) {
|
|
DBG(7, ErrorF("double tapping detected\n"));
|
|
priv->doubletap = TRUE;
|
|
priv->tap = FALSE;
|
|
} else {
|
|
DBG(7, ErrorF("tapping detected @ "));
|
|
priv->tapping_millis = hw->millis;
|
|
priv->tap = TRUE;
|
|
if (priv->finger_count == 0) {
|
|
switch (edge) {
|
|
case RIGHT_TOP_EDGE:
|
|
DBG(7, ErrorF("right top edge\n"));
|
|
ReportTap(priv, RT_TAP);
|
|
break;
|
|
case RIGHT_BOTTOM_EDGE:
|
|
DBG(7, ErrorF("right bottom edge\n"));
|
|
ReportTap(priv, RB_TAP);
|
|
break;
|
|
case LEFT_TOP_EDGE:
|
|
DBG(7, ErrorF("left top edge\n"));
|
|
ReportTap(priv, LT_TAP);
|
|
break;
|
|
case LEFT_BOTTOM_EDGE:
|
|
DBG(7, ErrorF("left bottom edge\n"));
|
|
ReportTap(priv, LB_TAP);
|
|
break;
|
|
default:
|
|
DBG(7, ErrorF("no edge\n"));
|
|
ReportTap(priv, F1_TAP);
|
|
}
|
|
} else {
|
|
switch (priv->finger_count) {
|
|
case 2:
|
|
DBG(7, ErrorF("two finger tap\n"));
|
|
ReportTap(priv, F2_TAP);
|
|
break;
|
|
case 3:
|
|
DBG(7, ErrorF("three finger tap\n"));
|
|
ReportTap(priv, F3_TAP);
|
|
break;
|
|
default:
|
|
DBG(7, ErrorF("one finger\n"));
|
|
ReportTap(priv, F1_TAP);
|
|
}
|
|
}
|
|
}
|
|
} /* tap detection */
|
|
if ((timeleft <= 0) && priv->drag && para->locked_drags)
|
|
priv->draglock = TRUE;
|
|
priv->drag = FALSE;
|
|
} /* finger lost */
|
|
|
|
/* detecting 2 and 3 fingers */
|
|
timeleft = TIME_DIFF(priv->touch_on.millis + para->tap_time, hw->millis);
|
|
if (timeleft > 0)
|
|
delay = MIN(delay, timeleft);
|
|
if (finger && /* finger is on the surface */
|
|
(timeleft > 0)) { /* tap time is not succeeded */
|
|
/* count fingers when reported */
|
|
if ((hw->numFingers == 2) && (priv->finger_count == 0))
|
|
priv->finger_count = 2;
|
|
if (hw->numFingers == 3)
|
|
priv->finger_count = 3;
|
|
} else { /* reset finger counts */
|
|
priv->finger_count = 0;
|
|
}
|
|
|
|
/* reset tapping button flags */
|
|
if (!priv->tap && !priv->drag && !priv->doubletap && !priv->draglock) {
|
|
priv->tap_left = priv->tap_mid = priv->tap_right = FALSE;
|
|
}
|
|
|
|
/* tap processing */
|
|
timeleft = TIME_DIFF(priv->tapping_millis + para->tap_time, hw->millis);
|
|
if (timeleft > 0)
|
|
delay = MIN(delay, timeleft);
|
|
if (priv->tap && (timeleft > 0)) {
|
|
hw->left |= priv->tap_left;
|
|
mid |= priv->tap_mid;
|
|
hw->right |= priv->tap_right;
|
|
} else {
|
|
if (priv->tap)
|
|
priv->draglock = FALSE;
|
|
priv->tap = FALSE;
|
|
}
|
|
|
|
/* drag processing */
|
|
if (priv->drag || priv->draglock) {
|
|
hw->left |= priv->tap_left;
|
|
mid |= priv->tap_mid;
|
|
hw->right |= priv->tap_right;
|
|
}
|
|
|
|
/* double tap processing */
|
|
if (priv->doubletap && !priv->finger_flag) {
|
|
hw->left |= priv->tap_left;
|
|
mid |= priv->tap_mid;
|
|
hw->right |= priv->tap_right;
|
|
priv->doubletap = FALSE;
|
|
}
|
|
|
|
/* scroll detection */
|
|
if (finger && !priv->finger_flag) {
|
|
if (edge & RIGHT_EDGE) {
|
|
priv->vert_scroll_on = TRUE;
|
|
priv->scroll_y = hw->y;
|
|
DBG(7, ErrorF("vert edge scroll detected on right edge\n"));
|
|
}
|
|
if (edge & BOTTOM_EDGE) {
|
|
priv->horiz_scroll_on = TRUE;
|
|
priv->scroll_x = hw->x;
|
|
DBG(7, ErrorF("horiz edge scroll detected on bottom edge\n"));
|
|
}
|
|
}
|
|
if (priv->vert_scroll_on && (!(edge & RIGHT_EDGE) || !finger || priv->palm)) {
|
|
DBG(7, ErrorF("vert edge scroll off\n"));
|
|
priv->vert_scroll_on = FALSE;
|
|
}
|
|
if (priv->horiz_scroll_on && (!(edge & BOTTOM_EDGE) || !finger || priv->palm)) {
|
|
DBG(7, ErrorF("horiz edge scroll off\n"));
|
|
priv->horiz_scroll_on = FALSE;
|
|
}
|
|
|
|
/* scroll processing */
|
|
scroll_up = 0;
|
|
scroll_down = 0;
|
|
if (priv->vert_scroll_on) {
|
|
/* + = down, - = up */
|
|
while (hw->y - priv->scroll_y > para->scroll_dist_vert) {
|
|
scroll_down++;
|
|
priv->scroll_y += para->scroll_dist_vert;
|
|
}
|
|
while (hw->y - priv->scroll_y < -para->scroll_dist_vert) {
|
|
scroll_up++;
|
|
priv->scroll_y -= para->scroll_dist_vert;
|
|
}
|
|
}
|
|
scroll_left = 0;
|
|
scroll_right = 0;
|
|
if (priv->horiz_scroll_on) {
|
|
/* + = right, - = left */
|
|
while (hw->x - priv->scroll_x > para->scroll_dist_horiz) {
|
|
scroll_right++;
|
|
priv->scroll_x += para->scroll_dist_horiz;
|
|
}
|
|
while (hw->x - priv->scroll_x < -para->scroll_dist_horiz) {
|
|
scroll_left++;
|
|
priv->scroll_x -= para->scroll_dist_horiz;
|
|
}
|
|
}
|
|
|
|
/* movement */
|
|
if (finger && !priv->vert_scroll_on && !priv->horiz_scroll_on &&
|
|
!priv->finger_count && !priv->palm) {
|
|
delay = MIN(delay, 13);
|
|
if (priv->count_packet_finger > 3) { /* min. 3 packets */
|
|
dx = (hw->x - MOVE_HIST(2).x) / 2;
|
|
dy = (hw->y - MOVE_HIST(2).y) / 2;
|
|
|
|
if (priv->drag || priv->draglock) {
|
|
if (edge & RIGHT_EDGE) {
|
|
dx += clamp(para->edge_motion_speed - dx, 0, para->edge_motion_speed);
|
|
} else if (edge & LEFT_EDGE) {
|
|
dx -= clamp(para->edge_motion_speed + dx, 0, para->edge_motion_speed);
|
|
}
|
|
if (edge & TOP_EDGE) {
|
|
dy -= clamp(para->edge_motion_speed + dy, 0, para->edge_motion_speed);
|
|
} else if (edge & BOTTOM_EDGE) {
|
|
dy += clamp(para->edge_motion_speed - dy, 0, para->edge_motion_speed);
|
|
}
|
|
}
|
|
|
|
/* speed in depence of distance/packet */
|
|
dist = move_distance( dx, dy );
|
|
speed = dist * para->accl;
|
|
if (speed > para->max_speed) { /* set max speed factor */
|
|
speed = para->max_speed;
|
|
} else if (speed < para->min_speed) { /* set min speed factor */
|
|
speed = para->min_speed;
|
|
}
|
|
|
|
/* save the fraction for adding to the next priv->count_packet */
|
|
priv->frac_x = xf86modf((dx * speed) + priv->frac_x, &integral);
|
|
dx = integral;
|
|
priv->frac_y = xf86modf((dy * speed) + priv->frac_y, &integral);
|
|
dy = integral;
|
|
}
|
|
|
|
priv->count_packet_finger++;
|
|
} else { /* reset packet counter */
|
|
priv->count_packet_finger = 0;
|
|
}
|
|
|
|
|
|
buttons = ((hw->left ? 0x01 : 0) |
|
|
(mid ? 0x02 : 0) |
|
|
(hw->right ? 0x04 : 0) |
|
|
(hw->guest_left ? 0x01 : 0) |
|
|
(hw->guest_mid ? 0x02 : 0) |
|
|
(hw->guest_right ? 0x04 : 0) |
|
|
(hw->up ? 0x08 : 0) |
|
|
(hw->down ? 0x10 : 0) |
|
|
(hw->multi[2] ? 0x20 : 0) |
|
|
(hw->multi[3] ? 0x40 : 0));
|
|
|
|
/* Flags */
|
|
priv->finger_flag = finger;
|
|
|
|
/* generate a history of the absolute positions */
|
|
MOVE_HIST(0).y = hw->y;
|
|
MOVE_HIST(0).x = hw->x;
|
|
|
|
/* Add guest device movements */
|
|
dx += hw->guest_dx;
|
|
dy += hw->guest_dy;
|
|
|
|
/* Post events */
|
|
if (dx || dy)
|
|
xf86PostMotionEvent(local->dev, 0, 0, 2, dx, dy);
|
|
|
|
change = buttons ^ priv->lastButtons;
|
|
while (change) {
|
|
id = ffs(change); /* number of first set bit 1..32 is returned */
|
|
change &= ~(1 << (id - 1));
|
|
xf86PostButtonEvent(local->dev, FALSE, id, (buttons & (1 << (id - 1))), 0, 0);
|
|
}
|
|
priv->lastButtons = buttons;
|
|
|
|
while (scroll_up-- > 0) {
|
|
xf86PostButtonEvent(local->dev, FALSE, 4, !hw->up, 0, 0);
|
|
xf86PostButtonEvent(local->dev, FALSE, 4, hw->up, 0, 0);
|
|
}
|
|
while (scroll_down-- > 0) {
|
|
xf86PostButtonEvent(local->dev, FALSE, 5, !hw->down, 0, 0);
|
|
xf86PostButtonEvent(local->dev, FALSE, 5, hw->down, 0, 0);
|
|
}
|
|
while (scroll_left-- > 0) {
|
|
xf86PostButtonEvent(local->dev, FALSE, 6, TRUE, 0, 0);
|
|
xf86PostButtonEvent(local->dev, FALSE, 6, FALSE, 0, 0);
|
|
}
|
|
while (scroll_right-- > 0) {
|
|
xf86PostButtonEvent(local->dev, FALSE, 7, TRUE, 0, 0);
|
|
xf86PostButtonEvent(local->dev, FALSE, 7, FALSE, 0, 0);
|
|
}
|
|
if (double_click) {
|
|
int i;
|
|
for (i = 0; i < 2; i++) {
|
|
xf86PostButtonEvent(local->dev, FALSE, 1, !hw->left, 0, 0);
|
|
xf86PostButtonEvent(local->dev, FALSE, 1, hw->left, 0, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle auto repeat buttons
|
|
*/
|
|
if ((hw->up || hw->down || hw->multi[2] || hw->multi[3]) &&
|
|
para->updown_button_scrolling) {
|
|
priv->repeatButtons = buttons & 0x78;
|
|
if (!priv->nextRepeat) {
|
|
priv->nextRepeat = hw->millis + 200;
|
|
}
|
|
} else {
|
|
priv->repeatButtons = 0;
|
|
priv->nextRepeat = 0;
|
|
}
|
|
|
|
if (priv->repeatButtons) {
|
|
timeleft = TIME_DIFF(priv->nextRepeat, hw->millis);
|
|
if (timeleft > 0)
|
|
delay = MIN(delay, timeleft);
|
|
if (timeleft <= 0) {
|
|
int change, id;
|
|
change = priv->repeatButtons;
|
|
while (change) {
|
|
id = ffs(change);
|
|
change &= ~(1 << (id - 1));
|
|
xf86PostButtonEvent(local->dev, FALSE, id, FALSE, 0, 0);
|
|
xf86PostButtonEvent(local->dev, FALSE, id, TRUE, 0, 0);
|
|
}
|
|
|
|
priv->nextRepeat = hw->millis + 100;
|
|
delay = MIN(delay, 100);
|
|
}
|
|
}
|
|
|
|
return delay;
|
|
}
|
|
|
|
static int
|
|
ControlProc(LocalDevicePtr local, xDeviceCtl * control)
|
|
{
|
|
DBG(3, ErrorF("Control Proc called\n"));
|
|
return Success;
|
|
}
|
|
|
|
|
|
static void
|
|
CloseProc(LocalDevicePtr local)
|
|
{
|
|
DBG(3, ErrorF("Close Proc called\n"));
|
|
}
|
|
|
|
static int
|
|
SwitchMode(ClientPtr client, DeviceIntPtr dev, int mode)
|
|
{
|
|
ErrorF("SwitchMode called\n");
|
|
return Success;
|
|
}
|
|
|
|
static Bool
|
|
ConvertProc(LocalDevicePtr local,
|
|
int first,
|
|
int num,
|
|
int v0,
|
|
int v1,
|
|
int v2,
|
|
int v3,
|
|
int v4,
|
|
int v5,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
if (first != 0 || num != 2)
|
|
return FALSE;
|
|
|
|
*x = v0;
|
|
*y = v1;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static Bool
|
|
QueryHardware(LocalDevicePtr local)
|
|
{
|
|
SynapticsPrivate *priv = (SynapticsPrivate *) local->private;
|
|
SynapticsSHM *para = priv->synpara;
|
|
int retries;
|
|
int mode;
|
|
|
|
if (priv->proto == SYN_PROTO_EVENT)
|
|
return Success;
|
|
|
|
/* is the synaptics touchpad active? */
|
|
priv->isSynaptics = QueryIsSynaptics(local->fd);
|
|
|
|
if ((!priv->isSynaptics) && (!para->repeater || (priv->fifofd == -1))) {
|
|
xf86Msg(X_ERROR, "%s no synaptics touchpad detected and no repeater device\n",
|
|
local->name);
|
|
priv->isSynaptics = TRUE;
|
|
return !Success;
|
|
}
|
|
para->isSynaptics = priv->isSynaptics;
|
|
|
|
priv->protoBufTail = 0;
|
|
if (!priv->isSynaptics) {
|
|
xf86Msg(X_PROBED, "%s no synaptics touchpad, data piped to repeater fifo\n", local->name);
|
|
synaptics_reset(local->fd);
|
|
SynapticsEnableDevice(local->fd);
|
|
return Success;
|
|
}
|
|
|
|
xf86Msg(X_PROBED, "%s synaptics touchpad found\n", local->name);
|
|
|
|
retries = 3;
|
|
while ((retries++ <= 3) && (synaptics_reset(local->fd) != Success))
|
|
xf86Msg(X_ERROR, "%s reset failed\n", local->name);
|
|
|
|
if (synaptics_identify(local->fd, &priv->identity) != Success)
|
|
return !Success;
|
|
para->identity = priv->identity;
|
|
|
|
if (synaptics_model_id(local->fd, &priv->model_id) != Success)
|
|
return !Success;
|
|
para->model_id = priv->model_id;
|
|
|
|
if (synaptics_capability(local->fd, &priv->capabilities, &priv->ext_cap) != Success)
|
|
return !Success;
|
|
para->capabilities = priv->capabilities;
|
|
para->ext_cap = priv->ext_cap;
|
|
|
|
mode = SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE;
|
|
if (SYN_ID_MAJOR(priv->identity) >= 4)
|
|
mode |= SYN_BIT_DISABLE_GESTURE;
|
|
if (SYN_CAP_EXTENDED(priv->capabilities))
|
|
mode |= SYN_BIT_W_MODE;
|
|
if (synaptics_set_mode(local->fd, mode) != Success)
|
|
return !Success;
|
|
|
|
/* Check to see if the host mouse supports a guest */
|
|
if (SYN_CAP_PASSTHROUGH(priv->capabilities)) {
|
|
priv->hasGuest = TRUE;
|
|
|
|
/* Enable the guest mouse. Set it to relative mode, three byte
|
|
* packets */
|
|
|
|
/* Disable the host to talk to the guest */
|
|
SynapticsDisableDevice(local->fd);
|
|
/* Reset it, set defaults, streaming and enable it */
|
|
if ((SynapticsResetPassthrough(local->fd)) != Success) {
|
|
priv->hasGuest = FALSE;
|
|
}
|
|
}
|
|
|
|
SynapticsEnableDevice(local->fd);
|
|
|
|
PrintIdent(priv);
|
|
|
|
return Success;
|
|
}
|
|
|
|
static Bool
|
|
SynapticsGetHwState(LocalDevicePtr local, SynapticsPrivate *priv,
|
|
struct SynapticsHwState *hw)
|
|
{
|
|
if (priv->proto == SYN_PROTO_PSAUX) {
|
|
return SynapticsParseRawPacket(local, priv, hw);
|
|
} else if (priv->proto == SYN_PROTO_EVENT) {
|
|
return SynapticsParseEventData(local, priv, hw);
|
|
} else {
|
|
return !Success;
|
|
}
|
|
}
|
|
|
|
static Bool
|
|
SynapticsParseEventData(LocalDevicePtr local, SynapticsPrivate *priv,
|
|
struct SynapticsHwState *hwRet)
|
|
{
|
|
struct input_event ev;
|
|
Bool v;
|
|
struct SynapticsHwState *hw = &(priv->hwState);
|
|
|
|
while (SynapticsReadEvent(priv, &ev) == Success) {
|
|
switch (ev.type) {
|
|
case EV_SYN:
|
|
switch (ev.code) {
|
|
case SYN_REPORT:
|
|
if (priv->oneFinger)
|
|
hw->numFingers = 1;
|
|
else if (priv->twoFingers)
|
|
hw->numFingers = 2;
|
|
else if (priv->threeFingers)
|
|
hw->numFingers = 3;
|
|
else
|
|
hw->numFingers = 0;
|
|
*hwRet = *hw;
|
|
return Success;
|
|
}
|
|
case EV_KEY:
|
|
v = (ev.value ? TRUE : FALSE);
|
|
switch (ev.code) {
|
|
case BTN_LEFT:
|
|
hw->left = v;
|
|
break;
|
|
case BTN_RIGHT:
|
|
hw->right = v;
|
|
break;
|
|
case BTN_FORWARD:
|
|
hw->up = v;
|
|
break;
|
|
case BTN_BACK:
|
|
hw->down = v;
|
|
break;
|
|
case BTN_0:
|
|
hw->multi[0] = v;
|
|
break;
|
|
case BTN_1:
|
|
hw->multi[1] = v;
|
|
break;
|
|
case BTN_2:
|
|
hw->multi[2] = v;
|
|
break;
|
|
case BTN_3:
|
|
hw->multi[3] = v;
|
|
break;
|
|
case BTN_4:
|
|
hw->multi[4] = v;
|
|
break;
|
|
case BTN_5:
|
|
hw->multi[5] = v;
|
|
break;
|
|
case BTN_6:
|
|
hw->multi[6] = v;
|
|
break;
|
|
case BTN_7:
|
|
hw->multi[7] = v;
|
|
break;
|
|
case BTN_TOOL_FINGER:
|
|
priv->oneFinger = v;
|
|
break;
|
|
case BTN_TOOL_DOUBLETAP:
|
|
priv->twoFingers = v;
|
|
break;
|
|
case BTN_TOOL_TRIPLETAP:
|
|
priv->threeFingers = v;
|
|
break;
|
|
}
|
|
break;
|
|
case EV_ABS:
|
|
switch (ev.code) {
|
|
case ABS_X:
|
|
hw->x = ev.value;
|
|
break;
|
|
case ABS_Y:
|
|
hw->y = ev.value;
|
|
break;
|
|
case ABS_PRESSURE:
|
|
hw->z = ev.value;
|
|
break;
|
|
case ABS_TOOL_WIDTH:
|
|
hw->fingerWidth = ev.value;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return !Success;
|
|
}
|
|
|
|
static Bool
|
|
SynapticsReadEvent(SynapticsPrivate *priv, struct input_event *ev)
|
|
{
|
|
int i, c;
|
|
unsigned char *pBuf, u;
|
|
|
|
for (i = 0; i < sizeof(struct input_event); i++) {
|
|
if ((c = XisbRead(priv->buffer)) < 0)
|
|
return !Success;
|
|
u = (unsigned char)c;
|
|
pBuf = (unsigned char *)ev;
|
|
pBuf[i] = u;
|
|
}
|
|
return Success;
|
|
}
|
|
|
|
static Bool
|
|
SynapticsParseRawPacket(LocalDevicePtr local, SynapticsPrivate *priv,
|
|
struct SynapticsHwState *hwRet)
|
|
{
|
|
Bool ret = SynapticsGetPacket(local, priv);
|
|
int newabs = SYN_MODEL_NEWABS(priv->model_id);
|
|
unsigned char *buf = priv->protoBuf;
|
|
struct SynapticsHwState *hw = &(priv->hwState);
|
|
int w, i;
|
|
|
|
if (ret != Success)
|
|
return ret;
|
|
|
|
/* Handle guest packets */
|
|
hw->guest_dx = hw->guest_dy = 0;
|
|
if (newabs && priv->hasGuest) {
|
|
w = (((buf[0] & 0x30) >> 2) |
|
|
((buf[0] & 0x04) >> 1) |
|
|
((buf[3] & 0x04) >> 2));
|
|
if (w == 3) { /* If w is 3, this is a guest packet */
|
|
if (buf[4] != 0)
|
|
hw->guest_dx = buf[4] - ((buf[1] & 0x10) ? 256 : 0);
|
|
if (buf[5] != 0)
|
|
hw->guest_dy = -(buf[5] - ((buf[1] & 0x20) ? 256 : 0));
|
|
hw->guest_left = (buf[1] & 0x01) ? TRUE : FALSE;
|
|
hw->guest_mid = (buf[1] & 0x04) ? TRUE : FALSE;
|
|
hw->guest_right = (buf[1] & 0x02) ? TRUE : FALSE;
|
|
*hwRet = *hw;
|
|
return Success;
|
|
}
|
|
}
|
|
|
|
/* Handle normal packets */
|
|
hw->x = hw->y = hw->z = hw->numFingers = hw->fingerWidth = 0;
|
|
hw->left = hw->right = hw->up = hw->down = FALSE;
|
|
for (i = 0; i < 8; i++)
|
|
hw->multi[i] = FALSE;
|
|
|
|
if (newabs) { /* newer protos...*/
|
|
DBG(7, ErrorF("using new protocols\n"));
|
|
hw->x = (((buf[3] & 0x10) << 8) |
|
|
((buf[1] & 0x0f) << 8) |
|
|
buf[4]);
|
|
hw->y = (((buf[3] & 0x20) << 7) |
|
|
((buf[1] & 0xf0) << 4) |
|
|
buf[5]);
|
|
|
|
hw->z = buf[2];
|
|
w = (((buf[0] & 0x30) >> 2) |
|
|
((buf[0] & 0x04) >> 1) |
|
|
((buf[3] & 0x04) >> 2));
|
|
|
|
hw->left = (buf[0] & 0x01) ? 1 : 0;
|
|
hw->right = (buf[0] & 0x02) ? 1 : 0;
|
|
|
|
if (SYN_CAP_EXTENDED(priv->capabilities)) {
|
|
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
|
|
hw->up = ((buf[3] & 0x01)) ? 1 : 0;
|
|
if (hw->left)
|
|
hw->up = !hw->up;
|
|
hw->down = ((buf[3] & 0x02)) ? 1 : 0;
|
|
if (hw->right)
|
|
hw->down = !hw->down;
|
|
}
|
|
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) {
|
|
if ((buf[3] & 2) ? !hw->right : hw->right) {
|
|
switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
|
|
default:
|
|
break;
|
|
case 8:
|
|
hw->multi[7] = ((buf[5] & 0x08)) ? 1 : 0;
|
|
hw->multi[6] = ((buf[4] & 0x08)) ? 1 : 0;
|
|
case 6:
|
|
hw->multi[5] = ((buf[5] & 0x04)) ? 1 : 0;
|
|
hw->multi[4] = ((buf[4] & 0x04)) ? 1 : 0;
|
|
case 4:
|
|
hw->multi[3] = ((buf[5] & 0x02)) ? 1 : 0;
|
|
hw->multi[2] = ((buf[4] & 0x02)) ? 1 : 0;
|
|
case 2:
|
|
hw->multi[1] = ((buf[5] & 0x01)) ? 1 : 0;
|
|
hw->multi[0] = ((buf[4] & 0x01)) ? 1 : 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else { /* old proto...*/
|
|
DBG(7, ErrorF("using old protocol\n"));
|
|
hw->x = (((buf[1] & 0x1F) << 8) |
|
|
buf[2]);
|
|
hw->y = (((buf[4] & 0x1F) << 8) |
|
|
buf[5]);
|
|
|
|
hw->z = (((buf[0] & 0x30) << 2) |
|
|
(buf[3] & 0x3F));
|
|
w = (((buf[1] & 0x80) >> 4) |
|
|
((buf[0] & 0x04) >> 1));
|
|
|
|
hw->left = (buf[0] & 0x01) ? 1 : 0;
|
|
hw->right = (buf[0] & 0x02) ? 1 : 0;
|
|
}
|
|
|
|
hw->y = YMAX_NOMINAL + YMIN_NOMINAL - hw->y;
|
|
|
|
if (hw->z > 0) {
|
|
int w_ok = 0;
|
|
/*
|
|
* Use capability bits to decide if the w value is valid.
|
|
* If not, set it to 5, which corresponds to a finger of
|
|
* normal width.
|
|
*/
|
|
if (SYN_CAP_EXTENDED(priv->capabilities)) {
|
|
if ((w >= 0) && (w <= 1)) {
|
|
w_ok = SYN_CAP_MULTIFINGER(priv->capabilities);
|
|
} else if (w == 2) {
|
|
w_ok = SYN_MODEL_PEN(priv->model_id);
|
|
} else if ((w >= 4) && (w <= 15)) {
|
|
w_ok = SYN_CAP_PALMDETECT(priv->capabilities);
|
|
}
|
|
}
|
|
if (!w_ok)
|
|
w = 5;
|
|
|
|
switch (w) {
|
|
case 0:
|
|
hw->numFingers = 2;
|
|
hw->fingerWidth = 5;
|
|
break;
|
|
case 1:
|
|
hw->numFingers = 3;
|
|
hw->fingerWidth = 5;
|
|
break;
|
|
default:
|
|
hw->numFingers = 1;
|
|
hw->fingerWidth = w;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*hwRet = *hw;
|
|
return Success;
|
|
}
|
|
|
|
/*
|
|
* Decide if the current packet stored in priv->protoBuf is valid.
|
|
*/
|
|
static Bool
|
|
PacketOk(SynapticsPrivate *priv)
|
|
{
|
|
unsigned char *buf = priv->protoBuf;
|
|
int newabs = SYN_MODEL_NEWABS(priv->model_id);
|
|
|
|
if (newabs ? ((buf[0] & 0xC0) != 0x80) : ((buf[0] & 0xC0) != 0xC0)) {
|
|
DBG(4, ErrorF("Synaptics driver lost sync at 1st byte\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!newabs && ((buf[1] & 0x60) != 0x00)) {
|
|
DBG(4, ErrorF("Synaptics driver lost sync at 2nd byte\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if ((newabs ? ((buf[3] & 0xC0) != 0xC0) : ((buf[3] & 0xC0) != 0x80))) {
|
|
DBG(4, ErrorF("Synaptics driver lost sync at 4th byte\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!newabs && ((buf[4] & 0x60) != 0x00)) {
|
|
DBG(4, ErrorF("Synaptics driver lost sync at 5th byte\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
SynapticsGetPacket(LocalDevicePtr local, SynapticsPrivate *priv)
|
|
{
|
|
int count = 0;
|
|
int c;
|
|
unsigned char u;
|
|
|
|
while ((c = XisbRead(priv->buffer)) >= 0) {
|
|
u = (unsigned char)c;
|
|
|
|
/* test if there is a reset sequence received */
|
|
if ((c == 0x00) && (priv->lastByte == 0xAA)) {
|
|
if (xf86WaitForInput(local->fd, 50000) == 0) {
|
|
DBG(7, ErrorF("Reset received\n"));
|
|
QueryHardware(local);
|
|
} else
|
|
DBG(3, ErrorF("faked reset received\n"));
|
|
}
|
|
priv->lastByte = u;
|
|
|
|
/* when there is no synaptics touchpad pipe the data to the repeater fifo */
|
|
if (!priv->isSynaptics) {
|
|
xf86write(priv->fifofd, &u, 1);
|
|
if (++count >= 3)
|
|
return !Success;
|
|
continue;
|
|
}
|
|
|
|
/* to avoid endless loops */
|
|
if (count++ > 30) {
|
|
ErrorF("Synaptics driver lost sync... got gigantic packet!\n");
|
|
return !Success;
|
|
}
|
|
|
|
priv->protoBuf[priv->protoBufTail++] = u;
|
|
|
|
/* Check that we have a valid packet. If not, we are out of sync,
|
|
so we throw away the first byte in the packet.*/
|
|
if (priv->protoBufTail >= 6) {
|
|
if (!PacketOk(priv)) {
|
|
int i;
|
|
for (i = 0; i < priv->protoBufTail - 1; i++)
|
|
priv->protoBuf[i] = priv->protoBuf[i + 1];
|
|
priv->protoBufTail--;
|
|
priv->outOfSync++;
|
|
if (priv->outOfSync > MAX_UNSYNC_PACKETS) {
|
|
priv->outOfSync = 0;
|
|
DBG(3, ErrorF("Synaptics synchronization lost too long -> reset touchpad.\n"));
|
|
QueryHardware(local); /* including a reset */
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (priv->protoBufTail >= 6) { /* Full packet received */
|
|
if (priv->outOfSync > 0) {
|
|
priv->outOfSync = 0;
|
|
DBG(4, ErrorF("Synaptics driver resynced.\n"));
|
|
}
|
|
priv->protoBufTail = 0;
|
|
return Success;
|
|
}
|
|
}
|
|
|
|
return !Success;
|
|
}
|
|
|
|
static void
|
|
PrintIdent(SynapticsPrivate *priv)
|
|
{
|
|
xf86Msg(X_PROBED, " Synaptics Touchpad, model: %d\n", SYN_ID_MODEL(priv->identity));
|
|
xf86Msg(X_PROBED, " Firmware: %d.%d\n", SYN_ID_MAJOR(priv->identity),
|
|
SYN_ID_MINOR(priv->identity));
|
|
|
|
if (SYN_MODEL_ROT180(priv->model_id))
|
|
xf86Msg(X_PROBED, " 180 degree mounted touchpad\n");
|
|
if (SYN_MODEL_PORTRAIT(priv->model_id))
|
|
xf86Msg(X_PROBED, " portrait touchpad\n");
|
|
xf86Msg(X_PROBED, " Sensor: %d\n", SYN_MODEL_SENSOR(priv->model_id));
|
|
if (SYN_MODEL_NEWABS(priv->model_id))
|
|
xf86Msg(X_PROBED, " new absolute packet format\n");
|
|
if (SYN_MODEL_PEN(priv->model_id))
|
|
xf86Msg(X_PROBED, " pen detection\n");
|
|
|
|
if (SYN_CAP_EXTENDED(priv->capabilities)) {
|
|
xf86Msg(X_PROBED, " Touchpad has extended capability bits\n");
|
|
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
|
|
xf86Msg(X_PROBED, " -> %d multi buttons, i.e. besides standard buttons\n",
|
|
(int)(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)));
|
|
else if (SYN_CAP_FOUR_BUTTON(priv->capabilities))
|
|
xf86Msg(X_PROBED, " -> four buttons\n");
|
|
if (SYN_CAP_MULTIFINGER(priv->capabilities))
|
|
xf86Msg(X_PROBED, " -> multifinger detection\n");
|
|
if (SYN_CAP_PALMDETECT(priv->capabilities))
|
|
xf86Msg(X_PROBED, " -> palm detection\n");
|
|
if (SYN_CAP_PASSTHROUGH(priv->capabilities))
|
|
xf86Msg(X_PROBED, " -> pass-through port\n");
|
|
}
|
|
}
|