synaptics.c 109 KB
Newer Older
Peter Osterlund's avatar
Peter Osterlund committed
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14
 * Copyright  1999 Henry Davies
 * Copyright  2001 Stefan Gmeiner
 * Copyright  2002 S. Lehner
 * Copyright  2002 Peter Osterlund
 * Copyright  2002 Linuxcare Inc. David Kennedy
 * Copyright  2003 Hartwig Felger
 * Copyright  2003 Jrg Bsner
 * Copyright  2003 Fred Hucht
 * Copyright  2004 Alexei Gilchrist
 * Copyright  2004 Matthias Ihmig
 * Copyright  2006 Stefan Bethge
 * Copyright  2006 Christian Thaeter
 * Copyright  2007 Joseph P. Skudlarek
15
 * Copyright  2008 Fedor P. Goncharov
16
 * Copyright  2008-2012 Red Hat, Inc.
17
 * Copyright  2011 The Chromium OS Authors
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
 *
 * 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.
 *
 * Authors:
 *      Joseph P. Skudlarek <Jskud@Jskud.com>
 *      Christian Thaeter <chth@gmx.net>
 *      Stefan Bethge <stefan.bethge@web.de>
 *      Matthias Ihmig <m.ihmig@gmx.net>
 *      Alexei Gilchrist <alexei@physics.uq.edu.au>
 *      Jrg Bsner <ich@joerg-boesner.de>
 *      Hartwig Felger <hgfelger@hgfelger.de>
 *      Peter Osterlund <petero2@telia.com>
 *      S. Lehner <sam_x@bluemail.ch>
 *      Stefan Gmeiner <riddlebox@freesurf.ch>
 *      Henry Davies <hdavies@ameritech.net> for the
 *      Linuxcare Inc. David Kennedy <dkennedy@linuxcare.com>
 *      Fred Hucht <fred@thp.Uni-Duisburg.de>
52
 *      Fedor P. Goncharov <fedgo@gorodok.net>
53
 *      Simon Thum <simon.thum@gmx.de>
54
 *
55
 * Trademarks are the property of their respective owners.
56 57
 */

58 59 60 61
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

62
#include <xorg-server.h>
63
#include <unistd.h>
64 65
#include <misc.h>
#include <xf86.h>
66 67
#include <math.h>
#include <stdio.h>
68 69
#include <xf86_OSproc.h>
#include <xf86Xinput.h>
Peter Hutterer's avatar
Peter Hutterer committed
70
#include <exevents.h>
71

72
#include <X11/Xatom.h>
73
#include <X11/extensions/XI2.h>
74
#include <xserver-properties.h>
75
#include <ptrveloc.h>
76

Daniel Stone's avatar
Daniel Stone committed
77 78 79
#include "synapticsstr.h"
#include "synaptics-properties.h"

80
enum EdgeType {
81
    NO_EDGE = 0,
82 83 84 85 86 87 88 89
    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
90
};
91

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
/*
 * We expect to be receiving a steady 80 packets/sec (which gives 40
 * reports/sec with more than one finger on the pad, as Advanced Gesture Mode
 * requires two PS/2 packets per report).  Instead of a random scattering of
 * magic 13 and 20ms numbers scattered throughout the driver, introduce
 * POLL_MS as 14ms, which is slightly less than 80Hz.  13ms is closer to
 * 80Hz, but if the kernel event reporting was even slightly delayed,
 * we would produce synthetic motion followed immediately by genuine
 * motion, so use 14.
 *
 * We use this to call back at a constant rate to at least produce the
 * illusion of smooth motion.  It works a lot better than you'd expect.
*/
#define POLL_MS 14

107
#define MAX(a, b) (((a)>(b))?(a):(b))
108
#define MIN(a, b) (((a)<(b))?(a):(b))
109
#define TIME_DIFF(a, b) ((int)((a)-(b)))
110

111 112
#define SQR(x) ((x) * (x))

113 114 115 116
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

117 118
#define INPUT_BUFFER_SIZE 200

119 120 121
/*****************************************************************************
 * Forward declaration
 ****************************************************************************/
122
static int SynapticsPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
Henrik Rydberg's avatar
Henrik Rydberg committed
123
static void SynapticsUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
124
static Bool DeviceControl(DeviceIntPtr, int);
125
static void ReadInput(InputInfoPtr);
Peter Hutterer's avatar
Peter Hutterer committed
126
static int HandleState(InputInfoPtr, struct SynapticsHwState *, CARD32 now,
127
                       Bool from_timer);
Peter Hutterer's avatar
Peter Hutterer committed
128
static int ControlProc(InputInfoPtr, xDeviceCtl *);
129
static int SwitchMode(ClientPtr, DeviceIntPtr, int);
130 131 132 133
static int DeviceInit(DeviceIntPtr);
static int DeviceOn(DeviceIntPtr);
static int DeviceOff(DeviceIntPtr);
static int DeviceClose(DeviceIntPtr);
134 135
static Bool QueryHardware(InputInfoPtr);
static void ReadDevDimensions(InputInfoPtr);
136
#ifndef NO_DRIVER_SCALING
Peter Hutterer's avatar
Peter Hutterer committed
137 138 139
static void ScaleCoordinates(SynapticsPrivate * priv,
                             struct SynapticsHwState *hw);
static void CalculateScalingCoeffs(SynapticsPrivate * priv);
140
#endif
141
static void SanitizeDimensions(InputInfoPtr pInfo);
142

143
void InitDeviceProperties(InputInfoPtr pInfo);
144 145
int SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
                BOOL checkonly);
146

