There are at least two separate details here:
- Hardware cursor
- 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.