X Server Input Device Grabs

Grabs are a way of forcing devices to only report to a specific client (the grabbing client). There are two different types of grabs: core grabs and device grabs (X input extension).

Core grabs are fairly simple: when a client issues a request (e.g. GrabPointer), the server takes the core pointer and initializes a GrabRec structure to be put in the grab field of the DeviceIntRec. Each time the device now emits an event, it is delivered only to the client that owns the grab. Grabs are resources and thus the owner of the grab can be looked up using the rClient(grab) macro. A typical event flow would be mieqProccessInputEvent -> CoreProcessPointerEvent -> DeliverGrabbedEvent -> TryClientEvents.

Device grabs are the same but require some knowledge about the role of the core pointer. Each time the client issues a GrabDevice request, a grab is initialized in the same way to the core grab. The grab is stored in the device itself.

But here is the important bit about event generation: Each time a device emits an event, it creates a device event and (maybe) a core event. The device event is put on the event queue and processed with a reference to the original device. The core event however is processed with the core pointer. After GetPointerEvents, all core events appear to have originated from the core pointer and all device events from their device.

And suddenly all the bits with grabs fall into place. If we have a core grab, we have it on the core pointer, thus all core events, no matter which device caused them, belong to us. A device grab will only send us the Xi events, while the core events still go wherever they are supposed to. As a result, we can have multiple device grabs but only ever one core grab. This is probably also the reason why the core pointer and core keyboard could not be configured to send Xi events.

There are always two core devices: VirtualCorePointer and VirtualCoreKeyboard were added by Daniel Stone when he wrote InputHotplug. Reason being is that the X server always has to have at least one keyboard and one mouse. But with hotplugging, you should be able to start X without any devices. So the solution was to add virtual devices that aren't connected to real ones and make sure they are always there. That way you can add and remove physical devices without breaking anything. They aren't physical devices! But with the event delivery described above, they still emit core events.

Passive grabs

Passive grabs are a different matter. A client can create multiple passive grabs (GrabButton/GrabKey), and they become active whenever the specified button/key is pressed, until it is released again. The server has two kinds of passive grabs, explicit and implicit ones. Explicit ones are the one requested by the client, they are stored as GrabRec in the window's resource system.

An implicit passive grab is activated only if there is no active grab on the device and no passive grab has been activated either. In this case, the server creates a new grab, fills it with data and sets it on the device. The purpose of the grab is to ensure that a ButtonRelease event is delivered to the same client as the ButtonPress event. This grab is deactivated when the button is released again.

There can only be one grab on a device, so the check for the activation of passive grabs only happens when the first button is pressed. Further buttons are not checked.

A passive grab is only created once, when the client requests it. When it is activated, the grab is copied into the device. Thus modifying a passive grab will not actually modify a grab that is currently active on a device.