147 148 149 150 151
const static struct {
    const char *name;
    struct SynapticsProtocolOperations *proto_ops;
} protocols[] = {
#ifdef BUILD_EVENTCOMM
152
    { "event", &event_proto_operations },
153 154
#endif
#ifdef BUILD_PSMCOMM
155
    { "psm", &psm_proto_operations },
156
#endif
157
#ifdef BUILD_PS2COMM
158 159
    { "psaux", &psaux_proto_operations },
    { "alps", &alps_proto_operations },
160
#endif
161
    { NULL, NULL }
162 163
};

164
InputDriverRec SYNAPTICS = {
165 166 167 168
    1,
    "synaptics",
    NULL,
    SynapticsPreInit,
Henrik Rydberg's avatar
Henrik Rydberg committed
169
    SynapticsUnInit,
170
    NULL,
171 172 173 174
    NULL,
#ifdef XI86_DRV_CAP_SERVER_FD
    XI86_DRV_CAP_SERVER_FD
#endif
175 176
};

177 178 179 180 181
static XF86ModuleVersionInfo VersionRec = {
    "synaptics",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
182 183
    XORG_VERSION_CURRENT,
    PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,
184 185 186
    ABI_CLASS_XINPUT,
    ABI_XINPUT_VERSION,
    MOD_CLASS_XINPUT,
187
    {0, 0, 0, 0}
188 189 190
};

static pointer
191
SetupProc(pointer module, pointer options, int *errmaj, int *errmin)
192
{
193 194
    xf86AddInputDriver(&SYNAPTICS, module, 0);
    return module;
195 196
}

Magnus Kessler's avatar
Magnus Kessler committed
197 198 199 200 201
_X_EXPORT XF86ModuleData synapticsModuleData = {
    &VersionRec,
    &SetupProc,
    NULL
};
202 203 204 205

/*****************************************************************************
 *	Function Definitions
 ****************************************************************************/
206 207 208 209 210 211 212 213 214
static inline void
SynapticsCloseFd(InputInfoPtr pInfo)
{
    if (pInfo->fd > -1 && !(pInfo->flags & XI86_SERVER_FD)) {
        xf86CloseSerial(pInfo->fd);
        pInfo->fd = -1;
    }
}

215 216 217 218 219 220 221 222 223
/**
 * Fill in default dimensions for backends that cannot query the hardware.
 * Eventually, we want the edges to be 1900/5400 for x, 1900/4000 for y.
 * These values are based so that calculate_edge_widths() will give us the
 * right values.
 *
 * The default values 1900, etc. come from the dawn of time, when men where
 * men, or possibly apes.
 */
224
static void
225
SanitizeDimensions(InputInfoPtr pInfo)
226
{
Peter Hutterer's avatar
Peter Hutterer committed
227
    SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
228

Peter Hutterer's avatar
Peter Hutterer committed
229 230 231 232
    if (priv->minx >= priv->maxx) {
        priv->minx = 1615;
        priv->maxx = 5685;
        priv->resx = 0;
233

Peter Hutterer's avatar
Peter Hutterer committed
234 235 236
        xf86IDrvMsg(pInfo, X_PROBED,
                    "invalid x-axis range.  defaulting to %d - %d\n",
                    priv->minx, priv->maxx);
237
    }
238

Peter Hutterer's avatar
Peter Hutterer committed
239 240 241 242
    if (priv->miny >= priv->maxy) {
        priv->miny = 1729;
        priv->maxy = 4171;
        priv->resy = 0;
243

Peter Hutterer's avatar
Peter Hutterer committed
244 245 246
        xf86IDrvMsg(pInfo, X_PROBED,
                    "invalid y-axis range.  defaulting to %d - %d\n",
                    priv->miny, priv->maxy);
247
    }
248

Peter Hutterer's avatar
Peter Hutterer committed
249 250 251
    if (priv->minp >= priv->maxp) {
        priv->minp = 0;
        priv->maxp = 255;
252

Peter Hutterer's avatar
Peter Hutterer committed
253 254 255
        xf86IDrvMsg(pInfo, X_PROBED,
                    "invalid pressure range.  defaulting to %d - %d\n",
                    priv->minp, priv->maxp);
256 257
    }

Peter Hutterer's avatar
Peter Hutterer committed
258 259 260
    if (priv->minw >= priv->maxw) {
        priv->minw = 0;
        priv->maxw = 15;
261

Peter Hutterer's avatar
Peter Hutterer committed
262 263 264
        xf86IDrvMsg(pInfo, X_PROBED,
                    "invalid finger width range.  defaulting to %d - %d\n",
                    priv->minw, priv->maxw);
265
    }
266
}
267

268
static Bool
269
SetDeviceAndProtocol(InputInfoPtr pInfo)
270
{
271
    SynapticsPrivate *priv = pInfo->private;
272 273
    char *proto, *device;
    int i;
274

275
    proto = xf86SetStrOption(pInfo->options, "Protocol", NULL);
276
    device = xf86SetStrOption(pInfo->options, "Device", NULL);
277 278 279 280 281 282 283

    /* If proto is auto-dev, unset and let the code do the rest */
    if (proto && !strcmp(proto, "auto-dev")) {
        free(proto);
        proto = NULL;
    }

284 285 286
    for (i = 0; protocols[i].name; i++) {
        if ((!device || !proto) &&
            protocols[i].proto_ops->AutoDevProbe &&
287
            protocols[i].proto_ops->AutoDevProbe(pInfo, device))
288 289 290
            break;
        else if (proto && !strcmp(proto, protocols[i].name))
            break;
291
    }
292 293 294 295
    free(proto);
    free(device);

    priv->proto_ops = protocols[i].proto_ops;
296 297

    return (priv->proto_ops != NULL);
298 299
}

