[PATCH evemu 3/3] tools: play: allows evemu-play to create the device before replaying events
Peter Hutterer
peter.hutterer at who-t.net
Thu Jan 9 15:29:43 PST 2014
On Thu, Jan 09, 2014 at 02:34:09PM -0500, Benjamin Tissoires wrote:
> This is useful when you want to have a direct look at the recording.
> If the <device> argument is an input node, the backward compatibility is preserved.
> It the <device> argument is an evemu recording, then evemu-play creates the virtual
> device and feeds the device with the events in the recording.
>
> By default, evemu-play waits for user input before sending events, but for
> scripting purposes, we can pass an argument to tell how many seconds do we
> wait before sending the events.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires at gmail.com>
> ---
> tools/Makefile.am | 4 +--
> tools/evemu-play.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++----
> tools/evemu-play.txt | 44 +++++++++++++++++++++++
> 3 files changed, 137 insertions(+), 9 deletions(-)
> create mode 100644 tools/evemu-play.txt
>
> diff --git a/tools/Makefile.am b/tools/Makefile.am
> index bb1eae6..f61aca1 100644
> --- a/tools/Makefile.am
> +++ b/tools/Makefile.am
> @@ -21,9 +21,9 @@ evemu_event_LDADD = $(LIBEVDEV_LIBS)
> # man page generation
> if HAVE_DOCTOOLS
> # actual man pages
> -man_pages_sources = evemu-describe.txt evemu-device.txt
> +man_pages_sources = evemu-describe.txt evemu-device.txt evemu-play.txt
> # shadow man pages
> -man_pages_shadows = evemu-record.1 evemu-play.1 evemu-event.1
> +man_pages_shadows = evemu-record.1 evemu-event.1
can you take the evemu-play mentions out of evemu-device.txt then, and link
to it from all the other evemu-foo man pages (in the SEE ALSO).
I think evemu-event should also be moved to the evemu-play man page.
>
> man_pages = $(man_pages_sources:.txt=.1) $(man_pages_shadows)
>
> diff --git a/tools/evemu-play.c b/tools/evemu-play.c
> index 030e142..f2db40a 100644
> --- a/tools/evemu-play.c
> +++ b/tools/evemu-play.c
> @@ -45,10 +45,54 @@
> #include <fcntl.h>
> #include <string.h>
> #include <unistd.h>
> +#include <stdlib.h>
> #include <getopt.h>
>
> +#define UINPUT_NODE "/dev/uinput"
> +#define INPUT_NODE "/dev/input/event"
> +
> +static int evemu_device(FILE *fp, struct evemu_device **out_dev)
> +{
> + struct evemu_device *dev;
> + int ret = -ENOMEM;
> + int fd;
> +
> + dev = evemu_new(NULL);
> + if (!dev)
> + goto out;
> + ret = evemu_read(dev, fp);
> + if (ret <= 0)
> + goto out;
> +
> + if (strlen(evemu_get_name(dev)) == 0) {
> + char name[64];
> + sprintf(name, "evemu-%d", getpid());
> + evemu_set_name(dev, name);
> + }
> +
> + ret = fd = open(UINPUT_NODE, O_WRONLY);
> + if (ret < 0)
> + goto out;
> +
> + ret = evemu_create(dev, fd);
> + if (ret < 0)
> + goto out_close;
> +
> + *out_dev = dev;
> +
> + return fd;
> +
> +out_close:
> + close(fd);
> +out:
> + evemu_delete(dev);
> +
> + return ret;
> +}
> +
move this into a shared.c file and compile it in? I hate code duplication,
it always comes back to bite you.
(I refrain from other comments now because your evemu-device cleanup patch
just appeared in my inbox)
> static struct option opts[] = {
> { "help", no_argument, 0, 'h'},
> + { "sleep", required_argument, 0, 's'},
> };
>
> static void usage(void)
> @@ -57,23 +101,37 @@ static void usage(void)
> fprintf(stderr, "\n");
> fprintf(stderr, "Where OPTION is:\n");
> fprintf(stderr, " -h or --help: print this message\n");
> + fprintf(stderr, " -s X or --sleep X: sleep X seconds once the device is created before\n");
> + fprintf(stderr, " feeding the device node with events.\n");
long option only please. mixing short and long options usually only results
in having weird short options that don't correspond intuitively, or
accidental mismatch (-s may stand for --silent one day)
> fprintf(stderr, "\n");
> - fprintf(stderr, "Event data is read from standard input.\n");
> + fprintf(stderr, " * If <device> is a device node (in the form of \"/dev/input/eventX\"),\n");
> + fprintf(stderr, " then event data is read from standard input.\n");
> + fprintf(stderr, " * If <device> is an evemu-record capture, then the corresponding input\n");
> + fprintf(stderr, " node is created according to the description in the capture, and\n");
> + fprintf(stderr, " events are read from this very same capture.\n");
I think the documentation should say
Usage %s [<device>|<recording>]
and then the above adjusted accordingly.
> }
>
> int main(int argc, char *argv[])
> {
> + FILE *events_file = stdin;
> + const char *path = NULL;
> + struct evemu_device *dev = NULL;
> + int sleep_time = -1;
> + char line[40];
> int fd;
>
> while(1) {
> int option_index = 0;
> int c;
>
> - c = getopt_long(argc, argv, "h", opts, &option_index);
> + c = getopt_long(argc, argv, "hs:", opts, &option_index);
> if (c == -1) /* Detect the end of the options. */
> break;
>
> switch(c) {
> + case 's': /* sleep */
> + sleep_time = atoi(optarg);
strtol() please, with error checking for negative numbers.
> + break;
> case 'h': /* help */
> default:
> usage();
> @@ -87,15 +145,41 @@ int main(int argc, char *argv[])
> goto out;
> }
>
> - fd = open(argv[optind], O_WRONLY);
> - if (fd < 0) {
> - fprintf(stderr, "error: could not open device\n");
> - return -1;
> + path = argv[optind];
> +
> + if (!strncmp(path, INPUT_NODE, sizeof(INPUT_NODE) - 1)) {
this would prevent us from open symlinks to files. Why not just stat for a
character device?
> + fd = open(path, O_WRONLY);
> + if (fd < 0) {
> + fprintf(stderr, "error: could not open device\n");
> + return -1;
> + }
> + } else {
> + events_file = fopen(path, "r");
> + if (!events_file) {
> + fprintf(stderr, "error: could not open event file\n");
> + return -1;
> + }
> + fd = evemu_device(events_file, &dev);
> + if (fd <= 0)
> + goto out;
> + fseek(events_file, 0, 0);
> +
> + /* now either wait for the user or the timer to start playing the events */
> +
> + if (sleep_time >= 0) {
> + sleep(sleep_time);
> + } else {
> + fprintf(stdout, "Hit enter to start replaying events.");
> + fflush(stdout);
> + fgets(line, sizeof(line), stdin);
> + }
> }
> - if (evemu_play(stdin, fd)) {
> +
> + if (evemu_play(events_file, fd)) {
> fprintf(stderr, "error: could not describe device\n");
> }
you could drop the {} here
also, did you test this with an old evemu recording? what happens if it's a
pure events file?
> close(fd);
> + fclose(events_file);
> return 0;
> out:
> return -1;
> diff --git a/tools/evemu-play.txt b/tools/evemu-play.txt
> new file mode 100644
> index 0000000..207565b
> --- /dev/null
> +++ b/tools/evemu-play.txt
> @@ -0,0 +1,44 @@
> +EVEMU-PLAY(1)
> +=============
> +
> +NAME
> +----
> +
> + evemu-play - create a virtual input device and replay an event sequence
> +
> +SYNOPSIS
> +--------
> + evemu-play /dev/input/eventX < event-sequence
> +
> + or
> +
> + evemu-play [--sleep N] event-sequence
> +
> +DESCRIPTION
> +-----------
> +
> +If an input node in the form of /dev/input/eventX is given, then
> +evemu-play replays the event sequence given on stdin through the input
> +device. The event sequence must be in the form created by evemu-record(1).
> +
> +If the event-sequence is directly given to evemu-play, evemu-play creates
> +a virtual input device based on the description-file. This description is
> +usually created by evemu-record(1). evemu-play then creates a new input
> +device with uinput and then injects the events found in the recording.
> +
> +If *--sleep* (or *-s*) is given, instead of waiting for user input after
> +creating the device node, evemu-play waits for *N* seconds and then injects
> +the events.
> +
> +evemu-play must be able to write to the uinput device node, or it must be
> +able to write to the device node specified; in most cases this means it must
> +be run as root.
> +
> +SEE ALSO
> +--------
> +evemu-describe(1)
> +evemu-record(1)
> +
> +AUTHOR
> +------
> +evemu was written by Henrik Rydberg <rydberg at euromail.se>
not to diminish Henrik's work, but at some point we should probably add to
that :)
Cheers,
Peter
> --
> 1.8.3.1
>
More information about the Input-tools
mailing list