Edit

IABSD.fr/xenocara/driver/xf86-input-synaptics/tools/syndaemon.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2025-10-27 16:19:31
    Hash : 946f386a
    Message : Update to xf86-input-synaptics 1.10.0

  • driver/xf86-input-synaptics/tools/syndaemon.c
  • /*
     * Copyright © 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
     * appear 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,
     * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     *
     * Authors:
     *      Peter Osterlund (petero2@telia.com)
     */
    
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include <X11/Xlib.h>
    #include <X11/Xatom.h>
    #include <X11/extensions/XInput.h>
    #ifdef HAVE_X11_EXTENSIONS_RECORD_H
    #include <X11/Xproto.h>
    #include <X11/extensions/record.h>
    #endif                          /* HAVE_X11_EXTENSIONS_RECORD_H */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/time.h>
    #include <sys/stat.h>
    
    #include "synaptics-properties.h"
    
    enum KeyboardActivity {
        ActivityNew,
        ActivityNone,
        ActivityReset
    };
    
    enum TouchpadState {
        TouchpadOn = 0,
        TouchpadOff = 1,
        TappingOff = 2
    };
    
    static Bool pad_disabled
        /* internal flag, this does not correspond to device state */ ;
    static int ignore_modifier_combos;
    static int ignore_modifier_keys;
    static int background;
    static const char *pid_file;
    static Display *display;
    static XDevice *dev;
    static Atom touchpad_off_prop;
    static enum TouchpadState previous_state;
    static enum TouchpadState disable_state = TouchpadOff;
    static int verbose;
    
    #define KEYMAP_SIZE 32
    static unsigned char keyboard_mask[KEYMAP_SIZE];
    
    static void
    usage(void)
    {
        fprintf(stderr,
                "Usage: syndaemon [-i idle-time] [-m poll-delay] [-d] [-t] [-k]\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");
        fprintf(stderr, "  -m How many milli-seconds to wait until next poll.\n");
        fprintf(stderr, "     (default is 200ms)\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,
                "  -t Only disable tapping and scrolling, not mouse movements.\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");
        fprintf(stderr, "  -v Print diagnostic messages.\n");
        fprintf(stderr, "  -? Show this help message.\n");
        exit(1);
    }
    
    static void
    store_current_touchpad_state(void)
    {
        Atom real_type;
        int real_format;
        unsigned long nitems, bytes_after;
        unsigned char *data;
    
        if ((XGetDeviceProperty(display, dev, touchpad_off_prop, 0, 1, False,
                                XA_INTEGER, &real_type, &real_format, &nitems,
                                &bytes_after, &data) == Success) &&
            (real_type != None)) {
            previous_state = data[0];
        }
    }
    
    /**
     * Toggle touchpad enabled/disabled state, decided by value.
     */
    static void
    toggle_touchpad(Bool enable)
    {
        unsigned char data;
    
        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
    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_RESETHAND
        act.sa_flags = SA_RESETHAND;
    #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);
            }
        }
    }
    
    static enum KeyboardActivity
    keyboard_activity(Display * display)
    {
        static unsigned char old_key_state[KEYMAP_SIZE];
        unsigned char key_state[KEYMAP_SIZE];
        int i;
        int ret = ActivityNone;
    
        XQueryKeymap(display, (char *) key_state);
    
        for (i = 0; i < KEYMAP_SIZE; i++) {
            if ((key_state[i] & ~old_key_state[i]) & keyboard_mask[i]) {
                ret = ActivityNew;
                break;
            }
        }
        if (ignore_modifier_combos) {
            for (i = 0; i < KEYMAP_SIZE; i++) {
                if (key_state[i] & ~keyboard_mask[i]) {
                    if (old_key_state[i] & ~keyboard_mask[i])
                        ret = ActivityNone;
                    else
                        ret = ActivityReset;
                    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();
            switch (keyboard_activity(display)) {
                case ActivityNew:
                    last_activity = current_time;
                    break;
                case ActivityNone:
                    /* NOP */;
                    break;
                case ActivityReset:
                    last_activity = 0.0;
                    break;
            }
    
            /* 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);
        }
    }
    
    /* ---- the following code is for using the xrecord extension ----- */
    #ifdef HAVE_X11_EXTENSIONS_RECORD_H
    
    #define MAX_MODIFIERS 16
    
    /* used for exchanging information with the callback function */
    struct xrecord_callback_results {
        XModifierKeymap *modifiers;
        Bool key_event;
        Bool non_modifier_event;
        KeyCode pressed_modifiers[MAX_MODIFIERS];
    };
    
    /* test if the xrecord extension is found */
    static Bool
    check_xrecord(Display * display)
    {
    
        Bool found;
        Status status;
        int major_opcode, minor_opcode, first_error;
        int version[2];
    
        found = XQueryExtension(display,
                                "RECORD",
                                &major_opcode, &minor_opcode, &first_error);
    
        status = XRecordQueryVersion(display, version, version + 1);
        if (verbose && status) {
            printf("X RECORD extension version %d.%d\n", version[0], version[1]);
        }
        return found;
    }
    
    /* called by XRecordProcessReplies() */
    static void
    xrecord_callback(XPointer closure, XRecordInterceptData * recorded_data)
    {
    
        struct xrecord_callback_results *cbres;
        xEvent *xev;
        int nxev;
    
        cbres = (struct xrecord_callback_results *) closure;
    
        if (recorded_data->category != XRecordFromServer) {
            XRecordFreeData(recorded_data);
            return;
        }
    
        nxev = recorded_data->data_len / 8;
        xev = (xEvent *) recorded_data->data;
        while (nxev--) {
    
            if ((xev->u.u.type == KeyPress) || (xev->u.u.type == KeyRelease)) {
                int i;
                int is_modifier = 0;
    
                cbres->key_event = 1;       /* remember, a key was pressed or released. */
    
                /* test if it was a modifier */
                for (i = 0; i < 8 * cbres->modifiers->max_keypermod; i++) {
                    KeyCode kc = cbres->modifiers->modifiermap[i];
    
                    if (kc == xev->u.u.detail) {
                        is_modifier = 1;    /* yes, it is a modifier. */
                        break;
                    }
                }
    
                if (is_modifier) {
                    if (xev->u.u.type == KeyPress) {
                        for (i = 0; i < MAX_MODIFIERS; ++i)
                            if (!cbres->pressed_modifiers[i]) {
                                cbres->pressed_modifiers[i] = xev->u.u.detail;
                                break;
                            }
                    }
                    else {          /* KeyRelease */
                        for (i = 0; i < MAX_MODIFIERS; ++i)
                            if (cbres->pressed_modifiers[i] == xev->u.u.detail)
                                cbres->pressed_modifiers[i] = 0;
                    }
    
                }
                else {
                    /* remember, a non-modifier was pressed. */
                    cbres->non_modifier_event = 1;
                }
            }
    
            xev++;
        }
    
        XRecordFreeData(recorded_data);     /* cleanup */
    }
    
    static int
    is_modifier_pressed(const struct xrecord_callback_results *cbres)
    {
        int i;
    
        for (i = 0; i < MAX_MODIFIERS; ++i)
            if (cbres->pressed_modifiers[i])
                return 1;
    
        return 0;
    }
    
    static void
    record_main_loop(Display * display, double idle_time)
    {
    
        struct xrecord_callback_results cbres;
        XRecordContext context;
        XRecordClientSpec cspec = XRecordAllClients;
        Display *dpy_data;
        XRecordRange *range;
        int i;
    
        dpy_data = XOpenDisplay(NULL);      /* we need an additional data connection. */
        range = XRecordAllocRange();
    
        range->device_events.first = KeyPress;
        range->device_events.last = KeyRelease;
    
        context = XRecordCreateContext(dpy_data, 0, &cspec, 1, &range, 1);
    
        XRecordEnableContextAsync(dpy_data, context, xrecord_callback,
                                  (XPointer) & cbres);
    
        cbres.modifiers = XGetModifierMapping(display);
        /* clear list of modifiers */
        for (i = 0; i < MAX_MODIFIERS; ++i)
            cbres.pressed_modifiers[i] = 0;
    
        while (1) {
    
            int fd = ConnectionNumber(dpy_data);
            fd_set read_fds;
            int ret;
            int disable_event = 0;
            int modifier_event = 0;
            struct timeval timeout;
    
            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 */ );
    
            if (FD_ISSET(fd, &read_fds)) {
    
                cbres.key_event = 0;
                cbres.non_modifier_event = 0;
    
                XRecordProcessReplies(dpy_data);
    
                /* If there are any events left over, they are in error. Drain them
                 * from the connection queue so we don't get stuck. */
                while (XEventsQueued(dpy_data, QueuedAlready) > 0) {
                    XEvent event;
    
                    XNextEvent(dpy_data, &event);
                    fprintf(stderr, "bad event received, major opcode %d\n",
                            event.type);
                }
    
                if (!ignore_modifier_keys && cbres.key_event) {
                    disable_event = 1;
                }
    
                if (cbres.non_modifier_event) {
                    if (ignore_modifier_combos && is_modifier_pressed(&cbres)) {
                        modifier_event = 1;
                    } else {
                        disable_event = 1;
                    }
                } else if (ignore_modifier_keys) {
                    modifier_event = 1;
                }
            }
    
            if (disable_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);
            }
    
            if (modifier_event && pad_disabled) {
                toggle_touchpad(True);
            }
    
            if (ret == 0 && pad_disabled) { /* timeout => enable event */
                toggle_touchpad(True);
            }
        }                           /* end while(1) */
    
        XFreeModifiermap(cbres.modifiers);
    }
    #endif                          /* HAVE_X11_EXTENSIONS_RECORD_H */
    
    static XDevice *
    dp_get_device(Display * dpy)
    {
        XDevice *dev = NULL;
        XDeviceInfo *info = NULL;
        int ndevices = 0;
        Atom touchpad_type = 0;
        Atom *properties = NULL;
        int nprops = 0;
        int error = 0;
    
        touchpad_type = XInternAtom(dpy, XI_TOUCHPAD, True);
        touchpad_off_prop = XInternAtom(dpy, SYNAPTICS_PROP_OFF, True);
        info = XListInputDevices(dpy, &ndevices);
    
        while (ndevices--) {
            if (info[ndevices].type == touchpad_type) {
                dev = XOpenDevice(dpy, info[ndevices].id);
                if (!dev) {
                    fprintf(stderr, "Failed to open device '%s'.\n",
                            info[ndevices].name);
                    error = 1;
                    goto unwind;
                }
    
                properties = XListDeviceProperties(dpy, dev, &nprops);
                if (!properties || !nprops) {
                    fprintf(stderr, "No properties on device '%s'.\n",
                            info[ndevices].name);
                    error = 1;
                    goto unwind;
                }
    
                while (nprops--) {
                    if (properties[nprops] == touchpad_off_prop)
                        break;
                }
                if (nprops < 0) {
                    fprintf(stderr, "No synaptics properties on device '%s'.\n",
                            info[ndevices].name);
                    error = 1;
                    goto unwind;
                }
    
                break;              /* Yay, device is suitable */
            }
        }
    
     unwind:
        XFree(properties);
        XFreeDeviceList(info);
        if (!dev)
            fprintf(stderr, "Unable to find a synaptics device.\n");
        else if (error && dev) {
            XCloseDevice(dpy, dev);
            dev = NULL;
        }
        return dev;
    }
    
    int
    main(int argc, char *argv[])
    {
        double idle_time = 2.0;
        int poll_delay = 200000;    /* 200 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 't':
                disable_state = TappingOff;
                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;
            case '?':
            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);
        }
    
        if (!(dev = dp_get_device(display)))
            exit(2);
    
        /* Install a signal handler to restore synaptics 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(3);
                }
                fprintf(fd, "%d\n", getpid());
                fclose(fd);
            }
        }
    
        pad_disabled = False;
        store_current_touchpad_state();
    
    #ifdef HAVE_X11_EXTENSIONS_RECORD_H
        if (use_xrecord) {
            if (check_xrecord(display))
                record_main_loop(display, idle_time);
            else {
                fprintf(stderr, "Use of XRecord requested, but failed to "
                        " initialize.\n");
                exit(4);
            }
        }
        else
    #endif                          /* HAVE_X11_EXTENSIONS_RECORD_H */
        {
            setup_keyboard_mask(display, ignore_modifier_keys);
    
            /* Run the main loop */
            main_loop(display, idle_time, poll_delay);
        }
        return 0;
    }
    
    /* vim: set noexpandtab tabstop=8 shiftwidth=4: */