diff --git a/examples/01_client_connect.zig b/examples/01_client_connect.zig index 8e367a1..544d788 100644 --- a/examples/01_client_connect.zig +++ b/examples/01_client_connect.zig @@ -196,41 +196,16 @@ pub fn main() !void { const wl_shm_pool_id = id_pool.create(); { - var buffer: [10]u32 = undefined; - const message = try wayland.serialize( + std.debug.print("framebuffer_fd: {}\n", .{framebuffer_fd}); + try conn.send( wayland.core.Shm.Request, - &buffer, shm_id, .{ .create_pool = .{ .new_id = wl_shm_pool_id, + .fd = @enumFromInt(framebuffer_fd), .size = framebuffer_file_len, } }, ); - // Send the file descriptor through a control message - const message_bytes = std.mem.sliceAsBytes(message); - const msg_iov = [_]std.os.iovec_const{ - .{ - .iov_base = message_bytes.ptr, - .iov_len = message_bytes.len, - }, - }; - const control_message = cmsg(std.os.fd_t){ - .level = std.os.SOL.SOCKET, - .type = 0x01, // value of SCM_RIGHTS - .data = framebuffer_fd, - }; - const socket_message = std.os.msghdr_const{ - .name = null, - .namelen = 0, - .iov = &msg_iov, - .iovlen = msg_iov.len, - // .control = null, - // .controllen = 0, - .control = &control_message, - .controllen = @sizeOf(cmsg(std.os.fd_t)), - .flags = 0, - }; - _ = try std.os.sendmsg(conn.socket.handle, &socket_message, 0); } const wl_buffer_id = id_pool.create(); diff --git a/src/core.zig b/src/core.zig index df05c5a..2b36b9d 100644 --- a/src/core.zig +++ b/src/core.zig @@ -1,3 +1,5 @@ +const types = @import("types.zig"); + pub const Display = struct { pub const Request = union(enum) { sync: struct { @@ -90,7 +92,7 @@ pub const Shm = struct { create_pool: struct { new_id: u32, // file descriptors are sent through a control message - // fd: u32, + fd: types.Fd, size: u32, }, }; @@ -306,7 +308,7 @@ pub const Keyboard = struct { pub const Event = union(enum) { keymap: struct { format: KeymapFormat, - // fd: u32, + fd: types.Fd, size: u32, }, enter: struct { diff --git a/src/main.zig b/src/main.zig index 8ca6676..63eae7c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3,6 +3,7 @@ const testing = std.testing; pub const core = @import("./core.zig"); pub const xdg = @import("./xdg.zig"); pub const zxdg = @import("./zxdg.zig"); +pub const types = @import("./types.zig"); pub fn getDisplayPath(gpa: std.mem.Allocator) ![]u8 { const xdg_runtime_dir_path = try std.process.getEnvVarOwned(gpa, "XDG_RUNTIME_DIR"); @@ -112,6 +113,7 @@ pub fn deserializeArguments(comptime Signature: type, buffer: []const u32) !Sign var result: Signature = undefined; var pos: usize = 0; inline for (std.meta.fields(Signature)) |field| { + if (field.type == types.Fd) continue; // Must be handled switch (@typeInfo(field.type)) { .Int => |int_info| switch (int_info.signedness) { .signed => @field(result, field.name) = try readInt(buffer, &pos), @@ -169,11 +171,36 @@ pub fn calculateSerializedWordLen(comptime Signature: type, message: Signature) return pos; } +pub fn countFds(comptime Signature: type) usize { + if (Signature == void) return 0; + var count: usize = 0; + inline for (std.meta.fields(Signature)) |field| { + if (field.type == types.Fd) { + count += 1; + } + } + return count; +} + +pub fn extractFds(comptime Signature: type, message: *const Signature) [countFds(Signature)]*const types.Fd { + if (Signature == void) return [_]*const types.Fd{}; + var fds: [countFds(Signature)]*const types.Fd = undefined; + var i: usize = 0; + inline for (std.meta.fields(Signature)) |field| { + if (field.type == types.Fd) { + fds[i] = &@field(message, field.name); + i += 1; + } + } + return fds; +} + /// Message must live until the iovec array is written. pub fn serializeArguments(comptime Signature: type, buffer: []u32, message: Signature) ![]u32 { if (Signature == void) return buffer[0..0]; var pos: usize = 0; inline for (std.meta.fields(Signature)) |field| { + if (field.type == types.Fd) continue; switch (@typeInfo(field.type)) { .Int => { if (pos >= buffer.len) return error.OutOfMemory; @@ -416,6 +443,17 @@ pub fn registerGlobals(alloc: std.mem.Allocator, id_pool: *IdPool, socket: std.n 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 { + 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, + }; +} + pub const Conn = struct { allocator: std.mem.Allocator, send_buffer: []u32, @@ -455,11 +493,40 @@ pub const Conn = struct { break msg; }; - try conn.socket.writeAll(std.mem.sliceAsBytes(msg)); + const msg_bytes = std.mem.sliceAsBytes(msg); + const msg_iov = [_]std.os.iovec_const{ + .{ + .iov_base = msg_bytes.ptr, + .iov_len = msg_bytes.len, + }, + }; + const fds = switch (message) { + inline else => |*payload| extractFds(@TypeOf(payload.*), payload), + }; + var ctrl_msgs: [fds.len]cmsg(std.os.fd_t) = undefined; + for (fds, 0..) |fdp, i| { + std.debug.print("fd {}: {}\n", .{ i, fdp.* }); + ctrl_msgs[i] = .{ + .level = std.os.SOL.SOCKET, + .type = 0x01, + .data = @intFromEnum(fdp.*), + }; + } + const socket_msg = std.os.msghdr_const{ + .name = null, + .namelen = 0, + .iov = &msg_iov, + .iovlen = msg_iov.len, + .control = &ctrl_msgs, + .controllen = @intCast(@sizeOf(cmsg(std.os.fd_t)) * ctrl_msgs.len), + .flags = 0, + }; + _ = try std.os.sendmsg(conn.socket.handle, &socket_msg, 0); } pub const Message = struct { Header, []const u32 }; pub fn recv(conn: *Conn) !Message { + // TODO: recvmesg and read fds var header: Header = undefined; const header_bytes_read = try conn.socket.readAll(std.mem.asBytes(&header)); if (header_bytes_read < @sizeOf(Header)) { diff --git a/src/types.zig b/src/types.zig new file mode 100644 index 0000000..7172de5 --- /dev/null +++ b/src/types.zig @@ -0,0 +1,2 @@ +const std = @import("std"); +pub const Fd = enum(std.os.fd_t) { _ };