1b85037dbSmrg/* 2b85037dbSmrg * Copyright © 2003-2004 Peter Osterlund 3b85037dbSmrg * 4b85037dbSmrg * Permission to use, copy, modify, distribute, and sell this software 5b85037dbSmrg * and its documentation for any purpose is hereby granted without 6b85037dbSmrg * fee, provided that the above copyright notice appear in all copies 7b85037dbSmrg * and that both that copyright notice and this permission notice 8b85037dbSmrg * appear in supporting documentation, and that the name of Red Hat 9b85037dbSmrg * not be used in advertising or publicity pertaining to distribution 10b85037dbSmrg * of the software without specific, written prior permission. Red 11b85037dbSmrg * Hat makes no representations about the suitability of this software 12b85037dbSmrg * for any purpose. It is provided "as is" without express or implied 13b85037dbSmrg * warranty. 14b85037dbSmrg * 15b85037dbSmrg * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16b85037dbSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN 17b85037dbSmrg * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18b85037dbSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 19b85037dbSmrg * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 20b85037dbSmrg * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 21b85037dbSmrg * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22b85037dbSmrg * 23b85037dbSmrg * Authors: 24b85037dbSmrg * Peter Osterlund (petero2@telia.com) 25b85037dbSmrg */ 26b85037dbSmrg 27b85037dbSmrg#ifdef HAVE_CONFIG_H 28b85037dbSmrg#include "config.h" 29b85037dbSmrg#endif 30b85037dbSmrg 31b85037dbSmrg#include <X11/Xlib.h> 32b85037dbSmrg#include <X11/Xatom.h> 33b85037dbSmrg#include <X11/extensions/XInput.h> 34b85037dbSmrg#ifdef HAVE_X11_EXTENSIONS_RECORD_H 35b85037dbSmrg#include <X11/Xproto.h> 36b85037dbSmrg#include <X11/extensions/record.h> 3728515619Smrg#endif /* HAVE_X11_EXTENSIONS_RECORD_H */ 38b85037dbSmrg 39b85037dbSmrg#include <stdio.h> 40b85037dbSmrg#include <stdlib.h> 41302b15bdSmrg#include <string.h> 42b85037dbSmrg#include <sys/types.h> 43b85037dbSmrg#include <unistd.h> 44b85037dbSmrg#include <signal.h> 45b85037dbSmrg#include <sys/time.h> 46b85037dbSmrg#include <sys/stat.h> 47b85037dbSmrg 48b85037dbSmrg#include "synaptics-properties.h" 49b85037dbSmrg 5028515619Smrgenum TouchpadState { 51b85037dbSmrg TouchpadOn = 0, 52b85037dbSmrg TouchpadOff = 1, 53b85037dbSmrg TappingOff = 2 5428515619Smrg}; 55b85037dbSmrg 5628515619Smrgstatic Bool pad_disabled 5728515619Smrg /* internal flag, this does not correspond to device state */ ; 58b85037dbSmrgstatic int ignore_modifier_combos; 59b85037dbSmrgstatic int ignore_modifier_keys; 60b85037dbSmrgstatic int background; 61b85037dbSmrgstatic const char *pid_file; 62b85037dbSmrgstatic Display *display; 63b85037dbSmrgstatic XDevice *dev; 64b85037dbSmrgstatic Atom touchpad_off_prop; 6528515619Smrgstatic enum TouchpadState previous_state; 6628515619Smrgstatic enum TouchpadState disable_state = TouchpadOff; 67302b15bdSmrgstatic int verbose; 68b85037dbSmrg 69b85037dbSmrg#define KEYMAP_SIZE 32 70b85037dbSmrgstatic unsigned char keyboard_mask[KEYMAP_SIZE]; 71b85037dbSmrg 72b85037dbSmrgstatic void 73b85037dbSmrgusage(void) 74b85037dbSmrg{ 7528515619Smrg fprintf(stderr, 7628515619Smrg "Usage: syndaemon [-i idle-time] [-m poll-delay] [-d] [-t] [-k]\n"); 7728515619Smrg fprintf(stderr, 7828515619Smrg " -i How many seconds to wait after the last key press before\n"); 79b85037dbSmrg fprintf(stderr, " enabling the touchpad. (default is 2.0s)\n"); 80b85037dbSmrg fprintf(stderr, " -m How many milli-seconds to wait until next poll.\n"); 81b85037dbSmrg fprintf(stderr, " (default is 200ms)\n"); 82b85037dbSmrg fprintf(stderr, " -d Start as a daemon, i.e. in the background.\n"); 83b85037dbSmrg fprintf(stderr, " -p Create a pid file with the specified name.\n"); 8428515619Smrg fprintf(stderr, 8528515619Smrg " -t Only disable tapping and scrolling, not mouse movements.\n"); 8628515619Smrg fprintf(stderr, 8728515619Smrg " -k Ignore modifier keys when monitoring keyboard activity.\n"); 88b85037dbSmrg fprintf(stderr, " -K Like -k but also ignore Modifier+Key combos.\n"); 89b85037dbSmrg fprintf(stderr, " -R Use the XRecord extension.\n"); 90302b15bdSmrg fprintf(stderr, " -v Print diagnostic messages.\n"); 9128515619Smrg fprintf(stderr, " -? Show this help message.\n"); 92b85037dbSmrg exit(1); 93b85037dbSmrg} 94b85037dbSmrg 95b85037dbSmrgstatic void 96b85037dbSmrgstore_current_touchpad_state(void) 97b85037dbSmrg{ 98b85037dbSmrg Atom real_type; 99b85037dbSmrg int real_format; 100b85037dbSmrg unsigned long nitems, bytes_after; 101b85037dbSmrg unsigned char *data; 102b85037dbSmrg 10328515619Smrg if ((XGetDeviceProperty(display, dev, touchpad_off_prop, 0, 1, False, 10428515619Smrg XA_INTEGER, &real_type, &real_format, &nitems, 10528515619Smrg &bytes_after, &data) == Success) && 10628515619Smrg (real_type != None)) { 107b85037dbSmrg previous_state = data[0]; 108b85037dbSmrg } 109b85037dbSmrg} 110b85037dbSmrg 111b85037dbSmrg/** 112b85037dbSmrg * Toggle touchpad enabled/disabled state, decided by value. 113b85037dbSmrg */ 114b85037dbSmrgstatic void 115b85037dbSmrgtoggle_touchpad(Bool enable) 116b85037dbSmrg{ 117b85037dbSmrg unsigned char data; 11828515619Smrg 119b85037dbSmrg if (pad_disabled && enable) { 120b85037dbSmrg data = previous_state; 121b85037dbSmrg pad_disabled = False; 122302b15bdSmrg if (verbose) 123b85037dbSmrg printf("Enable\n"); 12428515619Smrg } 12528515619Smrg else if (!pad_disabled && !enable && 12628515619Smrg previous_state != disable_state && previous_state != TouchpadOff) { 127b85037dbSmrg store_current_touchpad_state(); 128b85037dbSmrg pad_disabled = True; 129b85037dbSmrg data = disable_state; 130302b15bdSmrg if (verbose) 131b85037dbSmrg printf("Disable\n"); 13228515619Smrg } 13328515619Smrg else 134b85037dbSmrg return; 135b85037dbSmrg 13628515619Smrg /* This potentially overwrites a different client's setting, but ... */ 137b85037dbSmrg XChangeDeviceProperty(display, dev, touchpad_off_prop, XA_INTEGER, 8, 13828515619Smrg PropModeReplace, &data, 1); 139b85037dbSmrg XFlush(display); 140b85037dbSmrg} 141b85037dbSmrg 142b85037dbSmrgstatic void 143b85037dbSmrgsignal_handler(int signum) 144b85037dbSmrg{ 145b85037dbSmrg toggle_touchpad(True); 146b85037dbSmrg 147b85037dbSmrg if (pid_file) 14828515619Smrg unlink(pid_file); 149b85037dbSmrg kill(getpid(), signum); 150b85037dbSmrg} 151b85037dbSmrg 152b85037dbSmrgstatic void 153b85037dbSmrginstall_signal_handler(void) 154b85037dbSmrg{ 155b85037dbSmrg static int signals[] = { 15628515619Smrg SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, 15728515619Smrg SIGBUS, SIGFPE, SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, 15828515619Smrg SIGALRM, SIGTERM, 159b85037dbSmrg#ifdef SIGPWR 16028515619Smrg SIGPWR 161b85037dbSmrg#endif 162b85037dbSmrg }; 163b85037dbSmrg int i; 164b85037dbSmrg struct sigaction act; 165b85037dbSmrg sigset_t set; 166b85037dbSmrg 167b85037dbSmrg sigemptyset(&set); 168b85037dbSmrg act.sa_handler = signal_handler; 169b85037dbSmrg act.sa_mask = set; 170b85037dbSmrg#ifdef SA_ONESHOT 171b85037dbSmrg act.sa_flags = SA_ONESHOT; 172b85037dbSmrg#else 173b85037dbSmrg act.sa_flags = 0; 174b85037dbSmrg#endif 175b85037dbSmrg 176b85037dbSmrg for (i = 0; i < sizeof(signals) / sizeof(int); i++) { 17728515619Smrg if (sigaction(signals[i], &act, NULL) == -1) { 17828515619Smrg perror("sigaction"); 17928515619Smrg exit(2); 18028515619Smrg } 181b85037dbSmrg } 182b85037dbSmrg} 183b85037dbSmrg 184b85037dbSmrg/** 185b85037dbSmrg * Return non-zero if the keyboard state has changed since the last call. 186b85037dbSmrg */ 187b85037dbSmrgstatic int 18828515619Smrgkeyboard_activity(Display * display) 189b85037dbSmrg{ 190b85037dbSmrg static unsigned char old_key_state[KEYMAP_SIZE]; 191b85037dbSmrg unsigned char key_state[KEYMAP_SIZE]; 192b85037dbSmrg int i; 193b85037dbSmrg int ret = 0; 194b85037dbSmrg 19528515619Smrg XQueryKeymap(display, (char *) key_state); 196b85037dbSmrg 197b85037dbSmrg for (i = 0; i < KEYMAP_SIZE; i++) { 19828515619Smrg if ((key_state[i] & ~old_key_state[i]) & keyboard_mask[i]) { 19928515619Smrg ret = 1; 20028515619Smrg break; 20128515619Smrg } 202b85037dbSmrg } 203b85037dbSmrg if (ignore_modifier_combos) { 20428515619Smrg for (i = 0; i < KEYMAP_SIZE; i++) { 20528515619Smrg if (key_state[i] & ~keyboard_mask[i]) { 20628515619Smrg ret = 0; 20728515619Smrg break; 20828515619Smrg } 20928515619Smrg } 210b85037dbSmrg } 211b85037dbSmrg for (i = 0; i < KEYMAP_SIZE; i++) 21228515619Smrg old_key_state[i] = key_state[i]; 213b85037dbSmrg return ret; 214b85037dbSmrg} 215b85037dbSmrg 216b85037dbSmrgstatic double 217b85037dbSmrgget_time(void) 218b85037dbSmrg{ 219b85037dbSmrg struct timeval tv; 22028515619Smrg 221b85037dbSmrg gettimeofday(&tv, NULL); 222b85037dbSmrg return tv.tv_sec + tv.tv_usec / 1000000.0; 223b85037dbSmrg} 224b85037dbSmrg 225b85037dbSmrgstatic void 22628515619Smrgmain_loop(Display * display, double idle_time, int poll_delay) 227b85037dbSmrg{ 228b85037dbSmrg double last_activity = 0.0; 229b85037dbSmrg double current_time; 230b85037dbSmrg 231b85037dbSmrg keyboard_activity(display); 232b85037dbSmrg 233b85037dbSmrg for (;;) { 23428515619Smrg current_time = get_time(); 23528515619Smrg if (keyboard_activity(display)) 23628515619Smrg last_activity = current_time; 23728515619Smrg 23828515619Smrg /* If system times goes backwards, touchpad can get locked. Make 23928515619Smrg * sure our last activity wasn't in the future and reset if it was. */ 24028515619Smrg if (last_activity > current_time) 24128515619Smrg last_activity = current_time - idle_time - 1; 24228515619Smrg 24328515619Smrg if (current_time > last_activity + idle_time) { /* Enable touchpad */ 24428515619Smrg toggle_touchpad(True); 24528515619Smrg } 24628515619Smrg else { /* Disable touchpad */ 24728515619Smrg toggle_touchpad(False); 24828515619Smrg } 24928515619Smrg 25028515619Smrg usleep(poll_delay); 251b85037dbSmrg } 252b85037dbSmrg} 253b85037dbSmrg 254b85037dbSmrgstatic void 255b85037dbSmrgclear_bit(unsigned char *ptr, int bit) 256b85037dbSmrg{ 257b85037dbSmrg int byte_num = bit / 8; 258b85037dbSmrg int bit_num = bit % 8; 25928515619Smrg 260b85037dbSmrg ptr[byte_num] &= ~(1 << bit_num); 261b85037dbSmrg} 262b85037dbSmrg 263b85037dbSmrgstatic void 26428515619Smrgsetup_keyboard_mask(Display * display, int ignore_modifier_keys) 265b85037dbSmrg{ 266b85037dbSmrg XModifierKeymap *modifiers; 267b85037dbSmrg int i; 268b85037dbSmrg 269b85037dbSmrg for (i = 0; i < KEYMAP_SIZE; i++) 27028515619Smrg keyboard_mask[i] = 0xff; 271b85037dbSmrg 272b85037dbSmrg if (ignore_modifier_keys) { 27328515619Smrg modifiers = XGetModifierMapping(display); 27428515619Smrg for (i = 0; i < 8 * modifiers->max_keypermod; i++) { 27528515619Smrg KeyCode kc = modifiers->modifiermap[i]; 27628515619Smrg 27728515619Smrg if (kc != 0) 27828515619Smrg clear_bit(keyboard_mask, kc); 27928515619Smrg } 28028515619Smrg XFreeModifiermap(modifiers); 281b85037dbSmrg } 282b85037dbSmrg} 283b85037dbSmrg 284b85037dbSmrg/* ---- the following code is for using the xrecord extension ----- */ 285b85037dbSmrg#ifdef HAVE_X11_EXTENSIONS_RECORD_H 286b85037dbSmrg 287b85037dbSmrg#define MAX_MODIFIERS 16 288b85037dbSmrg 289b85037dbSmrg/* used for exchanging information with the callback function */ 290b85037dbSmrgstruct xrecord_callback_results { 291b85037dbSmrg XModifierKeymap *modifiers; 292b85037dbSmrg Bool key_event; 293b85037dbSmrg Bool non_modifier_event; 294b85037dbSmrg KeyCode pressed_modifiers[MAX_MODIFIERS]; 295b85037dbSmrg}; 296b85037dbSmrg 297b85037dbSmrg/* test if the xrecord extension is found */ 29828515619SmrgBool 29928515619Smrgcheck_xrecord(Display * display) 30028515619Smrg{ 301b85037dbSmrg 30228515619Smrg Bool found; 303b85037dbSmrg Status status; 30428515619Smrg int major_opcode, minor_opcode, first_error; 30528515619Smrg int version[2]; 306b85037dbSmrg 307b85037dbSmrg found = XQueryExtension(display, 30828515619Smrg "RECORD", 30928515619Smrg &major_opcode, &minor_opcode, &first_error); 310b85037dbSmrg 31128515619Smrg status = XRecordQueryVersion(display, version, version + 1); 312302b15bdSmrg if (verbose && status) { 31328515619Smrg printf("X RECORD extension version %d.%d\n", version[0], version[1]); 314b85037dbSmrg } 315b85037dbSmrg return found; 316b85037dbSmrg} 317b85037dbSmrg 318b85037dbSmrg/* called by XRecordProcessReplies() */ 31928515619Smrgvoid 32028515619Smrgxrecord_callback(XPointer closure, XRecordInterceptData * recorded_data) 32128515619Smrg{ 322b85037dbSmrg 323b85037dbSmrg struct xrecord_callback_results *cbres; 324b85037dbSmrg xEvent *xev; 325b85037dbSmrg int nxev; 326b85037dbSmrg 32728515619Smrg cbres = (struct xrecord_callback_results *) closure; 328b85037dbSmrg 329b85037dbSmrg if (recorded_data->category != XRecordFromServer) { 33028515619Smrg XRecordFreeData(recorded_data); 33128515619Smrg return; 332b85037dbSmrg } 333b85037dbSmrg 334b85037dbSmrg nxev = recorded_data->data_len / 8; 33528515619Smrg xev = (xEvent *) recorded_data->data; 33628515619Smrg while (nxev--) { 33728515619Smrg 33828515619Smrg if ((xev->u.u.type == KeyPress) || (xev->u.u.type == KeyRelease)) { 33928515619Smrg int i; 34028515619Smrg int is_modifier = 0; 34128515619Smrg 34228515619Smrg cbres->key_event = 1; /* remember, a key was pressed or released. */ 34328515619Smrg 34428515619Smrg /* test if it was a modifier */ 34528515619Smrg for (i = 0; i < 8 * cbres->modifiers->max_keypermod; i++) { 34628515619Smrg KeyCode kc = cbres->modifiers->modifiermap[i]; 34728515619Smrg 34828515619Smrg if (kc == xev->u.u.detail) { 34928515619Smrg is_modifier = 1; /* yes, it is a modifier. */ 35028515619Smrg break; 35128515619Smrg } 35228515619Smrg } 35328515619Smrg 35428515619Smrg if (is_modifier) { 35528515619Smrg if (xev->u.u.type == KeyPress) { 35628515619Smrg for (i = 0; i < MAX_MODIFIERS; ++i) 35728515619Smrg if (!cbres->pressed_modifiers[i]) { 35828515619Smrg cbres->pressed_modifiers[i] = xev->u.u.detail; 35928515619Smrg break; 36028515619Smrg } 36128515619Smrg } 36228515619Smrg else { /* KeyRelease */ 36328515619Smrg for (i = 0; i < MAX_MODIFIERS; ++i) 36428515619Smrg if (cbres->pressed_modifiers[i] == xev->u.u.detail) 36528515619Smrg cbres->pressed_modifiers[i] = 0; 36628515619Smrg } 36728515619Smrg 36828515619Smrg } 36928515619Smrg else { 37028515619Smrg /* remember, a non-modifier was pressed. */ 37128515619Smrg cbres->non_modifier_event = 1; 37228515619Smrg } 37328515619Smrg } 37428515619Smrg 37528515619Smrg xev++; 376b85037dbSmrg } 377b85037dbSmrg 37828515619Smrg XRecordFreeData(recorded_data); /* cleanup */ 379b85037dbSmrg} 380b85037dbSmrg 38128515619Smrgstatic int 38228515619Smrgis_modifier_pressed(const struct xrecord_callback_results *cbres) 38328515619Smrg{ 384b85037dbSmrg int i; 385b85037dbSmrg 386b85037dbSmrg for (i = 0; i < MAX_MODIFIERS; ++i) 38728515619Smrg if (cbres->pressed_modifiers[i]) 38828515619Smrg return 1; 389b85037dbSmrg 390b85037dbSmrg return 0; 391b85037dbSmrg} 392b85037dbSmrg 39328515619Smrgvoid 39428515619Smrgrecord_main_loop(Display * display, double idle_time) 39528515619Smrg{ 396b85037dbSmrg 397b85037dbSmrg struct xrecord_callback_results cbres; 398b85037dbSmrg XRecordContext context; 399b85037dbSmrg XRecordClientSpec cspec = XRecordAllClients; 400b85037dbSmrg Display *dpy_data; 401b85037dbSmrg XRecordRange *range; 402b85037dbSmrg int i; 403b85037dbSmrg 40428515619Smrg dpy_data = XOpenDisplay(NULL); /* we need an additional data connection. */ 40528515619Smrg range = XRecordAllocRange(); 406b85037dbSmrg 407b85037dbSmrg range->device_events.first = KeyPress; 40828515619Smrg range->device_events.last = KeyRelease; 409b85037dbSmrg 41028515619Smrg context = XRecordCreateContext(dpy_data, 0, &cspec, 1, &range, 1); 411b85037dbSmrg 41228515619Smrg XRecordEnableContextAsync(dpy_data, context, xrecord_callback, 41328515619Smrg (XPointer) & cbres); 414b85037dbSmrg 41528515619Smrg cbres.modifiers = XGetModifierMapping(display); 416b85037dbSmrg /* clear list of modifiers */ 417b85037dbSmrg for (i = 0; i < MAX_MODIFIERS; ++i) 41828515619Smrg cbres.pressed_modifiers[i] = 0; 419b85037dbSmrg 420b85037dbSmrg while (1) { 421b85037dbSmrg 42228515619Smrg int fd = ConnectionNumber(dpy_data); 42328515619Smrg fd_set read_fds; 42428515619Smrg int ret; 42528515619Smrg int disable_event = 0; 42628515619Smrg struct timeval timeout; 427b85037dbSmrg 42828515619Smrg FD_ZERO(&read_fds); 42928515619Smrg FD_SET(fd, &read_fds); 430b85037dbSmrg 43128515619Smrg ret = select(fd + 1 /* =(max descriptor in read_fds) + 1 */ , 43228515619Smrg &read_fds, NULL, NULL, 43328515619Smrg pad_disabled ? &timeout : NULL 43428515619Smrg /* timeout only required for enabling */ ); 435b85037dbSmrg 43628515619Smrg if (FD_ISSET(fd, &read_fds)) { 437b85037dbSmrg 43828515619Smrg cbres.key_event = 0; 43928515619Smrg cbres.non_modifier_event = 0; 440b85037dbSmrg 44128515619Smrg XRecordProcessReplies(dpy_data); 442b85037dbSmrg 44328515619Smrg /* If there are any events left over, they are in error. Drain them 44428515619Smrg * from the connection queue so we don't get stuck. */ 44528515619Smrg while (XEventsQueued(dpy_data, QueuedAlready) > 0) { 44628515619Smrg XEvent event; 447302b15bdSmrg 44828515619Smrg XNextEvent(dpy_data, &event); 44928515619Smrg fprintf(stderr, "bad event received, major opcode %d\n", 45028515619Smrg event.type); 45128515619Smrg } 452b85037dbSmrg 45328515619Smrg if (!ignore_modifier_keys && cbres.key_event) { 45428515619Smrg disable_event = 1; 45528515619Smrg } 456b85037dbSmrg 45728515619Smrg if (cbres.non_modifier_event && 45828515619Smrg !(ignore_modifier_combos && is_modifier_pressed(&cbres))) { 45928515619Smrg disable_event = 1; 46028515619Smrg } 46128515619Smrg } 462b85037dbSmrg 46328515619Smrg if (disable_event) { 46428515619Smrg /* adjust the enable_time */ 46528515619Smrg timeout.tv_sec = (int) idle_time; 46628515619Smrg timeout.tv_usec = (idle_time - (double) timeout.tv_sec) * 1.e6; 467b85037dbSmrg 46828515619Smrg toggle_touchpad(False); 46928515619Smrg } 470b85037dbSmrg 47128515619Smrg if (ret == 0 && pad_disabled) { /* timeout => enable event */ 47228515619Smrg toggle_touchpad(True); 47328515619Smrg } 47428515619Smrg 47528515619Smrg } /* end while(1) */ 476b85037dbSmrg 477b85037dbSmrg XFreeModifiermap(cbres.modifiers); 478b85037dbSmrg} 47928515619Smrg#endif /* HAVE_X11_EXTENSIONS_RECORD_H */ 480b85037dbSmrg 481b85037dbSmrgstatic XDevice * 48228515619Smrgdp_get_device(Display * dpy) 483b85037dbSmrg{ 48428515619Smrg XDevice *dev = NULL; 48528515619Smrg XDeviceInfo *info = NULL; 48628515619Smrg int ndevices = 0; 48728515619Smrg Atom touchpad_type = 0; 48828515619Smrg Atom *properties = NULL; 48928515619Smrg int nprops = 0; 49028515619Smrg int error = 0; 491b85037dbSmrg 492b85037dbSmrg touchpad_type = XInternAtom(dpy, XI_TOUCHPAD, True); 493b85037dbSmrg touchpad_off_prop = XInternAtom(dpy, SYNAPTICS_PROP_OFF, True); 494b85037dbSmrg info = XListInputDevices(dpy, &ndevices); 495b85037dbSmrg 49628515619Smrg while (ndevices--) { 49728515619Smrg if (info[ndevices].type == touchpad_type) { 49828515619Smrg dev = XOpenDevice(dpy, info[ndevices].id); 49928515619Smrg if (!dev) { 50028515619Smrg fprintf(stderr, "Failed to open device '%s'.\n", 50128515619Smrg info[ndevices].name); 50228515619Smrg error = 1; 50328515619Smrg goto unwind; 50428515619Smrg } 50528515619Smrg 50628515619Smrg properties = XListDeviceProperties(dpy, dev, &nprops); 50728515619Smrg if (!properties || !nprops) { 50828515619Smrg fprintf(stderr, "No properties on device '%s'.\n", 50928515619Smrg info[ndevices].name); 51028515619Smrg error = 1; 51128515619Smrg goto unwind; 51228515619Smrg } 51328515619Smrg 51428515619Smrg while (nprops--) { 51528515619Smrg if (properties[nprops] == touchpad_off_prop) 51628515619Smrg break; 51728515619Smrg } 51828515619Smrg if (nprops < 0) { 51928515619Smrg fprintf(stderr, "No synaptics properties on device '%s'.\n", 52028515619Smrg info[ndevices].name); 52128515619Smrg error = 1; 52228515619Smrg goto unwind; 52328515619Smrg } 52428515619Smrg 52528515619Smrg break; /* Yay, device is suitable */ 52628515619Smrg } 527b85037dbSmrg } 528b85037dbSmrg 52928515619Smrg unwind: 530b85037dbSmrg XFree(properties); 531b85037dbSmrg XFreeDeviceList(info); 532b85037dbSmrg if (!dev) 53328515619Smrg fprintf(stderr, "Unable to find a synaptics device.\n"); 53428515619Smrg else if (error && dev) { 53528515619Smrg XCloseDevice(dpy, dev); 53628515619Smrg dev = NULL; 537b85037dbSmrg } 538b85037dbSmrg return dev; 539b85037dbSmrg} 540b85037dbSmrg 541b85037dbSmrgint 542b85037dbSmrgmain(int argc, char *argv[]) 543b85037dbSmrg{ 544b85037dbSmrg double idle_time = 2.0; 54528515619Smrg int poll_delay = 200000; /* 200 ms */ 546b85037dbSmrg int c; 547b85037dbSmrg int use_xrecord = 0; 548b85037dbSmrg 549b85037dbSmrg /* Parse command line parameters */ 550302b15bdSmrg while ((c = getopt(argc, argv, "i:m:dtp:kKR?v")) != EOF) { 55128515619Smrg switch (c) { 55228515619Smrg case 'i': 55328515619Smrg idle_time = atof(optarg); 55428515619Smrg break; 55528515619Smrg case 'm': 55628515619Smrg poll_delay = atoi(optarg) * 1000; 55728515619Smrg break; 55828515619Smrg case 'd': 55928515619Smrg background = 1; 56028515619Smrg break; 56128515619Smrg case 't': 56228515619Smrg disable_state = TappingOff; 56328515619Smrg break; 56428515619Smrg case 'p': 56528515619Smrg pid_file = optarg; 56628515619Smrg break; 56728515619Smrg case 'k': 56828515619Smrg ignore_modifier_keys = 1; 56928515619Smrg break; 57028515619Smrg case 'K': 57128515619Smrg ignore_modifier_combos = 1; 57228515619Smrg ignore_modifier_keys = 1; 57328515619Smrg break; 57428515619Smrg case 'R': 57528515619Smrg use_xrecord = 1; 57628515619Smrg break; 57728515619Smrg case 'v': 57828515619Smrg verbose = 1; 57928515619Smrg break; 58028515619Smrg case '?': 58128515619Smrg default: 58228515619Smrg usage(); 58328515619Smrg break; 58428515619Smrg } 585b85037dbSmrg } 586b85037dbSmrg if (idle_time <= 0.0) 58728515619Smrg usage(); 588b85037dbSmrg 589b85037dbSmrg /* Open a connection to the X server */ 590b85037dbSmrg display = XOpenDisplay(NULL); 591b85037dbSmrg if (!display) { 59228515619Smrg fprintf(stderr, "Can't open display.\n"); 59328515619Smrg exit(2); 594b85037dbSmrg } 595b85037dbSmrg 596b85037dbSmrg if (!(dev = dp_get_device(display))) 59728515619Smrg exit(2); 598b85037dbSmrg 599b85037dbSmrg /* Install a signal handler to restore synaptics parameters on exit */ 600b85037dbSmrg install_signal_handler(); 601b85037dbSmrg 602b85037dbSmrg if (background) { 60328515619Smrg pid_t pid; 60428515619Smrg 60528515619Smrg if ((pid = fork()) < 0) { 60628515619Smrg perror("fork"); 60728515619Smrg exit(3); 60828515619Smrg } 60928515619Smrg else if (pid != 0) 61028515619Smrg exit(0); 61128515619Smrg 61228515619Smrg /* Child (daemon) is running here */ 61328515619Smrg setsid(); /* Become session leader */ 61428515619Smrg chdir("/"); /* In case the file system gets unmounted */ 61528515619Smrg umask(0); /* We don't want any surprises */ 61628515619Smrg if (pid_file) { 61728515619Smrg FILE *fd = fopen(pid_file, "w"); 61828515619Smrg 61928515619Smrg if (!fd) { 62028515619Smrg perror("Can't create pid file"); 62128515619Smrg exit(3); 62228515619Smrg } 62328515619Smrg fprintf(fd, "%d\n", getpid()); 62428515619Smrg fclose(fd); 62528515619Smrg } 626b85037dbSmrg } 627b85037dbSmrg 628b85037dbSmrg pad_disabled = False; 629b85037dbSmrg store_current_touchpad_state(); 630b85037dbSmrg 631b85037dbSmrg#ifdef HAVE_X11_EXTENSIONS_RECORD_H 63228515619Smrg if (use_xrecord) { 63328515619Smrg if (check_xrecord(display)) 63428515619Smrg record_main_loop(display, idle_time); 63528515619Smrg else { 63628515619Smrg fprintf(stderr, "Use of XRecord requested, but failed to " 63728515619Smrg " initialize.\n"); 63828515619Smrg exit(4); 639b85037dbSmrg } 64028515619Smrg } 64128515619Smrg else 64228515619Smrg#endif /* HAVE_X11_EXTENSIONS_RECORD_H */ 64328515619Smrg { 64428515619Smrg setup_keyboard_mask(display, ignore_modifier_keys); 64528515619Smrg 64628515619Smrg /* Run the main loop */ 64728515619Smrg main_loop(display, idle_time, poll_delay); 64828515619Smrg } 649b85037dbSmrg return 0; 650b85037dbSmrg} 65128515619Smrg 65228515619Smrg/* vim: set noexpandtab tabstop=8 shiftwidth=4: */ 653