diff --git a/src/Surface.zig b/src/Surface.zig index 99c740c89..d78e68fb8 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -2333,34 +2333,35 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void { try self.io.terminal.screens.active.select(sel_); // If copy on select is false then exit early. - if (self.config.copy_on_select == .false) return; + if (self.config.copy_on_select == .none) return; // Set our selection clipboard. If the selection is cleared we do not // clear the clipboard. const sel = sel_ orelse return; switch (self.config.copy_on_select) { - .false => unreachable, // handled above with an early exit + .none => unreachable, // handled above with an early exit + + // The selection clipboard is set if supported, otherwise nothing is copied. + .primary => try self.copySelectionToClipboards( + sel, + &.{.selection}, + .mixed, + ), + + // Only the standard clipboard is set. + .clipboard => try self.copySelectionToClipboards( + sel, + &.{.standard}, + .mixed, + ), // Both standard and selection clipboards are set. - .clipboard => try self.copySelectionToClipboards( + .both => try self.copySelectionToClipboards( sel, &.{ .standard, .selection }, .mixed, ), - - // The selection clipboard is set if supported, otherwise the standard. - .true => { - const clipboard: apprt.Clipboard = if (self.rt_surface.supportsClipboard(.selection)) - .selection - else - .standard; - try self.copySelectionToClipboards( - sel, - &.{clipboard}, - .mixed, - ); - }, } } @@ -3995,22 +3996,16 @@ pub fn mouseButtonCallback( } } - // Middle-click paste source follows copy-on-select: when copy-on-select - // targets the selection clipboard, middle-click reads from it; when - // copy-on-select targets the system clipboard, middle-click reads from - // that instead. Falls back to the standard clipboard on platforms that - // do not support the selection clipboard. + // Middle-click action, either ignore, or paste from clipboard or paste from the selection clipboard if supported. if (button == .middle and action == .press) switch (self.config.middle_click_action) { .ignore => {}, + .@"clipboard-paste" => { + _ = try self.startClipboardRequest(.standard, .{ .paste = {} }); + }, .@"primary-paste" => { - const clipboard: apprt.Clipboard = switch (self.config.copy_on_select) { - .clipboard => .standard, - .true, .false => if (self.rt_surface.supportsClipboard(.selection)) - .selection - else - .standard, - }; - _ = try self.startClipboardRequest(clipboard, .{ .paste = {} }); + if (self.rt_surface.supportsClipboard(.selection)) { + _ = try self.startClipboardRequest(.selection, .{ .paste = {} }); + } }, }; diff --git a/src/config/Config.zig b/src/config/Config.zig index 66b8c6057..a027dbd0a 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -96,6 +96,12 @@ pub const compatibility = std.StaticStringMap( // Ghostty 1.3 rename the "window" option to "new-window". // See: https://github.com/ghostty-org/ghostty/pull/9764 .{ "macos-dock-drop-behavior", compatMacOSDockDropBehavior }, + + // Ghostty 1.4 updated "copy-on-select", allow copying to the selection + // clipboard (on supported operating systems), the system clipboard, or + // both. The semantics also changed but this is the correct mapping. + // See: https://github.com/ghostty-org/ghostty/pull/12604 + .{ "copy-on-select", compatCopyOnSelect }, }); /// Set Ghostty's graphical user interface language to a language other than the @@ -2398,25 +2404,28 @@ 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 prefer to copy to the selection clipboard, otherwise it will copy to -/// the system clipboard. +/// Whether to automatically copy selected text to the clipboard. /// -/// The value `clipboard` will always copy text to the selection clipboard -/// as well as the system clipboard. +/// Valid values: /// -/// Middle-click primary paste (see `middle-click-action`) is enabled by -/// default even if this is `false`. The clipboard it pastes from follows -/// this setting: with `true` (or `false`) it reads from the selection -/// clipboard (falling back to the system clipboard on platforms without a -/// selection clipboard); with `clipboard` it reads from the system -/// clipboard. +/// * `none` - Do not copy selected text automatically. /// -/// The default value is true on Linux and macOS. +/// * `primary` - On Linux, copy to the selection clipboard only. This has no +/// effect on macOS. (Available since: 1.4.0) +/// +/// * `clipboard` - Copy text to the system clipboard only. +/// +/// * `both` - Copy to both clipboards on Linux, and only the system clipboard +/// on macOS. (Available since: 1.4.0) +/// +/// For backward compatibility and convenience, a value of `true` is the same as +/// `primary` on Linux and `clipboard` on macOS, and `false` is an alias for +/// `none`. +/// +/// The default value is `primary` on Linux and `none` otherwise. @"copy-on-select": CopyOnSelect = switch (builtin.os.tag) { - .linux => .true, - .macos => .true, - else => .false, + .linux => .primary, + else => .none, }, /// The action to take when the user right-clicks on the terminal surface. @@ -2435,8 +2444,9 @@ keybind: Keybinds = .{}, /// The action to take when the user middle-clicks on the terminal surface. /// /// Valid values: -/// * `primary-paste` - Paste from the selection (or system) clipboard per -/// `copy-on-select`. +/// * `primary-paste` - Paste from the selection clipboard on Linux. +/// Does nothing on macOS. +/// * `clipboard-paste` - Paste from the system clipboard. /// * `ignore` - Do nothing, ignore the middle click. /// /// The default value is `primary-paste`. @@ -4886,6 +4896,31 @@ fn compatMacOSDockDropBehavior( return false; } +fn compatCopyOnSelect( + self: *Config, + alloc: Allocator, + key: []const u8, + value: ?[]const u8, +) bool { + _ = alloc; + assert(std.mem.eql(u8, key, "copy-on-select")); + + if (std.mem.eql(u8, value orelse "", "true")) { + self.@"copy-on-select" = switch (builtin.os.tag) { + .linux, .freebsd => .primary, + else => .clipboard, + }; + return true; + } + + if (std.mem.eql(u8, value orelse "", "false")) { + self.@"copy-on-select" = .none; + return true; + } + + return false; +} + /// Add a diagnostic message to the config with the given string. /// This is always added with a location of "none". pub fn addDiagnosticFmt( @@ -8617,16 +8652,20 @@ pub const RepeatableLink = struct { /// Options for copy on select behavior. pub const CopyOnSelect = enum { - /// Disables copy on select entirely. - false, + /// Disables copy on select entirely. This is the default on macOS. + none, /// Copy on select is enabled, but goes to the selection clipboard. - /// This is not supported on platforms such as macOS. This is the default. - true, + /// This is not supported on platforms such as macOS. This is the default + /// on Linux. + primary, + + /// Copy on select is enabled and goes to the system clipboard. + clipboard, /// Copy on select is enabled and goes to both the system clipboard /// and the selection clipboard (for Linux). - clipboard, + both, }; /// Options for right-click actions. @@ -8650,8 +8689,10 @@ pub const RightClickAction = enum { /// Options for middle-click actions. pub const MiddleClickAction = enum { - /// Paste from the selection/standard clipboard per `copy-on-select`. + /// Paste from the selection clipboard. @"primary-paste", + /// Paste from the standard clipboard. + @"clipboard-paste", /// No action is taken on middle click. ignore,