[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