The question is pretty straight forward.

What I would have used under X [xdotool] obviously does not work moving forward, and no obvious new solutions have arisen given the relative new adoption of wayland.

Solutions which require programming are acceptable.

  • Note, since late 2019 there is a packaged program ydotool that uses UInput to do mouse movement and clicks, and keyboard up/down events for Wayland (but also works for X11).
    – meuh
    Aug 13, 2023 at 13:36

3 Answers 3


You can use uinput (linux/uinput.h). It works across X as well as Wayland.

The documentation page above has an example that includes creating a virtual device that behaves as a mouse:

#include <linux/uinput.h>

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

   ie.type = type;
   ie.code = code;
   ie.value = val;
   /* timestamp values below are ignored */
   ie.time.tv_sec = 0;
   ie.time.tv_usec = 0;

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

int main(void)
   struct uinput_setup usetup;
   int i = 50;

   int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);

   /* 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));
   usetup.id.bustype = BUS_USB;
   usetup.id.vendor = 0x1234; /* sample vendor */
   usetup.id.product = 0x5678; /* sample product */
   strcpy(usetup.name, "Example device");

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

    * On UI_DEV_CREATE the kernel will create the device node for this
    * device. We are inserting a pause here so that userspace has time
    * to detect, initialize the new device, and can start listening to
    * the event, otherwise it will not notice the event we are about
    * to send. This pause is only needed in our example code!

   /* Move the mouse diagonally, 5 units per axis */
   while (i--) {
      emit(fd, EV_REL, REL_X, 5);
      emit(fd, EV_REL, REL_Y, 5);
      emit(fd, EV_SYN, SYN_REPORT, 0);

    * Give userspace some time to read the events before we destroy the
    * device with UI_DEV_DESTOY.

   ioctl(fd, UI_DEV_DESTROY);

   return 0;
  • 1
    Yeah I cant get ev_abs to work for me.
    – Anon
    Feb 9, 2018 at 10:01
  • 1
    I can't get ev_abs to work for me either! Oct 25, 2018 at 11:21

If you don't want to write C code for uinput, there are python packages and even some existing debug and test utilities that work at the same evdev level, namely evemu-describe, evemu-device, evemu-play, evemu-record, and evemu-event from the evemu package. You need to be root to use them. Here's an example that finds the mouse device and the events it generates, then artificially generates an event for it.

First we list the evdev devices:

$ sudo evemu-describe 
Available devices:
/dev/input/event5:     Logitech USB Optical Mouse

This is an interactive command, which after listing the physical devices asks us to choose one for further details on it. We choose 5, the mouse:

Select the device event number [0-9]: 5
# Input device name: "Logitech USB Optical Mouse"
# Supported events:
#   Event type 0 (EV_SYN)
#     Event code 0 (SYN_REPORT)
#   Event type 1 (EV_KEY)
#     Event code 272 (BTN_LEFT)
#     Event code 273 (BTN_RIGHT)
#     Event code 274 (BTN_MIDDLE)
#   Event type 2 (EV_REL)
#     Event code 0 (REL_X)
#     Event code 1 (REL_Y)
#     Event code 8 (REL_WHEEL)

Another of the evemu test commands will show us the events being generated when we move the mouse:

$ sudo evemu-record /dev/input/event5
E: 4.223 0002 0000 0004 # EV_REL / REL_X                4
E: 4.223 0000 0000 0000 # ------------ SYN_REPORT (0) ------ +8ms
E: 4.231 0002 0000 0007 # EV_REL / REL_X                7
E: 4.231 0002 0001 0001 # EV_REL / REL_Y                1
E: 4.231 0000 0000 0000 # ------------ SYN_REPORT (0) ------ +8ms

Typically, for a mouse movement there is the event type EV_REL, the event code REL_X and or REL_Y for the relative movement axis, and the event value distance moved (4, 7, 1 above). The events are followed by a synchronisation event of type EV_SYN with code SYN_REPORT to signal the end of the event.

We can inject our own event (say a move of 20,10) with another of the test commands:

sudo evemu-event /dev/input/event5 --type EV_REL --code REL_X --value 20
sudo evemu-event /dev/input/event5 --type EV_REL --code REL_Y --value 10 --sync

The --sync option adds the SYN_REPORT event to the end (the equivalent of --type EV_SYN --code SYN_REPORT).

Finally, another test command, evemu-device allows us to create a new input device by giving a description such as the ones we have already seen for the mouse. It uses /dev/uinput and creates a new /dev/input/event* device, which we can then use to send events to.

So even if you do not have a mouse, you can add one dynamically, and then control it as you wish. I do not have a device with absolute position events to provide you with an example, but you can similarly add a tablet-like device and send absolute moves events through it.


From meuh's answer, I wrote this little mousemove bash script, to run with XREL YREL as arguments.


while IFS=: read dev desc ;do
    case $desc in 
        *[Mm]ouse* ) mousedev=$dev;;
done < <(evemu-describe <<<'' 2>&1)

[ -c "$mousedev" ] && 
    evemu-event $mousedev --type EV_REL --code REL_X --value $1 &&
    evemu-event $mousedev --type EV_REL --code REL_Y --value $2 --sync

As root or via sudo:

sudo mousemove -20 10
sudo mousemove -4000 -4000

Because of mouse acceleration concept, real move don't correspond to exact values, except for very low values.

  • Perhaps for accurate large movement, you could break up the single large change into a loop of smaller changes, e.g. 100 moves of 10 pixels to do a 1000 pixel movement. It might appear slow on the screen, though.
    – meuh
    Sep 28, 2022 at 11:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .