mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
gtk: implement OSC 52 prompts
This commit is contained in:
@ -688,21 +688,21 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
|
|||||||
|
|
||||||
.cell_size => |size| try self.setCellSize(size),
|
.cell_size => |size| try self.setCellSize(size),
|
||||||
|
|
||||||
.clipboard_read => |kind| {
|
.clipboard_read => |clipboard| {
|
||||||
if (self.config.clipboard_read == .deny) {
|
if (self.config.clipboard_read == .deny) {
|
||||||
log.info("application attempted to read clipboard, but 'clipboard-read' is set to deny", .{});
|
log.info("application attempted to read clipboard, but 'clipboard-read' is set to deny", .{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.startClipboardRequest(.standard, .{ .osc_52 = kind });
|
try self.startClipboardRequest(.standard, .{ .osc_52_read = clipboard });
|
||||||
},
|
},
|
||||||
|
|
||||||
.clipboard_write => |req| switch (req) {
|
.clipboard_write => |w| switch (w.req) {
|
||||||
.small => |v| try self.clipboardWrite(v.data[0..v.len], .standard),
|
.small => |v| try self.clipboardWrite(v.data[0..v.len], w.clipboard_type),
|
||||||
.stable => |v| try self.clipboardWrite(v, .standard),
|
.stable => |v| try self.clipboardWrite(v, w.clipboard_type),
|
||||||
.alloc => |v| {
|
.alloc => |v| {
|
||||||
defer v.alloc.free(v.data);
|
defer v.alloc.free(v.data);
|
||||||
try self.clipboardWrite(v.data, .standard);
|
try self.clipboardWrite(v.data, w.clipboard_type);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -856,6 +856,9 @@ fn clipboardWrite(self: *const Surface, data: []const u8, loc: apprt.Clipboard)
|
|||||||
};
|
};
|
||||||
assert(buf[buf.len] == 0);
|
assert(buf[buf.len] == 0);
|
||||||
|
|
||||||
|
// When clipboard-write is "ask" a prompt is displayed to the user asking
|
||||||
|
// them to confirm the clipboard access. Each app runtime handles this
|
||||||
|
// differently.
|
||||||
const confirm = self.config.clipboard_write == .ask;
|
const confirm = self.config.clipboard_write == .ask;
|
||||||
self.rt_surface.setClipboardString(buf, loc, confirm) catch |err| {
|
self.rt_surface.setClipboardString(buf, loc, confirm) catch |err| {
|
||||||
log.err("error setting clipboard string err={}", .{err});
|
log.err("error setting clipboard string err={}", .{err});
|
||||||
@ -2516,19 +2519,21 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
/// data is defined as data that contains newlines, though this definition
|
/// data is defined as data that contains newlines, though this definition
|
||||||
/// may change later to detect other scenarios.
|
/// may change later to detect other scenarios.
|
||||||
///
|
///
|
||||||
/// - For OSC 52 pastes no prompt is shown to the user if `confirmed` is true.
|
/// - For OSC 52 reads and writes no prompt is shown to the user if
|
||||||
|
/// `confirmed` is true.
|
||||||
///
|
///
|
||||||
/// If `confirmed` is false and either unsafe data is detected or the
|
/// If `confirmed` is false then this may return either an UnsafePaste or
|
||||||
/// `clipboard-read` option is set to `ask`, this will return error.UnsafePaste.
|
/// UnauthorizedPaste error, depending on the type of clipboard request.
|
||||||
pub fn completeClipboardRequest(
|
pub fn completeClipboardRequest(
|
||||||
self: *Surface,
|
self: *Surface,
|
||||||
req: apprt.ClipboardRequest,
|
req: apprt.ClipboardRequest,
|
||||||
data: []const u8,
|
data: [:0]const u8,
|
||||||
confirmed: bool,
|
confirmed: bool,
|
||||||
) !void {
|
) !void {
|
||||||
switch (req) {
|
switch (req) {
|
||||||
.paste => try self.completeClipboardPaste(data, confirmed),
|
.paste => try self.completeClipboardPaste(data, confirmed),
|
||||||
.osc_52 => |kind| try self.completeClipboardReadOSC52(data, kind, confirmed),
|
.osc_52_read => |clipboard| try self.completeClipboardReadOSC52(data, clipboard, confirmed),
|
||||||
|
.osc_52_write => |clipboard| try self.rt_surface.setClipboardString(data, clipboard, !confirmed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2541,13 +2546,16 @@ fn startClipboardRequest(
|
|||||||
) !void {
|
) !void {
|
||||||
switch (req) {
|
switch (req) {
|
||||||
.paste => {}, // always allowed
|
.paste => {}, // always allowed
|
||||||
.osc_52 => if (self.config.clipboard_read == .deny) {
|
.osc_52_read => if (self.config.clipboard_read == .deny) {
|
||||||
log.info(
|
log.info(
|
||||||
"application attempted to read clipboard, but 'clipboard-read' is set to deny",
|
"application attempted to read clipboard, but 'clipboard-read' is set to deny",
|
||||||
.{},
|
.{},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// No clipboard write code paths travel through this function
|
||||||
|
.osc_52_write => unreachable,
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.rt_surface.clipboardRequest(loc, req);
|
try self.rt_surface.clipboardRequest(loc, req);
|
||||||
@ -2642,7 +2650,12 @@ fn completeClipboardPaste(
|
|||||||
try self.io_thread.wakeup.notify();
|
try self.io_thread.wakeup.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn completeClipboardReadOSC52(self: *Surface, data: []const u8, kind: u8, confirmed: bool) !void {
|
fn completeClipboardReadOSC52(
|
||||||
|
self: *Surface,
|
||||||
|
data: []const u8,
|
||||||
|
clipboard_type: apprt.Clipboard,
|
||||||
|
confirmed: bool,
|
||||||
|
) !void {
|
||||||
// We should never get here if clipboard-read is set to deny
|
// We should never get here if clipboard-read is set to deny
|
||||||
assert(self.config.clipboard_read != .deny);
|
assert(self.config.clipboard_read != .deny);
|
||||||
|
|
||||||
@ -2660,6 +2673,11 @@ fn completeClipboardReadOSC52(self: *Surface, data: []const u8, kind: u8, confir
|
|||||||
var buf = try self.alloc.alloc(u8, size + 9); // const for OSC
|
var buf = try self.alloc.alloc(u8, size + 9); // const for OSC
|
||||||
defer self.alloc.free(buf);
|
defer self.alloc.free(buf);
|
||||||
|
|
||||||
|
const kind: u8 = switch (clipboard_type) {
|
||||||
|
.standard => 'c',
|
||||||
|
.selection => 's',
|
||||||
|
};
|
||||||
|
|
||||||
// Wrap our data with the OSC code
|
// Wrap our data with the OSC code
|
||||||
const prefix = try std.fmt.bufPrint(buf, "\x1b]52;{c};", .{kind});
|
const prefix = try std.fmt.bufPrint(buf, "\x1b]52;{c};", .{kind});
|
||||||
assert(prefix.len == 7);
|
assert(prefix.len == 7);
|
||||||
|
@ -656,15 +656,14 @@ pub const Surface = struct {
|
|||||||
state: apprt.ClipboardRequest,
|
state: apprt.ClipboardRequest,
|
||||||
) !void {
|
) !void {
|
||||||
// GLFW can read clipboards immediately so just do that.
|
// GLFW can read clipboards immediately so just do that.
|
||||||
const str: []const u8 = switch (clipboard_type) {
|
const str: [:0]const u8 = switch (clipboard_type) {
|
||||||
.standard => glfw.getClipboardString() orelse return glfw.mustGetErrorCode(),
|
.standard => glfw.getClipboardString() orelse return glfw.mustGetErrorCode(),
|
||||||
.selection => selection: {
|
.selection => selection: {
|
||||||
// Not supported except on Linux
|
// Not supported except on Linux
|
||||||
if (comptime builtin.os.tag != .linux) break :selection "";
|
if (comptime builtin.os.tag != .linux) break :selection "";
|
||||||
|
|
||||||
const raw = glfwNative.getX11SelectionString() orelse
|
break :selection glfwNative.getX11SelectionString() orelse
|
||||||
return glfw.mustGetErrorCode();
|
return glfw.mustGetErrorCode();
|
||||||
break :selection std.mem.span(raw);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ const build_options = @import("build_options");
|
|||||||
const Surface = @import("Surface.zig");
|
const Surface = @import("Surface.zig");
|
||||||
const Window = @import("Window.zig");
|
const Window = @import("Window.zig");
|
||||||
const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig");
|
const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig");
|
||||||
const UnsafePasteWindow = @import("UnsafePasteWindow.zig");
|
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
|
||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
const inspector = @import("inspector.zig");
|
const inspector = @import("inspector.zig");
|
||||||
const key = @import("key.zig");
|
const key = @import("key.zig");
|
||||||
@ -49,8 +49,8 @@ menu: ?*c.GMenu = null,
|
|||||||
/// The configuration errors window, if it is currently open.
|
/// The configuration errors window, if it is currently open.
|
||||||
config_errors_window: ?*ConfigErrorsWindow = null,
|
config_errors_window: ?*ConfigErrorsWindow = null,
|
||||||
|
|
||||||
/// The unsafe paste window, if it is currently open.
|
/// The clipboard confirmation window, if it is currently open.
|
||||||
unsafe_paste_window: ?*UnsafePasteWindow = null,
|
clipboard_confirmation_window: ?*ClipboardConfirmationWindow = null,
|
||||||
|
|
||||||
/// This is set to false when the main loop should exit.
|
/// This is set to false when the main loop should exit.
|
||||||
running: bool = true,
|
running: bool = true,
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
/// Unsafe Paste Window
|
/// Clipboard Confirmation Window
|
||||||
const UnsafePaste = @This();
|
const ClipboardConfirmation = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const CoreSurface = @import("../../Surface.zig");
|
const CoreSurface = @import("../../Surface.zig");
|
||||||
const ClipboardRequest = @import("../structs.zig").ClipboardRequest;
|
const ClipboardRequest = @import("../structs.zig").ClipboardRequest;
|
||||||
|
const ClipboardPromptReason = @import("../structs.zig").ClipboardPromptReason;
|
||||||
const App = @import("App.zig");
|
const App = @import("App.zig");
|
||||||
const View = @import("View.zig");
|
const View = @import("View.zig");
|
||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
@ -16,9 +17,10 @@ app: *App,
|
|||||||
window: *c.GtkWindow,
|
window: *c.GtkWindow,
|
||||||
view: PrimaryView,
|
view: PrimaryView,
|
||||||
|
|
||||||
data: []u8,
|
data: [:0]u8,
|
||||||
core_surface: CoreSurface,
|
core_surface: CoreSurface,
|
||||||
pending_req: ClipboardRequest,
|
pending_req: ClipboardRequest,
|
||||||
|
reason: ClipboardPromptReason,
|
||||||
|
|
||||||
pub fn create(
|
pub fn create(
|
||||||
app: *App,
|
app: *App,
|
||||||
@ -26,40 +28,49 @@ pub fn create(
|
|||||||
core_surface: CoreSurface,
|
core_surface: CoreSurface,
|
||||||
request: ClipboardRequest,
|
request: ClipboardRequest,
|
||||||
) !void {
|
) !void {
|
||||||
if (app.unsafe_paste_window != null) return error.WindowAlreadyExists;
|
if (app.clipboard_confirmation_window != null) return error.WindowAlreadyExists;
|
||||||
|
|
||||||
const alloc = app.core_app.alloc;
|
const alloc = app.core_app.alloc;
|
||||||
const self = try alloc.create(UnsafePaste);
|
const self = try alloc.create(ClipboardConfirmation);
|
||||||
errdefer alloc.destroy(self);
|
errdefer alloc.destroy(self);
|
||||||
|
|
||||||
|
const reason: ClipboardPromptReason = switch (request) {
|
||||||
|
.paste => .unsafe,
|
||||||
|
.osc_52_read => .read,
|
||||||
|
.osc_52_write => .write,
|
||||||
|
};
|
||||||
|
|
||||||
try self.init(
|
try self.init(
|
||||||
app,
|
app,
|
||||||
data,
|
data,
|
||||||
core_surface,
|
core_surface,
|
||||||
request,
|
request,
|
||||||
|
reason,
|
||||||
);
|
);
|
||||||
|
|
||||||
app.unsafe_paste_window = self;
|
app.clipboard_confirmation_window = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Not public because this should be called by the GTK lifecycle.
|
/// Not public because this should be called by the GTK lifecycle.
|
||||||
fn destroy(self: *UnsafePaste) void {
|
fn destroy(self: *ClipboardConfirmation) void {
|
||||||
const alloc = self.app.core_app.alloc;
|
const alloc = self.app.core_app.alloc;
|
||||||
self.app.unsafe_paste_window = null;
|
self.app.clipboard_confirmation_window = null;
|
||||||
alloc.destroy(self);
|
alloc.destroy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(
|
fn init(
|
||||||
self: *UnsafePaste,
|
self: *ClipboardConfirmation,
|
||||||
app: *App,
|
app: *App,
|
||||||
data: []const u8,
|
data: []const u8,
|
||||||
core_surface: CoreSurface,
|
core_surface: CoreSurface,
|
||||||
request: ClipboardRequest,
|
request: ClipboardRequest,
|
||||||
|
reason: ClipboardPromptReason,
|
||||||
) !void {
|
) !void {
|
||||||
// Create the window
|
// Create the window
|
||||||
const window = c.gtk_window_new();
|
const window = c.gtk_window_new();
|
||||||
const gtk_window: *c.GtkWindow = @ptrCast(window);
|
const gtk_window: *c.GtkWindow = @ptrCast(window);
|
||||||
errdefer c.gtk_window_destroy(gtk_window);
|
errdefer c.gtk_window_destroy(gtk_window);
|
||||||
c.gtk_window_set_title(gtk_window, "Warning: Potentially Unsafe Paste");
|
c.gtk_window_set_title(gtk_window, titleText(reason));
|
||||||
c.gtk_window_set_default_size(gtk_window, 550, 275);
|
c.gtk_window_set_default_size(gtk_window, 550, 275);
|
||||||
c.gtk_window_set_resizable(gtk_window, 0);
|
c.gtk_window_set_resizable(gtk_window, 0);
|
||||||
_ = c.g_signal_connect_data(
|
_ = c.g_signal_connect_data(
|
||||||
@ -76,9 +87,10 @@ fn init(
|
|||||||
.app = app,
|
.app = app,
|
||||||
.window = gtk_window,
|
.window = gtk_window,
|
||||||
.view = undefined,
|
.view = undefined,
|
||||||
.data = try app.core_app.alloc.dupe(u8, data),
|
.data = try app.core_app.alloc.dupeZ(u8, data),
|
||||||
.core_surface = core_surface,
|
.core_surface = core_surface,
|
||||||
.pending_req = request,
|
.pending_req = request,
|
||||||
|
.reason = reason,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Show the window
|
// Show the window
|
||||||
@ -93,7 +105,7 @@ fn init(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn gtkDestroy(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
fn gtkDestroy(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
||||||
const self: *UnsafePaste = @ptrCast(@alignCast(ud orelse return));
|
const self: *ClipboardConfirmation = @ptrCast(@alignCast(ud orelse return));
|
||||||
self.destroy();
|
self.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,12 +113,9 @@ const PrimaryView = struct {
|
|||||||
root: *c.GtkWidget,
|
root: *c.GtkWidget,
|
||||||
text: *c.GtkTextView,
|
text: *c.GtkTextView,
|
||||||
|
|
||||||
pub fn init(root: *UnsafePaste, data: []const u8) !PrimaryView {
|
pub fn init(root: *ClipboardConfirmation, data: []const u8) !PrimaryView {
|
||||||
// All our widgets
|
// All our widgets
|
||||||
const label = c.gtk_label_new(
|
const label = c.gtk_label_new(promptText(root.reason));
|
||||||
"Pasting this text into the terminal may be dangerous as " ++
|
|
||||||
"it looks like some commands may be executed.",
|
|
||||||
);
|
|
||||||
const buf = unsafeBuffer(data);
|
const buf = unsafeBuffer(data);
|
||||||
defer c.g_object_unref(buf);
|
defer c.g_object_unref(buf);
|
||||||
const buttons = try ButtonsView.init(root);
|
const buttons = try ButtonsView.init(root);
|
||||||
@ -157,20 +166,26 @@ const PrimaryView = struct {
|
|||||||
const ButtonsView = struct {
|
const ButtonsView = struct {
|
||||||
root: *c.GtkWidget,
|
root: *c.GtkWidget,
|
||||||
|
|
||||||
pub fn init(root: *UnsafePaste) !ButtonsView {
|
pub fn init(root: *ClipboardConfirmation) !ButtonsView {
|
||||||
const cancel_button = c.gtk_button_new_with_label("Cancel");
|
const cancel_text, const confirm_text = switch (root.reason) {
|
||||||
|
.unsafe => .{ "Cancel", "Paste" },
|
||||||
|
.read, .write => .{ "Deny", "Allow" },
|
||||||
|
_ => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancel_button = c.gtk_button_new_with_label(cancel_text);
|
||||||
errdefer c.g_object_unref(cancel_button);
|
errdefer c.g_object_unref(cancel_button);
|
||||||
|
|
||||||
const paste_button = c.gtk_button_new_with_label("Paste");
|
const confirm_button = c.gtk_button_new_with_label(confirm_text);
|
||||||
errdefer c.g_object_unref(paste_button);
|
errdefer c.g_object_unref(confirm_button);
|
||||||
|
|
||||||
// TODO: Focus on the paste button
|
// TODO: Focus on the paste button
|
||||||
// c.gtk_widget_grab_focus(paste_button);
|
// c.gtk_widget_grab_focus(confirm_button);
|
||||||
|
|
||||||
// Create our view
|
// Create our view
|
||||||
const view = try View.init(&.{
|
const view = try View.init(&.{
|
||||||
.{ .name = "cancel", .widget = cancel_button },
|
.{ .name = "cancel", .widget = cancel_button },
|
||||||
.{ .name = "paste", .widget = paste_button },
|
.{ .name = "confirm", .widget = confirm_button },
|
||||||
}, &vfl);
|
}, &vfl);
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
@ -183,9 +198,9 @@ const ButtonsView = struct {
|
|||||||
c.G_CONNECT_DEFAULT,
|
c.G_CONNECT_DEFAULT,
|
||||||
);
|
);
|
||||||
_ = c.g_signal_connect_data(
|
_ = c.g_signal_connect_data(
|
||||||
paste_button,
|
confirm_button,
|
||||||
"clicked",
|
"clicked",
|
||||||
c.G_CALLBACK(>kPasteClick),
|
c.G_CALLBACK(>kConfirmClick),
|
||||||
root,
|
root,
|
||||||
null,
|
null,
|
||||||
c.G_CONNECT_DEFAULT,
|
c.G_CONNECT_DEFAULT,
|
||||||
@ -195,13 +210,13 @@ const ButtonsView = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn gtkCancelClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
fn gtkCancelClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
||||||
const self: *UnsafePaste = @ptrCast(@alignCast(ud));
|
const self: *ClipboardConfirmation = @ptrCast(@alignCast(ud));
|
||||||
c.gtk_window_destroy(@ptrCast(self.window));
|
c.gtk_window_destroy(@ptrCast(self.window));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gtkPasteClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
fn gtkConfirmClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
||||||
// Requeue the paste with force.
|
// Requeue the paste with force.
|
||||||
const self: *UnsafePaste = @ptrCast(@alignCast(ud));
|
const self: *ClipboardConfirmation = @ptrCast(@alignCast(ud));
|
||||||
self.core_surface.completeClipboardRequest(
|
self.core_surface.completeClipboardRequest(
|
||||||
self.pending_req,
|
self.pending_req,
|
||||||
self.data,
|
self.data,
|
||||||
@ -214,6 +229,34 @@ const ButtonsView = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const vfl = [_][*:0]const u8{
|
const vfl = [_][*:0]const u8{
|
||||||
"H:[cancel]-8-[paste]-8-|",
|
"H:[cancel]-8-[confirm]-8-|",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The title of the window, based on the reason the prompt is being shown.
|
||||||
|
fn titleText(reason: ClipboardPromptReason) [:0]const u8 {
|
||||||
|
return switch (reason) {
|
||||||
|
.unsafe => "Warning: Potentially Unsafe Paste",
|
||||||
|
.read, .write => "Authorize Clipboard Access",
|
||||||
|
_ => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The text to display in the prompt window, based on the reason the prompt
|
||||||
|
/// is being shown.
|
||||||
|
fn promptText(reason: ClipboardPromptReason) [:0]const u8 {
|
||||||
|
return switch (reason) {
|
||||||
|
.unsafe =>
|
||||||
|
\\Pasting this text into the terminal may be dangerous as it looks like some commands may be executed.
|
||||||
|
,
|
||||||
|
.read =>
|
||||||
|
\\An appliclication is attempting to read from the clipboard.
|
||||||
|
\\The current clipboard contents are shown below.
|
||||||
|
,
|
||||||
|
.write =>
|
||||||
|
\\An application is attempting to write to the clipboard.
|
||||||
|
\\The content to write is shown below.
|
||||||
|
,
|
||||||
|
_ => unreachable,
|
||||||
|
};
|
||||||
|
}
|
@ -13,7 +13,7 @@ const CoreSurface = @import("../../Surface.zig");
|
|||||||
|
|
||||||
const App = @import("App.zig");
|
const App = @import("App.zig");
|
||||||
const Window = @import("Window.zig");
|
const Window = @import("Window.zig");
|
||||||
const UnsafePasteWindow = @import("UnsafePasteWindow.zig");
|
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
|
||||||
const inspector = @import("inspector.zig");
|
const inspector = @import("inspector.zig");
|
||||||
const gtk_key = @import("key.zig");
|
const gtk_key = @import("key.zig");
|
||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
@ -517,10 +517,19 @@ pub fn setClipboardString(
|
|||||||
clipboard_type: apprt.Clipboard,
|
clipboard_type: apprt.Clipboard,
|
||||||
confirm: bool,
|
confirm: bool,
|
||||||
) !void {
|
) !void {
|
||||||
// TODO: implement confirmation dialog when clipboard-write is "ask"
|
if (!confirm) {
|
||||||
_ = confirm;
|
|
||||||
const clipboard = getClipboard(@ptrCast(self.gl_area), clipboard_type);
|
const clipboard = getClipboard(@ptrCast(self.gl_area), clipboard_type);
|
||||||
c.gdk_clipboard_set_text(clipboard, val.ptr);
|
c.gdk_clipboard_set_text(clipboard, val.ptr);
|
||||||
|
} else {
|
||||||
|
ClipboardConfirmationWindow.create(
|
||||||
|
self.app,
|
||||||
|
val,
|
||||||
|
self.core_surface,
|
||||||
|
.{ .osc_52_write = clipboard_type },
|
||||||
|
) catch |window_err| {
|
||||||
|
log.err("failed to create clipboard confirmation window err={}", .{window_err});
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ClipboardRequest = struct {
|
const ClipboardRequest = struct {
|
||||||
@ -557,15 +566,17 @@ fn gtkClipboardRead(
|
|||||||
str,
|
str,
|
||||||
false,
|
false,
|
||||||
) catch |err| switch (err) {
|
) catch |err| switch (err) {
|
||||||
error.UnsafePaste => {
|
error.UnsafePaste,
|
||||||
|
error.UnauthorizedPaste,
|
||||||
|
=> {
|
||||||
// Create a dialog and ask the user if they want to paste anyway.
|
// Create a dialog and ask the user if they want to paste anyway.
|
||||||
UnsafePasteWindow.create(
|
ClipboardConfirmationWindow.create(
|
||||||
self.app,
|
self.app,
|
||||||
str,
|
str,
|
||||||
self.core_surface,
|
self.core_surface,
|
||||||
req.state,
|
req.state,
|
||||||
) catch |window_err| {
|
) catch |window_err| {
|
||||||
log.err("failed to create unsafe paste window err={}", .{window_err});
|
log.err("failed to create clipboard confirmation window err={}", .{window_err});
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
@ -38,8 +38,11 @@ pub const ClipboardRequest = union(enum) {
|
|||||||
/// A direct paste of clipboard contents.
|
/// A direct paste of clipboard contents.
|
||||||
paste: void,
|
paste: void,
|
||||||
|
|
||||||
|
/// A request to read clipboard contents via OSC 52.
|
||||||
|
osc_52_read: Clipboard,
|
||||||
|
|
||||||
/// A request to write clipboard contents via OSC 52.
|
/// A request to write clipboard contents via OSC 52.
|
||||||
osc_52: u8,
|
osc_52_write: Clipboard,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The reason for displaying a clipboard prompt to the user
|
/// The reason for displaying a clipboard prompt to the user
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
const apprt = @import("../apprt.zig");
|
||||||
const App = @import("../App.zig");
|
const App = @import("../App.zig");
|
||||||
const Surface = @import("../Surface.zig");
|
const Surface = @import("../Surface.zig");
|
||||||
const renderer = @import("../renderer.zig");
|
const renderer = @import("../renderer.zig");
|
||||||
@ -24,10 +25,13 @@ pub const Message = union(enum) {
|
|||||||
cell_size: renderer.CellSize,
|
cell_size: renderer.CellSize,
|
||||||
|
|
||||||
/// Read the clipboard and write to the pty.
|
/// Read the clipboard and write to the pty.
|
||||||
clipboard_read: u8,
|
clipboard_read: apprt.Clipboard,
|
||||||
|
|
||||||
/// Write the clipboard contents.
|
/// Write the clipboard contents.
|
||||||
clipboard_write: WriteReq,
|
clipboard_write: struct {
|
||||||
|
clipboard_type: apprt.Clipboard,
|
||||||
|
req: WriteReq,
|
||||||
|
},
|
||||||
|
|
||||||
/// Change the configuration to the given configuration. The pointer is
|
/// Change the configuration to the given configuration. The pointer is
|
||||||
/// not valid after receiving this message so any config must be used
|
/// not valid after receiving this message so any config must be used
|
||||||
|
@ -2094,20 +2094,29 @@ const StreamHandler = struct {
|
|||||||
// iTerm also appears to do this but other terminals seem to only allow
|
// iTerm also appears to do this but other terminals seem to only allow
|
||||||
// certain. Let's investigate more.
|
// certain. Let's investigate more.
|
||||||
|
|
||||||
|
const clipboard_type: apprt.Clipboard = switch (kind) {
|
||||||
|
'c' => .standard,
|
||||||
|
's' => .selection,
|
||||||
|
else => .standard,
|
||||||
|
};
|
||||||
|
|
||||||
// Get clipboard contents
|
// Get clipboard contents
|
||||||
if (data.len == 1 and data[0] == '?') {
|
if (data.len == 1 and data[0] == '?') {
|
||||||
_ = self.ev.surface_mailbox.push(.{
|
_ = self.ev.surface_mailbox.push(.{
|
||||||
.clipboard_read = kind,
|
.clipboard_read = clipboard_type,
|
||||||
}, .{ .forever = {} });
|
}, .{ .forever = {} });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write clipboard contents
|
// Write clipboard contents
|
||||||
_ = self.ev.surface_mailbox.push(.{
|
_ = self.ev.surface_mailbox.push(.{
|
||||||
.clipboard_write = try apprt.surface.Message.WriteReq.init(
|
.clipboard_write = .{
|
||||||
|
.req = try apprt.surface.Message.WriteReq.init(
|
||||||
self.alloc,
|
self.alloc,
|
||||||
data,
|
data,
|
||||||
),
|
),
|
||||||
|
.clipboard_type = clipboard_type,
|
||||||
|
},
|
||||||
}, .{ .forever = {} });
|
}, .{ .forever = {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user