Getting started
Connecting
Everything starts with x11.createClient:
const x11 = require('x11');
x11.createClient((err, display) => {
if (err) throw err;
const X = display.client; // the request interface
// ...
});
createClient(options, callback) accepts an optional first argument:
| option | meaning |
|---|---|
display | display string, e.g. ':0', 'localhost:1.0' or a literal socket path (default: $DISPLAY) |
debug | log outgoing requests and capture per-request stack traces for errors |
disableBigRequests | skip the automatic BIG-REQUESTS handshake done at connect time |
bufferRequests | batch outgoing requests into fewer socket writes: true, or { maxSize, maxDelay, flushOnReply, shouldFlush } — see Buffering the output |
tcpNoDelay | turn Nagle's algorithm off on a TCP connection (default: on when bufferRequests is set) |
stream | an already-connected duplex stream to use instead of opening a socket (see Custom transports) |
auth | authentication override, { name, data } (see Custom transports) |
The client connects over a unix socket when the display refers to the local
host (on macOS the display must be a literal socket path, XQuartz launchd
style, e.g. /private/tmp/com.apple.launchd.../org.xquartz:0), and over TCP
(port 6000 + display number) otherwise. ~/.Xauthority (or $XAUTHORITY)
is consulted for authentication automatically.
createClient returns the client object immediately; subscribe to 'error'
on it to catch connection-phase failures:
const client = x11.createClient((err, display) => { /* ... */ });
client.on('error', err => console.error(err));
The display object
The display passed to the callback describes the connection setup block
sent by the server:
display.client— theXclient object all requests are called ondisplay.screen[n]— one entry per screen; each screen carriesroot(root window id),white_pixel,black_pixel,pixel_width/pixel_height,mm_width/mm_height,default_colormap,root_depth,root_visualand the list of supporteddepthswith their visualsdisplay.vendor,display.release— server identificationdisplay.min_keycode/display.max_keycode
Most programs only need the first screen:
const screen = display.screen[0];
const root = screen.root;
const white = screen.white_pixel;
const black = screen.black_pixel;
Making requests
All requests live on X = display.client and follow the Node callback style
— no promises. Requests with no reply take plain arguments; requests with a
reply take a trailing callback(err, result):
const wid = X.AllocID(); // allocate a resource id
X.CreateWindow(wid, root, 10, 10, 400, 300); // no reply: fire and forget
X.MapWindow(wid);
X.InternAtom(false, 'WM_NAME', (err, atom) => { // with reply
// ...
});
Resource ids (windows, pixmaps, GCs, …) are allocated client-side with
X.AllocID() and can be recycled with X.ReleaseID(id) once the resource
is destroyed. When you are done with the connection, call X.terminate();
the client emits 'end' when the stream closes.
That is already enough for a whole program. This one runs here, in this page,
against a pure-JavaScript X server compositing to the canvas above the editor
— no display, no DISPLAY, nothing installed. Edit it and press Run.
Every demo on this site works the same way, and the code is the code you would run in node. The playground has the longer ones.
Error handling
X errors arrive asynchronously. Errors caused by a request with a reply are
routed to that request's callback as err; errors from reply-less requests
— and errors nobody claims — are emitted as 'error' on the client:
X.on('error', err => console.error(err.message, err.badParam));
With createClient({ debug: true }, …) each error also carries the stack
trace of the request that caused it.
See the API reference overview for the full connection and error-handling details, and Core requests for every request signature.