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