f37ca90d
Fix clipboard send fd lifecycle
a73x 2026-04-09 18:32
diff --git a/src/wayland.zig b/src/wayland.zig index 98e3014..1da7ede 100644 --- a/src/wayland.zig +++ b/src/wayland.zig @@ -389,6 +389,29 @@ const ClipboardTransfer = struct { } }; fn serveOwnedSelectionRequest( alloc: std.mem.Allocator, text: ?[]const u8, mime_type: []const u8, fd: i32, ) !bool { var handed_off = false; defer { if (!handed_off) _ = c.close(fd); } if (text == null) return false; if (!(std.mem.eql(u8, mime_type, "text/plain;charset=utf-8") or std.mem.eql(u8, mime_type, "text/plain"))) return false; const transfer = try ClipboardTransfer.init(alloc, text.?, fd); errdefer alloc.free(transfer.payload); const thread = try std.Thread.spawn(.{}, ClipboardTransfer.run, .{transfer}); handed_off = true; thread.detach(); return true; } pub const Clipboard = struct { alloc: std.mem.Allocator, display: *wl.Display, @@ -478,20 +501,15 @@ pub const Clipboard = struct { } fn sendOwnedSelection(self: *Clipboard, mime_type: [*:0]const u8, fd: i32) void { if (!self.owned_selection.canServeMime(std.mem.span(mime_type))) return; const text = self.owned_selection.text orelse return; const transfer = ClipboardTransfer.init(self.alloc, text, fd) catch |err| { _ = serveOwnedSelectionRequest( self.alloc, self.owned_selection.text, std.mem.span(mime_type), fd, ) catch |err| { std.log.err("clipboard transfer setup failed: {any}", .{err}); _ = c.close(fd); return; }; const thread = std.Thread.spawn(.{}, ClipboardTransfer.run, .{transfer}) catch |err| { std.log.err("clipboard transfer thread spawn failed: {any}", .{err}); self.alloc.free(transfer.payload); _ = c.close(fd); return; }; thread.detach(); } }; @@ -985,6 +1003,40 @@ test "writeClipboardTransfer writes payload and closes fd" { try std.testing.expectEqualStrings("clipboard payload", out.items); } test "serveOwnedSelectionRequest closes fd for unsupported mime and missing text" { { const pipefds = try std.posix.pipe(); defer std.posix.close(pipefds[0]); try std.testing.expect(!try serveOwnedSelectionRequest( std.testing.allocator, null, "text/plain", pipefds[1], )); var buf: [1]u8 = undefined; const n = try std.posix.read(pipefds[0], &buf); try std.testing.expectEqual(@as(usize, 0), n); } { const pipefds = try std.posix.pipe(); defer std.posix.close(pipefds[0]); try std.testing.expect(!try serveOwnedSelectionRequest( std.testing.allocator, "payload", "text/html", pipefds[1], )); var buf: [1]u8 = undefined; const n = try std.posix.read(pipefds[0], &buf); try std.testing.expectEqual(@as(usize, 0), n); } } fn wmBaseListener(wm_base: *xdg.WmBase, event: xdg.WmBase.Event, _: *xdg.WmBase) void { switch (event) { .ping => |p| wm_base.pong(p.serial),