[PATCH libinput] tools: replace the tap time measuring tool with a python one

Peter Hutterer peter.hutterer at who-t.net
Thu Nov 16 23:19:40 UTC 2017


On Thu, Nov 16, 2017 at 03:03:41PM +0800, Dima Ryazanov wrote:
> Oh nice, much simpler. I have a few nitpick comments, but it looks good,
> and works for me.
> 
> Reviewed-By: Dima Ryazanov <dima at gmail.com>
> Tested-By: Dima Ryazanov <dima at gmail.com>

thx, much appreciated!

> On Thu, Nov 16, 2017 at 1:11 PM, Peter Hutterer <peter.hutterer at who-t.net>
> wrote:
> 
> > A lot easier to process data in python than in C.
> >
> > Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> > ---
> >  meson.build                           |  10 +-
> >  tools/libinput-measure-touchpad-tap   | 261 +++++++++++++++++
> >  tools/libinput-measure-touchpad-tap.c | 509
> > ----------------------------------
> >  3 files changed, 263 insertions(+), 517 deletions(-)
> >  create mode 100755 tools/libinput-measure-touchpad-tap
> >  delete mode 100644 tools/libinput-measure-touchpad-tap.c
> >
> > diff --git a/meson.build b/meson.build
> > index eefe3d61..4d433989 100644
> > --- a/meson.build
> > +++ b/meson.build
> > @@ -414,14 +414,8 @@ configure_file(input : 'tools/libinput-measure.man',
> >                install_dir : join_paths(get_option('mandir'), 'man1')
> >                )
> >
> > -libinput_measure_touchpad_tap_sources = [ 'tools/libinput-measure-touchpad-tap.c'
> > ]
> > -executable('libinput-measure-touchpad-tap',
> > -          libinput_measure_touchpad_tap_sources,
> > -          dependencies : deps_tools,
> > -          include_directories : [includes_src, includes_include],
> > -          install_dir : libinput_tool_path,
> > -          install : true,
> > -          )
> > +install_data('tools/libinput-measure-touchpad-tap',
> > +            install_dir : libinput_tool_path)
> >  configure_file(input : 'tools/libinput-measure-touchpad-tap.man',
> >                output : 'libinput-measure-touchpad-tap.1',
> >                configuration : man_config,
> > diff --git a/tools/libinput-measure-touchpad-tap
> > b/tools/libinput-measure-touchpad-tap
> > new file mode 100755
> > index 00000000..165cd65a
> > --- /dev/null
> > +++ b/tools/libinput-measure-touchpad-tap
> > @@ -0,0 +1,261 @@
> > +#!/usr/bin/python3
> > +# vim: set expandtab shiftwidth=4:
> > +# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
> > +#
> > +# Copyright © 2017 Red Hat, Inc.
> > +#
> > +# Permission is hereby granted, free of charge, to any person obtaining a
> > +# copy of this software and associated documentation files (the
> > "Software"),
> > +# to deal in the Software without restriction, including without
> > limitation
> > +# the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > +# and/or sell copies of the Software, and to permit persons to whom the
> > +# Software is furnished to do so, subject to the following conditions:
> > +#
> > +# The above copyright notice and this permission notice (including the
> > next
> > +# paragraph) shall be included in all copies or substantial portions of
> > the
> > +# Software.
> > +#
> > +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> > OR
> > +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> > OTHER
> > +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > +# DEALINGS IN THE SOFTWARE.
> > +#
> > +
> > +import sys
> > +import argparse
> > +try:
> > +    import evdev
> > +    import evdev.ecodes
> > +    import textwrap
> > +    import pyudev
> > +except ModuleNotFoundError as e:
> > +    print('Error: {}'.format(str(e)), file=sys.stderr)
> >
> 
> "str" is redundant here.

fixed, thanks.

