This page lists the differences between X servers with MPX support and previous servers (in terms of input processing only). MPX is the in-server implementation to support multiple simultaneous input devices. The protocol-specific parts to MPX are included in the X Input Extension 2.0 (XI2).

Device hierarchy

MPX features a two-layer device hierarchy. Any physical device is a "slave device", does not have a cursor and can only send XI events. Then there are "master devices", virtual devices that are represented by a cursor and a keyboard focus. MDs always come in pairs, one pointer, one keyboard. An SD can be attached to a MD. In this case any event by the SD is routed through the MD. To the client it appears that events come from the SD and from the MD (a client should never listen directly to SDs).

MDs generates core events and XI events. Creating a master device (ChangeDeviceHierarchy request) creates a master pointer and a master keyboard. By default, two MDs exist: the virtual core pointer and the virtual core keyboard. All physical devices are attached to either of these to but this attachment can be changed.

Input events

In earlier servers, all core events come from the virtual core pointer (VCP) or the virtual core keyboard (VCK). All device events from the device. It is not possible to XOpenDevice() the core pointer/core keyboard.

In MPX, SDs send XI events and MDs send core and XI events.


Each client can have one (master) pointer explicitly assigned. Each time pointer data is needed for request/reply handling, this ClientPointer is chosen (e.g. XQueryPointer() will return the ClientPointer's coordinates). The master keyboard paired with the ClientPointer will be used for request/reply handling (e.g. XGetInputFocus()).

The ClientPointer should only be used by the window manager to set the CP setting for traditional X applications. If you ever use XSetClientPointer() in your standard GUI application, you're most likely doing something wrong.

The general rule is that the ClientPointer will work just as the traditional pointer in the given client. All other pointers interacting with the same client may experience difficulties (see GrabOwnership).

Inside the DIX, the ClientPointer is realised with the PickPointer() and the PickKeyboard() functions (events.c). Both of which check the device list for available devices. If there is a physical device attached, then it is chosen as the ClientPointer and returned. If no physical device is available, then the VCP is returned. A call to PickPointer() also sets the ClientPointer, so subsequent calls to PickPointer() return the same pointer, unless changed by the client.

The ClientPointer can be set with XSetClientPointer().


Each device can only be grabbed by one client at a time. If a client core-grabs the device, the ClientPointer will be grabbed. Until the core grab is removed, no other client can grab this device (even XGrabDevice() will fail).

It works the other way around too. If a client uses XGrabDevice(), any XGrabPointer/Keyboard() on the same device will fail with AlreadyGrabbed.

While a grab is active, other clients may still get events from the other devices. The difference between core grabs and device grabs can be expressed as:

  • A core grab guarantees a client that it doesn't get events from any other device.
  • A device grab guarantees that no other client gets events from this device.

Grab ownership

If a client core-grabs a device, no other device will send events to this client while the core grab is active. This is called grab ownership and was necessary to not confuse standard X apps.

This can be annoying, as clients usually grab the pointer for popup menus. Which in turn means that each time you get a popup, all other devices are deactivated until the popup is gone. Even worse, if you are not the ClientPointer, you won't even be able to use the popup and the ClientPointer has to click on it. Use XSetClientPointer() to avoid this.

Grab ownership works different for passive grabs. A passive grab is stored on the window with the client's ClientPointer, but when the grab is activated (by pressing a button/key) the grab device is switched to the device that caused the grab to activate. The device now stays the same as long as the grab is active. If the client now starts an active grab, this active grab is put on the already grabbed device, not on the ClientPointer.

Grab ownership does not apply to device grabs.


XI2 requires XGE for its events.