Change copy-on-select behavior to be more idiomatic for Linux

Fixes #2345

The new docs for `copy-on-select`:

Whether to automatically copy selected text to the clipboard. `true`
will prefer to copy to the selection clipboard if supported by the
OS, otherwise it will copy to the system clipboard.

The value `clipboard` will always copy text to the system clipboard
(for supported systems) as well as the system clipboard. This is sometimes
a preferred behavior on Linux.

Middle-click paste will always use the selection clipboard on Linux
and the system clipboard on macOS. Middle-click paste is always enabled
even if this is `false`.

The default value is true on Linux and false on macOS. macOS copy on
select behavior is not typical for applications so it is disabled by
default. On Linux, this is a standard behavior so it is enabled by
default.
This commit is contained in:
Mitchell Hashimoto
2024-10-01 16:30:01 -07:00
parent a92c24159e
commit 66f2d75ddd
2 changed files with 67 additions and 37 deletions

View File

@ -1176,12 +1176,8 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
const prev_ = self.io.terminal.screen.selection; const prev_ = self.io.terminal.screen.selection;
try self.io.terminal.screen.select(sel_); try self.io.terminal.screen.select(sel_);
// Determine the clipboard we want to copy selection to, if it is enabled. // If copy on select is false then exit early.
const clipboard: apprt.Clipboard = switch (self.config.copy_on_select) { if (self.config.copy_on_select == .false) return;
.false => return,
.true => .selection,
.clipboard => .standard,
};
// Set our selection clipboard. If the selection is cleared we do not // Set our selection clipboard. If the selection is cleared we do not
// clear the clipboard. If the selection is set, we only set the clipboard // clear the clipboard. If the selection is set, we only set the clipboard
@ -1190,12 +1186,6 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
const sel = sel_ orelse return; const sel = sel_ orelse return;
if (prev_) |prev| if (sel.eql(prev)) return; if (prev_) |prev| if (sel.eql(prev)) return;
// Check if our runtime supports the selection clipboard at all.
// We can save a lot of work if it doesn't.
if (!self.rt_surface.supportsClipboard(clipboard)) {
return;
}
const buf = self.io.terminal.screen.selectionString(self.alloc, .{ const buf = self.io.terminal.screen.selectionString(self.alloc, .{
.sel = sel, .sel = sel,
.trim = self.config.clipboard_trim_trailing_spaces, .trim = self.config.clipboard_trim_trailing_spaces,
@ -1205,10 +1195,45 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
}; };
defer self.alloc.free(buf); defer self.alloc.free(buf);
self.rt_surface.setClipboardString(buf, clipboard, false) catch |err| { // Set the clipboard. This is not super DRY but it is clear what
log.err("error setting clipboard string err={}", .{err}); // we're doing for each setting without being clever.
return; switch (self.config.copy_on_select) {
.false => unreachable, // handled above with an early exit
// Both standard and selection clipboards are set.
.clipboard => {
const clipboards: []const apprt.Clipboard = &.{ .standard, .selection };
for (clipboards) |clipboard| self.rt_surface.setClipboardString(
buf,
clipboard,
false,
) catch |err| {
log.err(
"error setting clipboard string clipboard={} err={}",
.{ clipboard, err },
);
}; };
},
// The selection clipboard is set if supported, otherwise the standard.
.true => {
const clipboard: apprt.Clipboard = if (self.rt_surface.supportsClipboard(.selection))
.selection
else
.standard;
self.rt_surface.setClipboardString(
buf,
clipboard,
false,
) catch |err| {
log.err(
"error setting clipboard string clipboard={} err={}",
.{ clipboard, err },
);
};
},
}
} }
/// Change the cell size for the terminal grid. This can happen as /// Change the cell size for the terminal grid. This can happen as
@ -2680,20 +2705,12 @@ pub fn mouseButtonCallback(
// Middle-click pastes from our selection clipboard // Middle-click pastes from our selection clipboard
if (button == .middle and action == .press) { if (button == .middle and action == .press) {
if (comptime builtin.target.isDarwin()) { const clipboard: apprt.Clipboard = if (self.rt_surface.supportsClipboard(.selection))
// Fast-path for MacOS - always paste from clipboard on .selection
// middle-click. else
try self.startClipboardRequest(.standard, .{ .paste = {} }); .standard;
} else if (self.config.copy_on_select != .false) {
const clipboard: apprt.Clipboard = switch (self.config.copy_on_select) {
.true => .selection,
.clipboard => .standard,
.false => unreachable,
};
try self.startClipboardRequest(clipboard, .{ .paste = {} }); try self.startClipboardRequest(clipboard, .{ .paste = {} });
} }
}
// Right-click down selects word for context menus. If the apprt // Right-click down selects word for context menus. If the apprt
// doesn't implement context menus this can be a bit weird but they // doesn't implement context menus this can be a bit weird but they

View File

@ -1114,15 +1114,27 @@ keybind: Keybinds = .{},
/// limit per surface is double. /// limit per surface is double.
@"image-storage-limit": u32 = 320 * 1000 * 1000, @"image-storage-limit": u32 = 320 * 1000 * 1000,
/// Whether to automatically copy selected text to the clipboard. `true` will /// Whether to automatically copy selected text to the clipboard. `true`
/// only copy on systems that support a selection clipboard. /// will prefer to copy to the selection clipboard if supported by the
/// OS, otherwise it will copy to the system clipboard.
/// ///
/// The value `clipboard` will copy to the system clipboard, making this work on /// The value `clipboard` will always copy text to the selection clipboard
/// macOS. Note that middle-click will also paste from the system clipboard in /// (for supported systems) as well as the system clipboard. This is sometimes
/// this case. /// a preferred behavior on Linux.
/// ///
/// Note that if this is disabled, middle-click paste will also be disabled. /// Middle-click paste will always use the selection clipboard on Linux
@"copy-on-select": CopyOnSelect = .true, /// and the system clipboard on macOS. Middle-click paste is always enabled
/// even if this is `false`.
///
/// The default value is true on Linux and false on macOS. macOS copy on
/// select behavior is not typical for applications so it is disabled by
/// default. On Linux, this is a standard behavior so it is enabled by
/// default.
@"copy-on-select": CopyOnSelect = switch (builtin.os.tag) {
.linux => .true,
.macos => .false,
else => .false,
},
/// The time in milliseconds between clicks to consider a click a repeat /// The time in milliseconds between clicks to consider a click a repeat
/// (double, triple, etc.) or an entirely new single click. A value of zero will /// (double, triple, etc.) or an entirely new single click. A value of zero will
@ -4326,7 +4338,8 @@ pub const CopyOnSelect = enum {
/// This is not supported on platforms such as macOS. This is the default. /// This is not supported on platforms such as macOS. This is the default.
true, true,
/// Copy on select is enabled and goes to the system clipboard. /// Copy on select is enabled and goes to both the system clipboard
/// and the selection clipboard (for Linux).
clipboard, clipboard,
}; };