300
static void
Peter Hutterer's avatar
Peter Hutterer committed
301
calculate_edge_widths(SynapticsPrivate * priv, int *l, int *r, int *t, int *b)
302 303
{
    int width, height;
Peter Hutterer's avatar
Peter Hutterer committed
304
    int ewidth, eheight;        /* edge width/height */
305 306 307 308

    width = abs(priv->maxx - priv->minx);
    height = abs(priv->maxy - priv->miny);

Peter Hutterer's avatar
Peter Hutterer committed
309
    if (priv->model == MODEL_SYNAPTICS) {
310 311
        ewidth = width * .07;
        eheight = height * .07;
Peter Hutterer's avatar
Peter Hutterer committed
312 313
    }
    else if (priv->model == MODEL_ALPS) {
314 315
        ewidth = width * .15;
        eheight = height * .15;
Peter Hutterer's avatar
Peter Hutterer committed
316
    }
317 318
    else if (priv->model == MODEL_APPLETOUCH ||
             priv->model == MODEL_UNIBODY_MACBOOK) {
319 320
        ewidth = width * .085;
        eheight = height * .085;
Peter Hutterer's avatar
Peter Hutterer committed
321 322
    }
    else {
323 324 325 326 327 328 329 330 331 332
        ewidth = width * .04;
        eheight = height * .054;
    }

    *l = priv->minx + ewidth;
    *r = priv->maxx - ewidth;
    *t = priv->miny + eheight;
    *b = priv->maxy - eheight;
}

333
static void
Peter Hutterer's avatar
Peter Hutterer committed
334
calculate_tap_hysteresis(SynapticsPrivate * priv, int range,
335
                         int *fingerLow, int *fingerHigh)
336
{
337 338
    switch (priv->model) {
    case MODEL_ELANTECH:
339 340 341 342
        /* All Elantech touchpads don't need the Z filtering to get the
         * number of fingers correctly. See Documentation/elantech.txt
         * in the kernel.
         */
Peter Hutterer's avatar
Peter Hutterer committed
343
        *fingerLow = priv->minp + 1;
344
        *fingerHigh = priv->minp + 1;
345 346 347 348 349 350
        break;
    case MODEL_UNIBODY_MACBOOK:
        *fingerLow = 70;
        *fingerHigh = 75;
        break;
    default:
Peter Hutterer's avatar
Peter Hutterer committed
351 352
        *fingerLow = priv->minp + range * (25.0 / 256);
        *fingerHigh = priv->minp + range * (30.0 / 256);
353
        break;
354 355 356
    }
}

357 358 359 360 361 362
/* Area options support both percent values and absolute values. This is
 * awkward. The xf86Set* calls will print to the log, but they'll
 * also print an error if we request a percent value but only have an
 * int. So - check first for percent, then call xf86Set* again to get
 * the log message.
 */
Peter Hutterer's avatar
Peter Hutterer committed
363 364 365
static int
set_percent_option(pointer options, const char *optname,
                   const int range, const int offset, const int default_value)
366 367
{
    int result;
368
    double percent = xf86CheckPercentOption(options, optname, -1);
369

370
    if (percent >= 0.0) {
371
        percent = xf86SetPercentOption(options, optname, -1);
Peter Hutterer's avatar
Peter Hutterer committed
372
        result = percent / 100.0 * range + offset;
373
    } else
374
        result = xf86SetIntOption(options, optname, default_value);
375 376 377

    return result;
}
378

Peter Hutterer's avatar
Peter Hutterer committed
379 380
Bool
SynapticsIsSoftButtonAreasValid(int *values)
Chase Douglas's avatar
Chase Douglas committed
381 382 383 384
{
    Bool right_disabled = FALSE;
    Bool middle_disabled = FALSE;

385 386 387 388 389 390 391 392 393 394 395 396 397
    enum {
        /* right button left, right, top, bottom */
        RBL = 0,
        RBR = 1,
        RBT = 2,
        RBB = 3,
        /* middle button left, right, top, bottom */
        MBL = 4,
        MBR = 5,
        MBT = 6,
        MBB = 7,
    };

Chase Douglas's avatar
Chase Douglas committed
398
    /* Check right button area */
399 400
    if ((((values[RBL] != 0) && (values[RBR] != 0)) && (values[RBL] > values[RBR])) ||
        (((values[RBT] != 0) && (values[RBB] != 0)) && (values[RBT] > values[RBB])))
Chase Douglas's avatar
Chase Douglas committed
401 402 403
        return FALSE;

    /* Check middle button area */
404 405
    if ((((values[MBL] != 0) && (values[MBR] != 0)) && (values[MBL] > values[MBR])) ||
        (((values[MBT] != 0) && (values[MBB] != 0)) && (values[MBT] > values[MBB])))
Chase Douglas's avatar
Chase Douglas committed
406 407
        return FALSE;

408
    if (values[RBL] == 0 && values[RBR] == 0 && values[RBT] == 0 && values[RBB] == 0)
Chase Douglas's avatar
Chase Douglas committed
409 410
        right_disabled = TRUE;

411
    if (values[MBL] == 0 && values[MBR] == 0 && values[MBT] == 0 && values[MBB] == 0)
Chase Douglas's avatar
Chase Douglas committed
412 413 414
        middle_disabled = TRUE;

    if (!right_disabled &&
415 416
        ((values[RBL] && values[RBL] == values[RBR]) ||
         (values[RBT] && values[RBT] == values[RBB])))
Chase Douglas's avatar
Chase Douglas committed
417 418 419
        return FALSE;

    if (!middle_disabled &&
420 421
        ((values[MBL] && values[MBL] == values[MBR]) ||
         (values[MBT] && values[MBT] == values[MBB])))
Chase Douglas's avatar
Chase Douglas committed
422 423 424
        return FALSE;

    /* Check for overlapping button areas */
Peter Hutterer's avatar
Peter Hutterer committed
425
    if (!right_disabled && !middle_disabled) {
426 427 428 429 430 431 432 433
        int right_left = values[RBL] ? values[RBL] : INT_MIN;
        int right_right = values[RBR] ? values[RBR] : INT_MAX;
        int right_top = values[RBT] ? values[RBT] : INT_MIN;
        int right_bottom = values[RBB] ? values[RBB] : INT_MAX;
        int middle_left = values[MBL] ? values[MBL] : INT_MIN;
        int middle_right = values[MBR] ? values[MBR] : INT_MAX;
        int middle_top = values[MBT] ? values[MBT] : INT_MIN;
        int middle_bottom = values[MBB] ? values[MBB] : INT_MAX;
Chase Douglas's avatar
Chase Douglas committed
434 435 436

        /* If areas overlap in the Y axis */
        if ((right_bottom <= middle_bottom && right_bottom >= middle_top) ||
Peter Hutterer's avatar
Peter Hutterer committed
437
            (right_top <= middle_bottom && right_top >= middle_top)) {
Chase Douglas's avatar
Chase Douglas committed
438
            /* Check for overlapping left edges */
439 440
            if ((right_left < middle_left && right_right > middle_left) ||
                (middle_left < right_left && middle_right > right_left))
Chase Douglas's avatar
Chase Douglas committed
441 442 443
                return FALSE;

            /* Check for overlapping right edges */
444 445
            if ((right_right > middle_right && right_left < middle_right) ||
                (middle_right > right_right && middle_left < right_right))
Chase Douglas's avatar
Chase Douglas committed
446 447 448 449 450
                return FALSE;
        }

        /* If areas overlap in the X axis */
        if ((right_left >= middle_left && right_left <= middle_right) ||
Peter Hutterer's avatar
Peter Hutterer committed
451
            (right_right >= middle_left && right_right <= middle_right)) {
Chase Douglas's avatar
Chase Douglas committed
452
            /* Check for overlapping top edges */
453 454
            if ((right_top < middle_top && right_bottom > middle_top) ||
                (middle_top < right_top && middle_bottom > right_top))
Chase Douglas's avatar
Chase Douglas committed
455 456 457
                return FALSE;

            /* Check for overlapping bottom edges */
458 459
            if ((right_bottom > middle_bottom && right_top < middle_bottom) ||
                (middle_bottom > right_bottom && middle_top < right_bottom))
Chase Douglas's avatar
Chase Douglas committed
460 461 462 463 464 465 466
                return FALSE;
        }
    }

    return TRUE;
}

