diff --git a/build.zig b/build.zig index cbea747..99d1e44 100644 --- a/build.zig +++ b/build.zig @@ -5,6 +5,7 @@ const examples = [_][]const u8{ "getserverfontnames", "testexample", "graphics", + "images", "queryfont", "example", "fontviewer", diff --git a/examples/images.zig b/examples/images.zig new file mode 100644 index 0000000..010def7 --- /dev/null +++ b/examples/images.zig @@ -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); + // } +}