From 6adccdbae79a52ee3e61eaa1b679d478a71482dd Mon Sep 17 00:00:00 2001 From: Stephen Chandler Paul Date: Thu, 13 Feb 2014 16:03:56 -0500 Subject: [PATCH synaptics] Add support for monitoring pointing sticks Using XSync syndaemon can watch for activity from a laptop's pointing stick and change the settings accordingly. Useful for machines with a pointing stick that relies on it's clickpad's software buttons. By default, enabling point stick monitoring will also disable keyboard monitoring so that the user doesn't have to have keyboard monitoring enabled. However, keyboard monitoring can be enabled simultaneously by specifying -M on the command line. --- configure.ac | 2 +- man/syndaemon.man | 56 ++++-- tools/Makefile.am | 2 +- tools/syndaemon.c | 560 +++++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 514 insertions(+), 106 deletions(-) diff --git a/configure.ac b/configure.ac index 77edbb2..c6cf29b 100644 --- a/configure.ac +++ b/configure.ac @@ -135,7 +135,7 @@ AM_CONDITIONAL([BUILD_PS2COMM], [test "x${BUILD_PS2COMM}" = "xyes"]) # Dependencies for synclient and syndaemon # ----------------------------------------------------------------------------- # Obtain compiler/linker options for the Synaptics apps dependencies -PKG_CHECK_MODULES(XI, x11 inputproto [xi >= 1.2]) +PKG_CHECK_MODULES(XI, x11 inputproto xext [xi >= 1.2]) # The syndaemon program uses an optional XRecord extension implementation # If libxtst >= 1.0.99 is installed, Cflags contains the path to record.h diff --git a/man/syndaemon.man b/man/syndaemon.man index 30ea20b..f08ee09 100644 --- a/man/syndaemon.man +++ b/man/syndaemon.man @@ -3,12 +3,11 @@ .TH syndaemon __appmansuffix__ __vendorversion__ .SH NAME .LP -syndaemon \- a program that monitors keyboard activity and disables -the touchpad when the keyboard is being used. +syndaemon \- a program that monitors keyboard and/or pointing stick activity and +disables the touchpad when either of them are being used. .SH "SYNOPSIS" .LP -syndaemon [\fI\-i idle\-time\fP] [\fI\-m poll-inverval\fP] [\fI\-d\fP] [\fI\-p pid\-file\fP] -[\fI\-t\fP] [\fI\-k\fP] [\fI\-K\fP] [\fI\-R\fP] +syndaemon [\fI\-i idle\-time\fP] [\fI\-m poll-inverval\fP] [\fI\-d\fP] [\fI\-p pid\-file\fP] [\fI\-t\fP] [\fI\-P\fP [\fIpointing-stick\fP]] [\fI\-M\fP] [\fI\-k\fP] [\fI\-K\fP] [\fI\-R\fP] .SH "DESCRIPTION" .LP Disabling the touchpad while typing avoids unwanted movements of the @@ -18,18 +17,17 @@ pointer that could lead to giving focus to the wrong window. .LP .TP \fB\-i\fR <\fIidle\-time\fP> -How many seconds to wait after the last key press before enabling the -touchpad. +How many seconds to wait after the last key press or pointing stick activity +before enabling the touchpad. . (default is 2.0s). .LP .TP \fB\-m\fR <\fIpoll\-interval\fP> How many milliseconds to wait between two polling intervals. If this value is -too low, it will cause unnecessary wake-ups. If this value is too high, -some key presses (press and release happen between two intervals) may not -be noticed. This switch has no effect when running with -\fB-R\fP. +too low, it will cause unnecessary wake-ups. If this value is too high, some key +presses (press and release happen between two intervals) may not be noticed. +This switch has no effect when running with \fB\-R\fP and/or \fB\-P\fP. . Default is 200ms. .LP @@ -46,10 +44,30 @@ mode. .LP .TP \fB\-t\fP [off|tapping|click-only] - Disable state. "off" for disabling the touchpad entirely, "tapping" for - disabling tapping and scrolling only, "click-only" for disabling - everything but physical clicks. The default if this option is missing is - "off". If this option is given without a mode it defaults to "tapping". +Disable state. "off" for disabling the touchpad entirely, "tapping" for +disabling tapping and scrolling only, "click-only" for disabling everything but +physical clicks. The default if this option is missing is "off". If this option +is given without a mode it defaults to "tapping". +.LP +.TP +\fB\-T\fP [off|tapping|click-only] +Same as \fB\-t\fP, but sets the disable state for pointing stick monitoring. +.LP +.TP +\fB\-P\fP [pointing-stick] +Monitors the pointing stick for activity instead of keyboard activity. XSync +support is required on the server for this to work. Useful on laptops that have +a pointing stick but lack a set of physical buttons for their pointing stick. +If this option is given without the name of a pointing stick, it defaults to +"TPPS/2 IBM TrackPoint". +. +When using this option, keyboard monitoring is disabled unless explicitly +enabled by specifying \fB\-R\fP or \fB\-M\fP. +.LP +.TP +\fB\-M\fP +Explicitly enables keyboard monitoring. This is the default behavior, unless +\fB\-P\fP has been enabled. .LP .TP \fB\-k\fP @@ -92,6 +110,16 @@ The fork into daemon mode failed or the pid file could not be created. .TP \fBExit code 4 XRECORD requested but not available or usable on the server. +.LP +.TP +\fBExit code 5 +Pointing stick monitoring requested, but XSync was not available or usable on +the server. +.LP +.TP +\fBExit code 6 +Pointing stick monitoring requested, but a pointing stick could not be found +(try specifying the name manually when using \fB\-P\fP). .SH "CAVEATS" .LP It doesn't make much sense to connect to a remote X server, because diff --git a/tools/Makefile.am b/tools/Makefile.am index e790905..02ea7ba 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -28,4 +28,4 @@ synclient_SOURCES = synclient.c syndaemon_SOURCES = syndaemon.c syndaemon_CFLAGS = $(AM_CFLAGS) $(XTST_CFLAGS) -syndaemon_LDFLAGS = $(AM_LDFLAGS) $(XTST_LIBS) +syndaemon_LDFLAGS = $(AM_LDFLAGS) $(XTST_LIBS) $(XEXT_LIBS) diff --git a/tools/syndaemon.c b/tools/syndaemon.c index b181d16..8e4dbc1 100644 --- a/tools/syndaemon.c +++ b/tools/syndaemon.c @@ -1,5 +1,6 @@ /* * Copyright © 2003-2004 Peter Osterlund + * Copyright © 2014 Stephen Chandler Paul * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without @@ -22,6 +23,7 @@ * * Authors: * Peter Osterlund (petero2@telia.com) + * Stephen Chandler Paul (thatslyude@gmail.com) */ #ifdef HAVE_CONFIG_H @@ -35,6 +37,7 @@ #include #include #endif /* HAVE_X11_EXTENSIONS_RECORD_H */ +#include #include #include @@ -44,18 +47,34 @@ #include #include #include +#include +#include #include "synaptics-properties.h" +#define DEFAULT_POINTING_STICK "TPPS/2 IBM TrackPoint" + +#define max(a_, b_) (a_ > b_) ? (a_) : (b_) + enum TouchpadState { TouchpadOn = 0, TouchpadOff = 1, TappingOff = 2, - ClickOnly = 3 + ClickOnly = 3, + InvalidState = -1 }; -static Bool pad_disabled - /* internal flag, this does not correspond to device state */ ; +enum TouchpadIdleMode { + KeyboardIdle = 0, + PStickIdle = 1, +}; + +typedef struct { + int x; + int y; +} CursorPosition; + +static Bool is_idle[2]; static int ignore_modifier_combos; static int ignore_modifier_keys; static int background; @@ -64,9 +83,15 @@ static Display *display; static XDevice *dev; static Atom touchpad_off_prop; static enum TouchpadState previous_state; -static enum TouchpadState disable_state = TouchpadOff; +static enum TouchpadState idle_state[2]; +static Bool monitor_keyboard; +static Bool monitor_pstick = False; static int verbose; +static int sync_event; /* first xsync opcode */ +static XSyncAlarm pstick_leave_idle_alarm; +static XSyncAlarm pstick_enter_idle_alarm; + #define KEYMAP_SIZE 32 static unsigned char keyboard_mask[KEYMAP_SIZE]; @@ -74,7 +99,7 @@ static void usage(void) { fprintf(stderr, - "Usage: syndaemon [-i idle-time] [-m poll-delay] [-d] [-t [off|tapping|click-only]] [-k]\n"); + "Usage: syndaemon [-i idle-time] [-m poll-delay] [-d] [-t [off|tapping|click-only]] [-T [off|tapping|click-only]] [-k] [-K] [-M] [-R] [-P [pointing-stick]]\n"); fprintf(stderr, " -i How many seconds to wait after the last key press before\n"); fprintf(stderr, " enabling the touchpad. (default is 2.0s)\n"); @@ -88,6 +113,15 @@ usage(void) " 'tapping' for disabling tapping and scrolling only,\n" " 'click-only' for disabling everything but physical clicks.\n"); fprintf(stderr, + " -T Disable state.\n" + " Same as -t, applies to pointing stick monitoring.\n"); + fprintf(stderr, + " -M Enables monitoring of the keyboard (default).\n"); + fprintf(stderr, + " -P Pointing Stick.\n" + " Enables monitoring of the pointing stick (disables keyboard \n" + " monitoring unless -R or -M is also specified)\n"); + fprintf(stderr, " -k Ignore modifier keys when monitoring keyboard activity.\n"); fprintf(stderr, " -K Like -k but also ignore Modifier+Key combos.\n"); fprintf(stderr, " -R Use the XRecord extension.\n"); @@ -112,41 +146,94 @@ store_current_touchpad_state(void) } } -/** - * Toggle touchpad enabled/disabled state, decided by value. - */ static void -toggle_touchpad(Bool enable) -{ - unsigned char data; +set_touchpad_state(enum TouchpadState new_state) { + unsigned char data = new_state; - if (pad_disabled && enable) { - data = previous_state; - pad_disabled = False; - if (verbose) - printf("Enable\n"); - } - else if (!pad_disabled && !enable && - previous_state != disable_state && previous_state != TouchpadOff) { - store_current_touchpad_state(); - pad_disabled = True; - data = disable_state; - if (verbose) - printf("Disable\n"); - } - else - return; - - /* This potentially overwrites a different client's setting, but ... */ XChangeDeviceProperty(display, dev, touchpad_off_prop, XA_INTEGER, 8, PropModeReplace, &data, 1); XFlush(display); } static void +update_touchpad_state(void) { + static Bool was_idle[2] = {1, 1}; + enum TouchpadState new_state = InvalidState; + + /* Decide on the idle state to use: + (1) PStick goes from idle to active + (1.1) Keyboard is idle + → Set touchpad to PStick mode + (1.2) Keyboard was active + → Set touchpad to PStick mode + (2) PStick goes from active to idle + (2.1) Keyboard is idle + → Set touchpad to enabled + (2.2) Keyboard is active + → Set touchpad to Keyboard mode + (3) Keyboard goes from idle to active + (3.1) PStick is idle + → Set touchpad to Keyboad mode + (3.2) PStick is active + → Do nothing, stay in PStick mode + (4) Keyboard goes from active to idle + (4.1) PStick is idle + → Set touchpad to enabled + (4.2) PStick is active + → Do nothing, stay in PStick mode + */ + + if (was_idle[PStickIdle] == is_idle[PStickIdle] && + was_idle[KeyboardIdle] == is_idle[KeyboardIdle]) + return; /* No state changes */ + + /* (1) */ + if (was_idle[PStickIdle] && !is_idle[PStickIdle]) { + if (is_idle[KeyboardIdle]) + new_state = idle_state[PStickIdle]; /* (1.1) */ + else + new_state = idle_state[PStickIdle]; /* (1.2) */ + /* (2) */ + } else if (!was_idle[PStickIdle] && is_idle[PStickIdle]) { + if (is_idle[KeyboardIdle]) + new_state = TouchpadOn; /* (2.1) */ + else + new_state = idle_state[KeyboardIdle]; /* (2.2) */ + /* (3) */ + } else if (was_idle[KeyboardIdle] && !is_idle[KeyboardIdle]) { + if (is_idle[PStickIdle]) + new_state = idle_state[KeyboardIdle]; /* (3.1) */ + else { + /* noop (3.2) */ + } + /* (4) */ + } else if (!was_idle[KeyboardIdle] && is_idle[KeyboardIdle]) { + if (is_idle[PStickIdle]) + new_state = TouchpadOn; /* (4.1) */ + else { + /* noop (4.2) */ + } + } + + if (new_state != InvalidState) { + if (verbose) { + if (new_state == TouchpadOn) + printf("Enabling touchpad.\n"); + else + printf("Setting mode %d\n", new_state); + } + + set_touchpad_state(new_state); + } + + was_idle[KeyboardIdle] = is_idle[KeyboardIdle]; + was_idle[PStickIdle] = is_idle[PStickIdle]; +} + +static void signal_handler(int signum) { - toggle_touchpad(True); + set_touchpad_state(previous_state); if (pid_file) unlink(pid_file); @@ -185,6 +272,24 @@ install_signal_handler(void) } } + +static int +setup_xsync(Display * display) +{ + int sync_error; + int sync_major, sync_minor; + + if (!XSyncQueryExtension(display, &sync_event, &sync_error)) + return 1; + + if (!XSyncInitialize(display, &sync_major, &sync_minor)) + return 1; + if (verbose) + printf("X Sync extension version %d.%d\n", sync_major, sync_minor); + + return 0; +} + /** * Return non-zero if the keyboard state has changed since the last call. */ @@ -217,6 +322,31 @@ keyboard_activity(Display * display) return ret; } +/** + * Handles XSync events from the pointing stick + */ +static void +handle_pstick_events(Display * display) { + while (XPending(display)) { + XEvent event; + XSyncAlarmNotifyEvent * alarm_event; + + XNextEvent(display, &event); + alarm_event = (XSyncAlarmNotifyEvent*)&event; + + if (alarm_event->type != sync_event + XSyncAlarmNotify) + continue; + + if (alarm_event->alarm == pstick_leave_idle_alarm) + is_idle[PStickIdle] = False; + else if (alarm_event->alarm == pstick_enter_idle_alarm) + is_idle[PStickIdle] = True; + else + fprintf(stderr, "Got event from unknown alarm %ld\n", + alarm_event->alarm); + } +} + static double get_time(void) { @@ -229,29 +359,51 @@ get_time(void) static void main_loop(Display * display, double idle_time, int poll_delay) { - double last_activity = 0.0; + double last_activity_time = 0.0; double current_time; + struct pollfd pstick_pollfd; - keyboard_activity(display); + if (monitor_keyboard) + keyboard_activity(display); + + if (monitor_pstick) { + pstick_pollfd.fd = XConnectionNumber(display); + pstick_pollfd.events = POLLIN; + } for (;;) { - current_time = get_time(); - if (keyboard_activity(display)) - last_activity = current_time; + if (monitor_keyboard) { + current_time = get_time(); - /* If system times goes backwards, touchpad can get locked. Make - * sure our last activity wasn't in the future and reset if it was. */ - if (last_activity > current_time) - last_activity = current_time - idle_time - 1; + if (keyboard_activity(display)) { + is_idle[KeyboardIdle] = False; + last_activity_time = current_time; + } + else if (is_idle[KeyboardIdle] == False) { + /* If system time goes backwards, touchpad can get locked. Make + * sure out last activity wasn't in the future and reset it if + * it was. */ + if (last_activity_time > current_time) + last_activity_time = 0.0; - if (current_time > last_activity + idle_time) { /* Enable touchpad */ - toggle_touchpad(True); + if (current_time > last_activity_time + idle_time) + is_idle[KeyboardIdle] = True; + } } - else { /* Disable touchpad */ - toggle_touchpad(False); + + if (monitor_pstick) { + /* Only have a timeout if we're doing plain monitoring for the + * keyboard too. Since we're using the same fd as we use for + * keyboard monitoring, Xlib may have pulled the events off when + * checking for keyboard activity, so unconditionally check for + * events. + */ + poll(&pstick_pollfd, 1, monitor_keyboard ? poll_delay : -1); + handle_pstick_events(display); } - usleep(poll_delay); + update_touchpad_state(); + usleep(poll_delay * 1000); } } @@ -285,6 +437,78 @@ setup_keyboard_mask(Display * display, int ignore_modifier_keys) } } +static XSyncAlarm +setup_device_alarm(Display * display, int device_id, XSyncTestType test_type, + int wait_time) +{ + XSyncAlarm alarm; + int alarm_flags; + XSyncAlarmAttributes attr; + XSyncValue delta, interval; + + int ncounters; + char idle_counter_name[20]; + XSyncCounter counter = 0; + XSyncSystemCounter * counters; + + snprintf(&idle_counter_name[0], sizeof(idle_counter_name), + "DEVICEIDLETIME %d", device_id); + counters = XSyncListSystemCounters(display, &ncounters); + + /* Find the idle counter for the trackpoint */ + for (int i = 0; i < ncounters; i++) { + if (strcmp(counters[i].name, idle_counter_name) == 0) { + counter = counters[i].counter; + break; + } + } + + if (counter == 0) + return 0; + + XSyncFreeSystemCounterList(counters); + + XSyncIntToValue(&delta, 0); + XSyncIntToValue(&interval, wait_time); + + attr.trigger.counter = counter; + attr.trigger.test_type = test_type; + attr.trigger.value_type = XSyncAbsolute; + attr.trigger.wait_value = interval; + attr.delta = delta; + attr.events = True; + + alarm_flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType | + XSyncCAValue | XSyncCADelta; + + alarm = XSyncCreateAlarm(display, alarm_flags, &attr); + return alarm; +} + +static int +get_device_id(Display * display, const char * dev_name, + char * dev_type) { + XDeviceInfo * info = NULL; + int ndevices = 0; + int dev_id = 0; + Atom dev_type_atom; + + dev_type_atom = + (dev_type != NULL) ? XInternAtom(display, dev_type, True) : 0; + info = XListInputDevices(display, &ndevices); + + while (ndevices--) { + if (info[ndevices].type == dev_type_atom && + strcmp(info[ndevices].name, dev_name) == 0) { + dev_id = info[ndevices].id; + break; + } + } + + XFreeDeviceList(info); + return dev_id; +} + /* ---- the following code is for using the xrecord extension ----- */ #ifdef HAVE_X11_EXTENSIONS_RECORD_H @@ -394,6 +618,50 @@ is_modifier_pressed(const struct xrecord_callback_results *cbres) return 0; } +/* from http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html */ +static int +timespec_subtract(struct timeval *result, + struct timespec *x, + struct timespec *y) +{ + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_nsec < y->tv_nsec) { + int nsec = (y->tv_nsec - x->tv_nsec) / 1000000 + 1; + y->tv_nsec -= 1000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_nsec - y->tv_nsec > 1000000) { + int nsec = (x->tv_nsec - y->tv_nsec) / 1000000; + y->tv_nsec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_nsec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = (x->tv_nsec - y->tv_nsec)/1000; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/* return 1, 0, -1 if a is greater, equal, less than b */ +static int +timespec_cmp(struct timespec *a, struct timespec *b) +{ + if (a->tv_sec > b->tv_sec) + return 1; + else if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_nsec > b->tv_nsec) + return 1; + else if (a->tv_nsec < b->tv_nsec) + return -1; + else + return 0; +} + void record_main_loop(Display * display, double idle_time) { @@ -403,9 +671,15 @@ record_main_loop(Display * display, double idle_time) XRecordClientSpec cspec = XRecordAllClients; Display *dpy_data; XRecordRange *range; - int i; + int keyboard_fd = -1; + int pstick_fd = -1; + int max_fd; + struct timeval timeout; + struct timespec kbd_activity_end; + fd_set read_fds; - dpy_data = XOpenDisplay(NULL); /* we need an additional data connection. */ + /* we need an additional data connection. */ + dpy_data = XOpenDisplay(NULL); range = XRecordAllocRange(); range->device_events.first = KeyPress; @@ -418,27 +692,53 @@ record_main_loop(Display * display, double idle_time) cbres.modifiers = XGetModifierMapping(display); /* clear list of modifiers */ - for (i = 0; i < MAX_MODIFIERS; ++i) + for (int i = 0; i < MAX_MODIFIERS; ++i) cbres.pressed_modifiers[i] = 0; + keyboard_fd = ConnectionNumber(dpy_data); + + if (monitor_pstick) + pstick_fd = ConnectionNumber(display); + + max_fd = max(keyboard_fd, pstick_fd); + while (1) { - - int fd = ConnectionNumber(dpy_data); - fd_set read_fds; int ret; - int disable_event = 0; - struct timeval timeout; + int keyboard_event = 0; FD_ZERO(&read_fds); - FD_SET(fd, &read_fds); - ret = select(fd + 1 /* =(max descriptor in read_fds) + 1 */ , - &read_fds, NULL, NULL, - pad_disabled ? &timeout : NULL - /* timeout only required for enabling */ ); + FD_SET(keyboard_fd, &read_fds); - if (FD_ISSET(fd, &read_fds)) { + if (monitor_pstick) + FD_SET(pstick_fd, &read_fds); + ret = select(max_fd + 1, &read_fds, NULL, NULL, + is_idle[KeyboardIdle] ? NULL : &timeout + /* timeout only required for enabling keyboard */); + + if (FD_ISSET(pstick_fd, &read_fds)) { + handle_pstick_events(display); + + if (!is_idle[KeyboardIdle]) { + struct timespec current_time; + clock_gettime(CLOCK_MONOTONIC, ¤t_time); + + /* Check to make sure the keyboard activity didn't stop while we + * were worrying about pointing stick events */ + if (timespec_cmp(¤t_time, &kbd_activity_end) >= 0) { + is_idle[KeyboardIdle] = True; + } else { + /* Update the keyboard timeout so we still get notified when + * keyboard activity ceases */ + timespec_subtract(&timeout, + &kbd_activity_end, + ¤t_time); + } + } + } + + if (FD_ISSET(keyboard_fd, &read_fds)) { cbres.key_event = 0; cbres.non_modifier_event = 0; @@ -455,28 +755,45 @@ record_main_loop(Display * display, double idle_time) } if (!ignore_modifier_keys && cbres.key_event) { - disable_event = 1; + keyboard_event = 1; } if (cbres.non_modifier_event && !(ignore_modifier_combos && is_modifier_pressed(&cbres))) { - disable_event = 1; + keyboard_event = 1; } } - if (disable_event) { + if (keyboard_event) { /* adjust the enable_time */ timeout.tv_sec = (int) idle_time; timeout.tv_usec = (idle_time - (double) timeout.tv_sec) * 1.e6; - toggle_touchpad(False); - } + /* Knowing when the end of the activity period for the keyboard + * only matters if we could get interrupted before a select() + * timeout by pstick events + */ + if (monitor_pstick) { + clock_gettime(CLOCK_MONOTONIC, &kbd_activity_end); + + kbd_activity_end.tv_sec += timeout.tv_sec; + kbd_activity_end.tv_nsec += timeout.tv_usec * 1000; - if (ret == 0 && pad_disabled) { /* timeout => enable event */ - toggle_touchpad(True); + // Normalize the tv_usec value + if (kbd_activity_end.tv_nsec >= 1.e9) { + kbd_activity_end.tv_sec++; + kbd_activity_end.tv_nsec -= 1.e9; + } + } + + is_idle[KeyboardIdle] = False; } - } /* end while(1) */ + if (ret == 0) /* timeout => enable event */ + is_idle[KeyboardIdle] = True; + + update_touchpad_state(); + } XFreeModifiermap(cbres.modifiers); } @@ -542,22 +859,40 @@ dp_get_device(Display * dpy) return dev; } +static enum TouchpadState +parse_idle_mode(const char * state) { + if (state[0] == '-') + return 0; + else if (strcmp(state, "off") == 0) + return TouchpadOff; + else if (strcmp(state, "tapping") == 0) + return TappingOff; + else if (strcmp(state, "click-only") == 0) + return ClickOnly; + else + return InvalidState; +} + int main(int argc, char *argv[]) { double idle_time = 2.0; - int poll_delay = 200000; /* 200 ms */ + int poll_delay = 200; /* ms */ int c; + const char *pstick_name; int use_xrecord = 0; + idle_state[KeyboardIdle] = TouchpadOff; + idle_state[PStickIdle] = ClickOnly; + /* Parse command line parameters */ - while ((c = getopt(argc, argv, ":i:m:dp:kKR?v")) != EOF) { + while ((c = getopt(argc, argv, ":i:m:dp:MkKR?v")) != EOF) { switch (c) { case 'i': idle_time = atof(optarg); break; case 'm': - poll_delay = atoi(optarg) * 1000; + poll_delay = atoi(optarg); break; case 'd': background = 1; @@ -573,28 +908,45 @@ main(int argc, char *argv[]) ignore_modifier_keys = 1; break; case 'R': - use_xrecord = 1; + use_xrecord = True; + break; + case 'M': + monitor_keyboard = True; break; case 'v': verbose = 1; break; case '?': - if (optopt != 't') - usage(); - else { + if (optopt == 'P') { + monitor_pstick = True; if (optind < argc) { if (argv[optind][0] == '-') - disable_state = TappingOff; - else if (strcmp(argv[optind], "off") == 0) - disable_state = TouchpadOff; - else if (strcmp(argv[optind], "tapping") == 0) - disable_state = TappingOff; - else if (strcmp(argv[optind], "click-only") == 0) - disable_state = ClickOnly; + pstick_name = DEFAULT_POINTING_STICK; else + pstick_name = argv[optind]; + } + else + pstick_name = DEFAULT_POINTING_STICK; + } + else if (optopt == 'T') { + if (optind < argc) { + idle_state[PStickIdle] = parse_idle_mode(argv[optind]); + if (idle_state[PStickIdle] == InvalidState) usage(); } else - disable_state = TappingOff; + usage(); + } + else if (optopt == 't') { + if (optind < argc) { + idle_state[KeyboardIdle] = parse_idle_mode(argv[optind]); + if (idle_state[KeyboardIdle] == InvalidState) + usage(); + } else + usage(); + } + else { + usage(); + break; } break; default: @@ -605,6 +957,11 @@ main(int argc, char *argv[]) if (idle_time <= 0.0) usage(); + /* If the keyboard was not selected and no alternate devices to monitor were + * selected, default to the keyboard */ + if (!monitor_pstick) + monitor_keyboard = True; + /* Open a connection to the X server */ display = XOpenDisplay(NULL); if (!display) { @@ -637,30 +994,53 @@ main(int argc, char *argv[]) if (!fd) { perror("Can't create pid file"); - exit(3); - } - fprintf(fd, "%d\n", getpid()); - fclose(fd); + } } } + if (idle_time <= 0.0) + usage(); - pad_disabled = False; store_current_touchpad_state(); + is_idle[PStickIdle] = True; + is_idle[KeyboardIdle] = True; + + if (monitor_pstick) { + int pstick_id; /* trackpoint device */ + if (setup_xsync(display) != 0) { + fprintf(stderr, "Monitoring of pointing stick requested, but " + "XSync could not be initialized.\n"); + exit(5); + } + + pstick_id = get_device_id(display, pstick_name, XI_MOUSE); + if (pstick_id == 0) { + fprintf(stderr, "Couldn't find pointing stick %s\n", pstick_name); + exit(6); + } + + pstick_leave_idle_alarm = + setup_device_alarm(display, pstick_id, XSyncNegativeTransition, + idle_time * 1000); + pstick_enter_idle_alarm = + setup_device_alarm(display, pstick_id, XSyncPositiveTransition, + idle_time * 1000); + } + #ifdef HAVE_X11_EXTENSIONS_RECORD_H if (use_xrecord) { - if (check_xrecord(display)) - record_main_loop(display, idle_time); - else { + if (!check_xrecord(display)) { fprintf(stderr, "Use of XRecord requested, but failed to " " initialize.\n"); exit(4); } + record_main_loop(display, idle_time); } else #endif /* HAVE_X11_EXTENSIONS_RECORD_H */ { - setup_keyboard_mask(display, ignore_modifier_keys); + if (monitor_keyboard) + setup_keyboard_mask(display, ignore_modifier_keys); /* Run the main loop */ main_loop(display, idle_time, poll_delay); -- 1.8.4.2