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:
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 withSendEvent)
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:
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:
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:
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.