Using Linux uinput From a Test Script

Using Linux uinput From a Test Script

With UI testing, one may need the Squish API for so-called native functions. The Squish native functions, as well as the mouse* and keyboard* functions, use the by the windowing system provided methods.

Toolkit specific APIs, like mouseClick, may also use native functions but might as well post events within the toolkit event queue. The point is that with the mouse* functions one can move the mouse and control click timings. Hovering the mouse may be necessary before clicking because the object identification may change upon mouse hover.

However, when targeting an embedded Linux device, not using X11 but e.g. Linux framebuffer or Wayland, there aren’t such functions. (For various Wayland compositors there will be a solution coming to Squish soon).

One way to get around this limitation is to create your own input device based on uinput 1. To interact with such an input device, the process implementing the fake device needs to read from somewhere. The easiest solution is probably to let such a program read from its standard input.
I’m going to use a named pipe, where one can write a line of text to and have the device program read the line from the named pipe. One caveat to overcome is that a single write using echo "x y z" > my-pipe will cause a close of the device process standard input. To keep the input open, at least one writer must keep the pipe open. A suggestion is to redirect the standard error of the shell that runs the device program, like this

mkfifo /tmp/input
exec 3>/tmp/input &
sudo ./uinput < /tmp/input

Which means the shell that runs the mouse device program must be kept open. (One could use screen if a permanent ssh connection is a problem).
Note that accessing /dev/uinput likely requires root privileges.

From the test script one can then use the Squish RemoteSystem API, e.g.

var sys = new RemoteSystem();
sys.execute(["/bin/sh", "-c", "echo 'm 50 10' > /tmp/input"])
sys.execute(["/bin/sh", "-c", "echo 'c' > /tmp/input"])

to move the mouse 50 pixels to the right, 10 down and click.

1 Code listing

/* gcc -o uinput uinput.c */
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/uinput.h>

static void emit(int fd, int type, int code, int val) {
    struct input_event ie;

    ie.type = type;
    ie.code = code;
    ie.value = val;
    ie.time.tv_sec = 0;
    ie.time.tv_usec = 0;

    write(fd, &ie, sizeof(ie));

void deleteDevice(int fd) {
    if (fd > 0) {
        ioctl(fd, UI_DEV_DESTROY);

int setupMouse() {
    struct uinput_setup usetup;
    int i = 50;

    int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
    if (fd < 0) {
        fprintf(stderr, "failed to open device %s\n", strerror(errno));
    /* enable mouse button left and relative events */
    ioctl(fd, UI_SET_EVBIT, EV_KEY);
    ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);

    ioctl(fd, UI_SET_EVBIT, EV_REL);
    ioctl(fd, UI_SET_RELBIT, REL_X);
    ioctl(fd, UI_SET_RELBIT, REL_Y);

    memset(&usetup, 0, sizeof(usetup)); = BUS_USB; = 0x1234; /* sample vendor */ = 0x5678; /* sample product */
    strcpy(, "Example device");

    ioctl(fd, UI_DEV_SETUP, &usetup);
    ioctl(fd, UI_DEV_CREATE);
    return fd;

void pointerClick(int fd) {
    emit(fd, EV_KEY, BTN_MOUSE, 1);
    emit(fd, EV_SYN, SYN_REPORT, 0);
    emit(fd, EV_KEY, BTN_MOUSE, 0);
    emit(fd, EV_SYN, SYN_REPORT, 0);

void pointerMove(int fd, int x, int y) {
    emit(fd, EV_REL, REL_X, x);
    emit(fd, EV_REL, REL_Y, y);
    emit(fd, EV_SYN, SYN_REPORT, 0);

void sighandler(int i) {

int main(void) {
    int done = 0;
    int fd = setupMouse();

    signal(SIGINT, sighandler);
    signal(SIGTERM, sighandler);
    signal(SIGPIPE, SIG_IGN);

    while (!done) {
        char ev;
        int key, x, y;
        int count = scanf("%c", &ev);
        if (count <= 0 || ev <= 0) {
            printf("count %d ev %d\n", count, ev);
        switch (ev) {
        case 'c':
        case 'm':
            count = scanf(" %d %d", &x, &y);
            if (count != 2) {
                done = 1;
            } else {
                pointerMove(fd, x, y);

    return 0;
Koos Vriezen was born in Arnhem, Netherlands and studied in Nijmegen. During his studies of math and physics he became more and more interested in software development, and fell in love with Linux and C++ in the early 1990s. After working as a math and physics teacher for a few years, Koos switched to teaching official Microsoft courses for Visual Basic and introductory courses in C and C++. Later on he joined Polderland, a Dutch company that specializes in language and speech software, where he took part in various C++ projects, including development for the Dutch version of Microsoft's Office's grammar checking tool. Later still, Koos worked for an industrial automation company, Van Gelder Engineering. Around this time he joined the KDE team, initially doing KHTML bugfixes, and then moving on to specialize in KHTML KPart usage, as well as improving the Java applet support and adding the KMPlayer video plugin. Koos joined froglogic in spring 2006.


Leave a reply

Your email address will not be published. Required fields are marked *