mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-29 11:57:57 +00:00
Merge pull request #266 from mitchellh/primary-clipboard
Primary (selection) clipboard support
This commit is contained in:
@@ -32,6 +32,9 @@ pub const App = struct {
|
||||
/// Userdata that is passed to all the callbacks.
|
||||
userdata: AppUD = null,
|
||||
|
||||
/// True if the selection clipboard is supported.
|
||||
supports_selection_clipboard: bool = false,
|
||||
|
||||
/// Callback called to wakeup the event loop. This should trigger
|
||||
/// a full tick of the app loop.
|
||||
wakeup: *const fn (AppUD) callconv(.C) void,
|
||||
@@ -47,10 +50,10 @@ pub const App = struct {
|
||||
/// Read the clipboard value. The return value must be preserved
|
||||
/// by the host until the next call. If there is no valid clipboard
|
||||
/// value then this should return null.
|
||||
read_clipboard: *const fn (SurfaceUD) callconv(.C) ?[*:0]const u8,
|
||||
read_clipboard: *const fn (SurfaceUD, c_int) callconv(.C) ?[*:0]const u8,
|
||||
|
||||
/// Write the clipboard value.
|
||||
write_clipboard: *const fn (SurfaceUD, [*:0]const u8) callconv(.C) void,
|
||||
write_clipboard: *const fn (SurfaceUD, [*:0]const u8, c_int) callconv(.C) void,
|
||||
|
||||
/// Create a new split view. If the embedder doesn't support split
|
||||
/// views then this can be null.
|
||||
@@ -239,13 +242,37 @@ pub const Surface = struct {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getClipboardString(self: *const Surface) ![:0]const u8 {
|
||||
const ptr = self.app.opts.read_clipboard(self.opts.userdata) orelse return "";
|
||||
pub fn supportsClipboard(
|
||||
self: *const Surface,
|
||||
clipboard_type: apprt.Clipboard,
|
||||
) bool {
|
||||
return switch (clipboard_type) {
|
||||
.standard => true,
|
||||
.selection => self.app.opts.supports_selection_clipboard,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getClipboardString(
|
||||
self: *const Surface,
|
||||
clipboard_type: apprt.Clipboard,
|
||||
) ![:0]const u8 {
|
||||
const ptr = self.app.opts.read_clipboard(
|
||||
self.opts.userdata,
|
||||
@intCast(@intFromEnum(clipboard_type)),
|
||||
) orelse return "";
|
||||
return std.mem.sliceTo(ptr, 0);
|
||||
}
|
||||
|
||||
pub fn setClipboardString(self: *const Surface, val: [:0]const u8) !void {
|
||||
self.app.opts.write_clipboard(self.opts.userdata, val.ptr);
|
||||
pub fn setClipboardString(
|
||||
self: *const Surface,
|
||||
val: [:0]const u8,
|
||||
clipboard_type: apprt.Clipboard,
|
||||
) !void {
|
||||
self.app.opts.write_clipboard(
|
||||
self.opts.userdata,
|
||||
val.ptr,
|
||||
@intCast(@intFromEnum(clipboard_type)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setShouldClose(self: *Surface) void {
|
||||
|
||||
@@ -25,6 +25,7 @@ const DevMode = @import("../DevMode.zig");
|
||||
// Get native API access on certain platforms so we can do more customization.
|
||||
const glfwNative = glfw.Native(.{
|
||||
.cocoa = builtin.target.isDarwin(),
|
||||
.x11 = builtin.os.tag == .linux,
|
||||
});
|
||||
|
||||
const log = std.log.scoped(.glfw);
|
||||
@@ -503,15 +504,39 @@ pub const Surface = struct {
|
||||
/// Read the clipboard. The windowing system is responsible for allocating
|
||||
/// a buffer as necessary. This should be a stable pointer until the next
|
||||
/// time getClipboardString is called.
|
||||
pub fn getClipboardString(self: *const Surface) ![:0]const u8 {
|
||||
pub fn getClipboardString(
|
||||
self: *const Surface,
|
||||
clipboard_type: apprt.Clipboard,
|
||||
) ![:0]const u8 {
|
||||
_ = self;
|
||||
return glfw.getClipboardString() orelse return glfw.mustGetErrorCode();
|
||||
return switch (clipboard_type) {
|
||||
.standard => glfw.getClipboardString() orelse glfw.mustGetErrorCode(),
|
||||
.selection => selection: {
|
||||
// Not supported except on Linux
|
||||
if (comptime builtin.os.tag != .linux) return "";
|
||||
|
||||
const raw = glfwNative.getX11SelectionString() orelse
|
||||
return glfw.mustGetErrorCode();
|
||||
break :selection std.mem.span(raw);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Set the clipboard.
|
||||
pub fn setClipboardString(self: *const Surface, val: [:0]const u8) !void {
|
||||
pub fn setClipboardString(
|
||||
self: *const Surface,
|
||||
val: [:0]const u8,
|
||||
clipboard_type: apprt.Clipboard,
|
||||
) !void {
|
||||
_ = self;
|
||||
glfw.setClipboardString(val);
|
||||
switch (clipboard_type) {
|
||||
.standard => glfw.setClipboardString(val),
|
||||
.selection => {
|
||||
// Not supported except on Linux
|
||||
if (comptime builtin.os.tag != .linux) return;
|
||||
glfwNative.setX11SelectionString(val.ptr);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// The cursor position from glfw directly is in screen coordinates but
|
||||
|
||||
@@ -15,6 +15,9 @@ pub const c = @cImport({
|
||||
@cInclude("gtk/gtk.h");
|
||||
});
|
||||
|
||||
// We need native X11 access to access the primary clipboard.
|
||||
const glfw_native = glfw.Native(.{ .x11 = true });
|
||||
|
||||
/// Compatibility with gobject < 2.74
|
||||
const G_CONNECT_DEFAULT = if (@hasDecl(c, "G_CONNECT_DEFAULT"))
|
||||
c.G_CONNECT_DEFAULT
|
||||
@@ -913,14 +916,25 @@ pub const Surface = struct {
|
||||
// ));
|
||||
}
|
||||
|
||||
pub fn getClipboardString(self: *Surface) ![:0]const u8 {
|
||||
const clipboard = c.gtk_widget_get_clipboard(@ptrCast(self.gl_area));
|
||||
|
||||
pub fn getClipboardString(
|
||||
self: *Surface,
|
||||
clipboard_type: apprt.Clipboard,
|
||||
) ![:0]const u8 {
|
||||
const clipboard = getClipboard(@ptrCast(self.gl_area), clipboard_type);
|
||||
const content = c.gdk_clipboard_get_content(clipboard) orelse {
|
||||
// On my machine, this NEVER works, so we fallback to glfw's
|
||||
// implementation...
|
||||
// implementation... I believe this never works because we need to
|
||||
// use the async mechanism with GTK but that doesn't play nice
|
||||
// with what our core expects.
|
||||
log.debug("no GTK clipboard contents, falling back to glfw", .{});
|
||||
return glfw.getClipboardString() orelse return glfw.mustGetErrorCode();
|
||||
return switch (clipboard_type) {
|
||||
.standard => glfw.getClipboardString() orelse glfw.mustGetErrorCode(),
|
||||
.selection => value: {
|
||||
const raw = glfw_native.getX11SelectionString() orelse
|
||||
return glfw.mustGetErrorCode();
|
||||
break :value std.mem.span(raw);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
c.g_value_unset(&self.clipboard);
|
||||
@@ -933,12 +947,22 @@ pub const Surface = struct {
|
||||
return std.mem.sliceTo(ptr, 0);
|
||||
}
|
||||
|
||||
pub fn setClipboardString(self: *const Surface, val: [:0]const u8) !void {
|
||||
const clipboard = c.gtk_widget_get_clipboard(@ptrCast(self.gl_area));
|
||||
|
||||
pub fn setClipboardString(
|
||||
self: *const Surface,
|
||||
val: [:0]const u8,
|
||||
clipboard_type: apprt.Clipboard,
|
||||
) !void {
|
||||
const clipboard = getClipboard(@ptrCast(self.gl_area), clipboard_type);
|
||||
c.gdk_clipboard_set_text(clipboard, val.ptr);
|
||||
}
|
||||
|
||||
fn getClipboard(widget: *c.GtkWidget, clipboard: apprt.Clipboard) ?*c.GdkClipboard {
|
||||
return switch (clipboard) {
|
||||
.standard => c.gtk_widget_get_clipboard(widget),
|
||||
.selection => c.gtk_widget_get_primary_clipboard(widget),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getCursorPos(self: *const Surface) !apprt.CursorPos {
|
||||
return self.cursor_pos;
|
||||
}
|
||||
|
||||
@@ -23,3 +23,11 @@ pub const IMEPos = struct {
|
||||
x: f64,
|
||||
y: f64,
|
||||
};
|
||||
|
||||
/// The clipboard type.
|
||||
///
|
||||
/// If this is changed, you must also update ghostty.h
|
||||
pub const Clipboard = enum(u1) {
|
||||
standard = 0, // ctrl+c/v
|
||||
selection = 1, // also known as the "primary" clipboard
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user