Peter Hutterer's avatar
Peter Hutterer committed
467
static void
468
set_softbutton_areas_option(InputInfoPtr pInfo, char *option_name, int offset)
Chase Douglas's avatar
Chase Douglas committed
469 470 471 472
{
    SynapticsPrivate *priv = pInfo->private;
    SynapticsParameters *pars = &priv->synpara;
    int values[8];
Peter Hutterer's avatar
Peter Hutterer committed
473
    int in_percent = 0;         /* bitmask for which ones are in % */
Chase Douglas's avatar
Chase Douglas committed
474 475 476 477
    char *option_string;
    char *next_num;
    char *end_str;
    int i;
478
    int width, height;
Chase Douglas's avatar
Chase Douglas committed
479

480 481 482
    if (!pars->clickpad)
        return;

483
    option_string = xf86SetStrOption(pInfo->options, option_name, NULL);
Chase Douglas's avatar
Chase Douglas committed
484 485 486 487 488
    if (!option_string)
        return;

    next_num = option_string;

Peter Hutterer's avatar
Peter Hutterer committed
489
    for (i = 0; i < 8 && *next_num != '\0'; i++) {
Chase Douglas's avatar
Chase Douglas committed
490
        long int value = strtol(next_num, &end_str, 0);
Peter Hutterer's avatar
Peter Hutterer committed
491

Chase Douglas's avatar
Chase Douglas committed
492 493 494 495 496
        if (value > INT_MAX || value < -INT_MAX)
            goto fail;

        values[i] = value;

Peter Hutterer's avatar
Peter Hutterer committed
497
        if (next_num != end_str) {
Peter Hutterer's avatar
Peter Hutterer committed
498
            if (*end_str == '%') {
499 500 501
                in_percent |= 1 << i;
                end_str++;
            }
Chase Douglas's avatar
Chase Douglas committed
502
            next_num = end_str;
Peter Hutterer's avatar
Peter Hutterer committed
503 504
        }
        else
Chase Douglas's avatar
Chase Douglas committed
505 506 507
            goto fail;
    }

508 509 510 511 512 513
    if (i < 8 || *next_num != '\0')
        goto fail;

    width = priv->maxx - priv->minx;
    height = priv->maxy - priv->miny;

Peter Hutterer's avatar
Peter Hutterer committed
514
    for (i = 0; in_percent && i < 8; i++) {
515 516 517 518 519 520 521
        int base, size;

        if ((in_percent & (1 << i)) == 0 || values[i] == 0)
            continue;

        size = ((i % 4) < 2) ? width : height;
        base = ((i % 4) < 2) ? priv->minx : priv->miny;
Peter Hutterer's avatar
Peter Hutterer committed
522
        values[i] = base + size * values[i] / 100.0;
523 524 525
    }

    if (!SynapticsIsSoftButtonAreasValid(values))
Chase Douglas's avatar
Chase Douglas committed
526 527
        goto fail;

528 529
    memcpy(pars->softbutton_areas[offset], values, 4 * sizeof(int));
    memcpy(pars->softbutton_areas[offset + 1], values + 4, 4 * sizeof(int));
Chase Douglas's avatar
Chase Douglas committed
530

531 532
    free(option_string);

Chase Douglas's avatar
Chase Douglas committed
533 534
    return;

Peter Hutterer's avatar
Peter Hutterer committed
535 536
 fail:
    xf86IDrvMsg(pInfo, X_ERROR,
537 538
                "invalid %s value '%s', keeping defaults\n",
                option_name, option_string);
539
    free(option_string);
Chase Douglas's avatar
Chase Douglas committed
540 541
}

