f37ca90d
Fix clipboard send fd lifecycle
a73x 2026-04-09 18:32
Commit message
src/wayland.zig
| Old | New | ||
|---|---|---|---|
| @@ -389,6 +389,29 @@ const ClipboardTransfer = struct { | |||
| 389 | } | 389 | } |
| 390 | }; | 390 | }; |
| 391 | 391 | ||
| 392 | fn serveOwnedSelectionRequest( | ||
| 393 | alloc: std.mem.Allocator, | ||
| 394 | text: ?[]const u8, | ||
| 395 | mime_type: []const u8, | ||
| 396 | fd: i32, | ||
| 397 | ) !bool { | ||
| 398 | var handed_off = false; | ||
| 399 | defer { | ||
| 400 | if (!handed_off) _ = c.close(fd); | ||
| 401 | } | ||
| 402 | |||
| 403 | if (text == null) return false; | ||
| 404 | if (!(std.mem.eql(u8, mime_type, "text/plain;charset=utf-8") or | ||
| 405 | std.mem.eql(u8, mime_type, "text/plain"))) return false; | ||
| 406 | |||
| 407 | const transfer = try ClipboardTransfer.init(alloc, text.?, fd); | ||
| 408 | errdefer alloc.free(transfer.payload); | ||
| 409 | const thread = try std.Thread.spawn(.{}, ClipboardTransfer.run, .{transfer}); | ||
| 410 | handed_off = true; | ||
| 411 | thread.detach(); | ||
| 412 | return true; | ||
| 413 | } | ||
| 414 | |||
| 392 | pub const Clipboard = struct { | 415 | pub const Clipboard = struct { |
| 393 | alloc: std.mem.Allocator, | 416 | alloc: std.mem.Allocator, |
| 394 | display: *wl.Display, | 417 | display: *wl.Display, |
| @@ -478,20 +501,15 @@ pub const Clipboard = struct { | |||
| 478 | } | 501 | } |
| 479 | 502 | ||
| 480 | fn sendOwnedSelection(self: *Clipboard, mime_type: [*:0]const u8, fd: i32) void { | 503 | fn sendOwnedSelection(self: *Clipboard, mime_type: [*:0]const u8, fd: i32) void { |
| 481 | if (!self.owned_selection.canServeMime(std.mem.span(mime_type))) return; | 504 | _ = serveOwnedSelectionRequest( |
| 482 | const text = self.owned_selection.text orelse return; | 505 | self.alloc, |
| 483 | const transfer = ClipboardTransfer.init(self.alloc, text, fd) catch |err| { | 506 | self.owned_selection.text, |
| 507 | std.mem.span(mime_type), | ||
| 508 | fd, | ||
| 509 | ) catch |err| { | ||
| 484 | std.log.err("clipboard transfer setup failed: {any}", .{err}); | 510 | std.log.err("clipboard transfer setup failed: {any}", .{err}); |
| 485 | _ = c.close(fd); | ||
| 486 | return; | 511 | return; |
| 487 | }; | 512 | }; |
| 488 | const thread = std.Thread.spawn(.{}, ClipboardTransfer.run, .{transfer}) catch |err| { | ||
| 489 | std.log.err("clipboard transfer thread spawn failed: {any}", .{err}); | ||
| 490 | self.alloc.free(transfer.payload); | ||
| 491 | _ = c.close(fd); | ||
| 492 | return; | ||
| 493 | }; | ||
| 494 | thread.detach(); | ||
| 495 | } | 513 | } |
| 496 | }; | 514 | }; |
| 497 | 515 | ||
| @@ -985,6 +1003,40 @@ test "writeClipboardTransfer writes payload and closes fd" { | |||
| 985 | try std.testing.expectEqualStrings("clipboard payload", out.items); | 1003 | try std.testing.expectEqualStrings("clipboard payload", out.items); |
| 986 | } | 1004 | } |
| 987 | 1005 | ||
| 1006 | test "serveOwnedSelectionRequest closes fd for unsupported mime and missing text" { | ||
| 1007 | { | ||
| 1008 | const pipefds = try std.posix.pipe(); | ||
| 1009 | defer std.posix.close(pipefds[0]); | ||
| 1010 | |||
| 1011 | try std.testing.expect(!try serveOwnedSelectionRequest( | ||
| 1012 | std.testing.allocator, | ||
| 1013 | null, | ||
| 1014 | "text/plain", | ||
| 1015 | pipefds[1], | ||
| 1016 | )); | ||
| 1017 | |||
| 1018 | var buf: [1]u8 = undefined; | ||
| 1019 | const n = try std.posix.read(pipefds[0], &buf); | ||
| 1020 | try std.testing.expectEqual(@as(usize, 0), n); | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | { | ||
| 1024 | const pipefds = try std.posix.pipe(); | ||
| 1025 | defer std.posix.close(pipefds[0]); | ||
| 1026 | |||
| 1027 | try std.testing.expect(!try serveOwnedSelectionRequest( | ||
| 1028 | std.testing.allocator, | ||
| 1029 | "payload", | ||
| 1030 | "text/html", | ||
| 1031 | pipefds[1], | ||
| 1032 | )); | ||
| 1033 | |||
| 1034 | var buf: [1]u8 = undefined; | ||
| 1035 | const n = try std.posix.read(pipefds[0], &buf); | ||
| 1036 | try std.testing.expectEqual(@as(usize, 0), n); | ||
| 1037 | } | ||
| 1038 | } | ||
| 1039 | |||
| 988 | fn wmBaseListener(wm_base: *xdg.WmBase, event: xdg.WmBase.Event, _: *xdg.WmBase) void { | 1040 | fn wmBaseListener(wm_base: *xdg.WmBase, event: xdg.WmBase.Event, _: *xdg.WmBase) void { |
| 989 | switch (event) { | 1041 | switch (event) { |
| 990 | .ping => |p| wm_base.pong(p.serial), | 1042 | .ping => |p| wm_base.pong(p.serial), |