1/* 2 * Copyright © 2002-2005,2007 Peter Osterlund 3 * 4 * Permission to use, copy, modify, distribute, and sell this software 5 * and its documentation for any purpose is hereby granted without 6 * fee, provided that the above copyright notice appear in all copies 7 * and that both that copyright notice and this permission notice 8 * appear in supporting documentation, and that the name of Red Hat 9 * not be used in advertising or publicity pertaining to distribution 10 * of the software without specific, written prior permission. Red 11 * Hat makes no representations about the suitability of this software 12 * for any purpose. It is provided "as is" without express or implied 13 * warranty. 14 * 15 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN 17 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Authors: 24 * Peter Osterlund (petero2@telia.com) 25 */ 26 27#ifdef HAVE_CONFIG_H 28#include "config.h" 29#endif 30 31#include <stdio.h> 32#include <stdlib.h> 33#include <sys/types.h> 34#include <sys/ipc.h> 35#include <sys/time.h> 36#include <unistd.h> 37#include <string.h> 38#include <stddef.h> 39#include <math.h> 40#include <limits.h> 41 42#include <X11/Xdefs.h> 43#include <X11/Xatom.h> 44#include <X11/extensions/XI.h> 45#include <X11/extensions/XInput.h> 46#include "synaptics-properties.h" 47 48#ifndef XATOM_FLOAT 49#define XATOM_FLOAT "FLOAT" 50#endif 51 52#define SYN_MAX_BUTTONS 12 53 54union flong { /* Xlibs 64-bit property handling madness */ 55 long l; 56 float f; 57}; 58 59enum ParaType { 60 PT_INT, 61 PT_BOOL, 62 PT_DOUBLE 63}; 64 65struct Parameter { 66 char *name; /* Name of parameter */ 67 enum ParaType type; /* Type of parameter */ 68 double min_val; /* Minimum allowed value */ 69 double max_val; /* Maximum allowed value */ 70 char *prop_name; /* Property name */ 71 int prop_format; /* Property format (0 for floats) */ 72 int prop_offset; /* Offset inside property */ 73}; 74 75static struct Parameter params[] = { 76 {"LeftEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 0}, 77 {"RightEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 1}, 78 {"TopEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 2}, 79 {"BottomEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 3}, 80 {"FingerLow", PT_INT, 0, 255, SYNAPTICS_PROP_FINGER, 32, 0}, 81 {"FingerHigh", PT_INT, 0, 255, SYNAPTICS_PROP_FINGER, 32, 1}, 82 {"MaxTapTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_TIME, 32, 0}, 83 {"MaxTapMove", PT_INT, 0, 2000, SYNAPTICS_PROP_TAP_MOVE, 32, 0}, 84 {"MaxDoubleTapTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS,32, 1}, 85 {"SingleTapTimeout", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS,32, 0}, 86 {"ClickTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS,32, 2}, 87 {"FastTaps", PT_BOOL, 0, 1, SYNAPTICS_PROP_TAP_FAST, 8, 0}, 88 {"EmulateMidButtonTime", PT_INT, 0, 1000, SYNAPTICS_PROP_MIDDLE_TIMEOUT,32, 0}, 89 {"EmulateTwoFingerMinZ", PT_INT, 0, 1000, SYNAPTICS_PROP_TWOFINGER_PRESSURE, 32, 0}, 90 {"EmulateTwoFingerMinW", PT_INT, 0, 15, SYNAPTICS_PROP_TWOFINGER_WIDTH, 32, 0}, 91 {"VertScrollDelta", PT_INT, -1000, 1000, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 0}, 92 {"HorizScrollDelta", PT_INT, -1000, 1000, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 1}, 93 {"VertEdgeScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 0}, 94 {"HorizEdgeScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 1}, 95 {"CornerCoasting", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 2}, 96 {"VertTwoFingerScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_TWOFINGER, 8, 0}, 97 {"HorizTwoFingerScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_TWOFINGER, 8, 1}, 98 {"MinSpeed", PT_DOUBLE, 0, 255.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 0}, 99 {"MaxSpeed", PT_DOUBLE, 0, 255.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 1}, 100 {"AccelFactor", PT_DOUBLE, 0, 1.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 2}, 101 {"TouchpadOff", PT_INT, 0, 2, SYNAPTICS_PROP_OFF, 8, 0}, 102 {"LockedDrags", PT_BOOL, 0, 1, SYNAPTICS_PROP_LOCKED_DRAGS, 8, 0}, 103 {"LockedDragTimeout", PT_INT, 0, 30000, SYNAPTICS_PROP_LOCKED_DRAGS_TIMEOUT, 32, 0}, 104 {"RTCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 0}, 105 {"RBCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 1}, 106 {"LTCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 2}, 107 {"LBCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 3}, 108 {"TapButton1", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 4}, 109 {"TapButton2", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 5}, 110 {"TapButton3", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 6}, 111 {"ClickFinger1", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 0}, 112 {"ClickFinger2", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 1}, 113 {"ClickFinger3", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 2}, 114 {"CircularScrolling", PT_BOOL, 0, 1, SYNAPTICS_PROP_CIRCULAR_SCROLLING, 8, 0}, 115 {"CircScrollDelta", PT_DOUBLE, .01, 3, SYNAPTICS_PROP_CIRCULAR_SCROLLING_DIST, 0 /* float */, 0}, 116 {"CircScrollTrigger", PT_INT, 0, 8, SYNAPTICS_PROP_CIRCULAR_SCROLLING_TRIGGER, 8, 0}, 117 {"PalmDetect", PT_BOOL, 0, 1, SYNAPTICS_PROP_PALM_DETECT, 8, 0}, 118 {"PalmMinWidth", PT_INT, 0, 15, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 0}, 119 {"PalmMinZ", PT_INT, 0, 255, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 1}, 120 {"CoastingSpeed", PT_DOUBLE, 0, 255, SYNAPTICS_PROP_COASTING_SPEED, 0 /* float*/, 0}, 121 {"CoastingFriction", PT_DOUBLE, 0, 255, SYNAPTICS_PROP_COASTING_SPEED, 0 /* float*/, 1}, 122 {"PressureMotionMinZ", PT_INT, 1, 255, SYNAPTICS_PROP_PRESSURE_MOTION, 32, 0}, 123 {"PressureMotionMaxZ", PT_INT, 1, 255, SYNAPTICS_PROP_PRESSURE_MOTION, 32, 1}, 124 {"PressureMotionMinFactor", PT_DOUBLE, 0, 10.0,SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 0 /*float*/, 0}, 125 {"PressureMotionMaxFactor", PT_DOUBLE, 0, 10.0,SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 0 /*float*/, 1}, 126 {"GrabEventDevice", PT_BOOL, 0, 1, SYNAPTICS_PROP_GRAB, 8, 0}, 127 {"TapAndDragGesture", PT_BOOL, 0, 1, SYNAPTICS_PROP_GESTURES, 8, 0}, 128 {"AreaLeftEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 0}, 129 {"AreaRightEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 1}, 130 {"AreaTopEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 2}, 131 {"AreaBottomEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 3}, 132 {"HorizHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 0}, 133 {"VertHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 1}, 134 {"ClickPad", PT_BOOL, 0, 1, SYNAPTICS_PROP_CLICKPAD, 8, 0}, 135 {"RightButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 0}, 136 {"RightButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 1}, 137 {"RightButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 2}, 138 {"RightButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 3}, 139 {"MiddleButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 4}, 140 {"MiddleButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 5}, 141 {"MiddleButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 6}, 142 {"MiddleButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 7}, 143 { NULL, 0, 0, 0, 0 } 144}; 145 146static double 147parse_cmd(char *cmd, struct Parameter **par) 148{ 149 char *eqp = strchr(cmd, '='); 150 151 *par = NULL; 152 153 if (eqp) { 154 int j; 155 int found = 0; 156 157 *eqp = 0; 158 for (j = 0; params[j].name; j++) { 159 if (strcasecmp(cmd, params[j].name) == 0) { 160 found = 1; 161 break; 162 } 163 } 164 if (found) { 165 double val = atof(&eqp[1]); 166 167 *par = ¶ms[j]; 168 169 if (val < (*par)->min_val) 170 val = (*par)->min_val; 171 if (val > (*par)->max_val) 172 val = (*par)->max_val; 173 174 return val; 175 } 176 else { 177 printf("Unknown parameter %s\n", cmd); 178 } 179 } 180 else { 181 printf("Invalid command: %s\n", cmd); 182 } 183 184 return 0; 185} 186 187/** Init display connection or NULL on error */ 188static Display * 189dp_init() 190{ 191 Display *dpy = NULL; 192 XExtensionVersion *v = NULL; 193 Atom touchpad_type = 0; 194 Atom synaptics_property = 0; 195 int error = 0; 196 197 dpy = XOpenDisplay(NULL); 198 if (!dpy) { 199 fprintf(stderr, "Failed to connect to X Server.\n"); 200 error = 1; 201 goto unwind; 202 } 203 204 v = XGetExtensionVersion(dpy, INAME); 205 if (!v->present || 206 (v->major_version * 1000 + v->minor_version) < 207 (XI_Add_DeviceProperties_Major * 1000 + 208 XI_Add_DeviceProperties_Minor)) { 209 fprintf(stderr, "X server supports X Input %d.%d. I need %d.%d.\n", 210 v->major_version, v->minor_version, 211 XI_Add_DeviceProperties_Major, XI_Add_DeviceProperties_Minor); 212 error = 1; 213 goto unwind; 214 } 215 216 /* We know synaptics sets XI_TOUCHPAD for all the devices. */ 217 touchpad_type = XInternAtom(dpy, XI_TOUCHPAD, True); 218 if (!touchpad_type) { 219 fprintf(stderr, "XI_TOUCHPAD not initialised.\n"); 220 error = 1; 221 goto unwind; 222 } 223 224 synaptics_property = XInternAtom(dpy, SYNAPTICS_PROP_EDGES, True); 225 if (!synaptics_property) { 226 fprintf(stderr, "Couldn't find synaptics properties. No synaptics " 227 "driver loaded?\n"); 228 error = 1; 229 goto unwind; 230 } 231 232 unwind: 233 XFree(v); 234 if (error && dpy) { 235 XCloseDisplay(dpy); 236 dpy = NULL; 237 } 238 return dpy; 239} 240 241static XDevice * 242dp_get_device(Display * dpy) 243{ 244 XDevice *dev = NULL; 245 XDeviceInfo *info = NULL; 246 int ndevices = 0; 247 Atom touchpad_type = 0; 248 Atom synaptics_property = 0; 249 Atom *properties = NULL; 250 int nprops = 0; 251 int error = 0; 252 253 touchpad_type = XInternAtom(dpy, XI_TOUCHPAD, True); 254 synaptics_property = XInternAtom(dpy, SYNAPTICS_PROP_EDGES, True); 255 info = XListInputDevices(dpy, &ndevices); 256 257 while (ndevices--) { 258 if (info[ndevices].type == touchpad_type) { 259 dev = XOpenDevice(dpy, info[ndevices].id); 260 if (!dev) { 261 fprintf(stderr, "Failed to open device '%s'.\n", 262 info[ndevices].name); 263 error = 1; 264 goto unwind; 265 } 266 267 properties = XListDeviceProperties(dpy, dev, &nprops); 268 if (!properties || !nprops) { 269 fprintf(stderr, "No properties on device '%s'.\n", 270 info[ndevices].name); 271 error = 1; 272 goto unwind; 273 } 274 275 while (nprops--) { 276 if (properties[nprops] == synaptics_property) 277 break; 278 } 279 if (!nprops) { 280 fprintf(stderr, "No synaptics properties on device '%s'.\n", 281 info[ndevices].name); 282 error = 1; 283 goto unwind; 284 } 285 286 break; /* Yay, device is suitable */ 287 } 288 } 289 290 unwind: 291 XFree(properties); 292 XFreeDeviceList(info); 293 if (!dev) 294 fprintf(stderr, "Unable to find a synaptics device.\n"); 295 else if (error && dev) { 296 XCloseDevice(dpy, dev); 297 dev = NULL; 298 } 299 return dev; 300} 301 302static void 303dp_set_variables(Display * dpy, XDevice * dev, int argc, char *argv[], 304 int first_cmd) 305{ 306 int i; 307 double val; 308 struct Parameter *par; 309 Atom prop, type, float_type; 310 int format; 311 unsigned char *data; 312 unsigned long nitems, bytes_after; 313 314 union flong *f; 315 long *n; 316 char *b; 317 318 float_type = XInternAtom(dpy, XATOM_FLOAT, True); 319 if (!float_type) 320 fprintf(stderr, "Float properties not available.\n"); 321 322 for (i = first_cmd; i < argc; i++) { 323 val = parse_cmd(argv[i], &par); 324 if (!par) 325 continue; 326 327 prop = XInternAtom(dpy, par->prop_name, True); 328 if (!prop) { 329 fprintf(stderr, "Property for '%s' not available. Skipping.\n", 330 par->name); 331 continue; 332 333 } 334 335 XGetDeviceProperty(dpy, dev, prop, 0, 1000, False, AnyPropertyType, 336 &type, &format, &nitems, &bytes_after, &data); 337 338 if (type == None) { 339 fprintf(stderr, "Property for '%s' not available. Skipping.\n", 340 par->name); 341 continue; 342 } 343 344 switch (par->prop_format) { 345 case 8: 346 if (format != par->prop_format || type != XA_INTEGER) { 347 fprintf(stderr, " %-23s = format mismatch (%d)\n", 348 par->name, format); 349 break; 350 } 351 b = (char *) data; 352 b[par->prop_offset] = rint(val); 353 break; 354 case 32: 355 if (format != par->prop_format || 356 (type != XA_INTEGER && type != XA_CARDINAL)) { 357 fprintf(stderr, " %-23s = format mismatch (%d)\n", 358 par->name, format); 359 break; 360 } 361 n = (long *) data; 362 n[par->prop_offset] = rint(val); 363 break; 364 case 0: /* float */ 365 if (!float_type) 366 continue; 367 if (format != 32 || type != float_type) { 368 fprintf(stderr, " %-23s = format mismatch (%d)\n", 369 par->name, format); 370 break; 371 } 372 f = (union flong *) data; 373 f[par->prop_offset].f = val; 374 break; 375 } 376 377 XChangeDeviceProperty(dpy, dev, prop, type, format, 378 PropModeReplace, data, nitems); 379 XFlush(dpy); 380 } 381} 382 383/* FIXME: horribly inefficient. */ 384static void 385dp_show_settings(Display * dpy, XDevice * dev) 386{ 387 int j; 388 Atom a, type, float_type; 389 int format; 390 unsigned long nitems, bytes_after; 391 unsigned char *data; 392 int len; 393 394 union flong *f; 395 long *i; 396 char *b; 397 398 float_type = XInternAtom(dpy, XATOM_FLOAT, True); 399 if (!float_type) 400 fprintf(stderr, "Float properties not available.\n"); 401 402 printf("Parameter settings:\n"); 403 for (j = 0; params[j].name; j++) { 404 struct Parameter *par = ¶ms[j]; 405 406 a = XInternAtom(dpy, par->prop_name, True); 407 if (!a) 408 continue; 409 410 len = 411 1 + 412 ((par->prop_offset * (par->prop_format ? par->prop_format : 32) / 413 8)) / 4; 414 415 XGetDeviceProperty(dpy, dev, a, 0, len, False, 416 AnyPropertyType, &type, &format, 417 &nitems, &bytes_after, &data); 418 if (type == None) 419 continue; 420 421 switch (par->prop_format) { 422 case 8: 423 if (format != par->prop_format || type != XA_INTEGER) { 424 fprintf(stderr, " %-23s = format mismatch (%d)\n", 425 par->name, format); 426 break; 427 } 428 429 b = (char *) data; 430 printf(" %-23s = %d\n", par->name, b[par->prop_offset]); 431 break; 432 case 32: 433 if (format != par->prop_format || 434 (type != XA_INTEGER && type != XA_CARDINAL)) { 435 fprintf(stderr, " %-23s = format mismatch (%d)\n", 436 par->name, format); 437 break; 438 } 439 440 i = (long *) data; 441 printf(" %-23s = %ld\n", par->name, i[par->prop_offset]); 442 break; 443 case 0: /* Float */ 444 if (!float_type) 445 continue; 446 if (format != 32 || type != float_type) { 447 fprintf(stderr, " %-23s = format mismatch (%d)\n", 448 par->name, format); 449 break; 450 } 451 452 f = (union flong *) data; 453 printf(" %-23s = %g\n", par->name, f[par->prop_offset].f); 454 break; 455 } 456 457 XFree(data); 458 } 459} 460 461static void 462usage(void) 463{ 464 fprintf(stderr, "Usage: synclient [-h] [-l] [-V] [-?] [var1=value1 [var2=value2] ...]\n"); 465 fprintf(stderr, " -l List current user settings\n"); 466 fprintf(stderr, " -V Print synclient version string and exit\n"); 467 fprintf(stderr, " -? Show this help message\n"); 468 fprintf(stderr, " var=value Set user parameter 'var' to 'value'.\n"); 469 exit(1); 470} 471 472int 473main(int argc, char *argv[]) 474{ 475 int c; 476 int dump_settings = 0; 477 int first_cmd; 478 479 Display *dpy; 480 XDevice *dev; 481 482 if (argc == 1) 483 dump_settings = 1; 484 485 /* Parse command line parameters */ 486 while ((c = getopt(argc, argv, "lV?")) != -1) { 487 switch (c) { 488 case 'l': 489 dump_settings = 1; 490 break; 491 case 'V': 492 printf("%s\n", VERSION); 493 exit(0); 494 case '?': 495 default: 496 usage(); 497 } 498 } 499 500 first_cmd = optind; 501 if (!dump_settings && first_cmd == argc) 502 usage(); 503 504 dpy = dp_init(); 505 if (!dpy || !(dev = dp_get_device(dpy))) 506 return 1; 507 508 dp_set_variables(dpy, dev, argc, argv, first_cmd); 509 if (dump_settings) 510 dp_show_settings(dpy, dev); 511 512 XCloseDevice(dpy, dev); 513 XCloseDisplay(dpy); 514 515 return 0; 516} 517