542 543 544
static void
set_primary_softbutton_areas_option(InputInfoPtr pInfo)
{
545
    set_softbutton_areas_option(pInfo, "SoftButtonAreas", BOTTOM_BUTTON_AREA);
546 547 548 549 550
}

static void
set_secondary_softbutton_areas_option(InputInfoPtr pInfo)
{
551
    set_softbutton_areas_option(pInfo, "SecondarySoftButtonAreas", TOP_BUTTON_AREA);
552 553
}

Peter Hutterer's avatar
Peter Hutterer committed
554 555
static void
set_default_parameters(InputInfoPtr pInfo)
556
{
Peter Hutterer's avatar
Peter Hutterer committed
557 558
    SynapticsPrivate *priv = pInfo->private;    /* read-only */
    pointer opts = pInfo->options;      /* read-only */
559
    SynapticsParameters *pars = &priv->synpara; /* modified */
Henrik Rydberg's avatar
Henrik Rydberg committed
560

Peter Hutterer's avatar
Peter Hutterer committed
561 562 563 564
    int horizScrollDelta, vertScrollDelta;      /* pixels */
    int tapMove;                /* pixels */
    int l, r, t, b;             /* left, right, top, bottom */
    double accelFactor;         /* 1/pixels */
565
    int fingerLow, fingerHigh;  /* pressure */
Peter Hutterer's avatar
Peter Hutterer committed
566 567 568 569
    int emulateTwoFingerMinZ;   /* pressure */
    int emulateTwoFingerMinW;   /* width */
    int pressureMotionMinZ, pressureMotionMaxZ; /* pressure */
    int palmMinWidth, palmMinZ; /* pressure */
570 571 572 573
    int tapButton1, tapButton2, tapButton3;
    int clickFinger1, clickFinger2, clickFinger3;
    Bool vertEdgeScroll, horizEdgeScroll;
    Bool vertTwoFingerScroll, horizTwoFingerScroll;
574 575
    int horizResolution = 1;
    int vertResolution = 1;
576
    int width, height, diag, range;
577
    int horizHyst, vertHyst;
578
    int middle_button_timeout;
579 580
    int grab_event_device = 0;
    const char *source;
581

582 583 584
    /* The synaptics specs specify typical edge widths of 4% on x, and 5.4% on
     * y (page 7) [Synaptics TouchPad Interfacing Guide, 510-000080 - A
     * Second Edition, http://www.synaptics.com/support/dev_support.cfm, 8 Sep
585 586 587 588
     * 2008]. We use 7% for both instead for synaptics devices, and 15% for
     * ALPS models.
     * http://bugs.freedesktop.org/show_bug.cgi?id=21214
     *
589 590 591 592
     * If the range was autodetected, apply these edge widths to all four
     * sides.
     */

593 594 595 596 597 598 599 600 601 602
    width = abs(priv->maxx - priv->minx);
    height = abs(priv->maxy - priv->miny);
    diag = sqrt(width * width + height * height);

    calculate_edge_widths(priv, &l, &r, &t, &b);

    /* Again, based on typical x/y range and defaults */
    horizScrollDelta = diag * .020;
    vertScrollDelta = diag * .020;
    tapMove = diag * .044;
603
    accelFactor = 200.0 / diag; /* trial-and-error */
604

605 606 607 608
    /* hysteresis, assume >= 0 is a detected value (e.g. evdev fuzz) */
    horizHyst = pars->hyst_x >= 0 ? pars->hyst_x : diag * 0.005;
    vertHyst = pars->hyst_y >= 0 ? pars->hyst_y : diag * 0.005;

609
    range = priv->maxp - priv->minp + 1;
610

611
    calculate_tap_hysteresis(priv, range, &fingerLow, &fingerHigh);
612

613
    /* scaling based on defaults and a pressure of 256 */
Peter Hutterer's avatar
Peter Hutterer committed
614 615 616 617
    emulateTwoFingerMinZ = priv->minp + range * (282.0 / 256);
    pressureMotionMinZ = priv->minp + range * (30.0 / 256);
    pressureMotionMaxZ = priv->minp + range * (160.0 / 256);
    palmMinZ = priv->minp + range * (200.0 / 256);
618

619
    range = priv->maxw - priv->minw + 1;
620 621

    /* scaling based on defaults below and a tool width of 16 */
Peter Hutterer's avatar
Peter Hutterer committed
622 623
    palmMinWidth = priv->minw + range * (10.0 / 16);
    emulateTwoFingerMinW = priv->minw + range * (7.0 / 16);
624 625 626

    /* Enable tap if we don't have a phys left button */
    tapButton1 = priv->has_left ? 0 : 1;
627 628
    tapButton2 = priv->has_left ? 0 : 3;
    tapButton3 = priv->has_left ? 0 : 2;
629

630
    /* Enable multifinger-click if only have one physical button,
631 632
       otherwise clickFinger is always button 1. */
    clickFinger1 = 1;
633 634
    clickFinger2 = (priv->has_right || priv->has_middle) ? 1 : 3;
    clickFinger3 = (priv->has_right || priv->has_middle) ? 1 : 2;
635 636 637 638 639 640 641 642 643

    /* Enable vert edge scroll if we can't detect doubletap */
    vertEdgeScroll = priv->has_double ? FALSE : TRUE;
    horizEdgeScroll = FALSE;

    /* Enable twofinger scroll if we can detect doubletap */
    vertTwoFingerScroll = priv->has_double ? TRUE : FALSE;
    horizTwoFingerScroll = FALSE;

644 645 646 647 648 649
    /* Use resolution reported by hardware if available */
    if ((priv->resx > 0) && (priv->resy > 0)) {
        horizResolution = priv->resx;
        vertResolution = priv->resy;
    }

650
    /* set the parameters */
651 652 653 654
    pars->left_edge = xf86SetIntOption(opts, "LeftEdge", l);
    pars->right_edge = xf86SetIntOption(opts, "RightEdge", r);
    pars->top_edge = xf86SetIntOption(opts, "TopEdge", t);
    pars->bottom_edge = xf86SetIntOption(opts, "BottomEdge", b);
655

Peter Hutterer's avatar
Peter Hutterer committed
656 657 658 659 660 661 662 663
    pars->area_top_edge =
        set_percent_option(opts, "AreaTopEdge", height, priv->miny, 0);
    pars->area_bottom_edge =
        set_percent_option(opts, "AreaBottomEdge", height, priv->miny, 0);
    pars->area_left_edge =
        set_percent_option(opts, "AreaLeftEdge", width, priv->minx, 0);
    pars->area_right_edge =
        set_percent_option(opts, "AreaRightEdge", width, priv->minx, 0);
664

Peter Hutterer's avatar
Peter Hutterer committed
665 666 667 668
    pars->hyst_x =
        set_percent_option(opts, "HorizHysteresis", width, 0, horizHyst);
    pars->hyst_y =
        set_percent_option(opts, "VertHysteresis", height, 0, vertHyst);
669

670 671
    pars->finger_low = xf86SetIntOption(opts, "FingerLow", fingerLow);
    pars->finger_high = xf86SetIntOption(opts, "FingerHigh", fingerHigh);
672
    pars->tap_time = xf86SetIntOption(opts, "MaxTapTime", 180);
673
    pars->tap_move = xf86SetIntOption(opts, "MaxTapMove", tapMove);
674 675
    pars->tap_time_2 = xf86SetIntOption(opts, "MaxDoubleTapTime", 180);
    pars->click_time = xf86SetIntOption(opts, "ClickTime", 100);
Peter Hutterer's avatar
Peter Hutterer committed
676
    pars->clickpad = xf86SetBoolOption(opts, "ClickPad", pars->clickpad);       /* Probed */
677 678 679 680
    if (pars->clickpad)
        pars->has_secondary_buttons = xf86SetBoolOption(opts,
                                                        "HasSecondarySoftButtons",
                                                        pars->has_secondary_buttons);
681
    pars->clickpad_ignore_motion_time = 100; /* ms */
682 683
    /* middle mouse button emulation on a clickpad? nah, you're joking */
    middle_button_timeout = pars->clickpad ? 0 : 75;
Peter Hutterer's avatar
Peter Hutterer committed
684 685 686 687 688 689 690 691 692 693 694 695 696 697
    pars->emulate_mid_button_time =
        xf86SetIntOption(opts, "EmulateMidButtonTime", middle_button_timeout);
    pars->emulate_twofinger_z =
        xf86SetIntOption(opts, "EmulateTwoFingerMinZ", emulateTwoFingerMinZ);
    pars->emulate_twofinger_w =
        xf86SetIntOption(opts, "EmulateTwoFingerMinW", emulateTwoFingerMinW);
    pars->scroll_dist_vert =
        xf86SetIntOption(opts, "VertScrollDelta", vertScrollDelta);
    pars->scroll_dist_horiz =
        xf86SetIntOption(opts, "HorizScrollDelta", horizScrollDelta);
    pars->scroll_edge_vert =
        xf86SetBoolOption(opts, "VertEdgeScroll", vertEdgeScroll);
    pars->scroll_edge_horiz =
        xf86SetBoolOption(opts, "HorizEdgeScroll", horizEdgeScroll);
698
    pars->scroll_edge_corner = xf86SetBoolOption(opts, "CornerCoasting", FALSE);
Peter Hutterer's avatar
Peter Hutterer committed
699 700 701 702
    pars->scroll_twofinger_vert =
        xf86SetBoolOption(opts, "VertTwoFingerScroll", vertTwoFingerScroll);
    pars->scroll_twofinger_horiz =
        xf86SetBoolOption(opts, "HorizTwoFingerScroll", horizTwoFingerScroll);
703
    pars->touchpad_off = xf86SetIntOption(opts, "TouchpadOff", TOUCHPAD_ON);
704

705 706 707 708 709 710
    if (priv->has_scrollbuttons) {
        priv->has_trackpoint_buttons = xf86SetBoolOption(opts, "HasTrackpointButtons", FALSE);
        if (priv->has_trackpoint_buttons)
            priv->has_scrollbuttons = FALSE;
    }

711 712 713 714 715 716 717 718 719 720 721 722 723
    if (priv->has_scrollbuttons) {
        pars->updown_button_scrolling =
            xf86SetBoolOption(opts, "UpDownScrolling", TRUE);
        pars->leftright_button_scrolling =
            xf86SetBoolOption(opts, "LeftRightScrolling", TRUE);
        pars->updown_button_repeat =
            xf86SetBoolOption(opts, "UpDownScrollRepeat", TRUE);
        pars->leftright_button_repeat =
            xf86SetBoolOption(opts, "LeftRightScrollRepeat", TRUE);
    }
    pars->scroll_button_repeat =
        xf86SetIntOption(opts, "ScrollButtonRepeat", 100);

724
    pars->locked_drags = xf86SetBoolOption(opts, "LockedDrags", FALSE);
725
    pars->locked_drag_time = xf86SetIntOption(opts, "LockedDragTimeout", 5000);
726 727
    pars->tap_action[RT_TAP] = xf86SetIntOption(opts, "RTCornerButton", 0);
    pars->tap_action[RB_TAP] = xf86SetIntOption(opts, "RBCornerButton", 0);
728 729
    pars->tap_action[LT_TAP] = xf86SetIntOption(opts, "LTCornerButton", 0);
    pars->tap_action[LB_TAP] = xf86SetIntOption(opts, "LBCornerButton", 0);
Peter Hutterer's avatar
Peter Hutterer committed
730 731 732 733 734 735 736 737 738 739 740 741
    pars->tap_action[F1_TAP] = xf86SetIntOption(opts, "TapButton1", tapButton1);
    pars->tap_action[F2_TAP] = xf86SetIntOption(opts, "TapButton2", tapButton2);
    pars->tap_action[F3_TAP] = xf86SetIntOption(opts, "TapButton3", tapButton3);
    pars->click_action[F1_CLICK1] =
        xf86SetIntOption(opts, "ClickFinger1", clickFinger1);
    pars->click_action[F2_CLICK1] =
        xf86SetIntOption(opts, "ClickFinger2", clickFinger2);
    pars->click_action[F3_CLICK1] =
        xf86SetIntOption(opts, "ClickFinger3", clickFinger3);
    pars->circular_scrolling =
        xf86SetBoolOption(opts, "CircularScrolling", FALSE);
    pars->circular_trigger = xf86SetIntOption(opts, "CircScrollTrigger", 0);
742
    pars->circular_pad = xf86SetBoolOption(opts, "CircularPad", FALSE);
Peter Hutterer's avatar
Peter Hutterer committed
743 744 745
    pars->palm_detect = xf86SetBoolOption(opts, "PalmDetect", FALSE);
    pars->palm_min_width = xf86SetIntOption(opts, "PalmMinWidth", palmMinWidth);
    pars->palm_min_z = xf86SetIntOption(opts, "PalmMinZ", palmMinZ);
746
    pars->single_tap_timeout = xf86SetIntOption(opts, "SingleTapTimeout", 180);
Peter Hutterer's avatar
Peter Hutterer committed
747 748 749 750
    pars->press_motion_min_z =
        xf86SetIntOption(opts, "PressureMotionMinZ", pressureMotionMinZ);
    pars->press_motion_max_z =
        xf86SetIntOption(opts, "PressureMotionMaxZ", pressureMotionMaxZ);
751

752 753 754 755
    pars->min_speed = xf86SetRealOption(opts, "MinSpeed", 0.4);
    pars->max_speed = xf86SetRealOption(opts, "MaxSpeed", 0.7);
    pars->accl = xf86SetRealOption(opts, "AccelFactor", accelFactor);
    pars->scroll_dist_circ = xf86SetRealOption(opts, "CircScrollDelta", 0.1);
756
    pars->coasting_speed = xf86SetRealOption(opts, "CoastingSpeed", 20.0);
757
    pars->coasting_friction = xf86SetRealOption(opts, "CoastingFriction", 50);
Peter Hutterer's avatar
Peter Hutterer committed
758 759 760 761
    pars->press_motion_min_factor =
        xf86SetRealOption(opts, "PressureMotionMinFactor", 1.0);
    pars->press_motion_max_factor =
        xf86SetRealOption(opts, "PressureMotionMaxFactor", 1.0);
762 763 764 765 766 767 768 769 770 771

    /* Only grab the device by default if it's not coming from a config
       backend. This way we avoid the device being added twice and sending
       duplicate events.
      */
    source = xf86CheckStrOption(opts, "_source", NULL);
    if (source == NULL || strncmp(source, "server/", 7) != 0)
        grab_event_device = TRUE;
    pars->grab_event_device = xf86SetBoolOption(opts, "GrabEventDevice", grab_event_device);

Peter Hutterer's avatar
Peter Hutterer committed
772 773 774 775 776 777
    pars->tap_and_drag_gesture =
        xf86SetBoolOption(opts, "TapAndDragGesture", TRUE);
    pars->resolution_horiz =
        xf86SetIntOption(opts, "HorizResolution", horizResolution);
    pars->resolution_vert =
        xf86SetIntOption(opts, "VertResolution", vertResolution);
778 779 780 781 782 783 784 785 786 787
    if (pars->resolution_horiz <= 0) {
        xf86IDrvMsg(pInfo, X_ERROR,
                    "Invalid X resolution, using 1 instead.\n");
        pars->resolution_horiz = 1;
    }
    if (pars->resolution_vert <= 0) {
        xf86IDrvMsg(pInfo, X_ERROR,
                    "Invalid Y resolution, using 1 instead.\n");
        pars->resolution_vert = 1;
    }
788

789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
    /* Touchpad sampling rate is too low to detect all movements.
       A user may lift one finger and put another one down within the same
       EV_SYN or even between samplings so the driver doesn't notice at all.

       We limit the movement to 20 mm within one event, that is more than
       recordings showed is needed (17mm on a T440).
      */
    if (pars->resolution_horiz > 1 &&
        pars->resolution_vert > 1)
        pars->maxDeltaMM = 20;
    else {
        /* on devices without resolution set the vector length to 0.25 of
           the touchpad diagonal */
        pars->maxDeltaMM = diag * 0.25;
    }


Peter Osterlund's avatar
Peter Osterlund committed
806
    /* Warn about (and fix) incorrectly configured TopEdge/BottomEdge parameters */
807
    if (pars->top_edge > pars->bottom_edge) {
Peter Hutterer's avatar
Peter Hutterer committed
808 809 810 811 812 813
        int tmp = pars->top_edge;

        pars->top_edge = pars->bottom_edge;
        pars->bottom_edge = tmp;
        xf86IDrvMsg(pInfo, X_WARNING,
                    "TopEdge is bigger than BottomEdge. Fixing.\n");
814
    }
Chase Douglas's avatar
Chase Douglas committed
815

816
    set_primary_softbutton_areas_option(pInfo);
817 818
    if (pars->has_secondary_buttons)
        set_secondary_softbutton_areas_option(pInfo);
Henrik Rydberg's avatar
Henrik Rydberg committed
819
}
820

