d0084ff5
Copy visible selection to clipboard
a73x 2026-04-09 18:53
Commit message
src/main.zig
| Old | New | ||
|---|---|---|---|
| @@ -273,6 +273,10 @@ fn runTerminal(alloc: std.mem.Allocator) !void { | |||
| 273 | } | 273 | } |
| 274 | continue; | 274 | continue; |
| 275 | } | 275 | } |
| 276 | if (isClipboardCopyEvent(ev)) { | ||
| 277 | _ = try copySelectionText(alloc, clipboard, term, activeSelectionSpan(selection), ev.serial); | ||
| 278 | continue; | ||
| 279 | } | ||
| 276 | if (ev.utf8_len > 0) { | 280 | if (ev.utf8_len > 0) { |
| 277 | _ = try p.write(ev.utf8[0..ev.utf8_len]); | 281 | _ = try p.write(ev.utf8[0..ev.utf8_len]); |
| 278 | } else if (try encodeKeyboardEvent(term, ev, &key_buf)) |encoded| { | 282 | } else if (try encodeKeyboardEvent(term, ev, &key_buf)) |encoded| { |
| @@ -555,6 +559,22 @@ fn isClipboardCopyEvent(ev: wayland_client.KeyboardEvent) bool { | |||
| 555 | ev.keysym == c.XKB_KEY_C; | 559 | ev.keysym == c.XKB_KEY_C; |
| 556 | } | 560 | } |
| 557 | 561 | ||
| 562 | fn copySelectionText( | ||
| 563 | alloc: std.mem.Allocator, | ||
| 564 | clipboard: ?*wayland_client.Clipboard, | ||
| 565 | term: *vt.Terminal, | ||
| 566 | selection: ?SelectionSpan, | ||
| 567 | serial: u32, | ||
| 568 | ) !bool { | ||
| 569 | const cb = clipboard orelse return false; | ||
| 570 | const span = selection orelse return false; | ||
| 571 | const text = try extractSelectedText(alloc, term.render_state.row_data.items(.cells), span); | ||
| 572 | defer alloc.free(text); | ||
| 573 | if (text.len == 0) return false; | ||
| 574 | try cb.setSelectionText(text, serial); | ||
| 575 | return true; | ||
| 576 | } | ||
| 577 | |||
| 558 | fn remainingRepeatTimeoutMs(deadline_ns: ?i128) ?i32 { | 578 | fn remainingRepeatTimeoutMs(deadline_ns: ?i128) ?i32 { |
| 559 | const deadline = deadline_ns orelse return null; | 579 | const deadline = deadline_ns orelse return null; |
| 560 | const now = std.time.nanoTimestamp(); | 580 | const now = std.time.nanoTimestamp(); |
| @@ -2555,6 +2575,19 @@ test "isClipboardCopyEvent matches Ctrl-Shift-C press" { | |||
| 2555 | })); | 2575 | })); |
| 2556 | } | 2576 | } |
| 2557 | 2577 | ||
| 2578 | test "copySelectionText returns false for empty visible selection" { | ||
| 2579 | var term = try vt.Terminal.init(std.testing.allocator, .{ .cols = 4, .rows = 1 }); | ||
| 2580 | defer term.deinit(); | ||
| 2581 | |||
| 2582 | try std.testing.expect(!try copySelectionText( | ||
| 2583 | std.testing.allocator, | ||
| 2584 | null, | ||
| 2585 | term, | ||
| 2586 | null, | ||
| 2587 | 0, | ||
| 2588 | )); | ||
| 2589 | } | ||
| 2590 | |||
| 2558 | test "extractSelectedText trims trailing blanks on each visible row" { | 2591 | test "extractSelectedText trims trailing blanks on each visible row" { |
| 2559 | var term = try vt.Terminal.init(std.testing.allocator, .{ | 2592 | var term = try vt.Terminal.init(std.testing.allocator, .{ |
| 2560 | .cols = 8, | 2593 | .cols = 8, |
src/wayland.zig
| Old | New | ||
|---|---|---|---|
| @@ -24,6 +24,7 @@ pub const KeyboardEvent = struct { | |||
| 24 | action: Action, | 24 | action: Action, |
| 25 | utf8: [8]u8, | 25 | utf8: [8]u8, |
| 26 | utf8_len: u8, | 26 | utf8_len: u8, |
| 27 | serial: u32 = 0, | ||
| 27 | 28 | ||
| 28 | pub const Action = enum { press, release, repeat }; | 29 | pub const Action = enum { press, release, repeat }; |
| 29 | }; | 30 | }; |
| @@ -751,6 +752,7 @@ fn keyboardListener(_: *wl.Keyboard, event: wl.Keyboard.Event, kb: *Keyboard) vo | |||
| 751 | .action = action, | 752 | .action = action, |
| 752 | .utf8 = utf8, | 753 | .utf8 = utf8, |
| 753 | .utf8_len = @intCast(@min(len, 8)), | 754 | .utf8_len = @intCast(@min(len, 8)), |
| 755 | .serial = k.serial, | ||
| 754 | }; | 756 | }; |
| 755 | 757 | ||
| 756 | kb.event_queue.append(kb.alloc, ev) catch |err| { | 758 | kb.event_queue.append(kb.alloc, ev) catch |err| { |