Skip to content

Commit

Permalink
Change copy-on-select behavior to be more idiomatic for Linux
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mitchellh committed Oct 2, 2024
1 parent a92c241 commit 66f2d75
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 39 deletions.
75 changes: 46 additions & 29 deletions src/Surface.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1176,12 +1176,8 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
const prev_ = self.io.terminal.screen.selection;
try self.io.terminal.screen.select(sel_);

// Determine the clipboard we want to copy selection to, if it is enabled.
const clipboard: apprt.Clipboard = switch (self.config.copy_on_select) {
.false => return,
.true => .selection,
.clipboard => .standard,
};
// If copy on select is false then exit early.
if (self.config.copy_on_select == .false) return;

// 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
Expand All @@ -1190,12 +1186,6 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
const sel = sel_ orelse 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, .{
.sel = sel,
.trim = self.config.clipboard_trim_trailing_spaces,
Expand All @@ -1205,10 +1195,45 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
};
defer self.alloc.free(buf);

self.rt_surface.setClipboardString(buf, clipboard, false) catch |err| {
log.err("error setting clipboard string err={}", .{err});
return;
};
// Set the clipboard. This is not super DRY but it is clear what
// we're doing for each setting without being clever.
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
Expand Down Expand Up @@ -2680,19 +2705,11 @@ pub fn mouseButtonCallback(

// Middle-click pastes from our selection clipboard
if (button == .middle and action == .press) {
if (comptime builtin.target.isDarwin()) {
// Fast-path for MacOS - always paste from clipboard on
// middle-click.
try self.startClipboardRequest(.standard, .{ .paste = {} });
} 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 = {} });
}
const clipboard: apprt.Clipboard = if (self.rt_surface.supportsClipboard(.selection))
.selection
else
.standard;
try self.startClipboardRequest(clipboard, .{ .paste = {} });
}

// Right-click down selects word for context menus. If the apprt
Expand Down
33 changes: 23 additions & 10 deletions src/config/Config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1114,15 +1114,27 @@ keybind: Keybinds = .{},
/// limit per surface is double.
@"image-storage-limit": u32 = 320 * 1000 * 1000,

/// Whether to automatically copy selected text to the clipboard. `true` will
/// only copy on systems that support a selection clipboard.
///
/// The value `clipboard` will copy to the system clipboard, making this work on
/// macOS. Note that middle-click will also paste from the system clipboard in
/// this case.
///
/// Note that if this is disabled, middle-click paste will also be disabled.
@"copy-on-select": CopyOnSelect = .true,
/// 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 selection 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.
@"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
/// (double, triple, etc.) or an entirely new single click. A value of zero will
Expand Down Expand Up @@ -4326,7 +4338,8 @@ pub const CopyOnSelect = enum {
/// This is not supported on platforms such as macOS. This is the default.
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,
};

Expand Down

0 comments on commit 66f2d75

Please sign in to comment.