Peter Hutterer's avatar
Peter Hutterer committed
821 822 823 824 825
static double
SynapticsAccelerationProfile(DeviceIntPtr dev,
                             DeviceVelocityPtr vel,
                             double velocity, double thr, double acc)
{
826 827
    InputInfoPtr pInfo = dev->public.devicePrivate;
    SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
Peter Hutterer's avatar
Peter Hutterer committed
828
    SynapticsParameters *para = &priv->synpara;
829 830 831

    double accelfct;

simonthum's avatar
simonthum committed
832 833 834 835 836 837
    /*
     * synaptics accel was originally base on device coordinate based
     * velocity, which we recover this way so para->accl retains its scale.
     */
    velocity /= vel->const_acceleration;

838 839 840 841
    /* speed up linear with finger velocity */
    accelfct = velocity * para->accl;

    /* clip acceleration factor */
842
    if (accelfct > para->max_speed * acc)
Peter Hutterer's avatar
Peter Hutterer committed
843
        accelfct = para->max_speed * acc;
844
    else if (accelfct < para->min_speed)
Peter Hutterer's avatar
Peter Hutterer committed
845
        accelfct = para->min_speed;
846 847 848

    /* modify speed according to pressure */
    if (priv->moving_state == MS_TOUCHPAD_RELATIVE) {
Peter Hutterer's avatar
Peter Hutterer committed
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
        int minZ = para->press_motion_min_z;
        int maxZ = para->press_motion_max_z;
        double minFctr = para->press_motion_min_factor;
        double maxFctr = para->press_motion_max_factor;

        if (priv->hwState->z <= minZ) {
            accelfct *= minFctr;
        }
        else if (priv->hwState->z >= maxZ) {
            accelfct *= maxFctr;
        }
        else {
            accelfct *=
                minFctr + (priv->hwState->z - minZ) * (maxFctr -
                                                       minFctr) / (maxZ - minZ);
        }
865 866 867 868 869
    }

    return accelfct;
}

