My library uses Io interface to consume X11 protocol. It’s nice and dandy until library client needs to hook into polling. Usually poll for some timeout to schedule other tasks. With xlib I could do something like:
pub fn run(app: *App) !void {
// setup pollfd
const fd = c.ConnectionNumber(app.ctx.gfx.display);
var pollfd = [1]std.posix.pollfd{.{
.fd = xfd,
.events = std.posix.POLL.IN,
.revents = 0,
}};
// event loop
while (true) {
// While there are pending X11 events
while (c.XPending(app.ctx.gfx.display) > 0) {
var event: c.XEvent = undefined;
// Get next event
_ = c.XNextEvent(app.ctx.gfx.display, &event);
// Process event
switch (event.type) { ... }
}
app.doStuff() // takes some time
// There could be more events available already.
// So only need to block for polling only on zero events.
if (c.XPending(app.ctx.gfx.display) == 0) {
const timeout_ms = app.nextPollTimeoutMs();
_ = try std.posix.poll(&pollfd, timeout_ms);
}
}
}
I don’t understand how to make similar API with Io provided facilities. I assume I could check reader buffer and also give fd to client for std.posix.poll. But it doesn’t feel right. Current library event loop looks like:
while (true) {
// Blocks until data is available
// conn is inited with Io interface and uses std.Io.net.Stream
// to get Reader and Writer.
const event = try conn.nextEvent();
switch (event) {
...
}
}
Am I missing something obvious?
Current solution
I’ve added reader buffer check and pre-poll to guarantee main decode path wouldn’t block.
It allows to use library in simple cases where timeout is enough and to wire a custom poll around.
pub fn pollEventTimeout(self: *Connection, timeout_ms: c_int) !?xproto.Event {
...
if (self.reader().bufferedLen() == 0) {
var pollfd = [1]std.posix.pollfd{.{
.fd = self.fd(),
.events = std.posix.POLL.IN,
.revents = 0,
}};
const n = try std.posix.poll(&pollfd, timeout_ms);
if (n == 0) return null;
}
// Could safely use reader methods here
}