mirror of
				https://github.com/ARM-software/workload-automation.git
				synced 2025-11-04 09:02:12 +00:00 
			
		
		
		
	revent: Added record and replay commands
Added two commands to WA to record and replay input events using revent. As part of this also added the ability to get a device model from android and linux device. This may need to be improved in the future.
This commit is contained in:
		@@ -17,6 +17,11 @@ to Android UI Automator for providing automation for workloads. ::
 | 
			
		||||
                        info:shows info about each event char device
 | 
			
		||||
                        any additional parameters make it verbose
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. note:: There are now also WA commands that perform the below steps.
 | 
			
		||||
          Please see ``wa show record/replay`` and ``wa record/replay --help``
 | 
			
		||||
          for details.
 | 
			
		||||
 | 
			
		||||
Recording
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										165
									
								
								wlauto/commands/record.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								wlauto/commands/record.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from wlauto import ExtensionLoader, Command, settings
 | 
			
		||||
from wlauto.common.resources import Executable
 | 
			
		||||
from wlauto.core.resource import NO_ONE
 | 
			
		||||
from wlauto.core.resolver import ResourceResolver
 | 
			
		||||
from wlauto.core.configuration import RunConfiguration
 | 
			
		||||
from wlauto.core.agenda import Agenda
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RecordCommand(Command):
 | 
			
		||||
 | 
			
		||||
    name = 'record'
 | 
			
		||||
    description = '''Performs a revent recording
 | 
			
		||||
 | 
			
		||||
    This command helps making revent recordings. It will automatically
 | 
			
		||||
    deploy revent and even has the option of automatically opening apps.
 | 
			
		||||
 | 
			
		||||
    Revent allows you to record raw inputs such as screen swipes or button presses.
 | 
			
		||||
    This can be useful for recording inputs for workloads such as games that don't
 | 
			
		||||
    have XML UI layouts that can be used with UIAutomator. As a drawback from this,
 | 
			
		||||
    revent recordings are specific to the device type they were recorded on.
 | 
			
		||||
 | 
			
		||||
    WA uses two parts to the names of revent recordings in the format,
 | 
			
		||||
    {device_name}.{suffix}.revent.
 | 
			
		||||
 | 
			
		||||
     - device_name can either be specified manually with the ``-d`` argument or
 | 
			
		||||
       it can be automatically determined. On Android device it will be obtained
 | 
			
		||||
       from ``build.prop``, on Linux devices it is obtained from ``/proc/device-tree/model``.
 | 
			
		||||
     - suffix is used by WA to determine which part of the app execution the
 | 
			
		||||
       recording is for, currently these are either ``setup`` or ``run``. This
 | 
			
		||||
       should be specified with the ``-s`` argument.
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context):
 | 
			
		||||
        self.context = context
 | 
			
		||||
        self.parser.add_argument('-d', '--device', help='The name of the device')
 | 
			
		||||
        self.parser.add_argument('-s', '--suffix', help='The suffix of the revent file, e.g. ``setup``')
 | 
			
		||||
        self.parser.add_argument('-o', '--output', help='Directory to save the recording in')
 | 
			
		||||
        self.parser.add_argument('-p', '--package', help='Package to launch before recording')
 | 
			
		||||
        self.parser.add_argument('-C', '--clear', help='Clear app cache before launching it',
 | 
			
		||||
                                 action="store_true")
 | 
			
		||||
 | 
			
		||||
    # Validate command options
 | 
			
		||||
    def validate_args(self, args):
 | 
			
		||||
        if args.clear and not args.package:
 | 
			
		||||
            print "Package must be specified if you want to clear cache\n"
 | 
			
		||||
            self.parser.print_help()
 | 
			
		||||
            sys.exit()
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=W0201
 | 
			
		||||
    def execute(self, args):
 | 
			
		||||
        self.validate_args(args)
 | 
			
		||||
        self.logger.info("Connecting to device...")
 | 
			
		||||
 | 
			
		||||
        ext_loader = ExtensionLoader(packages=settings.extension_packages,
 | 
			
		||||
                                     paths=settings.extension_paths)
 | 
			
		||||
 | 
			
		||||
        # Setup config
 | 
			
		||||
        self.config = RunConfiguration(ext_loader)
 | 
			
		||||
        for filepath in settings.get_config_paths():
 | 
			
		||||
            self.config.load_config(filepath)
 | 
			
		||||
        self.config.set_agenda(Agenda())
 | 
			
		||||
        self.config.finalize()
 | 
			
		||||
 | 
			
		||||
        context = LightContext(self.config)
 | 
			
		||||
 | 
			
		||||
        # Setup device
 | 
			
		||||
        self.device = ext_loader.get_device(settings.device, **settings.device_config)
 | 
			
		||||
        self.device.validate()
 | 
			
		||||
        self.device.dynamic_modules = []
 | 
			
		||||
        self.device.connect()
 | 
			
		||||
        self.device.initialize(context)
 | 
			
		||||
 | 
			
		||||
        host_binary = context.resolver.get(Executable(NO_ONE, self.device.abi, 'revent'))
 | 
			
		||||
        self.target_binary = self.device.install_if_needed(host_binary)
 | 
			
		||||
 | 
			
		||||
        self.run(args)
 | 
			
		||||
 | 
			
		||||
    def run(self, args):
 | 
			
		||||
        if args.device:
 | 
			
		||||
            self.device_name = args.device
 | 
			
		||||
        else:
 | 
			
		||||
            self.device_name = self.device.get_device_model()
 | 
			
		||||
 | 
			
		||||
        if args.suffix:
 | 
			
		||||
            args.suffix += "."
 | 
			
		||||
 | 
			
		||||
        revent_file = self.device.path.join(self.device.working_directory,
 | 
			
		||||
                                            '{}.{}revent'.format(self.device_name, args.suffix or ""))
 | 
			
		||||
 | 
			
		||||
        if args.clear:
 | 
			
		||||
            self.device.execute("pm clear {}".format(args.package))
 | 
			
		||||
 | 
			
		||||
        if args.package:
 | 
			
		||||
            self.logger.info("Starting {}".format(args.package))
 | 
			
		||||
            self.device.execute('monkey -p {} -c android.intent.category.LAUNCHER 1'.format(args.package))
 | 
			
		||||
 | 
			
		||||
        self.logger.info("Press Enter when you are ready to record...")
 | 
			
		||||
        raw_input("")
 | 
			
		||||
        command = "{} record -t 100000 -s {}".format(self.target_binary, revent_file)
 | 
			
		||||
        self.device.kick_off(command)
 | 
			
		||||
 | 
			
		||||
        self.logger.info("Press Enter when you have finished recording...")
 | 
			
		||||
        raw_input("")
 | 
			
		||||
        self.device.killall("revent")
 | 
			
		||||
 | 
			
		||||
        self.logger.info("Pulling files from device")
 | 
			
		||||
        self.device.pull_file(revent_file, args.output or os.getcwdu())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ReplayCommand(RecordCommand):
 | 
			
		||||
 | 
			
		||||
    name = 'replay'
 | 
			
		||||
    description = '''Replay a revent recording
 | 
			
		||||
 | 
			
		||||
    Revent allows you to record raw inputs such as screen swipes or button presses.
 | 
			
		||||
    See ``wa show record`` to see how to make an revent recording.
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context):
 | 
			
		||||
        self.context = context
 | 
			
		||||
        self.parser.add_argument('revent', help='The name of the file to replay')
 | 
			
		||||
        self.parser.add_argument('-p', '--package', help='Package to launch before recording')
 | 
			
		||||
        self.parser.add_argument('-C', '--clear', help='Clear app cache before launching it',
 | 
			
		||||
                                 action="store_true")
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=W0201
 | 
			
		||||
    def run(self, args):
 | 
			
		||||
        self.logger.info("Pushing file to device")
 | 
			
		||||
        self.device.push_file(args.revent, self.device.working_directory)
 | 
			
		||||
        revent_file = self.device.path.join(self.device.working_directory, os.path.split(args.revent)[1])
 | 
			
		||||
 | 
			
		||||
        if args.clear:
 | 
			
		||||
            self.device.execute("pm clear {}".format(args.package))
 | 
			
		||||
 | 
			
		||||
        if args.package:
 | 
			
		||||
            self.logger.info("Starting {}".format(args.package))
 | 
			
		||||
            self.device.execute('monkey -p {} -c android.intent.category.LAUNCHER 1'.format(args.package))
 | 
			
		||||
 | 
			
		||||
        command = "{} replay {}".format(self.target_binary, revent_file)
 | 
			
		||||
        self.device.execute(command)
 | 
			
		||||
        self.logger.info("Finished replay")
 | 
			
		||||
 | 
			
		||||