Peter Hutterer's avatar
Peter Hutterer committed
870
static int
871
SynapticsPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
Peter Hutterer's avatar
Peter Hutterer committed
872
{
Henrik Rydberg's avatar
Henrik Rydberg committed
873 874 875
    SynapticsPrivate *priv;

    /* allocate memory for SynapticsPrivateRec */
876
    priv = calloc(1, sizeof(SynapticsPrivate));
Henrik Rydberg's avatar
Henrik Rydberg committed
877
    if (!priv)
Peter Hutterer's avatar
Peter Hutterer committed
878
        return BadAlloc;
Peter Hutterer's avatar
Peter Hutterer committed
879

Peter Hutterer's avatar
Peter Hutterer committed
880 881 882 883 884 885
    pInfo->type_name = XI_TOUCHPAD;
    pInfo->device_control = DeviceControl;
    pInfo->read_input = ReadInput;
    pInfo->control_proc = ControlProc;
    pInfo->switch_mode = SwitchMode;
    pInfo->private = priv;
Peter Hutterer's avatar
Peter Hutterer committed
886

887 888 889
    /* allocate now so we don't allocate in the signal handler */
    priv->timer = TimerSet(NULL, 0, 0, NULL, NULL);
    if (!priv->timer) {
Peter Hutterer's avatar
Peter Hutterer committed
890 891
        free(priv);
        return BadAlloc;
892 893
    }

894
    /* may change pInfo->options */
895
    if (!SetDeviceAndProtocol(pInfo)) {
Peter Hutterer's avatar
Peter Hutterer committed
896 897
        xf86IDrvMsg(pInfo, X_ERROR,
                    "Synaptics driver unable to detect protocol\n");
898 899
        goto SetupProc_fail;
    }
Henrik Rydberg's avatar
Henrik Rydberg committed
900

901 902
    priv->device = xf86FindOptionValue(pInfo->options, "Device");

Henrik Rydberg's avatar
Henrik Rydberg committed
903
    /* open the touchpad device */
904 905
    pInfo->fd = xf86OpenSerial(pInfo->options);
    if (pInfo->fd == -1) {
Peter Hutterer's avatar
Peter Hutterer committed
906 907
        xf86IDrvMsg(pInfo, X_ERROR, "Synaptics driver unable to open device\n");
        goto SetupProc_fail;
Henrik Rydberg's avatar
Henrik Rydberg committed
908 909 910 911
    }
    xf86ErrorFVerb(6, "port opened successfully\n");

    /* initialize variables */
912 913
    priv->repeatButtons = 0;
    priv->nextRepeat = 0;
Henrik Rydberg's avatar
Henrik Rydberg committed
914 915 916 917 918
    priv->count_packet_finger = 0;
    priv->tap_state = TS_START;
    priv->tap_button = 0;
    priv->tap_button_state = TBS_BUTTON_UP;
    priv->touch_on.millis = 0;
919 920
    priv->synpara.hyst_x = -1;
    priv->synpara.hyst_y = -1;
Henrik Rydberg's avatar
Henrik Rydberg committed
921

922
    /* read hardware dimensions */
923
    ReadDevDimensions(pInfo);
924

925
    set_default_parameters(pInfo);
Henrik Rydberg's avatar
Henrik Rydberg committed
926

927
#ifndef NO_DRIVER_SCALING
928
    CalculateScalingCoeffs(priv);
929 930
#endif

931

932
    priv->comm.buffer = XisbNew(pInfo->fd, INPUT_BUFFER_SIZE);
Henrik Rydberg's avatar
Henrik Rydberg committed
933

934
    if (!QueryHardware(pInfo)) {
Peter Hutterer's avatar
Peter Hutterer committed
935 936 937
        xf86IDrvMsg(pInfo, X_ERROR,
                    "Unable to query/initialize Synaptics hardware.\n");
        goto SetupProc_fail;
938
    }
939