Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
main: main.c
gcc -Wall main.c -lpthread -o DelayDaemon
TARGET = DelayDaemon

CFLAGS = -Wall -pedantic -O3 -std=gnu11 $(shell pkg-config --cflags libevdev)
LDFLAGS = $(shell pkg-config --libs libevdev)
LIBS = -pthread -lm

OBJECTS = \
log.o \
args.o \
main.o

$(TARGET) : $(OBJECTS)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)

%.o : %.c
$(CC) $(CFLAGS) -o $@ -c $<

clean :
rm -f $(TARGET) *.o
44 changes: 29 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,55 @@ It grabs all input events from the specified input device and blocks them from b
A new virtual input device is created and grabbed events are passed to this device after a certain delay.

It is possible to add a fixed delay to all events (by using the same value for **min** and **max**) or a range of possible delay times which leads to a distribution.
For now, only an even distribution is implemented.
Delays within range can distributed linearly or normally.

The delays for click events and movement events can be set separately.
Note that a varying delay for movement events leads to stuttering mouse movement.

The delay times can also be changed during runtime using a FIFO.

## Usage:
This program must be run as superuser, unless your user has permissions to access uinput.

## Usage:
```
DelayDaemon [OPTION...]
--input <FILE> --min_key_delay <NUM> --max_key_delay <NUM>
```
```
DelayDaemon [event_handle] [min_delay_click] [max_delay_click] [min_delay_move] [max_delay_move] [fifo_path]
-0, --min_key_delay=NUM Minimum delay for keys/clicks
-1, --max_key_delay=NUM Maximum delay for keys/clicks
-2, --min_move_delay=NUM Minimum delay for mouse movement
-3, --max_move_delay=NUM Maximum delay for mouse movement
-d, --distribution[=STRING] [linear] (default) or [normal] distributed
random values
-f, --fifo[=FILE] path to the fifo file
-i, --input=FILE /dev/input/eventX
-m, --mean[=NUM] target mean value for normal distribution
-s, --std[=NUM] target standard distribution for normal
distribution
-v, --verbose turn on debug prints
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version
```
Example:
```
sudo ./DelayDaemon -0 0 -1 100 -2 0 -3 200 -i /dev/input/event6
```

* **event_handle**: path to input device, e.g. `/dev/input/event5`
* **min_delay_click**: minimum delay to be added to mouse clicks (in milliseconds)
* **max_delay_click**: maximum delay to be added to mouse clicks (in milliseconds)
* **min_delay_move**: minimum delay to be added to mouse movement (in milliseconds)
* **max_delay_move**: maximum delay to be added to mouse movement (in milliseconds)
* **fifo_path**: path to a FIFO used to remotely set delay times during runtime (optional)
This will set each click delay to a random value between 0 and 100 and each mouse movement to a random value between 0 and 200 for the input device corresponding to event6.

## Remotely Controlling Delay Times

If **fifo_path** is set, a FIFO is created at this path.
If `--fifo` is set, a FIFO is created at this path.
By writing into this FIFO (which can be done with normal file operations), delay times can be changed during runtime.
The new values have to be written to the FIFO seperated by whitespaces and all four values have to be set.

**Example:**

```
./DelayDaemon /dev/input/event5 100 200 0 0 /tmp/delaydaemon
sudo ./DelayDaemon -i /dev/input/event6 -0 100 -1 200 -2 0 -3 0 -f /tmp/delaydaemon
echo "200 300 0 0" > /tmp/delaydaemon
```

This would start the program with a click delay of 100-200 ms and then increase the delay to 200-300 ms.

## Future Work:

* add the possibility to use different distributions (like gaussian)
101 changes: 101 additions & 0 deletions args.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "args.h"

static char doc[] =
"DelayDaemon 1.1 -- A GNU/Linux tool to add (varying) latency to input devices\n"
"Run as superuser\n";

const char *argp_program_version =
"DelayDaemon 1.1";

static char args_doc[] =
"--input <FILE> --min_key_delay <NUM> --max_key_delay <NUM>";

static struct argp_option options[] =
{
{"input", 'i', "FILE", 0, "/dev/input/eventX"},
{"min_key_delay", '0', "NUM", 0, "Minimum delay for keys/clicks"},
{"max_key_delay", '1', "NUM", 0, "Maximum delay for keys/clicks"},
{"min_move_delay", '2', "NUM", 0, "Minimum delay for mouse movement"},
{"max_move_delay", '3', "NUM", 0, "Maximum delay for mouse movement"},
{"distribution", 'd', "STRING", OPTION_ARG_OPTIONAL, "[linear] (default) or [normal] distributed random values"},
{"mean", 'm', "NUM", OPTION_ARG_OPTIONAL, "target mean value for normal distribution"},
{"std", 's', "NUM", OPTION_ARG_OPTIONAL, "target standard distribution for normal distribution"},
{"fifo", 'f', "FILE", OPTION_ARG_OPTIONAL, "path to the fifo file"},
{"verbose", 'v', NULL, OPTION_ARG_OPTIONAL, "turn on debug prints"},
{0}
};

