From adadca682799313c50466cb1d9cc246ccedb7590 Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Wed, 7 Feb 2024 23:34:00 -0700 Subject: [PATCH] feat: reduce repetition when calling wayland api --- src/core.zig | 154 ++++++++++++++ src/main.zig | 571 ++++----------------------------------------------- src/root.zig | 188 ++++++++--------- src/xdg.zig | 59 ++++++ 4 files changed, 351 insertions(+), 621 deletions(-) diff --git a/src/core.zig b/src/core.zig index 297ee14..db6c178 100644 --- a/src/core.zig +++ b/src/core.zig @@ -1,4 +1,5 @@ const types = @import("types.zig"); +const Conn = @import("root.zig").Conn; pub const Display = struct { pub const Request = union(enum) { @@ -29,9 +30,44 @@ pub const Display = struct { no_memory, implementation, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Compositor { + return .{ .conn = conn, .id = id }; + } + + // TODO: take callback + pub fn sync(display: Display) !u32 { + const new_id = display.conn.id_pool.create(); + try display.conn.send( + Request, + display.id, + .{ .sync = .{ + .callback = new_id, + } }, + ); + return new_id; + } + + pub fn get_registry(display: Display) Registry { + const new_id = display.conn.id_pool.create(); + try display.conn.send( + Request, + display.id, + .{ .get_registry = .{ + .registery = new_id, + } }, + ); + return Registry.init(display.conn, new_id); + } }; pub const Registry = struct { + pub const INTERFACE = "wl_registry"; + pub const VERSION = 1; + pub const Event = union(enum) { global: struct { name: u32, @@ -51,6 +87,13 @@ pub const Registry = struct { new_id: u32, }, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Registry { + return .{ .conn = conn, .id = id }; + } }; pub const Compositor = struct { @@ -65,6 +108,33 @@ pub const Compositor = struct { new_id: u32, }, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Compositor { + return .{ .conn = conn, .id = id }; + } + + pub fn create_surface(compositor: *const Compositor) !Surface { + const new_id = compositor.conn.id_pool.create(); + try compositor.conn.send( + Request, + compositor.id, + .{ .create_surface = .{ .new_id = new_id } }, + ); + return Surface.init(compositor.conn, new_id); + } + + // pub fn create_region(compositor: *Compositor) Region { + // const new_id = compositor.conn.id_pool.create(); + // try compositor.conn.send( + // Request, + // compositor.id, + // .{ .create_region = .{.new_id = new_id} }, + // ); + // return Region.init(compositor.conn, new_id); + // } }; pub const ShmPool = struct { @@ -82,6 +152,30 @@ pub const ShmPool = struct { size: i32, }, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) ShmPool { + return .{ .conn = conn, .id = id }; + } + + pub fn create_buffer(shm_pool: ShmPool, offset: i32, width: i32, height: i32, stride: i32, format: Shm.Format) u32 { + const new_id = shm_pool.conn.id_pool.create(); + try shm_pool.conn.send( + Request, + shm_pool.id, + .{ .create_buffer = .{ + .new_id = new_id, + .offset = offset, + .width = width, + .height = height, + .stride = stride, + .format = format, + } }, + ); + return Surface.init(shm_pool.conn, new_id); + } }; pub const Shm = struct { @@ -114,6 +208,23 @@ pub const Shm = struct { xrgb8888, _, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Shm { + return .{ .conn = conn, .id = id }; + } + + pub fn create_pool(shm: *const Shm, pool_fd: u32, size: u32) ShmPool { + const new_id = shm.conn.id_pool.create(); + try shm.conn.send( + Request, + shm.id, + .{ .create_pool = .{ .new_id = new_id, .fd = @enumFromInt(pool_fd), .size = size } }, + ); + return ShmPool.init(shm.conn, new_id); + } }; pub const Surface = struct { @@ -165,6 +276,21 @@ pub const Surface = struct { invalid_offset, defunct_role_object, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Surface { + return .{ .conn = conn, .id = id }; + } + + pub fn commit(surface: Surface) !void { + try surface.conn.send( + Request, + surface.id, + .commit, + ); + } }; pub const Buffer = struct { @@ -211,6 +337,13 @@ pub const Seat = struct { }; pub const Error = enum(u32) {}; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Seat { + return .{ .conn = conn, .id = id }; + } }; pub const Pointer = struct { @@ -298,6 +431,13 @@ pub const Pointer = struct { identical, inverted, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Seat { + return .{ .conn = conn, .id = id }; + } }; pub const Keyboard = struct { @@ -348,6 +488,13 @@ pub const Keyboard = struct { released, pressed, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Seat { + return .{ .conn = conn, .id = id }; + } }; pub const Touch = struct { @@ -387,4 +534,11 @@ pub const Touch = struct { orientation: i32, }, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Seat { + return .{ .conn = conn, .id = id }; + } }; diff --git a/src/main.zig b/src/main.zig index e0fa65c..ccef19a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,5 +1,28 @@ const std = @import("std"); const wayland = @import("root.zig"); + +const Conn = wayland.Conn; +const Context = wayland.Context(&.{ + // Core protocol + wayland.core.Registry, + wayland.core.Compositor, + // wayland.core.ShmPool, + wayland.core.Shm, + // wayland.core.Surface, + // wayland.core.Buffer, + wayland.core.Seat, + // wayland.core.Pointer, + // wayland.core.Keyboard, + // wayland.core.Touch, + + // XDG Shell protocol + wayland.xdg.WmBase, + + // XDG Decoration + // wayland.zxdg.DecorationManagerV1, + // wayland.zxdg.TopLevelDecorationV1, +}); + const font8x8 = @cImport({ @cInclude("font8x8.h"); }); @@ -37,91 +60,25 @@ pub fn main() !void { const display_path = try wayland.getDisplayPath(gpa); defer gpa.free(display_path); - var conn = try wayland.Conn.init(gpa, display_path); + var conn = try Conn.init(gpa, display_path); defer conn.deinit(); - // Create an id pool to allocate ids for us - var id_pool = wayland.IdPool{}; + var ctx = try Context.init(&conn); - const ids = try wayland.registerGlobals(gpa, &id_pool, conn.socket, &.{ - wayland.core.Shm, - wayland.core.Compositor, - wayland.xdg.WmBase, - wayland.core.Seat, - wayland.zxdg.DecorationManagerV1, - wayland.zwp.TextInputManagerV3, - }); + const compositor = try ctx.getGlobal(wayland.core.Compositor); + const surface = try compositor.create_surface(); - const DISPLAY_ID = 1; - const shm_id = ids[0] orelse return error.NeccessaryWaylandExtensionMissing; - const compositor_id = ids[1] orelse return error.NeccessaryWaylandExtensionMissing; - const xdg_wm_base_id = ids[2] orelse return error.NeccessaryWaylandExtensionMissing; - const wl_seat_id = ids[3] orelse return error.NeccessaryWaylandExtensionMissing; - const zwp_text_input_manager_v3 = ids[5] orelse return error.NeccessaryWaylandExtensionMissing; + const xdg_wm_base = try ctx.getGlobal(wayland.xdg.WmBase); + const xdg_surface = try xdg_wm_base.get_xdg_surface(surface); + const xdg_toplevel = try xdg_surface.get_toplevel(); - const surface_id = id_pool.create(); - try conn.send( - wayland.core.Compositor.Request, - compositor_id, - .{ .create_surface = .{ - .new_id = surface_id, - } }, - ); + try surface.commit(); - const xdg_surface_id = id_pool.create(); - try conn.send( - wayland.xdg.WmBase.Request, - xdg_wm_base_id, - .{ .get_xdg_surface = .{ - .id = xdg_surface_id, - .surface = surface_id, - } }, - ); + const display = try ctx.getGlobal(wayland.core.Display); + const registry_done = try display.sync(); - const xdg_toplevel_id = id_pool.create(); - try conn.send( - wayland.xdg.Surface.Request, - xdg_surface_id, - .{ .get_toplevel = .{ - .id = xdg_toplevel_id, - } }, - ); - - const zwp_text_input_v3_id = id_pool.create(); - try conn.send( - wayland.zwp.TextInputManagerV3.Request, - zwp_text_input_manager_v3, - .{ .get_text_input = .{ - .id = zwp_text_input_v3_id, - .seat = wl_seat_id, - } }, - ); - - var zxdg_toplevel_decoration_id_opt: ?u32 = null; - if (ids[4]) |zxdg_decoration_manager_id| { - zxdg_toplevel_decoration_id_opt = id_pool.create(); - try conn.send( - wayland.zxdg.DecorationManagerV1.Request, - zxdg_decoration_manager_id, - .{ .get_toplevel_decoration = .{ - .new_id = zxdg_toplevel_decoration_id_opt.?, - .toplevel = xdg_toplevel_id, - } }, - ); - } - - try conn.send( - wayland.core.Surface.Request, - surface_id, - wayland.core.Surface.Request.commit, - ); - - const registry_done_id = id_pool.create(); - try conn.send( - wayland.core.Display.Request, - DISPLAY_ID, - .{ .sync = .{ .callback = registry_done_id } }, - ); + const shm = try ctx.getGlobal(wayland.core.Shm); + const seat = try ctx.getGlobal(wayland.core.Seat); var done = false; var surface_configured = false; @@ -129,34 +86,25 @@ pub fn main() !void { while (!done or !surface_configured) { const header, const body = try conn.recv(); - if (header.object_id == xdg_surface_id) { + if (header.object_id == xdg_surface.id) { const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body); switch (event) { .configure => |conf| { - try conn.send( - wayland.xdg.Surface.Request, - xdg_surface_id, - .{ .ack_configure = .{ - .serial = conf.serial, - } }, - ); + try xdg_surface.ack_configure(conf.serial); surface_configured = true; }, } - } else if (zxdg_toplevel_decoration_id_opt != null and header.object_id == zxdg_toplevel_decoration_id_opt.?) { - const event = try wayland.deserialize(wayland.zxdg.ToplevelDecorationV1.Event, header, body); - std.debug.print("<- zxdg_toplevel_decoration@{}\n", .{event}); - } else if (header.object_id == xdg_toplevel_id) { + } else if (header.object_id == xdg_toplevel.id) { const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body); std.debug.print("<- {}\n", .{event}); - } else if (header.object_id == registry_done_id) { + } else if (header.object_id == registry_done) { done = true; - } else if (header.object_id == shm_id) { + } else if (header.object_id == shm.id) { const event = try wayland.deserialize(wayland.core.Shm.Event, header, body); switch (event) { .format => |format| std.debug.print("<- format {} {}\n", .{ format.format, std.zig.fmtEscapes(std.mem.asBytes(&format.format)) }), } - } else if (header.object_id == wl_seat_id) { + } else if (header.object_id == seat.id) { const event = try wayland.deserialize(wayland.core.Seat.Event, header, body); switch (event) { .capabilities => |capabilities| { @@ -168,450 +116,17 @@ pub fn main() !void { std.debug.print("<- wl_seat.name = {s}\n", .{name.name}); }, } - } else if (header.object_id == DISPLAY_ID) { + } else if (header.object_id == display.id) { const event = try wayland.deserialize(wayland.core.Display.Event, header, body); switch (event) { .@"error" => |err| std.debug.print("<- error({}): {} {s}\n", .{ err.object_id, err.code, err.message }), .delete_id => |id| { std.debug.print("id {} deleted\n", .{id}); - id_pool.destroy(id.id); + conn.id_pool.destroy(id.id); }, } } else { std.debug.print("{} {x} \"{}\"\n", .{ header.object_id, header.size_and_opcode.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(body)) }); } } - - var wl_touch_id_opt: ?u32 = null; - var wl_keyboard_id_opt: ?u32 = null; - if (seat_capabilties) |caps| { - if (caps.touch) { - wl_touch_id_opt = id_pool.create(); - std.debug.print("wl touch id: {}\n", .{wl_touch_id_opt.?}); - try conn.send( - wayland.core.Seat.Request, - wl_seat_id, - .{ .get_touch = .{ - .new_id = wl_touch_id_opt.?, - } }, - ); - } - if (caps.keyboard) { - wl_keyboard_id_opt = id_pool.create(); - std.debug.print("wl keyboard id: {}\n", .{wl_keyboard_id_opt.?}); - try conn.send( - wayland.core.Seat.Request, - wl_seat_id, - .{ .get_keyboard = .{ - .new_id = wl_keyboard_id_opt.?, - } }, - ); - } - } - const wl_touch_id = wl_touch_id_opt orelse return error.MissingTouch; - const wl_keyboard_id = wl_keyboard_id_opt orelse return error.MissingKeyboard; - - // allocate a shared memory file for display purposes - const framebuffer_size = [2]u32{ 128, 128 }; - const pool_file_len = 1024 * framebuffer_size[0] * framebuffer_size[1] * @sizeOf(Pixel); - - const pool_fd = try std.os.memfd_create("my-wayland-framebuffer", 0); - try std.os.ftruncate(pool_fd, pool_file_len); - const pool_bytes = try std.os.mmap(null, pool_file_len, std.os.PROT.READ | std.os.PROT.WRITE, std.os.MAP.SHARED, pool_fd, 0); - var pool_fixed_buffer_allocator = std.heap.FixedBufferAllocator.init(pool_bytes); - var pool_general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){ .backing_allocator = pool_fixed_buffer_allocator.allocator() }; - const pool_alloc = pool_general_purpose_allocator.allocator(); - - const framebuffer = try pool_alloc.alloc(Pixel, framebuffer_size[0] * framebuffer_size[1]); - - // put some interesting colors into the framebuffer - renderGradient(framebuffer, framebuffer_size); - - const wl_shm_pool_id = id_pool.create(); - { - std.debug.print("framebuffer_fd: {}\n", .{pool_fd}); - try conn.send( - wayland.core.Shm.Request, - shm_id, - .{ .create_pool = .{ - .new_id = wl_shm_pool_id, - .fd = @enumFromInt(pool_fd), - .size = pool_file_len, - } }, - ); - } - - var framebuffers = std.AutoHashMap(u32, []Pixel).init(gpa); - defer framebuffers.deinit(); - try framebuffers.put(wl_shm_pool_id, framebuffer); - - const wl_buffer_id = id_pool.create(); - try conn.send( - wayland.core.ShmPool.Request, - wl_shm_pool_id, - .{ .create_buffer = .{ - .new_id = wl_buffer_id, - .offset = 0, - .width = framebuffer_size[0], - .height = framebuffer_size[1], - .stride = framebuffer_size[0] * @sizeOf([4]u8), - .format = .argb8888, - } }, - ); - - try conn.send( - wayland.core.Surface.Request, - surface_id, - .{ .attach = .{ - .buffer = wl_buffer_id, - .x = 0, - .y = 0, - } }, - ); - - try conn.send( - wayland.core.Surface.Request, - surface_id, - .{ .damage = .{ - .x = 0, - .y = 0, - .width = std.math.maxInt(i32), - .height = std.math.maxInt(i32), - } }, - ); - - try conn.send( - wayland.core.Surface.Request, - surface_id, - wayland.core.Surface.Request.commit, - ); - - var window_size: [2]u32 = [2]u32{ @intCast(framebuffer_size[0]), @intCast(framebuffer_size[1]) }; - - var scrollpos: [2]i32 = .{ 10, 10 }; - var touchstart: ?[2]i32 = null; - - var running = true; - while (running) { - const header, const body = try conn.recv(); - - if (header.object_id == xdg_surface_id) { - const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body); - switch (event) { - .configure => |conf| { - try conn.send( - wayland.xdg.Surface.Request, - xdg_surface_id, - .{ .ack_configure = .{ - .serial = conf.serial, - } }, - ); - - const new_buffer_id = id_pool.create(); - const new_framebuffer = try pool_alloc.alloc(Pixel, window_size[0] * window_size[1]); - try framebuffers.put(new_buffer_id, new_framebuffer); - - // put some interesting colors into the new_framebuffer - renderGradient(new_framebuffer, window_size); - - // blit some characters - renderText(new_framebuffer, window_size, .{ @intCast(scrollpos[0]), @intCast(scrollpos[1]) }, "Hello, World!"); - - try conn.send( - wayland.core.ShmPool.Request, - wl_shm_pool_id, - .{ .create_buffer = .{ - .new_id = new_buffer_id, - .offset = @intCast(@intFromPtr(new_framebuffer.ptr) - @intFromPtr(pool_bytes.ptr)), - .width = @intCast(window_size[0]), - .height = @intCast(window_size[1]), - .stride = @as(i32, @intCast(window_size[0])) * @sizeOf([4]u8), - .format = .argb8888, - } }, - ); - - try conn.send( - wayland.core.Surface.Request, - surface_id, - .{ .attach = .{ - .buffer = new_buffer_id, - .x = 0, - .y = 0, - } }, - ); - - try conn.send( - wayland.core.Surface.Request, - surface_id, - .{ .damage = .{ - .x = 0, - .y = 0, - .width = std.math.maxInt(i32), - .height = std.math.maxInt(i32), - } }, - ); - - // commit the configuration - try conn.send( - wayland.core.Surface.Request, - surface_id, - wayland.core.Surface.Request.commit, - ); - }, - } - } else if (header.object_id == xdg_toplevel_id) { - const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body); - switch (event) { - .configure => |conf| { - std.debug.print("<- xdg_toplevel@{} configure <{}, {}> {any}\n", .{ header.object_id, conf.width, conf.height, conf.states }); - window_size = .{ - @intCast(conf.width), - @intCast(conf.height), - }; - }, - .close => running = false, - else => |tag| std.debug.print("<- xdg_toplevel@{} {s} {}\n", .{ header.object_id, @tagName(tag), event }), - } - } else if (header.object_id == xdg_wm_base_id) { - const event = try wayland.deserialize(wayland.xdg.WmBase.Event, header, body); - switch (event) { - .ping => |ping| { - try conn.send( - wayland.xdg.WmBase.Request, - xdg_wm_base_id, - .{ .pong = .{ - .serial = ping.serial, - } }, - ); - }, - } - } else if (header.object_id == wl_touch_id) { - const event = try wayland.deserialize(wayland.core.Touch.Event, header, body); - std.debug.print("<- wl_touch@{}\n", .{event}); - event: { - switch (event) { - .down => |down| { - if (down.id != 0) break :event; - touchstart = .{ @intCast(down.x), @intCast(down.y) }; - }, - .motion => |motion| { - if (motion.id != 0) break :event; - if (touchstart) |start| { - scrollpos[0] = motion.x - start[0]; - scrollpos[1] = motion.y - start[1]; - } - }, - .up => |up| { - if (up.id != 0) break :event; - touchstart = null; - }, - .frame => { - const new_buffer_id = id_pool.create(); - const new_framebuffer = try pool_alloc.alloc(Pixel, window_size[0] * window_size[1]); - try framebuffers.put(new_buffer_id, new_framebuffer); - - // put some interesting colors into the new_framebuffer - renderGradient(new_framebuffer, window_size); - - // blit some characters - renderText(new_framebuffer, window_size, .{ @intCast(@divTrunc(scrollpos[0], 100)), @intCast(@divTrunc(scrollpos[1], 100)) }, "Hello, World!"); - - try conn.send( - wayland.core.ShmPool.Request, - wl_shm_pool_id, - .{ .create_buffer = .{ - .new_id = new_buffer_id, - .offset = @intCast(@intFromPtr(new_framebuffer.ptr) - @intFromPtr(pool_bytes.ptr)), - .width = @intCast(window_size[0]), - .height = @intCast(window_size[1]), - .stride = @as(i32, @intCast(window_size[0])) * @sizeOf([4]u8), - .format = .argb8888, - } }, - ); - - try conn.send( - wayland.core.Surface.Request, - surface_id, - .{ .attach = .{ - .buffer = new_buffer_id, - .x = 0, - .y = 0, - } }, - ); - - try conn.send( - wayland.core.Surface.Request, - surface_id, - .{ .damage = .{ - .x = 0, - .y = 0, - .width = std.math.maxInt(i32), - .height = std.math.maxInt(i32), - } }, - ); - - // commit the configuration - try conn.send( - wayland.core.Surface.Request, - surface_id, - wayland.core.Surface.Request.commit, - ); - }, - else => {}, - } - } - } else if (header.object_id == wl_keyboard_id) { - const event = try wayland.deserialize(wayland.core.Keyboard.Event, header, body); - switch (event) { - .keymap => |keymap| { - const fd = conn.fd_queue.orderedRemove(0); - std.debug.print("keymap format={}, size={}, fd={}\n", .{ - keymap.format, - keymap.size, - fd, - }); - const mem = try std.os.mmap( - null, - keymap.size, - std.os.PROT.READ, - std.os.MAP.PRIVATE, - fd, - 0, - ); - std.debug.print("---START xkb file---\n{s}\n---END xkb file---\n", .{mem}); - }, - .modifiers => |mods| { - std.debug.print("mod event: {}\n", .{mods}); - }, - .key => |key| { - std.debug.print("key event: {}\n", .{key}); - const new_buffer_id, const new_framebuffer = try getFramebuffer(&framebuffers, &id_pool, pool_alloc, window_size); - - // put some interesting colors into the new_framebuffer - renderGradient(new_framebuffer, window_size); - - // blit some characters - renderText(new_framebuffer, window_size, .{ 10, 10 }, "Hello, World!"); - - try conn.send( - wayland.core.ShmPool.Request, - wl_shm_pool_id, - .{ .create_buffer = .{ - .new_id = new_buffer_id, - .offset = @intCast(@intFromPtr(new_framebuffer.ptr) - @intFromPtr(pool_bytes.ptr)), - .width = @intCast(window_size[0]), - .height = @intCast(window_size[1]), - .stride = @as(i32, @intCast(window_size[0])) * @sizeOf([4]u8), - .format = .argb8888, - } }, - ); - - try conn.send( - wayland.core.Surface.Request, - surface_id, - .{ .attach = .{ - .buffer = new_buffer_id, - .x = 0, - .y = 0, - } }, - ); - - try conn.send( - wayland.core.Surface.Request, - surface_id, - .{ .damage = .{ - .x = 0, - .y = 0, - .width = std.math.maxInt(i32), - .height = std.math.maxInt(i32), - } }, - ); - - // commit the configuration - try conn.send( - wayland.core.Surface.Request, - surface_id, - wayland.core.Surface.Request.commit, - ); - }, - else => { - std.debug.print("<- wl_keyboard@{}\n", .{event}); - }, - } - } else if (framebuffers.get(header.object_id)) |framebuffer_slice| { - const event = try wayland.deserialize(wayland.core.Buffer.Event, header, body); - switch (event) { - .release => { - _ = framebuffers.remove(header.object_id); - pool_alloc.free(framebuffer_slice); - }, - } - } else if (header.object_id == DISPLAY_ID) { - const event = try wayland.deserialize(wayland.core.Display.Event, header, body); - switch (event) { - .@"error" => |err| std.debug.print("<- error({}): {} {s}\n", .{ err.object_id, err.code, err.message }), - .delete_id => |id| id_pool.destroy(id.id), - } - } else { - std.debug.print("{} {x} \"{}\"\n", .{ header.object_id, header.size_and_opcode.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(body)) }); - } - } -} - -fn cmsg(comptime T: type) type { - const padding_size = (@sizeOf(T) + @sizeOf(c_long) - 1) & ~(@as(usize, @sizeOf(c_long)) - 1); - return extern struct { - len: c_ulong = @sizeOf(@This()) - padding_size, - level: c_int, - type: c_int, - data: T, - _padding: [padding_size]u8 align(1) = [_]u8{0} ** padding_size, - }; -} - -fn getFramebuffer(framebuffers: *std.AutoHashMap(u32, []Pixel), id_pool: *wayland.IdPool, pool_alloc: std.mem.Allocator, fb_size: [2]u32) !struct { u32, []Pixel } { - const new_buffer_id = id_pool.create(); - const new_framebuffer = try pool_alloc.alloc(Pixel, fb_size[0] * fb_size[1]); - try framebuffers.put(new_buffer_id, new_framebuffer); - return .{ new_buffer_id, new_framebuffer }; -} - -fn renderGradient(framebuffer: []Pixel, fb_size: [2]u32) void { - for (0..fb_size[1]) |y| { - const row = framebuffer[y * fb_size[0] .. (y + 1) * fb_size[0]]; - for (row, 0..fb_size[0]) |*pixel, x| { - pixel.* = .{ - @truncate(x), - @truncate(y), - 0x00, - 0xFF, - }; - } - } -} - -fn textWidth(str: []const u8) usize { - return std.unicode.utf8CountCodepoints(str) catch 0; // incorrect, but I'm going with it -} - -fn renderText(framebuffer: []Pixel, fb_size: [2]u32, pos: [2]usize, str: []const u8) void { - const left, const top = pos; - const bot = @min(top + 8, fb_size[1]); - const right = @min(left + textWidth(str) * 8, fb_size[0]); - - for (top..bot) |y| { - const row = framebuffer[y * fb_size[0] .. (y + 1) * fb_size[0]]; - for (row[left..right], left..right) |*pixel, x| { - const col = ((x - left) / 8); - const which_char = str[col]; - if (!std.ascii.isPrint(which_char)) continue; - const char = font8x8.font8x8_basic[which_char]; - const line = char[(y - top) % 8]; - if ((line >> @intCast((x - left) % 8)) & 0x1 != 0) { - pixel.* = toPixel(Theme.foreground); - } else { - pixel.* = toPixel(Theme.background); - } - } - } } diff --git a/src/root.zig b/src/root.zig index 90126bc..79b7bf5 100644 --- a/src/root.zig +++ b/src/root.zig @@ -407,78 +407,6 @@ pub const IdPool = struct { } }; -pub fn registerGlobals(alloc: std.mem.Allocator, id_pool: *IdPool, socket: std.net.Stream, comptime T: []const type) ![T.len]?u32 { - const Item = struct { version: u32, index: u32 }; - const Pair = struct { []const u8, Item }; - comptime var kvs_list: []const Pair = &[_]Pair{}; - inline for (T, 0..) |t, i| { - kvs_list = kvs_list ++ &[_]Pair{.{ t.INTERFACE, .{ .version = t.VERSION, .index = i } }}; - } - const map = std.ComptimeStringMap(Item, kvs_list); - - const registry_id = id_pool.create(); - { - var buffer: [5]u32 = undefined; - const message = try serialize(core.Display.Request, &buffer, 1, .{ .get_registry = .{ .registry = registry_id } }); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } - - const registry_done_id = id_pool.create(); - { - var buffer: [5]u32 = undefined; - const message = try serialize(core.Display.Request, &buffer, 1, .{ .sync = .{ .callback = registry_done_id } }); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } - - var ids: [T.len]?u32 = [_]?u32{null} ** T.len; - var message_buffer = std.ArrayList(u32).init(alloc); - defer message_buffer.deinit(); - while (true) { - var header: Header = undefined; - const header_bytes_read = try socket.readAll(std.mem.asBytes(&header)); - if (header_bytes_read < @sizeOf(Header)) break; - - try message_buffer.resize((header.size_and_opcode.size - @sizeOf(Header)) / @sizeOf(u32)); - const bytes_read = try socket.readAll(std.mem.sliceAsBytes(message_buffer.items)); - message_buffer.shrinkRetainingCapacity(bytes_read / @sizeOf(u32)); - - if (header.object_id == registry_id) { - const event = try deserialize(core.Registry.Event, header, message_buffer.items); - switch (event) { - .global => |global| { - var buffer: [20]u32 = undefined; - if (map.get(global.interface)) |item| { - if (global.version < item.version) { - // TODO: Add diagnostics API - return error.OutdatedCompositorProtocol; - } - const new_id = id_pool.create(); - ids[item.index] = new_id; - const message = try serialize(core.Registry.Request, &buffer, registry_id, .{ .bind = .{ - .name = global.name, - .interface = global.interface, - .version = item.version, - .new_id = new_id, - } }); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } - }, - .global_remove => {}, - } - } else if (header.object_id == registry_done_id) { - break; - } else { - std.log.info("{} {x} \"{}\"", .{ - header.object_id, - header.size_and_opcode.opcode, - std.zig.fmtEscapes(std.mem.sliceAsBytes(message_buffer.items)), - }); - } - } - - return ids; -} - fn cmsg(comptime T: type) type { const padding_size = (@sizeOf(T) + @sizeOf(c_long) - 1) & ~(@as(usize, @sizeOf(c_long)) - 1); return extern struct { @@ -496,6 +424,7 @@ pub const Conn = struct { recv_buffer: []u32, fd_queue: std.ArrayListUnmanaged(std.os.fd_t), socket: std.net.Stream, + id_pool: IdPool, pub fn init(alloc: std.mem.Allocator, display_path: []const u8) !Conn { const send_buffer = try alloc.alloc(u32, 16); @@ -506,6 +435,7 @@ pub const Conn = struct { .recv_buffer = recv_buffer, .fd_queue = .{}, .socket = try std.net.connectUnixSocket(display_path), + .id_pool = .{}, }; } @@ -623,24 +553,96 @@ pub const Conn = struct { } }; -// pub const XKBKeymap = struct { -// keycodes: Keycodes, -// types: Types, -// compatability: Compatability, -// symbols: Symbols, -// -// const Keycodes = struct { -// min: usize, -// max: usize, -// map: std.AutoHashMap(usize, usize), -// indicators: std.StringHashMap(usize), -// }; -// -// const ISOKEY = [4]u8; -// -// const Types = struct {}; -// const Compatability = struct {}; -// const Symbols = struct {}; -// -// pub fn parse() {} -// }; +pub fn Context(comptime T: []const type) type { + const Item = struct { version: u32, index: u32 }; + const Pair = struct { []const u8, Item }; + comptime var kvs_list: []const Pair = &[_]Pair{}; + inline for (T, 0..) |t, i| { + kvs_list = kvs_list ++ &[_]Pair{.{ t.INTERFACE, .{ .version = t.VERSION, .index = i } }}; + } + const stringToType = std.ComptimeStringMap(Item, kvs_list); + + return struct { + conn: *Conn, + global_ids: [T.len]?u32, + + pub fn init(conn: *Conn) !@This() { + var ctx = @This(){ + .conn = conn, + .global_ids = [_]?u32{null} ** T.len, + }; + try ctx.registerGlobals(); + return ctx; + } + + pub fn getGlobal(ctx: *@This(), comptime G: type) !G { + if (G == core.Display) return .{ .id = 1, .conn = ctx.conn }; + const g = stringToType.get(G.INTERFACE) orelse return error.NoSuchGlobal; + const id = ctx.global_ids[g.index] orelse return error.NotAGlobal; + return G.init(ctx.conn, id); + } + + pub fn registerGlobals(ctx: *@This()) !void { + // ![T.len]?u32 { + const registry_id = ctx.conn.id_pool.create(); + { + var buffer: [5]u32 = undefined; + const message = try serialize(core.Display.Request, &buffer, 1, .{ .get_registry = .{ .registry = registry_id } }); + try ctx.conn.socket.writeAll(std.mem.sliceAsBytes(message)); + } + + const registry_done_id = ctx.conn.id_pool.create(); + { + var buffer: [5]u32 = undefined; + const message = try serialize(core.Display.Request, &buffer, 1, .{ .sync = .{ .callback = registry_done_id } }); + try ctx.conn.socket.writeAll(std.mem.sliceAsBytes(message)); + } + + // var ids: [T.len]?u32 = [_]?u32{null} ** T.len; + var message_buffer = std.ArrayList(u32).init(ctx.conn.allocator); + defer message_buffer.deinit(); + while (true) { + var header: Header = undefined; + const header_bytes_read = try ctx.conn.socket.readAll(std.mem.asBytes(&header)); + if (header_bytes_read < @sizeOf(Header)) break; + + try message_buffer.resize((header.size_and_opcode.size - @sizeOf(Header)) / @sizeOf(u32)); + const bytes_read = try ctx.conn.socket.readAll(std.mem.sliceAsBytes(message_buffer.items)); + message_buffer.shrinkRetainingCapacity(bytes_read / @sizeOf(u32)); + + if (header.object_id == registry_id) { + const event = try deserialize(core.Registry.Event, header, message_buffer.items); + switch (event) { + .global => |global| { + var buffer: [20]u32 = undefined; + if (stringToType.get(global.interface)) |item| { + if (global.version < item.version) { + // TODO: Add diagnostics API + return error.OutdatedCompositorProtocol; + } + const new_id = ctx.conn.id_pool.create(); + ctx.global_ids[item.index] = new_id; + const message = try serialize(core.Registry.Request, &buffer, registry_id, .{ .bind = .{ + .name = global.name, + .interface = global.interface, + .version = item.version, + .new_id = new_id, + } }); + try ctx.conn.socket.writeAll(std.mem.sliceAsBytes(message)); + } + }, + .global_remove => {}, + } + } else if (header.object_id == registry_done_id) { + break; + } else { + std.log.info("{} {x} \"{}\"", .{ + header.object_id, + header.size_and_opcode.opcode, + std.zig.fmtEscapes(std.mem.sliceAsBytes(message_buffer.items)), + }); + } + } + } + }; +} diff --git a/src/xdg.zig b/src/xdg.zig index 36ab603..f205f5d 100644 --- a/src/xdg.zig +++ b/src/xdg.zig @@ -1,3 +1,6 @@ +const core = @import("core.zig"); +const Conn = @import("root.zig").Conn; + pub const WmBase = struct { pub const INTERFACE = "xdg_wm_base"; pub const VERSION = 2; @@ -40,6 +43,26 @@ pub const WmBase = struct { invalid_positioner, unresponsive, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) WmBase { + return .{ .conn = conn, .id = id }; + } + + pub fn get_xdg_surface(base: WmBase, surface: core.Surface) !Surface { + const new_id = base.conn.id_pool.create(); + try base.conn.send( + Request, + base.id, + .{ .get_xdg_surface = .{ + .id = new_id, + .surface = surface.id, + } }, + ); + return Surface.init(base.conn, new_id); + } }; pub const Surface = struct { @@ -77,6 +100,35 @@ pub const Surface = struct { invalid_size, defunct_role_object, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Surface { + return .{ .conn = conn, .id = id }; + } + + pub fn get_toplevel(surface: Surface) !Toplevel { + const new_id = surface.conn.id_pool.create(); + try surface.conn.send( + Request, + surface.id, + .{ .get_toplevel = .{ + .id = new_id, + } }, + ); + return Toplevel.init(surface.conn, new_id); + } + + pub fn ack_configure(surface: Surface, serial: u32) !void { + try surface.conn.send( + Request, + surface.id, + .{ .ack_configure = .{ + .serial = serial, + } }, + ); + } }; pub const Toplevel = struct { @@ -168,4 +220,11 @@ pub const Toplevel = struct { defunct_role_object, _, }; + + conn: *Conn, + id: u32, + + pub fn init(conn: *Conn, id: u32) Toplevel { + return .{ .conn = conn, .id = id }; + } };