danos/library/runtime/display.zig

61 lines
2.4 KiB
Zig

//! User-space display client: talk to the display service (query the mode, and — from D3
//! — create layers, draw, and present) without hand-rolling the IPC. The `runtime.block`
//! shape: a cached `.display` lookup with a boot-race retry, then extern-struct request/
//! reply marshalling. See system/services/display/ and docs/display.md.
const std = @import("std");
const ipc = @import("ipc.zig");
const system = @import("system.zig");
const protocol = @import("display-protocol");
/// The display's current mode, as `info()` reports it.
pub const Info = struct {
width: u32,
height: u32,
pitch: u32, // bytes per row (may exceed width*4; see docs/framebuffer.md)
format: u32, // a device-abi DisplayFormat value (0 = rgbx, 1 = bgrx)
};
/// The service endpoint, looked up once and cached.
var handle: ?ipc.Handle = null;
/// Look up the display service, retrying while it comes up (a client races its
/// registration at boot). Returns the endpoint, or null if it never appears.
fn service() ?ipc.Handle {
if (handle) |h| return h;
var attempts: usize = 0;
while (attempts < 100) : (attempts += 1) {
if (ipc.lookup(.display)) |h| {
handle = h;
return h;
}
system.sleep(50);
}
return null;
}
/// Send one request, receive its reply; true on a zero status. `out` receives the reply
/// so callers can read `info`/`layer` fields on success.
fn transact(request: protocol.Request, out: *protocol.Reply) bool {
const h = service() orelse return false;
var req = request;
var reply: [protocol.reply_size]u8 = undefined;
const len = ipc.call(h, std.mem.asBytes(&req), &reply) catch return false;
if (len < protocol.reply_size) return false;
out.* = std.mem.bytesToValue(protocol.Reply, reply[0..protocol.reply_size]);
return out.status == 0;
}
/// The display's current mode, or null if the service never came up.
pub fn info() ?Info {
var reply: protocol.Reply = undefined;
if (!transact(.{ .operation = @intFromEnum(protocol.Operation.info) }, &reply)) return null;
return .{ .width = reply.width, .height = reply.height, .pitch = reply.pitch, .format = reply.format };
}
/// Composite the dirty layers and flush the frame to the screen.
pub fn present() bool {
var reply: protocol.Reply = undefined;
return transact(.{ .operation = @intFromEnum(protocol.Operation.present) }, &reply);
}