X11 clients use tcp or unix socket connection (usually port 6000 + display number for tcp and '/tmp/.X11-unix/X' + display number for domain socket) for communication. The protocol itself is duplex, you can send requests at any time and you receive replies, errors and events. Events and errors are always 32 bytes long packets.
There are various strategies on how to process incoming data from X server. With xlib, after each request known to produce response there is blocking read() call to read that much data. During 'idle' time you are expected to read all events and errors from connection manually:
while(1) {
XNextEvent(d, &e);
/* draw or redraw the window */
if(e.type==Expose) {
XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
}
/* exit on key press */
if(e.type==KeyPress)
break;
// Handle Windows Close Event
if(e.type==ClientMessage)
break;
}
In this snippet XNextEvent
consumes 32 bytes of data from socket into e
structure, and code in while
loop dispatches it depending on app logic and event type and payload.
In some other libraries like node-x11 (note: I'm author) event loop is hidden behind framework async io model and happens implicitly.
var x11 = require('x11');
var PointerMotion = x11.eventMask.PointerMotion;
x11.createClient(function(err, display) {
var X = display.client;
var root = display.screen[0].root;
var wid = X.AllocID();
X.CreateWindow(
wid, root,
0, 0, 400, 300,
0, 0, 0, 0,
{
eventMask: PointerMotion
}
);
X.MapWindow(wid);
X.on('event', function(ev) {
if (ev.name == 'PointerMotion')
{
console.log('Mouse motion!', [ev.x, ev.y]);
}
});
X.on('error', function(e) {
console.log(e);
});
});