Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const examples = [_][]const u8{
"getserverfontnames",
"testexample",
"graphics",
"images",
"queryfont",
"example",
"fontviewer",
Expand Down
209 changes: 209 additions & 0 deletions examples/images.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
const std = @import("std");
const x = @import("x");
const common = @import("common.zig");

var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
const allocator = arena.allocator();

const window_width = 400;
const window_height = 400;

pub fn main() !u8 {
try x.wsaStartup();
const conn = try common.connect(allocator);
defer std.posix.shutdown(conn.sock, .both) catch {};

const screen = blk: {
const fixed = conn.setup.fixed();
inline for (@typeInfo(@TypeOf(fixed.*)).@"struct".fields) |field| {
std.log.debug("{s}: {any}", .{ field.name, @field(fixed, field.name) });
}
std.log.debug("vendor: {s}", .{try conn.setup.getVendorSlice(fixed.vendor_len)});
const format_list_offset = x.ConnectSetup.getFormatListOffset(fixed.vendor_len);
const format_list_limit = x.ConnectSetup.getFormatListLimit(format_list_offset, fixed.format_count);
std.log.debug("fmt list off={} limit={}", .{ format_list_offset, format_list_limit });
const formats = try conn.setup.getFormatList(format_list_offset, format_list_limit);
for (formats, 0..) |format, i| {
std.log.debug("format[{}] depth={:3} bpp={:3} scanpad={:3}", .{ i, format.depth, format.bits_per_pixel, format.scanline_pad });
}
const screen = conn.setup.getFirstScreenPtr(format_list_limit);
inline for (@typeInfo(@TypeOf(screen.*)).@"struct".fields) |field| {
std.log.debug("SCREEN 0| {s}: {any}", .{ field.name, @field(screen, field.name) });
}
break :blk screen;
};

// TODO: maybe need to call conn.setup.verify or something?
var sequence: u16 = 0;

const ids: Ids = .{ .base = conn.setup.fixed().resource_id_base };
{
var msg_buf: [x.create_window.max_len]u8 = undefined;
const len = x.create_window.serialize(&msg_buf, .{
.window_id = ids.window(),
.parent_window_id = screen.root,
.depth = 0, // we don't care, just inherit from the parent
.x = 0,
.y = 0,
.width = window_width,
.height = window_height,
.border_width = 0, // TODO: what is this?
.class = .input_output,
.visual_id = screen.root_visual,
}, .{
.bg_pixel = 0xaabbccdd,
.event_mask = .{ .exposure = 1 },
//.dont_propagate = 1,
});
try conn.sendOne(&sequence, msg_buf[0..len]);
}

{
var msg_buf: [x.create_gc.max_len]u8 = undefined;
const len = x.create_gc.serialize(&msg_buf, .{
.gc_id = ids.gc(),
.drawable_id = ids.window(),
}, .{
.background = screen.white_pixel,
.foreground = screen.black_pixel,
});
try conn.sendOne(&sequence, msg_buf[0..len]);
}

const double_buf = try x.DoubleBuffer.init(
std.mem.alignForward(usize, 1000, std.heap.page_size_min),
.{ .memfd_name = "ZigX11DoubleBuffer" },
);
// double_buf.deinit() (not necessary)
std.log.info("read buffer capacity is {}", .{double_buf.half_len});
var buf = double_buf.contiguousReadBuffer();

{
var msg: [x.map_window.len]u8 = undefined;
x.map_window.serialize(&msg, ids.window());
try conn.sendOne(&sequence, &msg);
}

while (true) {
{
const recv_buf = buf.nextReadBuffer();
if (recv_buf.len == 0) {
std.log.err("buffer size {} not big enough!", .{buf.half_len});
return 1;
}
const len = try x.readSock(conn.sock, recv_buf, 0);
if (len == 0) {
std.log.info("X server connection closed", .{});
return 0;
}
buf.reserve(len);
}
while (true) {
const data = buf.nextReservedBuffer();
if (data.len < 32)
break;
const msg_len = x.parseMsgLen(data[0..32].*);
if (data.len < msg_len)
break;
buf.release(msg_len);
//buf.resetIfEmpty();
switch (x.serverMsgTaggedUnion(@alignCast(data.ptr))) {
.err => |msg| {
std.log.err("{}", .{msg});
return 1;
},
.reply => |msg| {
std.log.info("todo: handle a reply message {}", .{msg});
return error.TodoHandleReplyMessage;
},
.expose => |msg| {
std.log.info("expose: {}", .{msg});
try render(conn.sock, &sequence, ids.window(), ids.gc());
},
// .mapping_notify => |msg| {
// std.log.info("mapping_notify: {}", .{msg});
// },
// .no_exposure => |msg| std.debug.panic("unexpected no_exposure {}", .{msg}),
.unhandled => |msg| {
std.log.info("todo: server msg {}", .{msg});
return error.UnhandledServerMsg;
},
.key_press,
.key_release,
.keymap_notify,
.button_press,
.button_release,
.enter_notify,
.leave_notify,
.motion_notify,
.no_exposure,
.mapping_notify,
.map_notify,
.reparent_notify,
.configure_notify,
=> unreachable, // did not register for these
}
}
}
}

const Ids = struct {
base: u32,
fn window(self: Ids) u32 {
return self.base;
}
fn gc(self: Ids) u32 {
return self.base + 1;
}
};

const FontDims = struct {
width: u8,
height: u8,
font_left: i16, // pixels to the left of the text basepoint
font_ascent: i16, // pixels up from the text basepoint to the top of the text
};

fn render(
sock: std.posix.socket_t,
sequence: *u16,
drawable_id: u32,
gc_id: u32,
//font_dims: FontDims,
) !void {
{
var msg: [x.poly_fill_rectangle.getLen(1)]u8 = undefined;
x.poly_fill_rectangle.serialize(&msg, .{
.drawable_id = drawable_id,
.gc_id = gc_id,
}, &[_]x.Rectangle{
.{ .x = 100, .y = 100, .width = 200, .height = 200 },
});
try common.sendOne(sock, sequence, &msg);
}
{
var msg: [x.clear_area.len]u8 = undefined;
x.clear_area.serialize(&msg, false, drawable_id, .{
.x = 150,
.y = 150,
.width = 100,
.height = 100,
});
try common.sendOne(sock, sequence, &msg);
}
// {
// const text_literal: []const u8 = "Hello X!";
// const text = x.Slice(u8, [*]const u8){ .ptr = text_literal.ptr, .len = text_literal.len };
// var msg: [x.image_text8.getLen(text.len)]u8 = undefined;

// const text_width = font_dims.width * text_literal.len;

// x.image_text8.serialize(&msg, text, .{
// .drawable_id = drawable_id,
// .gc_id = fg_gc_id,
// .x = @divTrunc((window_width - @as(i16, @intCast(text_width))), 2) + font_dims.font_left,
// .y = @divTrunc((window_height - @as(i16, @intCast(font_dims.height))), 2) + font_dims.font_ascent,
// });
// try common.sendOne(sock, sequence, &msg);
// }
}