static error_t parse_opt(int key, char *arg, struct argp_state *state)
{
struct arguments *args = state->input;

switch (key) {
case 'i':
args->device_file = arg;
break;
case '0':
args->min_key_delay = strtol(arg, NULL, 10);
break;
case '1':
args->max_key_delay = strtol(arg, NULL, 10);
break;
case '2':
args->min_move_delay = strtol(arg, NULL, 10);
break;
case '3':
args->max_move_delay = strtol(arg, NULL, 10);
break;
case 'd':
args->distribution = arg + 1; // skip the '=' character
break;
case 'm':
args->mean = strtol(arg, NULL, 10);
break;
case 's':
args->std = strtol(arg, NULL, 10);
break;
case 'f':
args->fifo_path = arg +1;
break;
case 'v':
args->verbose = 1;
break;
case ARGP_KEY_END:

/* Check if file is specified. */
if (args->device_file == NULL)
{
argp_state_help(state, stdout, ARGP_HELP_STD_HELP);
}

if(args->min_key_delay > 0 && args->max_key_delay == 0)
{
args->max_key_delay = args->min_key_delay;
}
// set default values if none specified
if(strcmp(args->distribution, "normal") == 0)
{
if(args->mean == 0) args->mean = (args->max_key_delay + args->min_key_delay) / 2;
if(args->std == 0) args->std = args->mean / 10;

if(args->mean > args->max_key_delay
|| args->mean < args->min_key_delay
||(args->mean > args->max_move_delay && args->max_move_delay > 0) // since move delay is optional and can be 0
|| args->mean < args->min_move_delay)
{
printf("Illegal value for mu. Average must be between min and max delay!\n");
return 1;
}
}
break;
}

return 0;
}

error_t parse_args(int argc, char **argv, struct arguments *args)
{
struct argp argp = {options, parse_opt, args_doc, doc};

return argp_parse(&argp, argc, argv, 0, 0, args);
}
24 changes: 24 additions & 0 deletions args.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef _ARGS_H_
#define _ARGS_H_

#include <stdlib.h>
#include <argp.h>
#include <string.h>

struct arguments
{
char* device_file;
int min_key_delay;
int max_key_delay;
int min_move_delay;
int max_move_delay;
char* distribution;
float mean;
float std;
char* fifo_path;
int verbose;
};

error_t parse_args(int argc, char **argv, struct arguments *arg);

#endif
56 changes: 56 additions & 0 deletions log.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "log.h"

const char* log_file = "event_log.csv";

// https://stackoverflow.com/a/3536261
void init_vector(event_vector *ev, size_t size)
{
ev->events = malloc(size * sizeof(delayed_event));
ev->used = 0;
ev->size = size;
}

void append_to_vector(event_vector *ev, delayed_event event)
{
// upgrade allocated memory if necessary
if(ev->used >= ev->size)
{
ev->size *= 2;
ev->events = realloc(ev->events, ev->size * sizeof(delayed_event));
}
ev->events[ev->used++] = event;
}

void free_vector(event_vector *ev)
{
free(ev->events);
ev->events = NULL;
ev->used = ev->size = 0;
}

void write_event_log(event_vector *ev)
{
// write header if file doesn't exist
if(access(log_file, F_OK) != 0)
{
FILE *file = fopen(log_file, "w+");
const char* header = "timestamp;delay;type;value;code\n";
fwrite(header, 1, strlen(header), file);
fclose(file);
}

FILE *file = fopen(log_file, "a");
for(int i=0; i<ev->used; ++i)
{
delayed_event evnt = ev->events[i];
fprintf(file,
"%lu;%i;%i;%i;%i\n",
evnt.timestamp,
evnt.delay,
evnt.type,
evnt.value,
evnt.code);
}
fclose(file);
free_vector(ev);
}
30 changes: 30 additions & 0 deletions log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef _LOG_H_
#define _LOG_H_

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

typedef struct
{
int type; // event type (e.g. key press, relative movement, ...)
int code; // event code (e.g. for key pressses the key/button code)
int value; // event value (e.g. 0/1 for button up/down, coordinates for absolute movement, ...)
int delay; // delay time for the event in milliseconds
unsigned long timestamp; // time the event occured
} delayed_event;

typedef struct
{
size_t size;
size_t used;
delayed_event* events;
} event_vector;

void init_vector(event_vector *ev, size_t size);
void append_to_vector(event_vector *ev, delayed_event event);
void free_vector(event_vector *ev);
void write_event_log(event_vector *ev);

#endif
Loading