> > +    print('One or more python modules are missing. Please install those '
> > +          'modules and re-run this tool.')
> > +    sys.exit(1)
> > +
> > +print_dest = sys.stdout
> > +
> > +
> > +def error(msg, **kwargs):
> > +    print(msg, **kwargs, file=sys.stderr)
> > +
> > +
> > +def msg(msg, **kwargs):
> > +    print(msg, **kwargs, file=print_dest, flush=True)
> > +
> > +
> > +def tv2us(sec, usec):
> > +    return sec * 1000000 + usec
> > +
> > +
> > +def us2ms(us):
> > +    return int(us/1000)
> > +
> > +
> > +class Touch(object):
> > +    def __init__(self, down):
> > +        self._down = down
> > +        self._up = down
> > +
> > +    @property
> > +    def up(self):
> > +        return us2ms(self._up)
> > +
> > +    @up.setter
> > +    def up(self, up):
> > +        assert(up > self.down)
> >
> 
> Should this be "up >= self.down"? Or is it not possible to have the same
> timestamp?

Nah, with the usec granularity the kernel gives us it's impossible to have
up with the same time stamp as down, it would indicate a kernel or fw issue.

> > +        self._up = up
> > +
> > +    @property
> > +    def down(self):
> > +        return us2ms(self._down)
> > +
> > +    @property
> > +    def tdelta(self):
> > +        return self.up - self.down
> > +
> > +
> > +class InvalidDeviceError(Exception):
> > +    pass
> > +
> > +
> > +class Device(object):
> > +    def __init__(self, path):
> > +        if path is None:
> > +            self.path = self._find_touch_device()
> > +        else:
> > +            self.path = path
> > +
> > +        self.device = evdev.InputDevice(self.path)
> > +        # capabilities returns a dict with the EV_* codes as key,
> > +        # each of which is a list of tuples of (code, AbsInfo)
> > +        #
> > +        # Get the abs list first (or empty list if missing),
> > +        # then extract the pressure absinfo from that
> > +        codes = self.device.capabilities(absinfo=True).get(
> > +                evdev.ecodes.EV_KEY, []
> > +               )
> > +
> > +        if evdev.ecodes.BTN_TOUCH not in codes:
> > +            raise InvalidDeviceError("device does not have BTN_TOUCH")
> > +
> > +        self.touches = []
> > +
> > +    def _find_touch_device(self):
> > +        context = pyudev.Context()
> > +        device_node = None
> > +        for device in context.list_devices(subsystem='input'):
> > +            if not device.device_node or \
> >
> 
> Parenthesis are preferred over \ (according to PEP 8)

oh, good to know, I've always hated the \ but never thought of looking up a
pep for it :) fixed.

> > +                    not device.device_node.startswith(
> > '/dev/input/event'):
> > +                continue
> > +
> > +            # pick the touchpad by default, fallback to the first
> > +            # touchscreen only when there is no touchpad
> > +            if device.get('ID_INPUT_TOUCHPAD', 0):
> > +                device_node = device.device_node
> > +                break
> > +
> > +            if device.get('ID_INPUT_TOUCHSCREEN', 0) and device_node is
> > None:
> > +                device_node = device.device_node
> > +
> > +        if device_node is not None:
> > +            return device_node
> > +
> > +        print("Unable to find a touch device.", file=sys.stderr)
> >
> 
> Use the "error" helper? Or better yet, raise an exception instead of
> sys.exit.

fixed to use error(), but I don't like the exception here. This is a
normal-enough user-visible error case so we should not show the exceptions'
backtrace etc. Raising an exception here just means a try/error somewhere
above so we don't really save anything.

of course, if this was more than a simple script it'd be worth it.

