...
int fd;
fd = open("/dev/input/uinput", O_WRONLY | O_NONBLOCK);
if(fd < 0)
{
...
exit(EXIT_FAILURE);
}
Now the device is opened, we will configure our input device. First,
we need to inform the input subsystem which types of input event we want
to use. Types of events are defined in /usr/include/linux/input.h:
/usr/include/linux/input.h
...
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
...
- EV_KEY type represents key press and release events,
- EV_REL type represents relative axis events (such as mouse movements),
- EV_ABS type represents absolute axis events (such as touchscreen movements),
- …
The ioctl request UI_SET_EVBIT applied on the uinput file descriptor is used to enable a type of event. The two following lines enable key press/release and synchronization events.
ret = ioctl(fd, UI_SET_EVBIT, EV_KEY);
...
ret = ioctl(fd, UI_SET_EVBIT, EV_SYN);
When enabling EV_KEY events, we need to describe which keycodes are allowed to be sent via the input subsystem.
As linux/input.h defines the 'd' key as KEY_D, we can enable the keycode representing the 'd' key by using:
ret = ioctl(fd, UI_SET_KEYBIT, KEY_D);
...
Now some basic features have been enabled, we need to finish the configuration by using the struct uinput_user_dev from linux/uinput.h. This structure is defined as:
/usr/include/linux/uinput.h
#define UINPUT_MAX_NAME_SIZE 80
struct uinput_user_dev
{
char name[UINPUT_MAX_NAME_SIZE];
struct input_id id;
int ff_effects_max;
int absmax[ABS_MAX + 1];
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
};
The most important fields are:
- name is the given name to the input device we will create,
- id is a linux internal structure that describes the device bustype, vendor id, product id and version,
- absmin and absmax are integer array that defines mininal and maximal values for an absolute axis (i.e absmin[ABS_X] = 0, absmax[ABS_X] = 1024 for the X axis on a touchscreen device).
Now, we can fill this structure with appropriate values:
struct uinput_user_dev uidev;
memset(&uidev, 0, sizeof(uidev));
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-sample");
uidev.id.bustype = BUS_USB;
uidev.id.vendor = 0x1234;
uidev.id.product = 0xfedc;
uidev.id.version = 1;
Then, we write this structure in the uinput file descriptor.
ret = write(fd, &uidev, sizeof(uidev));
Last step is to request the creation of the device via the UI_DEV_CREATE ioctl request on the file descriptor:
ret = ioctl(fd, UI_DEV_CREATE);
Now, the file descriptor fd represents the end-point file descriptor of the new input device.
2. Injecting events in the input subsystem
The following block code injects a key press event in the input subsystem.
The input_event structure contains 3 important fields:
- type: is an event type (EV_KEY, EV_ABS, EV_REL, ...),
- code: could be either a key code when using EV_KEY, or an axis for EV_ABS and EV_REL,
- value: may be 1 (press) or 0 (release) for EV_KEY, or any values for others (positive integer for EV_ABS, signed integer for EV_REL, etc…).
To inject a press event on the 'd' key:
struct input_event ev;
memset(&ev, 0, sizeof(ev));
ev.type = EV_KEY;
ev.code = KEY_D;
ev.value = 1;
ret = write(fd, &ev, sizeof(ev));
3. Destroying an input device
ret = ioctl(fd, UI_DEV_DESTROY);
4. Handling absolute axis events
If we want to inject absolute events, we first need to activate EV_ABS event and the desired axes support with ioctl requests. The following ioctl requests enable X and Y absolute axes:
ret = ioctl(fd, UI_SET_EVBIT, EV_ABS);
...
ret = ioctl(fd, UI_SET_ABSBIT, ABS_X);
...
ret = ioctl(fd, UI_SET_ABSBIT, ABS_Y);
Then we need to defined a range of values for each axis with absmin and absmax fields from the uinput_user_dev structure:
uidev.absmin[ABS_X] = 0;
uidev.absmax[ABS_X] = 1023;
Event injection follows the same method as for any other events.
struct input_event ev[2];
memset(ev, 0, sizeof(ev));
ev[0].type = EV_ABS;
ev[0].code = ABS_X;
ev[0].value = 1023;
ev[1].type = EV_ABS;
ev[1].code = ABS_Y;
ev[1].value = 767;
ret = write(fd, ev, sizeof(ev));
5. Sample code
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <linux/input.h>
- #include <linux/uinput.h>
- #define die(str, args...) do { \
- perror(str); \
- exit(EXIT_FAILURE); \
- } while(0)
- int
- main(void)
- {
- int fd;
- struct uinput_user_dev uidev;
- struct input_event ev;
- int dx, dy;
- int i;
- fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
- if(fd < 0)
- die("error: open");
- if(ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0)
- die("error: ioctl");
- if(ioctl(fd, UI_SET_KEYBIT, BTN_LEFT) < 0)
- die("error: ioctl");
- if(ioctl(fd, UI_SET_EVBIT, EV_REL) < 0)
- die("error: ioctl");
- if(ioctl(fd, UI_SET_RELBIT, REL_X) < 0)
- die("error: ioctl");
- if(ioctl(fd, UI_SET_RELBIT, REL_Y) < 0)
- die("error: ioctl");
- memset(&uidev, 0, sizeof(uidev));
- snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-sample");
- uidev.id.bustype = BUS_USB;
- uidev.id.vendor = 0x1;
- uidev.id.product = 0x1;
- uidev.id.version = 1;
- if(write(fd, &uidev, sizeof(uidev)) < 0)
- die("error: write");
- if(ioctl(fd, UI_DEV_CREATE) < 0)
- die("error: ioctl");
- sleep(2);
- srand(time(NULL));
- while(1) {
- switch(rand() % 4) {
- case 0:
- dx = -10;
- dy = -1;
- break;
- case 1:
- dx = 10;
- dy = 1;
- break;
- case 2:
- dx = -1;
- dy = 10;
- break;
- case 3:
- dx = 1;
- dy = -10;
- break;
- }
- for(i = 0; i < 20; i++) {
- memset(&ev, 0, sizeof(struct input_event));
- ev.type = EV_REL;
- ev.code = REL_X;
- ev.value = dx;
- if(write(fd, &ev, sizeof(struct input_event)) < 0)
- die("error: write");
- memset(&ev, 0, sizeof(struct input_event));
- ev.type = EV_REL;
- ev.code = REL_Y;
- ev.value = dy;
- if(write(fd, &ev, sizeof(struct input_event)) < 0)
- die("error: write");
- memset(&ev, 0, sizeof(struct input_event));
- ev.type = EV_SYN;
- ev.code = 0;
- ev.value = 0;
- if(write(fd, &ev, sizeof(struct input_event)) < 0)
- die("error: write");
- usleep(15000);
- }
- sleep(5);
- }
- sleep(2);
- if(ioctl(fd, UI_DEV_DESTROY) < 0)
- die("error: ioctl");
- close(fd);
- return 0;
- }