How is the mouse cursor implemented in the X.org X Windows system? Specifically, how should we understand these error cases:

1) I remember sometimes seeing the entire GUI become slow and stutter or stall. But the mouse cursor still responded very quickly. E.g. this might happen if you use too much memory, and start "thrashing". (The system is continuously swapping out to disk and back in again).

2) Other times, Linux graphics drivers used to crash. The screen could be frozen, corrupted, or something in-between. But sometimes the mouse cursor still responded smoothly, on top of the frozen or corrupted display.

There also hints from some of the problems (and workarounds) that can happen with mouse cursor corruption:

i) Why does going into text mode and then back to graphical mode fix a mouse disappearance?
ii) Graphics dim, but mouse cursor not dim


There are at least two separate details here:

  1. Hardware cursor
  2. Cursor updates are deliberately prioritized

1. Hardware cursor

The first detail is more well-known. You can find it mentioned in documentation: the HWCursor option in Xorg.

Your graphics probably uses a hardware cursor. As the hardware scans the pixels out to the display, it overlays the mouse cursor on top of the main framebuffer.

  • A bug in the graphics driver can mis-configure the framebuffer, without breaking the hardware cursor.
  • Or a mis-configured hardware cursor might appear corrupted. But you might still see the corrupted cursor move and respond, while the rest of the display appears broken.
  • Or the cursor can be mis-configured while the framebuffer is OK. As in the "mouse disappearance" case.

1.1 Software cursor

Note, though hardware cursors are very useful, there are also techniques to make software cursors work better.


If it is done in software, the cursor has to be back-buffered. Every time it moves we restore the previous image, save the window at the target position, then render the cursor into the stream.

xorg-server-1.20.5/mi/midispcur.c - miDCSaveUnderCursor() / miDCRestoreUnderCursor()

2. Cursor updates are deliberately prioritized

To be fair, the cursor is relatively simple. Also, it is in constant use. This means cursor-specific memory is unlikely to be swapped out, when you start running out of memory. But there is also another detail:


Previously, there were two options for how an input driver would pass on events to the X server: polling or from within the signal handler. Polling simply adds all input devices' file descriptors to a select(2) loop that is processed in the mainloop of the server. The downside here is that if the server is busy rendering something, your input is delayed until that rendering is complete. Historically, polling was primarily used by the keyboard driver because it just doesn't matter much when key strokes are delayed. Both because you need the client to render them anyway (which it can't when it's busy) and possibly also because we're just so bloody used to typing delays.

The signal handler approach circumvented the delays by installing a SIGIO handler for each input device fd and calling that when any input occurs. This effectively interrupts the process until the signal handler completes, regardless of what the server is currently busy with. A great solution to provide immediate visible cursor movement (hence it is used by evdev, synaptics, wacom, and most of the now-retired legacy drivers)


The drivers push events into the queue during the signal handler, in the main loop the server reads them and processes them. In a busy server that may be several seconds after the pointer motion was performed on the screen but hey, it still feels responsive.


We were still mostly "happy" with it until libinput came along. libinput is a full input stack and expecting it to work within a signal handler is the somewhere between optimistic, masochistic and sadistic. The xf86-input-libinput driver doesn't use the signal handler and the side effect of this is that a desktop with libinput didn't feel as responsive when the server was busy rendering.

Keith Packard stepped in and switched the server from the signal handler to using input threads. Or more specifically: one input thread on top of the main thread. That thread controls all the input device's file descriptors and continuously reads events off them. It otherwise provides the same functionality the signal handler did before: visible pointer movement and shoving events into the event queue for the main thread to process them later. But of course, once you switch to threads, problems have 2 you now. [...] some interesting race conditions kept happening. But as of today, we think most of these are solved.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.