From 8a7392dea729d3ed49a8bf8eee25906c4fd616ac Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Wed, 27 Aug 2025 09:02:43 +0200 Subject: Add ancillary data mechanism to send fds to compositor. Currently we are just attaching the fds to the object id. In theory this is not a valid implementation, since if we have more than MAX_FD file descriptors this will not work. But since this wont be the case in basically all cases of the wayland protocol, we can just ignore that for now. --- src/wl/display.zig | 87 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 27 deletions(-) (limited to 'src/wl/display.zig') diff --git a/src/wl/display.zig b/src/wl/display.zig index 3b5087d..c9e2e6e 100644 --- a/src/wl/display.zig +++ b/src/wl/display.zig @@ -86,7 +86,7 @@ pub fn request(self: *Self, object: anytype, args: @TypeOf(object.*).Request) !v const id: u32 = @truncate(object.handle); var size: u32 = 8; - comptime var fds: []std.posix.fd_t = &.{}; + comptime var count_fds = 0; inline for (data) |value| { switch (@TypeOf(value)) { @@ -97,7 +97,7 @@ pub fn request(self: *Self, object: anytype, args: @TypeOf(object.*).Request) !v const padding_size: usize = padding_len(u8, value.len + 1); size += @as(u32, @intCast(@sizeOf(u32) + (value.len + 1) + padding_size)); }, - wayland.types.Fd => |fd| fds = fds ++ .{ fd }, + wayland.types.Fd => count_fds += 1, else => |t| switch (@typeInfo(t)) { .pointer => |ptr| if (ptr.size == .slice) { const padding_size: usize = padding_len(ptr.child, value.len); @@ -108,14 +108,30 @@ pub fn request(self: *Self, object: anytype, args: @TypeOf(object.*).Request) !v } } - _ = try self.stream.writeAll(&std.mem.toBytes(id)); + { + var fds: [count_fds]std.posix.fd_t = undefined; + comptime var current_fd_index = 0; + + inline for (data) |value| { + switch (@TypeOf(value)) { + wayland.types.Fd => { + fds[current_fd_index] = value.fd; + current_fd_index += 1; + }, + else => {}, + } + } + + try ancillary_data.send_fds(self.stream.handle, &fds, std.mem.asBytes(&id)); + } + _ = try self.stream.writeAll(&std.mem.toBytes((size << 16) | opcode)); inline for (data) |value| { switch (@TypeOf(value)) { u32, i32 => _ = try self.stream.writeAll(std.mem.asBytes(&value)), wayland.Object.Ref => try self.stream.writeAll(std.mem.asBytes(&(@as(u32, @truncate(value))))), - ?wayland.Object.Ref => try self.stream.writeAll(std.mem.asBytes(&(@as(u32, 0)))), + ?wayland.Object.Ref => try self.stream.writeAll(std.mem.asBytes(&(@as(u32, @truncate(if (value) |v| v else 0))))), []const u8, []u8 => { const padding_size: usize = padding_len(u8, value.len + 1); _ = try self.stream.writeAll(std.mem.asBytes(&@as(u32, @truncate(value.len + 1)))); @@ -124,10 +140,10 @@ pub fn request(self: *Self, object: anytype, args: @TypeOf(object.*).Request) !v _ = try self.stream.writeAll(std.mem.asBytes(&@as(u8, 0))); } }, - wayland.types.Fd => {}, + wayland.types.Fd => { }, else => |t| switch (@typeInfo(t)) { .pointer => |ptr| if (ptr.size == .slice) { - const padding_size: usize = @mod(@as(usize, @bitCast(-@as(isize, @intCast(value.len * @sizeOf(ptr.child))))), 4); + const padding_size: usize = padding_len(ptr.child, value.len); _ = try self.stream.writeAll(std.mem.asBytes(&@as(u32, @truncate(value.len)))); _ = try self.stream.writeAll(std.mem.sliceAsBytes(value)); for (0..padding_size) |_| { @@ -139,10 +155,6 @@ pub fn request(self: *Self, object: anytype, args: @TypeOf(object.*).Request) !v } } - if (fds.len > 0) { - try ancillary_data.send_fds(self.stream.handle, fds); - } - return; } } @@ -150,7 +162,7 @@ pub fn request(self: *Self, object: anytype, args: @TypeOf(object.*).Request) !v return error.InvalidOpcode; } -fn read_event(self: *Self, ctx: *wayland.Context) !void { +fn read_event(self: *Self, ctx: *const wayland.Context) !void { const endian = @import("builtin").target.cpu.arch.endian(); const reader = self.stream.reader(); @@ -200,7 +212,7 @@ pub fn roundtrip(self: *Self, allocator: std.mem.Allocator) !void { fn err( self: *Self, - ctx: *wayland.Context, + ctx: *const wayland.Context, object: *wayland.Object, code: u32, message: []const u8, @@ -212,7 +224,7 @@ fn err( fn delete_id( self: *Self, - ctx: *wayland.Context, + ctx: *const wayland.Context, id: u32, ) void { _ = ctx; @@ -226,6 +238,7 @@ test "request" { req2: struct { wayland.Object.Ref }, req3: struct { []const u8 }, req4: struct { []const u16 }, + req5: struct { wayland.types.Fd }, }; pub const Events = wayland.EventSet(@This(), .{}); @@ -237,66 +250,86 @@ test "request" { } }; - const in, const out = try std.posix.pipe(); - defer std.posix.close(in); + var tmpdir = std.testing.tmpDir(.{}); + defer tmpdir.cleanup(); + + var pathbuf: [std.fs.max_path_bytes]u8 = undefined; + var dirbuf: [std.fs.max_path_bytes]u8 = undefined; + const path = try std.fmt.bufPrint(&pathbuf, "{s}/testing", .{try tmpdir.dir.realpath(".", &dirbuf)}); + + const address = try std.net.Address.initUnix(path); + var server = try address.listen(.{}); { var sample: Sample = .init(); - var displ: Self = .from_fd(out); + var displ: Self = try .open(path); defer displ.deinit(std.testing.allocator); try displ.init(std.testing.allocator, &.{}); try displ.request(&sample, .{ .req1 = .{432} }); try displ.request(&sample, .{ .req2 = .{sample.handle} }); try displ.request(&sample, .{ .req3 = .{"abcd"} }); try displ.request(&sample, .{ .req4 = .{&[_]u16{ 1, 32, 4, 5, 5 }} }); + try displ.request(&sample, .{ .req5 = .{ .{ .fd = displ.stream.handle } } }); } - const file = std.fs.File{ .handle = in }; + const conn = try server.accept(); + const file = conn.stream; var test_buffer: [64]u8 = undefined; - _ = try file.readAll(test_buffer[0..12]); + var size = try file.readAll(test_buffer[0..12]); try std.testing.expectEqualSlices( u8, &(std.mem.toBytes(@as(u32, 1)) ++ std.mem.toBytes(@as(u32, (12 << 16) | 1)) ++ std.mem.toBytes(@as(u32, 2))), - test_buffer[0..12], + test_buffer[0..size], ); - _ = try file.readAll(test_buffer[0..12]); + size = try file.readAll(test_buffer[0..12]); try std.testing.expectEqualSlices( u8, &(std.mem.toBytes(@as(u32, 42)) ++ std.mem.toBytes(@as(u32, (12 << 16) | 0)) ++ std.mem.toBytes(@as(u32, 432))), - test_buffer[0..12], + test_buffer[0..size], ); - _ = try file.readAll(test_buffer[0..12]); + size = try file.readAll(test_buffer[0..12]); try std.testing.expectEqualSlices( u8, &(std.mem.toBytes(@as(u32, 42)) ++ std.mem.toBytes(@as(u32, (12 << 16) | 1)) ++ std.mem.toBytes(@as(u32, 42))), - test_buffer[0..12], + test_buffer[0..size], ); - _ = try file.readAll(test_buffer[0..20]); + size = try file.readAll(test_buffer[0..20]); try std.testing.expectEqualSlices( u8, &(std.mem.toBytes(@as(u32, 42)) ++ std.mem.toBytes(@as(u32, (20 << 16) | 2)) ++ std.mem.toBytes(@as(u32, 5))) ++ "abcd\x00\x00\x00\x00", - test_buffer[0..20], + test_buffer[0..size], ); - _ = try file.readAll(test_buffer[0..24]); + size = try file.readAll(test_buffer[0..24]); try std.testing.expectEqualSlices( u8, &(std.mem.toBytes(@as(u32, 42)) ++ std.mem.toBytes(@as(u32, (24 << 16) | 3)) ++ std.mem.toBytes(@as(u32, 5))) ++ std.mem.toBytes([_]u16{ 1, 32, 4, 5, 5, 0 }), - test_buffer[0..24], + test_buffer[0..size], ); + + size = try file.readAll(test_buffer[0..8]); + try std.testing.expectEqualSlices( + u8, + &(std.mem.toBytes(@as(u32, 42)) ++ + std.mem.toBytes(@as(u32, (8 << 16) | 4))), + test_buffer[0..size], + ); + + size = try file.readAll(test_buffer[0..]); + try std.testing.expectEqual(0, size); } -- cgit v1.2.3-70-g09d2