Skip to main content

Events

Selecting events with eventMask

The X server only delivers the events you ask for. You opt in per window with an event mask — a bitwise OR of flags from x11.eventMask — either at window creation time or later with ChangeWindowAttributes:

const x11 = require('x11');

x11.createClient((err, display) => {
const X = display.client;
const wid = X.AllocID();
X.CreateWindow(wid, display.screen[0].root, 100, 100, 400, 300);
X.ChangeWindowAttributes(wid, {
eventMask: x11.eventMask.PointerMotion | x11.eventMask.KeyPress,
});
X.MapWindow(wid);
X.on('event', ev => {
if (ev.type === 2) // KeyPress
X.terminate();
console.log(ev);
});
});

Commonly used mask bits include Exposure, KeyPress, KeyRelease, ButtonPress, ButtonRelease, PointerMotion, EnterWindow, LeaveWindow, StructureNotify, SubstructureNotify, SubstructureRedirect, PropertyChange and FocusChange.

The demo below selects a deliberately broad mask and prints every event the server sends. Click and type inside the X screen and watch the console fill — then narrow the mask in the editor, press Run, and see what stops arriving:

Loading live demo…

Event delivery

All events arrive through a single 'event' emitter on the client. Every event object carries:

  • name — the event name, e.g. 'MotionNotify', 'Expose', 'KeyPress'
  • type — the numeric event code (e.g. 2 = KeyPress, 6 = MotionNotify, 12 = Expose)
  • seq — the sequence number of the last request processed by the server
  • event-specific fields (wid, x, y, rootx, rooty, keycode, buttons, …)
  • rawData — the raw 32-byte wire packet (useful to forward with SendEvent)

Dispatch on ev.name for readability:

X.on('event', ev => {
switch (ev.name) {
case 'Expose':
redraw();
break;
case 'MotionNotify':
console.log(ev.x, ev.y);
break;
}
});

Pointer events carry both window-relative (x, y) and screen-relative (rootx, rooty) coordinates. Drag inside this one to paint with them:

Loading live demo…

Keyboard events are lower-level than you may expect: a KeyPress carries a keycode, which is a physical key number, not a character. Turning it into something printable means looking up the keysym through GetKeyboardMapping and applying the modifier state yourself:

Loading live demo…

Every core event and its fields is documented in the core events reference.

Redirection

Two mask bits are not notifications but interceptions. A client holding SubstructureRedirect on a window receives MapRequest, ConfigureRequest and CirculateRequest for its children instead of those requests taking effect — the server asks permission rather than reporting a fact.

That is the entire mechanism a window manager is built from. There is no privileged API and no special connection: selecting one event mask on the root window is what makes a program a window manager, and only one client at a time may hold it (a second gets BadAccess, which is how a WM discovers another is already running).

A client's own requests are never redirected back to itself, so the demo below opens two connections — one managing the screen, one for the toy applications. Drag either window by its title bar; neither application drew that bar, or knows it exists:

Loading live demo…

Redirection is also skipped for override-redirect windows, which is how menus and tooltips escape being framed.

Extension events

Extension events are delivered through the same 'event' emitter once the extension has been initialised with X.require() — requiring the extension registers its event parsers. GenericEvents (X Generic Event Extension, used by Present and XInput 2) are framed by their length field and dispatched to per-extension parsers registered in X.geEventParsers.