summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitchell Hashimoto <m@mitchellh.com>2025-07-22 07:13:14 -0700
committerMitchell Hashimoto <m@mitchellh.com>2025-07-22 14:36:21 -0700
commitc3acbf1a4956ba7c6409b34036477c9fa957e874 (patch)
tree9c615d671ea666eb235216135ad68f3598154c9e
parent49cf8d80a6f689d97c8ddfa2b972da8858c07176 (diff)
apprt/gtk-ng: clipboard read
-rw-r--r--src/apprt/gtk-ng/Surface.zig7
-rw-r--r--src/apprt/gtk-ng/class/surface.zig131
2 files changed, 135 insertions, 3 deletions
diff --git a/src/apprt/gtk-ng/Surface.zig b/src/apprt/gtk-ng/Surface.zig
index e25ec48ee..1ee21bbc6 100644
--- a/src/apprt/gtk-ng/Surface.zig
+++ b/src/apprt/gtk-ng/Surface.zig
@@ -73,9 +73,10 @@ pub fn clipboardRequest(
clipboard_type: apprt.Clipboard,
state: apprt.ClipboardRequest,
) !void {
- _ = self;
- _ = clipboard_type;
- _ = state;
+ try self.surface.clipboardRequest(
+ clipboard_type,
+ state,
+ );
}
pub fn setClipboardString(
diff --git a/src/apprt/gtk-ng/class/surface.zig b/src/apprt/gtk-ng/class/surface.zig
index fd2e6c54b..70d769dbc 100644
--- a/src/apprt/gtk-ng/class/surface.zig
+++ b/src/apprt/gtk-ng/class/surface.zig
@@ -2,6 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const adw = @import("adw");
const gdk = @import("gdk");
+const gio = @import("gio");
const glib = @import("glib");
const gobject = @import("gobject");
const gtk = @import("gtk");
@@ -690,6 +691,18 @@ pub const Surface = extern struct {
return env;
}
+ pub fn clipboardRequest(
+ self: *Self,
+ clipboard_type: apprt.Clipboard,
+ state: apprt.ClipboardRequest,
+ ) !void {
+ try Clipboard.request(
+ self,
+ clipboard_type,
+ state,
+ );
+ }
+
//---------------------------------------------------------------
// Virtual Methods
@@ -1903,3 +1916,121 @@ fn translateMouseButton(button: c_uint) input.MouseButton {
else => .unknown,
};
}
+
+/// A namespace for our clipboard-related functions so Surface isn't SO large.
+const Clipboard = struct {
+ /// Get the specific type of clipboard for a widget.
+ pub fn get(
+ widget: *gtk.Widget,
+ clipboard: apprt.Clipboard,
+ ) ?*gdk.Clipboard {
+ return switch (clipboard) {
+ .standard => widget.getClipboard(),
+ .selection, .primary => widget.getPrimaryClipboard(),
+ };
+ }
+
+ pub fn request(
+ self: *Surface,
+ clipboard_type: apprt.Clipboard,
+ state: apprt.ClipboardRequest,
+ ) Allocator.Error!void {
+ // Get our requested clipboard
+ const clipboard = get(
+ self.private().gl_area.as(gtk.Widget),
+ clipboard_type,
+ ) orelse return;
+
+ // Allocate our userdata
+ const alloc = Application.default().allocator();
+ const ud = try alloc.create(Request);
+ errdefer alloc.destroy(ud);
+ ud.* = .{
+ // Important: we ref self here so that we can't free memory
+ // while we have an outstanding clipboard read.
+ .self = self.ref(),
+ .state = state,
+ };
+ errdefer self.unref();
+
+ // Read
+ clipboard.readTextAsync(
+ null,
+ clipboardReadText,
+ ud,
+ );
+ }
+
+ fn clipboardReadText(
+ source: ?*gobject.Object,
+ res: *gio.AsyncResult,
+ ud: ?*anyopaque,
+ ) callconv(.c) void {
+ const clipboard = gobject.ext.cast(
+ gdk.Clipboard,
+ source orelse return,
+ ) orelse return;
+ const req: *Request = @ptrCast(@alignCast(ud orelse return));
+
+ const alloc = Application.default().allocator();
+ defer alloc.destroy(req);
+
+ const self = req.self;
+ defer self.unref();
+
+ var gerr: ?*glib.Error = null;
+ const cstr_ = clipboard.readTextFinish(res, &gerr);
+ if (gerr) |err| {
+ defer err.free();
+ log.warn(
+ "failed to read clipboard err={s}",
+ .{err.f_message orelse "(no message)"},
+ );
+ return;
+ }
+ const cstr = cstr_ orelse return;
+ defer glib.free(cstr);
+ const str = std.mem.sliceTo(cstr, 0);
+
+ const surface = self.private().core_surface orelse return;
+ surface.completeClipboardRequest(
+ req.state,
+ str,
+ false,
+ ) catch |err| switch (err) {
+ error.UnsafePaste,
+ error.UnauthorizedPaste,
+ => {
+ log.warn("unsafe paste, TODO confirmation", .{});
+
+ // Create a dialog and ask the user if they want to paste anyway.
+ // ClipboardConfirmationWindow.create(
+ // self.app,
+ // str,
+ // &self.core_surface,
+ // req.state,
+ // self.is_secure_input,
+ // ) catch |window_err| {
+ // log.warn(
+ // "failed to create clipboard confirmation window err={}",
+ // .{window_err},
+ // );
+ // };
+ return;
+ },
+
+ else => log.warn(
+ "failed to complete clipboard request err={}",
+ .{err},
+ ),
+ };
+ }
+
+ /// The request we send as userdata to the clipboard read.
+ const Request = struct {
+ /// "Self" is reffed so we can't dispose it until the clipboard
+ /// read is complete. Callers must unref when done.
+ self: *Surface,
+ state: apprt.ClipboardRequest,
+ };
+};