> > +        sys.exit(1)
> > +
> > +    def handle_btn_touch(self, event):
> > +        if event.value != 0:
> > +            t = Touch(tv2us(event.sec, event.usec))
> > +            self.touches.append(t)
> > +        else:
> > +            self.touches[-1].up = tv2us(event.sec, event.usec)
> > +            msg("\rTouch sequences detected:
> > {}".format(len(self.touches)),
> > +                end='')
> > +
> > +    def handle_key(self, event):
> > +        tapcodes = [evdev.ecodes.BTN_TOOL_DOUBLETAP,
> > +                    evdev.ecodes.BTN_TOOL_TRIPLETAP,
> > +                    evdev.ecodes.BTN_TOOL_QUADTAP,
> > +                    evdev.ecodes.BTN_TOOL_QUINTTAP]
> > +        if event.code in tapcodes and event.value > 0:
> > +            error("\rThis tool cannot handle multiple fingers, "
> > +                  "output will be invalid")
> > +            return
> > +
> > +        if event.code == evdev.ecodes.BTN_TOUCH:
> > +            self.handle_btn_touch(event)
> > +
> > +    def handle_syn(self, event):
> > +        if self.touch.dirty:
> > +            self.current_sequence().append(self.touch)
> > +            self.touch = Touch(major=self.touch.major,
> > +                               minor=self.touch.minor,
> > +                               orientation=self.touch.orientation)
> > +
> > +    def handle_event(self, event):
> > +        if event.type == evdev.ecodes.EV_KEY:
> > +            self.handle_key(event)
> > +
> > +    def read_events(self):
> > +        for event in self.device.read_loop():
> > +            self.handle_event(event)
> > +
> > +    def print_summary(self):
> > +        deltas = sorted([t.tdelta for t in self.touches])
> >
> 
> Nit: don't need [ and ] here.

good to know. This error is likely to be in the other python scripts too, if
you can give those a review for the same nitpicks that'd be appreciated.
Plus your chance at everlasting glory by getting a patch into the repo ;)