# Used to satisfy the API
 | 
			
		||||
class LightContext(object):
 | 
			
		||||
    def __init__(self, config):
 | 
			
		||||
        self.resolver = ResourceResolver(config)
 | 
			
		||||
        self.resolver.load()
 | 
			
		||||
@@ -645,6 +645,12 @@ class AndroidDevice(BaseLinuxDevice):  # pylint: disable=W0223
 | 
			
		||||
            if se_status == 'Enforcing':
 | 
			
		||||
                self.execute('setenforce 0', as_root=True)
 | 
			
		||||
 | 
			
		||||
    def get_device_model(self):
 | 
			
		||||
        try:
 | 
			
		||||
            return self.getprop(prop='ro.product.device')
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    # Internal methods: do not use outside of the class.
 | 
			
		||||
 | 
			
		||||
    def _update_build_properties(self, filepath, props):
 | 
			
		||||
 
 | 
			
		||||
@@ -302,7 +302,7 @@ class ReventWorkload(Workload):
 | 
			
		||||
        if _call_super:
 | 
			
		||||
            super(ReventWorkload, self).__init__(device, **kwargs)
 | 
			
		||||
        devpath = self.device.path
 | 
			
		||||
        self.on_device_revent_binary = devpath.join(self.device.working_directory, 'revent')
 | 
			
		||||
        self.on_device_revent_binary = devpath.join(self.device.binaries_directory, 'revent')
 | 
			
		||||
        self.on_device_setup_revent = devpath.join(self.device.working_directory, '{}.setup.revent'.format(self.device.name))
 | 
			
		||||
        self.on_device_run_revent = devpath.join(self.device.working_directory, '{}.run.revent'.format(self.device.name))
 | 
			
		||||
        self.setup_timeout = kwargs.get('setup_timeout', self.default_setup_timeout)
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -536,6 +536,14 @@ class BaseLinuxDevice(Device):  # pylint: disable=abstract-method
 | 
			
		||||
            command = 'cd {} && {}'.format(in_directory, command)
 | 
			
		||||
        return self.execute(command, background=background, as_root=as_root, timeout=timeout)
 | 
			
		||||
 | 
			
		||||
    def get_device_model(self):
 | 
			
		||||
        if self.file_exists("/proc/device-tree/model"):
 | 
			
		||||
            raw_model = self.execute("cat /proc/device-tree/model")
 | 
			
		||||
            return '_'.join(raw_model.split()[:2])
 | 
			
		||||
        # Right now we don't know any other way to get device model
 | 
			
		||||
        # info in linux on arm platforms
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    # internal methods
 | 
			
		||||
 | 
			
		||||
    def _check_ready(self):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										259
									
								
								wlauto/external/revent/revent.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										259
									
								
								wlauto/external/revent/revent.c
									
									
									
									
										vendored
									
									
								
							@@ -23,6 +23,8 @@
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <linux/input.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
#ifdef ANDROID
 | 
			
		||||
