/* * touchstop.c - Supplemental touchpad stopper * License: See below * Revision: 130530 * Authors: Peter Osterlund - Original version * Robert Kiraly - PS/2 mouse version * Copyright (c) 2003-2004 Peter Osterlund * * 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 ap- * pear 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, NEGLI- * GENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: * Peter Osterlund (petero2@telia.com) * Robert Kiraly (oldcoder@yahoo.com) - "touchstop.c" version */ //-------------------------------------------------------------------- // Documentation. // 1. This is a useful single-file program named "touchstop", derived // from Peter Osterlund's "syndaemon". It reduces problems with jumpy // cursors on some laptops; in particular, laptops which have touch- // pads that "syndaemon" can't see. // 2. "touchstop" should be compiled as follows: // // cc -o touchstop touchstop.c -lX11 // strip touchstop // chown 0.0 touchstop // chmod u+s touchstop // 3. It should be executed in a user's ".xinitrc" file as follows: // // N=`ps ax | grep touchstop | grep -v grep | wc -l` // if [ "x$N" = "x0" ]; then /usr/local/sbin/touchstop 2> /dev/null & fi //-------------------------------------------------------------------- #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include static Bool pad_disabled = False; static int ignore_modifier_combos; static int ignore_modifier_keys; static int background; static const char *pid_file; static Display *display; static int verbose; #define KEYMAP_SIZE 32 static unsigned char keyboard_mask [KEYMAP_SIZE]; static void usage (void) { fprintf (stderr, "Usage: touchstop [-i idle-time] [-m poll-delay] [-dkKv]\n"); fprintf (stderr, " -i How many seconds to wait after the last key press before\n"); fprintf (stderr, " enabling the touchpad (default is 1.0s)\n"); fprintf (stderr, " -m How many milli-seconds to wait until next poll.\n"); fprintf (stderr, " (default is 200)\n"); fprintf (stderr, " -d Start as a daemon, i.e. in the background\n"); fprintf (stderr, " -p Create a pid file with the specified name\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, " -v Print diagnostic messages\n"); exit (1); } /** * Toggle touchpad enabled/disabled state, decided by value. */ static void toggle_touchpad (Bool enable) { if (pad_disabled && enable) { system ("modprobe psaux 2> /dev/null"); system ("modprobe psmouse 2> /dev/null"); pad_disabled = False; } else if (!pad_disabled && !enable) { system ("rmmod psaux 2> /dev/null"); system ("rmmod psmouse 2> /dev/null"); pad_disabled = True; } } static void signal_handler(int signum) { toggle_touchpad(True); if (pid_file) unlink(pid_file); kill(getpid(), signum); } static void install_signal_handler(void) { static int signals[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, #ifdef SIGPWR SIGPWR #endif }; int i; struct sigaction act; sigset_t set; sigemptyset(&set); act.sa_handler = signal_handler; act.sa_mask = set; #ifdef SA_ONESHOT act.sa_flags = SA_ONESHOT; #else act.sa_flags = 0; #endif for (i = 0; i < sizeof(signals) / sizeof(int); i++) { if (sigaction(signals[i], &act, NULL) == -1) { perror("sigaction"); exit(2); } } } /** * Return non-zero if the keyboard state has changed since the last call. */ static int keyboard_activity(Display *display) { static unsigned char old_key_state[KEYMAP_SIZE]; unsigned char key_state[KEYMAP_SIZE]; int i; int ret = 0; XQueryKeymap(display, (char*)key_state); for (i = 0; i < KEYMAP_SIZE; i++) { if ((key_state[i] & ~old_key_state[i]) & keyboard_mask[i]) { ret = 1; break; } } if (ignore_modifier_combos) { for (i = 0; i < KEYMAP_SIZE; i++) { if (key_state[i] & ~keyboard_mask[i]) { ret = 0; break; } } } for (i = 0; i < KEYMAP_SIZE; i++) old_key_state[i] = key_state[i]; return ret; } static double get_time(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + tv.tv_usec / 1000000.0; } static void main_loop(Display *display, double idle_time, int poll_delay) { double last_activity = 0.0; double current_time; keyboard_activity (display); for (;;) { current_time = get_time(); if (keyboard_activity(display)) last_activity = current_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 (current_time > last_activity + idle_time) { /* Enable touchpad */ toggle_touchpad(True); } else { /* Disable touchpad */ toggle_touchpad(False); } usleep(poll_delay); } } static void clear_bit (unsigned char *ptr, int bit) { int byte_num = bit / 8; int bit_num = bit % 8; ptr[byte_num] &= ~(1 << bit_num); } static void setup_keyboard_mask(Display *display, int ignore_modifier_keys) { XModifierKeymap *modifiers; int i; for (i = 0; i < KEYMAP_SIZE; i++) keyboard_mask[i] = 0xff; if (ignore_modifier_keys) { modifiers = XGetModifierMapping(display); for (i = 0; i < 8 * modifiers->max_keypermod; i++) { KeyCode kc = modifiers->modifiermap[i]; if (kc != 0) clear_bit(keyboard_mask, kc); } XFreeModifiermap(modifiers); } } int main(int argc, char *argv[]) { double idle_time = 2.0; int poll_delay = 100000; /* 1000 ms */ int c; int use_xrecord = 0; /* Parse command line parameters */ while ((c = getopt(argc, argv, "i:m:dtp:kKR?v")) != EOF) { switch(c) { case 'i': idle_time = atof(optarg); break; case 'm': poll_delay = atoi(optarg) * 1000; break; case 'd': background = 1; break; case 'p': pid_file = optarg; break; case 'k': ignore_modifier_keys = 1; break; case 'K': ignore_modifier_combos = 1; ignore_modifier_keys = 1; break; case 'R': use_xrecord = 1; break; case 'v': verbose = 1; break; default: usage(); break; } } if (idle_time <= 0.0) usage(); /* Open a connection to the X server */ display = XOpenDisplay(NULL); if (!display) { fprintf(stderr, "Can't open display.\n"); exit(2); } /* Install a signal handler to restore parameters on exit */ install_signal_handler(); if (background) { pid_t pid; if ((pid = fork()) < 0) { perror("fork"); exit(3); } else if (pid != 0) exit(0); /* Child (daemon) is running here */ setsid(); /* Become session leader */ chdir("/"); /* In case the file system gets unmounted */ umask(0); /* We don't want any surprises */ if (pid_file) { FILE *fd = fopen(pid_file, "w"); if (!fd) { perror("Can't create pid file"); exit(2); } fprintf(fd, "%d\n", getpid()); fclose(fd); } } pad_disabled = False; { setup_keyboard_mask(display, ignore_modifier_keys); /* Run the main loop */ main_loop(display, idle_time, poll_delay); } return 0; }