From ea4a75cc76a5cf6328dc7f3b50d0475d39b2db40 Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Mon, 15 Jan 2024 20:40:50 -0700 Subject: [PATCH] feat: add wayland.Conn --- examples/01_client_connect.zig | 290 ++++++++++++--------------------- src/main.zig | 71 +++++++- 2 files changed, 172 insertions(+), 189 deletions(-) diff --git a/examples/01_client_connect.zig b/examples/01_client_connect.zig index 36085bb..8e367a1 100644 --- a/examples/01_client_connect.zig +++ b/examples/01_client_connect.zig @@ -9,13 +9,13 @@ pub fn main() !void { const display_path = try wayland.getDisplayPath(gpa); defer gpa.free(display_path); - const socket = try std.net.connectUnixSocket(display_path); - defer socket.close(); + var conn = try wayland.Conn.init(gpa, display_path); + defer conn.deinit(); // Create an id pool to allocate ids for us var id_pool = wayland.IdPool{}; - const ids = try wayland.registerGlobals(gpa, &id_pool, socket, &.{ + const ids = try wayland.registerGlobals(gpa, &id_pool, conn.socket, &.{ wayland.core.Shm, wayland.core.Compositor, wayland.xdg.WmBase, @@ -23,139 +23,101 @@ pub fn main() !void { wayland.zxdg.DecorationManagerV1, }); + 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 surface_id = id_pool.create(); - { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( - wayland.core.Compositor.Request, - &buffer, - compositor_id, - .{ .create_surface = .{ - .new_id = surface_id, - } }, - ); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } + try conn.send( + wayland.core.Compositor.Request, + compositor_id, + .{ .create_surface = .{ + .new_id = surface_id, + } }, + ); const xdg_surface_id = id_pool.create(); - { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( - wayland.xdg.WmBase.Request, - &buffer, - xdg_wm_base_id, - .{ .get_xdg_surface = .{ - .id = xdg_surface_id, - .surface = surface_id, - } }, - ); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } + try conn.send( + wayland.xdg.WmBase.Request, + xdg_wm_base_id, + .{ .get_xdg_surface = .{ + .id = xdg_surface_id, + .surface = surface_id, + } }, + ); const xdg_toplevel_id = id_pool.create(); - { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( - wayland.xdg.Surface.Request, - &buffer, - xdg_surface_id, - .{ .get_toplevel = .{ - .id = xdg_toplevel_id, - } }, - ); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } + try conn.send( + wayland.xdg.Surface.Request, + xdg_surface_id, + .{ .get_toplevel = .{ + .id = xdg_toplevel_id, + } }, + ); var zxdg_toplevel_decoration_id_opt: ?u32 = null; if (ids[4]) |zxdg_decoration_manager_id| { zxdg_toplevel_decoration_id_opt = id_pool.create(); - { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( - wayland.zxdg.DecorationManagerV1.Request, - &buffer, - zxdg_decoration_manager_id, - .{ .get_toplevel_decoration = .{ - .new_id = zxdg_toplevel_decoration_id_opt.?, - .toplevel = xdg_toplevel_id, - } }, - ); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } + 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, + } }, + ); } - { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( - wayland.core.Surface.Request, - &buffer, - surface_id, - wayland.core.Surface.Request.commit, - ); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } + try conn.send( + wayland.core.Surface.Request, + surface_id, + wayland.core.Surface.Request.commit, + ); const registry_done_id = id_pool.create(); - { - var buffer: [5]u32 = undefined; - const message = try wayland.serialize(wayland.core.Display.Request, &buffer, 1, .{ .sync = .{ .callback = registry_done_id } }); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } - - var message_buffer = std.ArrayList(u32).init(gpa); - defer message_buffer.deinit(); + try conn.send( + wayland.core.Display.Request, + DISPLAY_ID, + .{ .sync = .{ .callback = registry_done_id } }, + ); var done = false; var surface_configured = false; var seat_capabilties: ?wayland.core.Seat.Capability = null; while (!done or !surface_configured) { - var header: wayland.Header = undefined; - const header_bytes_read = try socket.readAll(std.mem.asBytes(&header)); - if (header_bytes_read < @sizeOf(wayland.Header)) { - return error.SocketClosed; - } - - try message_buffer.resize((header.size_and_opcode.size - @sizeOf(wayland.Header)) / @sizeOf(u32)); - const bytes_read = try socket.readAll(std.mem.sliceAsBytes(message_buffer.items)); - message_buffer.shrinkRetainingCapacity(bytes_read / @sizeOf(u32)); + const header, const body = try conn.recv(); if (header.object_id == xdg_surface_id) { - const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, message_buffer.items); + const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body); switch (event) { .configure => |conf| { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( + try conn.send( wayland.xdg.Surface.Request, - &buffer, xdg_surface_id, .{ .ack_configure = .{ .serial = conf.serial, } }, ); - try socket.writeAll(std.mem.sliceAsBytes(message)); 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, message_buffer.items); + 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) { - const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, message_buffer.items); + const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body); std.debug.print("<- {}\n", .{event}); } else if (header.object_id == registry_done_id) { done = true; } else if (header.object_id == shm_id) { - const event = try wayland.deserialize(wayland.core.Shm.Event, header, message_buffer.items); + 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) { - const event = try wayland.deserialize(wayland.core.Seat.Event, header, message_buffer.items); + const event = try wayland.deserialize(wayland.core.Seat.Event, header, body); switch (event) { .capabilities => |capabilities| { const cap: wayland.core.Seat.Capability = @bitCast(capabilities.capability); @@ -167,7 +129,7 @@ pub fn main() !void { }, } } else if (header.object_id == 1) { - const event = try wayland.deserialize(wayland.core.Display.Event, header, message_buffer.items); + 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| { @@ -176,7 +138,7 @@ pub fn main() !void { }, } } else { - std.debug.print("{} {x} \"{}\"\n", .{ header.object_id, header.size_and_opcode.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(message_buffer.items)) }); + std.debug.print("{} {x} \"{}\"\n", .{ header.object_id, header.size_and_opcode.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(body)) }); } } @@ -184,32 +146,26 @@ pub fn main() !void { var wl_keyboard_id_opt: ?u32 = null; if (seat_capabilties) |caps| { if (caps.pointer) { - var buffer: [10]u32 = undefined; wl_pointer_id_opt = id_pool.create(); std.debug.print("wl pointer id: {}\n", .{wl_pointer_id_opt.?}); - const message = try wayland.serialize( + try conn.send( wayland.core.Seat.Request, - &buffer, wl_seat_id, .{ .get_pointer = .{ .new_id = wl_pointer_id_opt.?, } }, ); - try socket.writeAll(std.mem.sliceAsBytes(message)); } if (caps.keyboard) { - var buffer: [10]u32 = undefined; wl_keyboard_id_opt = id_pool.create(); std.debug.print("wl keyboard id: {}\n", .{wl_keyboard_id_opt.?}); - const message = try wayland.serialize( + try conn.send( wayland.core.Seat.Request, - &buffer, wl_seat_id, .{ .get_keyboard = .{ .new_id = wl_keyboard_id_opt.?, } }, ); - try socket.writeAll(std.mem.sliceAsBytes(message)); } } const wl_pointer_id = wl_pointer_id_opt orelse return error.MissingPointer; @@ -274,111 +230,76 @@ pub fn main() !void { .controllen = @sizeOf(cmsg(std.os.fd_t)), .flags = 0, }; - _ = try std.os.sendmsg(socket.handle, &socket_message, 0); + _ = try std.os.sendmsg(conn.socket.handle, &socket_message, 0); } const wl_buffer_id = id_pool.create(); - { - std.debug.print("buffer id: {}\n", .{wl_buffer_id}); - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( - wayland.core.ShmPool.Request, - &buffer, - 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 socket.writeAll(std.mem.sliceAsBytes(message)); - } + 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, + } }, + ); - { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( - wayland.core.Surface.Request, - &buffer, - surface_id, - .{ .attach = .{ - .buffer = wl_buffer_id, - .x = 0, - .y = 0, - } }, - ); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } + try conn.send( + wayland.core.Surface.Request, + surface_id, + .{ .attach = .{ + .buffer = wl_buffer_id, + .x = 0, + .y = 0, + } }, + ); - { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( - wayland.core.Surface.Request, - &buffer, - surface_id, - .{ .damage = .{ - .x = 0, - .y = 0, - .width = std.math.maxInt(i32), - .height = std.math.maxInt(i32), - } }, - ); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } + try conn.send( + wayland.core.Surface.Request, + surface_id, + .{ .damage = .{ + .x = 0, + .y = 0, + .width = std.math.maxInt(i32), + .height = std.math.maxInt(i32), + } }, + ); - { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( - wayland.core.Surface.Request, - &buffer, - surface_id, - wayland.core.Surface.Request.commit, - ); - try socket.writeAll(std.mem.sliceAsBytes(message)); - } + try conn.send( + wayland.core.Surface.Request, + surface_id, + wayland.core.Surface.Request.commit, + ); var running = true; while (running) { - var header: wayland.Header = undefined; - const header_bytes_read = try socket.readAll(std.mem.asBytes(&header)); - if (header_bytes_read < @sizeOf(wayland.Header)) { - return error.SocketClosed; - } - - try message_buffer.resize((header.size_and_opcode.size - @sizeOf(wayland.Header)) / @sizeOf(u32)); - const bytes_read = try socket.readAll(std.mem.sliceAsBytes(message_buffer.items)); - message_buffer.shrinkRetainingCapacity(bytes_read / @sizeOf(u32)); + const header, const body = try conn.recv(); if (header.object_id == xdg_surface_id) { - const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, message_buffer.items); + const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body); switch (event) { .configure => |conf| { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( + try conn.send( wayland.xdg.Surface.Request, - &buffer, xdg_surface_id, .{ .ack_configure = .{ .serial = conf.serial, } }, ); - try socket.writeAll(std.mem.sliceAsBytes(message)); // commit the configuration - var buffer2: [10]u32 = undefined; - const message2 = try wayland.serialize( + try conn.send( wayland.core.Surface.Request, - &buffer2, surface_id, wayland.core.Surface.Request.commit, ); - try socket.writeAll(std.mem.sliceAsBytes(message2)); }, } } else if (header.object_id == xdg_toplevel_id) { - const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, message_buffer.items); + 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 }); @@ -387,29 +308,26 @@ pub fn main() !void { else => |tag| std.debug.print("<- xdg_toplevel@{} {s} {}\n", .{ header.object_id, @tagName(tag), event }), } } else if (header.object_id == wl_buffer_id) { - const event = try wayland.deserialize(wayland.core.Buffer.Event, header, message_buffer.items); + const event = try wayland.deserialize(wayland.core.Buffer.Event, header, body); std.debug.print("<- wl_buffer@{} {}\n", .{ header.object_id, event }); } else if (header.object_id == xdg_wm_base_id) { - const event = try wayland.deserialize(wayland.xdg.WmBase.Event, header, message_buffer.items); + const event = try wayland.deserialize(wayland.xdg.WmBase.Event, header, body); switch (event) { .ping => |ping| { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( + try conn.send( wayland.xdg.WmBase.Request, - &buffer, xdg_wm_base_id, .{ .pong = .{ .serial = ping.serial, } }, ); - try socket.writeAll(std.mem.sliceAsBytes(message)); }, } } else if (header.object_id == wl_pointer_id) { - const event = try wayland.deserialize(wayland.core.Pointer.Event, header, message_buffer.items); + const event = try wayland.deserialize(wayland.core.Pointer.Event, header, body); std.debug.print("<- wl_pointer@{}\n", .{event}); } else if (header.object_id == wl_keyboard_id) { - const event = try wayland.deserialize(wayland.core.Keyboard.Event, header, message_buffer.items); + const event = try wayland.deserialize(wayland.core.Keyboard.Event, header, body); switch (event) { // .keymap => |keymap| {}, else => { @@ -417,13 +335,13 @@ pub fn main() !void { }, } } else if (header.object_id == 1) { - const event = try wayland.deserialize(wayland.core.Display.Event, header, message_buffer.items); + 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(message_buffer.items)) }); + std.debug.print("{} {x} \"{}\"\n", .{ header.object_id, header.size_and_opcode.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(body)) }); } } } diff --git a/src/main.zig b/src/main.zig index 6fb6e8e..8ca6676 100644 --- a/src/main.zig +++ b/src/main.zig @@ -353,8 +353,6 @@ pub fn registerGlobals(alloc: std.mem.Allocator, id_pool: *IdPool, socket: std.n const Pair = struct { []const u8, Item }; comptime var kvs_list: []const Pair = &[_]Pair{}; inline for (T, 0..) |t, i| { - // if (!@hasField(t, "INTERFACE")) @compileError("Missing INTERFACE for " ++ @typeName(t)); - // if (!@hasField(t, "VERSION")) @compileError("Missing VERSION for " ++ @typeName(t)); kvs_list = kvs_list ++ &[_]Pair{.{ t.INTERFACE, .{ .version = t.VERSION, .index = i } }}; } const map = std.ComptimeStringMap(Item, kvs_list); @@ -407,9 +405,76 @@ pub fn registerGlobals(alloc: std.mem.Allocator, id_pool: *IdPool, socket: std.n } 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)) }); + std.log.info("{} {x} \"{}\"", .{ + header.object_id, + header.size_and_opcode.opcode, + std.zig.fmtEscapes(std.mem.sliceAsBytes(message_buffer.items)), + }); } } return ids; } + +pub const Conn = struct { + allocator: std.mem.Allocator, + send_buffer: []u32, + recv_buffer: []u32, + socket: std.net.Stream, + + pub fn init(alloc: std.mem.Allocator, display_path: []const u8) !Conn { + const send_buffer = try alloc.alloc(u32, 16); + const recv_buffer = try alloc.alloc(u32, 16); + return .{ + .allocator = alloc, + .send_buffer = send_buffer, + .recv_buffer = recv_buffer, + .socket = try std.net.connectUnixSocket(display_path), + }; + } + + pub fn deinit(conn: *Conn) void { + conn.allocator.free(conn.send_buffer); + conn.allocator.free(conn.recv_buffer); + conn.socket.close(); + } + + pub fn send(conn: *Conn, comptime Signature: type, id: u32, message: Signature) !void { + const msg = while (true) { + const msg = serialize( + Signature, + conn.send_buffer, + id, + message, + ) catch |e| switch (e) { + error.OutOfMemory => { + conn.send_buffer = try conn.allocator.realloc(conn.send_buffer, conn.send_buffer.len * 2); + continue; + }, + }; + + break msg; + }; + try conn.socket.writeAll(std.mem.sliceAsBytes(msg)); + } + + pub const Message = struct { Header, []const u32 }; + pub fn recv(conn: *Conn) !Message { + var header: Header = undefined; + const header_bytes_read = try conn.socket.readAll(std.mem.asBytes(&header)); + if (header_bytes_read < @sizeOf(Header)) { + return error.SocketClosed; + } + + const msg_size = (header.size_and_opcode.size - @sizeOf(Header)) / @sizeOf(u32); + if (msg_size > conn.recv_buffer.len) { + var new_size = conn.recv_buffer.len * 2; + while (new_size < msg_size) new_size *= 2; + conn.recv_buffer = try conn.allocator.realloc(conn.recv_buffer, new_size); + } + const bytes_read = try conn.socket.readAll(std.mem.sliceAsBytes(conn.recv_buffer[0..msg_size])); + const message = conn.recv_buffer[0 .. bytes_read / @sizeOf(u32)]; + + return .{ header, message }; + } +};