> > +
> > +        dmax = max(deltas)
> > +        dmin = min(deltas)
> > +
> > +        l = len(deltas)
> > +
> > +        davg = sum(deltas)/l
> > +        dmedian = deltas[int(l/2)]
> > +        d95pc = deltas[int(l * 0.95)]
> > +        d90pc = deltas[int(l * 0.90)]
> > +
> > +        print("Time: ")
> > +        print("  Max delta: {}ms".format(int(dmax)))
> > +        print("  Min delta: {}ms".format(int(dmin)))
> > +        print("  Average delta: {}ms".format(int(davg)))
> > +        print("  Median delta: {}ms".format(int(dmedian)))
> > +        print("  90th percentile: {}ms".format(int(d90pc)))
> > +        print("  95th percentile: {}ms".format(int(d95pc)))
> > +
> > +    def print_dat(self):
> > +        print("# libinput-measure-touchpad-tap")
> > +        print(textwrap.dedent('''\
> > +              # File contents:
> > +              #    This file contains multiple prints of the data in
> > +              #    different sort order. Row number is index of touch
> > +              #    point within each group. Comparing data across groups
> > +              #    will result in invalid analysis.
> > +              # Columns (1-indexed):
> > +              # Group 1, sorted by time of occurence
> > +              #  1: touch down time in ms, offset by first event
> > +              #  2: touch up time in ms, offset by first event
> > +              #  3: time delta in ms);
> > +              # Group 2, sorted by touch down-up delta time (ascending)
> > +              #  4: touch down time in ms, offset by first event
> > +              #  5: touch up time in ms, offset by first event
> > +              #  6: time delta in ms
> > +              '''))
> > +
> > +        deltas = [t for t in self.touches]
> > +        deltas_sorted = sorted(deltas, key=lambda t: t.tdelta)
> > +
> > +        offset = deltas[0].down
> > +
> > +        for t1, t2 in zip(deltas, deltas_sorted):
> > +            print(t1.down - offset, t1.up - offset, t1.tdelta,
> > +                  t2.down - offset, t2.up - offset, t2.tdelta)
> > +
> > +    def print(self, format):
> > +        if not self.touches:
> > +            error("No tap data available")
> > +            return
> > +
> > +        if format == 'summary':
> > +            self.print_summary()
> > +        elif format == 'dat':
> > +            self.print_dat()
> > +
> > +
> > +def main(args):
> > +    parser = argparse.ArgumentParser(
> > +            description="Measure tap-to-click properties of devices"
> > +    )
> > +    parser.add_argument('path', metavar='/dev/input/event0',
> >
> 
> "/dev/input/event0" should probably be the default value, not the metavar?

nope, that's intentional to show the format the path is supposed to have.
event0 is never the touchpad, so having it as default value would be
counterproductive (and circumvent the touchpad detection logic)
> 
> 
> > +                        nargs='?', type=str, help='Path to device
> > (optional)')
> > +    parser.add_argument('--format', metavar='format',
> > +                        choices=['summary', 'dat'],
> > +                        default='summary',
> > +                        help='data format to print ("summary" or "dat")')
> > +    args = parser.parse_args()
> > +
> > +    if not sys.stdout.isatty():
> > +        global print_dest
> > +        print_dest = sys.stderr
> > +
> > +    try:
> > +        device = Device(args.path)
> > +        error("Ready for recording data.\n"
> > +              "Tap the touchpad multiple times with a single finger
> > only.\n"
> > +              "For useful data we recommend at least 20 taps.\n"
> > +              "Ctrl+C to exit")
> > +        device.read_events()
> > +    except KeyboardInterrupt:
> > +        msg('')
> > +        device.print(args.format)
> > +    except (PermissionError, OSError):
> > +        print("Error: failed to open device")
> >
> 
> Might as well print out the exception, too.

fixed, thanks.

Cheers,
   Peter

 
> > +    except InvalidDeviceError as e:
> > +        print("Error: {}".format(e))
> > +
> > +
> > +if __name__ == "__main__":
> > +    main(sys.argv)
> > diff --git a/tools/libinput-measure-touchpad-tap.c
> > b/tools/libinput-measure-touchpad-tap.c
> > deleted file mode 100644
> > index 735202c1..00000000
> > --- a/tools/libinput-measure-touchpad-tap.c
> > +++ /dev/null
> > @@ -1,509 +0,0 @@
> > -/*
> > - * Copyright © 2017 Red Hat, Inc.
> > - *
> > - * Permission is hereby granted, free of charge, to any person obtaining a
> > - * copy of this software and associated documentation files (the
> > "Software"),
> > - * to deal in the Software without restriction, including without
> > limitation
> > - * the rights to use, copy, modify, merge, publish, distribute,
> > sublicense,
> > - * and/or sell copies of the Software, and to permit persons to whom the
> > - * Software is furnished to do so, subject to the following conditions:
> > - *
> > - * The above copyright notice and this permission notice (including the
> > next
> > - * paragraph) shall be included in all copies or substantial portions of
> > the
> > - * Software.
> > - *
> > - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> > EXPRESS OR
> > - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > MERCHANTABILITY,
> > - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT
> > SHALL
> > - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> > OTHER
> > - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > - * DEALINGS IN THE SOFTWARE.
> > - */
> > -
> > -#include "config.h"
> > -
> > -#include <errno.h>
> > -#include <fcntl.h>
> > -#include <getopt.h>
> > -#include <poll.h>
> > -#include <signal.h>
> > -#include <stdbool.h>
> > -#include <stdio.h>
> > -#include <string.h>
> > -#include <unistd.h>
> > -#include <sys/signalfd.h>
> > -
> > -#include <libudev.h>
> > -#include <libevdev/libevdev.h>
> > -
> > -#include <libinput-util.h>
> > -#include <libinput-version.h>
> > -
> > -#include "shared.h"
> > -
> > -static bool print_dat_file;
> > -/* interactive goes to stdout unless we're redirected, then it goes to
> > - * stderr */
> > -static FILE *pdest;
> > -
> > -#define error(...) fprintf(stderr, __VA_ARGS__)
> > -#define msg(...) fprintf(pdest, __VA_ARGS__)
> > -
> > -struct touch {
> > -       uint32_t tdown, tup; /* in ms */
> > -};
> > -
> > -struct tap_data {
> > -       struct touch *touches;
> > -       size_t touches_sz;
> > -       unsigned int count;
> > -
> > -       uint32_t toffset; /* in ms */
> > -};
> > -
> > -static inline uint32_t
> > -touch_tdelta_ms(const struct touch *t)
> > -{
> > -       return t->tup - t->tdown;
> > -}
> > -
> > -static inline struct tap_data *
> > -tap_data_new(void)
> > -{
> > -       struct tap_data *tap_data;
> > -
> > -       tap_data = zalloc(sizeof(struct tap_data));
> > -
> > -       return tap_data;
> > -}
> > -
> > -static inline unsigned int
> > -tap_data_ntouches(struct tap_data *tap_data)
> > -{
> > -       return tap_data->count;
> > -}
> > -
> > -static inline void
> > -tap_data_free(struct tap_data **tap_data)
> > -{
> > -       free((*tap_data)->touches);
> > -       free((*tap_data));
> > -       *tap_data = NULL;
> > -}
> > -
> > -static inline struct touch *
> > -tap_data_get_current_touch(struct tap_data *tap_data)
> > -{
> > -       assert(tap_data->count > 0);
> > -
> > -       return &tap_data->touches[tap_data->count - 1];
> > -}
> > -
> > -static inline struct touch *
> > -tap_data_get_touch(struct tap_data *tap_data, unsigned int idx)
> > -{
> > -       assert(idx < tap_data->count);
> > -
> > -       return &tap_data->touches[idx];
> > -}
> > -
> > -#define tap_data_for_each(tapdata_, t_) \
> > -       for (unsigned i_ = 0; i_ < (tapdata_)->count && (t_ =
> > &(tapdata_)->touches[i_]); i_++)
> > -
> > -static inline struct tap_data *
> > -tap_data_duplicate_sorted(const struct tap_data *src,
> > -                         int (*cmp)(const void *a, const void *b))
> > -{
> > -       struct tap_data *dest = tap_data_new();
> > -
> > -       assert(src->count > 0);
> > -
> > -       dest->count = src->count;
> > -       dest->toffset = src->toffset;
> > -       dest->touches_sz = dest->count;
> > -       dest->touches = zalloc(dest->count * sizeof(*dest->touches));
> > -
> > -       memcpy(dest->touches,
> > -              src->touches,
> > -              dest->count * sizeof(*dest->touches));
> > -
> > -       if (cmp)
> > -               qsort(dest->touches,
> > -                     dest->count,
> > -                     sizeof(*dest->touches),
> > -                     cmp);
> > -
> > -       return dest;
> > -}
> > -
> > -static inline struct touch*
> > -tap_data_new_touch(struct tap_data *tap_data)
> > -{
> > -       tap_data->count++;
> > -       if (tap_data->touches_sz < tap_data->count) {
> > -               tap_data->touches_sz += 50;
> > -               tap_data->touches = realloc(tap_data->touches,
> > -                            tap_data->touches_sz *
> > sizeof(*tap_data->touches));
> > -               if (tap_data->touches == NULL) {
> > -                       error("Allocation error. Oops\n");
> > -                       abort();
> > -               }
> > -               memset(&tap_data->touches[tap_data->count - 1],
> > -                      0,
> > -                      sizeof(*tap_data->touches));
> > -       }
> > -
> > -       return &tap_data->touches[tap_data->count - 1];
> > -}
> > -
> > -static int
> > -sort_by_time_delta(const void *ap, const void *bp)
> > -{
> > -       const struct touch *a = ap;
> > -       const struct touch *b = bp;
> > -       uint32_t da, db;
> > -
> > -       da = touch_tdelta_ms(a);
> > -       db = touch_tdelta_ms(b);
> > -
> > -       return da == db ? 0 : da > db ? 1 : -1;
> > -}
> > -
> > -static inline void
> > -print_statistics(struct tap_data *tap_data)
> > -{
> > -       uint64_t delta_sum = 0;
> > -       uint32_t average;
> > -       uint32_t max = 0,
> > -                min = UINT_MAX;
> > -       struct tap_data *data_sorted_tdelta;
> > -       struct touch *t,
> > -                    *median,
> > -                    *pc90,
> > -                    *pc95;
> > -
> > -       if (tap_data->count == 0) {
> > -               error("No tap data available.\n");
> > -               return;
> > -       }
> > -
> > -       tap_data_for_each(tap_data, t) {
> > -               uint32_t delta = touch_tdelta_ms(t);
> > -
> > -               delta_sum += delta;
> > -               max = max(delta, max);
> > -               min = min(delta, min);
> > -       }
> > -
> > -       average = delta_sum/tap_data_ntouches(tap_data);
> > -
> > -       printf("Time:\n");
> > -       printf("  Max delta: %dms\n", max);
> > -       printf("  Min delta: %dms\n", min);
> > -       printf("  Average delta: %dms\n", average);
> > -
> > -       /* Median, 90th, 95th percentile, requires sorting by time delta */
> > -       data_sorted_tdelta = tap_data_duplicate_sorted(tap_data,
> > -                                                      sort_by_time_delta);
> > -       median = tap_data_get_touch(tap_data,
> > -                                   tap_data_ntouches(tap_data)/2);
> > -       pc90= tap_data_get_touch(tap_data,
> > -                                tap_data_ntouches(tap_data) * 0.9);
> > -       pc95 = tap_data_get_touch(tap_data,
> > -                                 tap_data_ntouches(tap_data) * 0.95);
> > -       printf("  Median delta: %dms\n", touch_tdelta_ms(median));
> > -       printf("  90th percentile: %dms\n", touch_tdelta_ms(pc90));
> > -       printf("  95th percentile: %dms\n", touch_tdelta_ms(pc95));
> > -
> > -       tap_data_free(&data_sorted_tdelta);
> > -}
> > -
> > -static inline void
> > -print_dat(struct tap_data *tap_data)
> > -{
> > -       unsigned int i;
> > -       struct touch *t;
> > -       struct tap_data *sorted;
> > -
> > -       printf("# libinput-measure-touchpad-tap (v%s)\n",
> > LIBINPUT_VERSION);
> > -       printf("# File contents:\n"
> > -              "#    This file contains multiple prints of the data in
> > different\n"
> > -              "#    sort order. Row number is index of touch point within
> > each group.\n"
> > -              "#    Comparing data across groups will result in invalid
> > analysis.\n"
> > -              "# Columns (1-indexed):\n");
> > -       printf("# Group 1, sorted by time of occurence\n"
> > -              "#  1: touch down time in ms, offset by first event\n"
> > -              "#  2: touch up time in ms, offset by first event\n"
> > -              "#  3: time delta in ms\n");
> > -       printf("# Group 2, sorted by touch down-up delta time
> > (ascending)\n"
> > -              "#  4: touch down time in ms, offset by first event\n"
> > -              "#  5: touch up time in ms, offset by first event\n"
> > -              "#  6: time delta in ms\n");
> > -
> > -       sorted = tap_data_duplicate_sorted(tap_data, sort_by_time_delta);
> > -
> > -       for (i = 0; i < tap_data_ntouches(tap_data); i++) {
> > -               t = tap_data_get_touch(tap_data, i);
> > -               printf("%d %d %d ",
> > -                      t->tdown,
> > -                      t->tup,
> > -                      touch_tdelta_ms(t));
> > -
> > -               t = tap_data_get_touch(sorted, i);
> > -               printf("%d %d %d ",
> > -                      t->tdown,
> > -                      t->tup,
> > -                      touch_tdelta_ms(t));
> > -
> > -               printf("\n");
> > -       }
> > -
> > -       tap_data_free(&sorted);
> > -}
> > -
> > -static inline void
> > -handle_btn_touch(struct tap_data *tap_data,
> > -                struct libevdev *evdev,
> > -                const struct input_event *ev)
> > -{
> > -
> > -       if (ev->value) {
> > -               struct touch *new_touch = tap_data_new_touch(tap_data);
> > -
> > -               new_touch->tdown = us2ms(tv2us(&ev->time)) -
> > tap_data->toffset;
> > -       } else {
> > -               struct touch *current = tap_data_get_current_touch(tap
> > _data);
> > -
> > -               msg("\rTouch sequences detected: %d", tap_data->count);
> > -
> > -               current->tup = us2ms(tv2us(&ev->time)) - tap_data->toffset;
> > -       }
> > -}
> > -
> > -static inline bool
> > -handle_key(struct tap_data *tap_data,
> > -          struct libevdev *evdev,
> > -          const struct input_event *ev)
> > -{
> > -       switch (ev->code) {
> > -       case BTN_TOOL_DOUBLETAP:
> > -       case BTN_TOOL_TRIPLETAP:
> > -       case BTN_TOOL_QUADTAP:
> > -       case BTN_TOOL_QUINTTAP:
> > -               error("This tool only supports single-finger taps.
> > Aborting.\n");
> > -               return false;
> > -       case BTN_TOUCH:
> > -               handle_btn_touch(tap_data, evdev, ev);
> > -               break;
> > -       default:
> > -               break;
> > -       }
> > -
> > -       return true;
> > -}
> > -
> > -static inline bool
> > -handle_abs(struct tap_data *tap_data,
> > -          struct libevdev *evdev,
> > -          const struct input_event *ev)
> > -{
> > -       return true;
> > -}
> > -
> > -static inline bool
> > -handle_event(struct tap_data *tap_data,
> > -            struct libevdev *evdev,
> > -            const struct input_event *ev)
> > -{
> > -       bool rc = true;
> > -
> > -       if (tap_data->toffset == 0)
> > -               tap_data->toffset = us2ms(tv2us(&ev->time));
> > -
> > -       switch (ev->type) {
> > -       default:
> > -               error("Unexpected event %s %s (%d, %d). Aborting.\n",
> > -                     libevdev_event_type_get_name(ev->type),
> > -                     libevdev_event_code_get_name(ev->type, ev->code),
> > -                     ev->type,
> > -                     ev->code);
> > -               break;
> > -       case EV_KEY:
> > -               rc = handle_key(tap_data, evdev, ev);
> > -               break;
> > -       case EV_ABS:
> > -               rc = handle_abs(tap_data, evdev, ev);
> > -               break;
> > -       case EV_SYN:
> > -               rc = true;
> > -               break;
> > -       }
> > -
> > -       return rc;
> > -}
> > -
> > -static int
> > -loop(struct tap_data *data, const char *path)
> > -{
> > -       struct libevdev *evdev;
> > -       struct pollfd fds[2];
> > -       sigset_t mask;
> > -       int fd;
> > -       int rc;
> > -
> > -       fd = open(path, O_RDONLY|O_NONBLOCK);
> > -       if (fd < 0) {
> > -               error("Failed to open device: %s\n", strerror(errno));
> > -               return EXIT_FAILURE;
> > -       }
> > -
> > -       rc = libevdev_new_from_fd(fd, &evdev);
> > -       if (rc < 0) {
> > -               error("Failed to init device: %s\n", strerror(-rc));
> > -               close(fd);
> > -               return EXIT_FAILURE;
> > -       }
> > -       libevdev_set_clock_id(evdev, CLOCK_MONOTONIC);
> > -
> > -       fds[0].fd = fd;
> > -       fds[0].events = POLLIN;
> > -
> > -       sigemptyset(&mask);
> > -       sigaddset(&mask, SIGINT);
> > -       fds[1].fd = signalfd(-1, &mask, SFD_NONBLOCK);
> > -       fds[1].events = POLLIN;
> > -
> > -       sigprocmask(SIG_BLOCK, &mask, NULL);
> > -
> > -       rc = EXIT_FAILURE;
> > -
> > -       error("Ready for recording data.\n"
> > -             "Tap the touchpad multiple times with a single finger
> > only.\n"
> > -             "For useful data we recommend at least 20 taps.\n"
> > -             "Ctrl+C to exit\n");
> > -
> > -       while (poll(fds, 2, -1)) {
> > -               struct input_event ev;
> > -               int rc;
> > -
> > -               if (fds[1].revents)
> > -                       break;
> > -
> > -               do {
> > -                       rc = libevdev_next_event(evdev,
> > LIBEVDEV_READ_FLAG_NORMAL, &ev);
> > -                       if (rc == LIBEVDEV_READ_STATUS_SYNC) {
> > -                               error("Error: cannot keep up\n");
> > -                               goto out;
> > -                       } else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) {
> > -                               if (!handle_event(data, evdev, &ev))
> > -                                       goto out;
> > -                       } else if (rc != -EAGAIN && rc < 0) {
> > -                               error("Error: %s\n", strerror(-rc));
> > -                               goto out;
> > -                       }
> > -               } while (rc != -EAGAIN);
> > -       }
> > -
> > -       rc = EXIT_SUCCESS;
> > -out:
> > -       close(fd);
> > -       if (evdev)
> > -               libevdev_free(evdev);
> > -
> > -       printf("\n");
> > -
> > -       return rc;
> > -}
> > -
> > -static inline void
> > -usage(void)
> > -{
> > -       printf("Usage: libinput measure touchpad-tap [--help]
> > [/dev/input/event0]\n");
> > -}
> > -
> > -int
> > -main(int argc, char **argv)
> > -{
> > -       struct tap_data *tap_data;
> > -       char path[PATH_MAX];
> > -       int option_index = 0;
> > -       const char *format = "summary";
> > -       int rc;
> > -
> > -       while (1) {
> > -               enum opts {
> > -                       OPT_HELP,
> > -                       OPT_FORMAT,
> > -               };
> > -               static struct option opts[] = {
> > -                       { "help",             no_argument, 0, OPT_HELP },
> > -                       { "format",     required_argument, 0, OPT_FORMAT},
> > -                       { 0, 0, 0, 0 },
> > -               };
> > -               int c;
> > -
> > -               c = getopt_long(argc, argv, "", opts, &option_index);
> > -               if (c == -1)
> > -                       break;
> > -
> > -               switch(c) {
> > -               case OPT_HELP:
> > -                       usage();
> > -                       return EXIT_SUCCESS;;
> > -               case OPT_FORMAT:
> > -                       format = optarg;
> > -                       break;
> > -               default:
> > -                       usage();
> > -                       return EXIT_FAILURE;
> > -               }
> > -       }
> > -
> > -       if (streq(format, "summary")) {
> > -               print_dat_file = false;
> > -       } else if (streq(format, "dat")) {
> > -               print_dat_file = true;
> > -       } else {
> > -               error("Unknown print format '%s'\n", format);
> > -               return EXIT_FAILURE;
> > -       }
> > -
> > -       if (optind == argc) {
> > -               if (!find_touchpad_device(path, sizeof(path))) {
> > -                       error("Failed to find a touchpad device.\n");
> > -                       return EXIT_FAILURE;
> > -               }
> > -       } else {
> > -               snprintf(path, sizeof(path), "%s", argv[optind]);
> > -               if (!is_touchpad_device(path)) {
> > -                       error("Device is not a touchpad.\n");
> > -                       return EXIT_FAILURE;
> > -               }
> > -       }
> > -
> > -       if (!isatty(STDOUT_FILENO)) {
> > -               pdest = stderr;
> > -       } else {
> > -               pdest = stdout;
> > -               setbuf(stdout, NULL);
> > -       }
> > -
> > -       tap_data = tap_data_new();
> > -       rc = loop(tap_data, path);
> > -
> > -       if (rc != EXIT_SUCCESS)
> > -               goto out;
> > -
> > -       msg("\n");
> > -
> > -       if (print_dat_file)
> > -               print_dat(tap_data);
> > -       else
> > -               print_statistics(tap_data);
> > -
> > -out:
> > -       tap_data_free(&tap_data);
> > -
> > -       return rc;
> > -}
> > --
> > 2.13.6
> >
> > _______________________________________________
> > wayland-devel mailing list
> > wayland-devel at lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/wayland-devel
> >


More information about the wayland-devel mailing list