#include <android/log.h>
 | 
			
		||||
@@ -91,14 +93,14 @@ typedef struct {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool_t verbose = FALSE;
 | 
			
		||||
 | 
			
		||||
bool_t wait_for_stdin = TRUE;
 | 
			
		||||
 | 
			
		||||
bool_t is_numeric(char *string)
 | 
			
		||||
{
 | 
			
		||||
    int len = strlen(string);
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    while(i < len) 
 | 
			
		||||
    while(i < len)
 | 
			
		||||
    {
 | 
			
		||||
       if(!isdigit(string[i]))
 | 
			
		||||
           return FALSE;
 | 
			
		||||
@@ -115,7 +117,7 @@ off_t get_file_size(const char *filename) {
 | 
			
		||||
        return st.st_size;
 | 
			
		||||
 | 
			
		||||
    die("Cannot determine size of %s: %s\n", filename, strerror(errno));
 | 
			
		||||
} 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int inpdev_init(inpdev_t **inpdev, int devid)
 | 
			
		||||
{
 | 
			
		||||
@@ -242,15 +244,15 @@ int replay_buffer_init(replay_buffer_t **buffer, const char *logfile)
 | 
			
		||||
        die("out of memory\n");
 | 
			
		||||
 | 
			
		||||
    int fdin = open(logfile, O_RDONLY);
 | 
			
		||||
    if (fdin < 0) 
 | 
			
		||||
    if (fdin < 0)
 | 
			
		||||
        die("Could not open eventlog %s\n", logfile);
 | 
			
		||||
 | 
			
		||||
    size_t rb = read(fdin, &(buff->num_fds), sizeof(buff->num_fds));
 | 
			
		||||
    if (rb!=sizeof(buff->num_fds)) 
 | 
			
		||||
    if (rb!=sizeof(buff->num_fds))
 | 
			
		||||
        die("problems reading eventlog\n");
 | 
			
		||||
 | 
			
		||||
    buff->fds = malloc(sizeof(int) * buff->num_fds);
 | 
			
		||||
    if (!buff->fds) 
 | 
			
		||||
    if (!buff->fds)
 | 
			
		||||
        die("out of memory\n");
 | 
			
		||||
 | 
			
		||||
    int32_t len, i;
 | 
			
		||||
@@ -258,10 +260,10 @@ int replay_buffer_init(replay_buffer_t **buffer, const char *logfile)
 | 
			
		||||
    for (i = 0; i < buff->num_fds; i++) {
 | 
			
		||||
        memset(path_buff, 0, sizeof(path_buff));
 | 
			
		||||
        rb = read(fdin, &len, sizeof(len));
 | 
			
		||||
        if (rb!=sizeof(len)) 
 | 
			
		||||
        if (rb!=sizeof(len))
 | 
			
		||||
            die("problems reading eventlog\n");
 | 
			
		||||
        rb = read(fdin, &path_buff[0], len);
 | 
			
		||||
        if (rb != len) 
 | 
			
		||||
        if (rb != len)
 | 
			
		||||
            die("problems reading eventlog\n");
 | 
			
		||||
 | 
			
		||||
        buff->fds[i] = open(path_buff, O_WRONLY | O_NDELAY);
 | 
			
		||||
@@ -274,7 +276,7 @@ int replay_buffer_init(replay_buffer_t **buffer, const char *logfile)
 | 
			
		||||
    i = 0;
 | 
			
		||||
    while(1) {
 | 
			
		||||
        rb = read(fdin, &rep_ev, sizeof(rep_ev));
 | 
			
		||||
        if (rb < (int)sizeof(rep_ev)) 
 | 
			
		||||
        if (rb < (int)sizeof(rep_ev))
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        if (i == 0) {
 | 
			
		||||
@@ -321,7 +323,7 @@ int replay_buffer_play(replay_buffer_t *buff)
 | 
			
		||||
        struct input_event ev = (buff->events[i]).event;
 | 
			
		||||
        while((i < buff->num_events) && !timercmp(&ev.time, &last_event_delta, !=)) {
 | 
			
		||||
            rb = write(buff->fds[idx], &ev, sizeof(ev));
 | 
			
		||||
            if (rb!=sizeof(ev)) 
 | 
			
		||||
            if (rb!=sizeof(ev))
 | 
			
		||||
                die("problems writing\n");
 | 
			
		||||
            dprintf("replayed event: type %d code %d value %d\n", ev.type, ev.code, ev.value);
 | 
			
		||||
 | 
			
		||||
@@ -347,101 +349,6 @@ void replay(const char *logfile)
 | 
			
		||||
    replay_buffer_close(replay_buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void record(inpdev_t *inpdev, int delay, const char *logfile)
 | 
			
		||||
{
 | 
			
		||||
    fd_set readfds;
 | 
			
		||||
    FILE* fdout;
 | 
			
		||||
    struct input_event ev;
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    int32_t _padding = 0xdeadbeef;
 | 
			
		||||
    int32_t maxfd = 0;
 | 
			
		||||
    int32_t keydev=0;
 | 
			
		||||
 | 
			
		||||
    int* fds = malloc(sizeof(int)*inpdev->id_pathc);
 | 
			
		||||
    if (!fds) die("out of memory\n");
 | 
			
		||||
 | 
			
		||||
    fdout = fopen(logfile, "wb");
 | 
			
		||||
    if (!fdout) die("Could not open eventlog %s\n", logfile);
 | 
			
		||||
 | 
			
		||||
    fwrite(&inpdev->id_pathc, sizeof(inpdev->id_pathc), 1, fdout);
 | 
			
		||||
    for (i=0; i<inpdev->id_pathc; i++) {
 | 
			
		||||
        int32_t len = strlen(inpdev->id_pathv[i]);
 | 
			
		||||
        fwrite(&len, sizeof(len), 1, fdout);
 | 
			
		||||
        fwrite(inpdev->id_pathv[i], len, 1, fdout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
    {
 | 
			
		||||
        fds[i] = open(inpdev->id_pathv[i], O_RDONLY);
 | 
			
		||||
        if (fds[i]>maxfd) maxfd = fds[i];
 | 
			
		||||
        dprintf("opened %s with %d\n", inpdev->id_pathv[i], fds[i]);
 | 
			
		||||
        if (fds[i]<0) die("could not open \%s\n", inpdev->id_pathv[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int count =0;
 | 
			
		||||
    struct timeval tout;
 | 
			
		||||
    while(1)
 | 
			
		||||
    {
 | 
			
		||||
        FD_ZERO(&readfds);
 | 
			
		||||
        FD_SET(STDIN_FILENO, &readfds);
 | 
			
		||||
        for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
            FD_SET(fds[i], &readfds);
 | 
			
		||||
        /* wait for input */
 | 
			
		||||
        tout.tv_sec = delay;
 | 
			
		||||
        tout.tv_usec = 0;
 | 
			
		||||
        int32_t r = select(maxfd+1, &readfds, NULL, NULL, &tout);
 | 
			
		||||
        /* dprintf("got %d (err %d)\n", r, errno); */
 | 
			
		||||
        if (!r) break;
 | 
			
		||||
        if (FD_ISSET(STDIN_FILENO, &readfds)) {
 | 
			
		||||
            // in this case the key down for the return key will be recorded
 | 
			
		||||
            // so we need to up the key up
 | 
			
		||||
            memset(&ev, 0, sizeof(ev));
 | 
			
		||||
            ev.type = EV_KEY;
 | 
			
		||||
            ev.code = KEY_ENTER;
 | 
			
		||||
            ev.value = 0;
 | 
			
		||||
            gettimeofday(&ev.time, NULL);
 | 
			
		||||
            fwrite(&keydev, sizeof(keydev), 1, fdout);
 | 
			
		||||
            fwrite(&_padding, sizeof(_padding), 1, fdout);
 | 
			
		||||
            fwrite(&ev, sizeof(ev), 1, fdout);
 | 
			
		||||
            memset(&ev, 0, sizeof(ev)); // SYN
 | 
			
		||||
            gettimeofday(&ev.time, NULL);
 | 
			
		||||
            fwrite(&keydev, sizeof(keydev), 1, fdout);
 | 
			
		||||
            fwrite(&_padding, sizeof(_padding), 1, fdout);
 | 
			
		||||
            fwrite(&ev, sizeof(ev), 1, fdout);
 | 
			
		||||
            dprintf("added fake return exiting...\n");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
        {
 | 
			
		||||
            if (FD_ISSET(fds[i], &readfds))
 | 
			
		||||
            {
 | 
			
		||||
                dprintf("Got event from %s\n", inpdev->id_pathv[i]);
 | 
			
		||||
                memset(&ev, 0, sizeof(ev));
 | 
			
		||||
                size_t rb = read(fds[i], (void*) &ev, sizeof(ev));
 | 
			
		||||
                dprintf("%d event: type %d code %d value %d\n",
 | 
			
		||||
                        (unsigned int)rb, ev.type, ev.code, ev.value);
 | 
			
		||||
                if (ev.type == EV_KEY && ev.code == KEY_ENTER && ev.value == 1)
 | 
			
		||||
                    keydev = i;
 | 
			
		||||
                fwrite(&i, sizeof(i), 1, fdout);
 | 
			
		||||
                fwrite(&_padding, sizeof(_padding), 1, fdout);
 | 
			
		||||
                fwrite(&ev, sizeof(ev), 1, fdout);
 | 
			
		||||
                count++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
    {
 | 
			
		||||
        close(fds[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fdout);
 | 
			
		||||
    free(fds);
 | 
			
		||||
    dprintf("Recorded %d events\n", count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void usage()
 | 
			
		||||
{
 | 
			
		||||
    printf("usage:\n    revent [-h] [-v] COMMAND [OPTIONS] \n"
 | 
			
		||||
@@ -490,7 +397,7 @@ void revent_args_init(revent_args_t **rargs, int argc, char** argv)
 | 
			
		||||
    revent_args->file = NULL;
 | 
			
		||||
 | 
			
		||||
    int opt;
 | 
			
		||||
    while ((opt = getopt(argc, argv, "ht:d:v")) != -1)
 | 
			
		||||
    while ((opt = getopt(argc, argv, "ht:d:vs")) != -1)
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
            case 'h':
 | 
			
		||||
@@ -516,6 +423,10 @@ void revent_args_init(revent_args_t **rargs, int argc, char** argv)
 | 
			
		||||
            case 'v':
 | 
			
		||||
                verbose = TRUE;
 | 
			
		||||
                break;
 | 
			
		||||
            case 's':
 | 
			
		||||
                wait_for_stdin = FALSE;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                die("Unexpected option: %c", opt);
 | 
			
		||||
        }
 | 
			
		||||
@@ -526,13 +437,13 @@ void revent_args_init(revent_args_t **rargs, int argc, char** argv)
 | 
			
		||||
        usage();
 | 
			
		||||
        die("Must specify a command.\n");
 | 
			
		||||
    }
 | 
			
		||||
    if (!strcmp(argv[next_arg], "record")) 
 | 
			
		||||
    if (!strcmp(argv[next_arg], "record"))
 | 
			
		||||
        revent_args->mode = RECORD;
 | 
			
		||||
    else if (!strcmp(argv[next_arg], "replay")) 
 | 
			
		||||
    else if (!strcmp(argv[next_arg], "replay"))
 | 
			
		||||
        revent_args->mode = REPLAY;
 | 
			
		||||
    else if (!strcmp(argv[next_arg], "dump")) 
 | 
			
		||||
    else if (!strcmp(argv[next_arg], "dump"))
 | 
			
		||||
        revent_args->mode = DUMP;
 | 
			
		||||
    else if (!strcmp(argv[next_arg], "info")) 
 | 
			
		||||
    else if (!strcmp(argv[next_arg], "info"))
 | 
			
		||||
        revent_args->mode = INFO;
 | 
			
		||||
    else {
 | 
			
		||||
        usage();
 | 
			
		||||
@@ -569,15 +480,138 @@ int revent_args_close(revent_args_t *rargs)
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int* fds = NULL;
 | 
			
		||||
FILE* fdout = NULL;
 | 
			
		||||
revent_args_t *rargs = NULL;
 | 
			
		||||
inpdev_t *inpdev = NULL;
 | 
			
		||||
int count;
 | 
			
		||||
 | 
			
		||||
void term_handler(int signum)
 | 
			
		||||
{
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
    {
 | 
			
		||||
        close(fds[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fdout);
 | 
			
		||||
    free(fds);
 | 
			
		||||
    dprintf("Recorded %d events\n", count);
 | 
			
		||||
 | 
			
		||||
    inpdev_close(inpdev);
 | 
			
		||||
    revent_args_close(rargs);
 | 
			
		||||
    exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void record(inpdev_t *inpdev, int delay, const char *logfile)
 | 
			
		||||
{
 | 
			
		||||
    fd_set readfds;
 | 
			
		||||
    struct input_event ev;
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    int32_t _padding = 0xdeadbeef;
 | 
			
		||||
    int32_t maxfd = 0;
 | 
			
		||||
    int32_t keydev=0;
 | 
			
		||||
 | 
			
		||||
    //signal handler
 | 
			
		||||
    struct sigaction action;
 | 
			
		||||
    memset(&action, 0, sizeof(struct sigaction));
 | 
			
		||||
    action.sa_handler = term_handler;
 | 
			
		||||
    sigaction(SIGTERM, &action, NULL);
 | 
			
		||||
 | 
			
		||||
    fds = malloc(sizeof(int)*inpdev->id_pathc);
 | 
			
		||||
    if (!fds) die("out of memory\n");
 | 
			
		||||
 | 
			
		||||
    fdout = fopen(logfile, "wb");
 | 
			
		||||
    if (!fdout) die("Could not open eventlog %s\n", logfile);
 | 
			
		||||
 | 
			
		||||
    fwrite(&inpdev->id_pathc, sizeof(inpdev->id_pathc), 1, fdout);
 | 
			
		||||
    for (i=0; i<inpdev->id_pathc; i++) {
 | 
			
		||||
        int32_t len = strlen(inpdev->id_pathv[i]);
 | 
			
		||||
        fwrite(&len, sizeof(len), 1, fdout);
 | 
			
		||||
        fwrite(inpdev->id_pathv[i], len, 1, fdout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
    {
 | 
			
		||||
        fds[i] = open(inpdev->id_pathv[i], O_RDONLY);
 | 
			
		||||
        if (fds[i]>maxfd) maxfd = fds[i];
 | 
			
		||||
        dprintf("opened %s with %d\n", inpdev->id_pathv[i], fds[i]);
 | 
			
		||||
        if (fds[i]<0) die("could not open \%s\n", inpdev->id_pathv[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    count = 0;
 | 
			
		||||
    struct timeval tout;
 | 
			
		||||
    while(1)
 | 
			
		||||
    {
 | 
			
		||||
        FD_ZERO(&readfds);
 | 
			
		||||
        if (wait_for_stdin)
 | 
			
		||||
        {
 | 
			
		||||
            FD_SET(STDIN_FILENO, &readfds);
 | 
			
		||||
        }
 | 
			
		||||
        for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
            FD_SET(fds[i], &readfds);
 | 
			
		||||
        /* wait for input */
 | 
			
		||||
        tout.tv_sec = delay;
 | 
			
		||||
        tout.tv_usec = 0;
 | 
			
		||||
        int32_t r = select(maxfd+1, &readfds, NULL, NULL, &tout);
 | 
			
		||||
        /* dprintf("got %d (err %d)\n", r, errno); */
 | 
			
		||||
        if (!r) break;
 | 
			
		||||
        if (wait_for_stdin && FD_ISSET(STDIN_FILENO, &readfds)) {
 | 
			
		||||
            // in this case the key down for the return key will be recorded
 | 
			
		||||
            // so we need to up the key up
 | 
			
		||||
            memset(&ev, 0, sizeof(ev));
 | 
			
		||||
            ev.type = EV_KEY;
 | 
			
		||||
            ev.code = KEY_ENTER;
 | 
			
		||||
            ev.value = 0;
 | 
			
		||||
            gettimeofday(&ev.time, NULL);
 | 
			
		||||
            fwrite(&keydev, sizeof(keydev), 1, fdout);
 | 
			
		||||
            fwrite(&_padding, sizeof(_padding), 1, fdout);
 | 
			
		||||
            fwrite(&ev, sizeof(ev), 1, fdout);
 | 
			
		||||
            memset(&ev, 0, sizeof(ev)); // SYN
 | 
			
		||||
            gettimeofday(&ev.time, NULL);
 | 
			
		||||
            fwrite(&keydev, sizeof(keydev), 1, fdout);
 | 
			
		||||
            fwrite(&_padding, sizeof(_padding), 1, fdout);
 | 
			
		||||
            fwrite(&ev, sizeof(ev), 1, fdout);
 | 
			
		||||
            dprintf("added fake return exiting...\n");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
        {
 | 
			
		||||
            if (FD_ISSET(fds[i], &readfds))
 | 
			
		||||
            {
 | 
			
		||||
                dprintf("Got event from %s\n", inpdev->id_pathv[i]);
 | 
			
		||||
                memset(&ev, 0, sizeof(ev));
 | 
			
		||||
                size_t rb = read(fds[i], (void*) &ev, sizeof(ev));
 | 
			
		||||
                dprintf("%d event: type %d code %d value %d\n",
 | 
			
		||||
                        (unsigned int)rb, ev.type, ev.code, ev.value);
 | 
			
		||||
                if (ev.type == EV_KEY && ev.code == KEY_ENTER && ev.value == 1)
 | 
			
		||||
                    keydev = i;
 | 
			
		||||
                fwrite(&i, sizeof(i), 1, fdout);
 | 
			
		||||
                fwrite(&_padding, sizeof(_padding), 1, fdout);
 | 
			
		||||
                fwrite(&ev, sizeof(ev), 1, fdout);
 | 
			
		||||
                count++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
    {
 | 
			
		||||
        close(fds[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fdout);
 | 
			
		||||
    free(fds);
 | 
			
		||||
    dprintf("Recorded %d events\n", count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    char *logfile = NULL;
 | 
			
		||||
 | 
			
		||||
    revent_args_t *rargs;
 | 
			
		||||
    revent_args_init(&rargs, argc, argv);
 | 
			
		||||
 | 
			
		||||
    inpdev_t *inpdev;
 | 
			
		||||
    inpdev_init(&inpdev, rargs->device_number);
 | 
			
		||||
 | 
			
		||||
    switch(rargs->mode) {
 | 
			
		||||
@@ -600,4 +634,3 @@ int main(int argc, char** argv)
 | 
			
		||||
    revent_args_close(rargs);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user