[Guide] How to simulate ABSOLUTE mouse movement using python-uinput

originally posted on r/python on 04 Dec 2020 @ 12:25:34 EST

( I hope this becomes visible to search engines so people will find this guide )

The first (and maybe fastest) choice with virtual input using Python is python-uinput, and it offers a simple example on how to easily move the mouse cursor. [GitHub Repo]

However, that movement is only relative, implied by the REL_X/REL_Y names. For the uninitiated, relative means moving the mouse to a position relative to its current location.

In my search to find a way to do absolute mouse movement with this module, I came across these three pages that offered important information on it:

Absolute movement is when your mouse moves to a position relative to an area, as opposed to relative to itself. For example 100,100 is the center of a 200x200 area. In this case, an area will most likely be your monitor.

How to simulate absolute mouse movement with python-uinput:

First, we have to talk about how “screen space” is calculated on Linux. The whole “screen space” is a rectangle that is just big enough to contain all connected monitors. That means that a setup with one monitor with the size of 1280x720 will have a total “screen space” of 1280x720. A setup with two monitors, 1366x768 and 1440x900 side-by-side will be 2806x900. This, of course, changes if you have one monitor on top of another or one monitor is higher than the one next to it etc.

Oh, and keep in mind that y is counted top-to-bottom (0 at the top and 900 at the bottom).

Now, the important thing to know about python-uinput that it registers input events on the uinput device, which includes mouse movement. Quoting from the Launchpad page, “Xorg does not assign mouse handlers for absolute axis devices by default.” That means that we have to do it ourselves, and thankfully, python-uinput provides a helpful feature:

Line 183 in __init.py__: absmin, absmax, absfuzz, absflat = ev_spec[2:]

All we have to do is add a tuple to an argument that involves mouse movement events, such as ABS_X and ABS_Y:

device = uinput.device([uinput.ABS_X + (0, 255, 0, 0), uinput.ABS_Y + (0, 255, 0, 0)])

The first two numbers in the tuple are the most important for most applications, since they dictate the range for each axis.

How does this tie into the “screen space” thing? The size of the “screen space” is what we can use as a range for ABS_X and ABS_Y.

That also means that we can define an area, say in a monitor, by mapping (or scaling) a range 0 to monitor_sizex to monitor_topleft_x to monitor_topright_x (same with y). Meaning that 0,0, is the top-left of the monitor, 1440,900 is the bottom-right and so on. (I will not show an example of that since there are many out there)

Example

Create ranges for ABS_X and ABS_Y that correspond to a single 1280x720 display, and moves the mouse to 10,10.

  import uinput
  import time
  
  device_events = (
    uinput.ABS_X + (0, 1280, 0, 0),
    uinput.ABS_Y + (0, 720, 0, 0),
  )
  
  with uinput.Device(device_events) as device:
    time.sleep(1)
    device.emit(uinput.ABS_X, 10)
    device.emit(uinput.ABS_Y, 10)

Notice that we use time.sleep(1) before doing anything with the device. That is a weird quirk with uinput where it won’t initialize completely instantly, and we must wait a little bit before doing anything. You can make that delay shorter if you like.

Further reading:

this document was generated with pandoc.