diff --git a/src/apprt/gtk/ClipboardConfirmationWindow.zig b/src/apprt/gtk/ClipboardConfirmationWindow.zig
index cf417b668..d1494d0ae 100644
--- a/src/apprt/gtk/ClipboardConfirmationWindow.zig
+++ b/src/apprt/gtk/ClipboardConfirmationWindow.zig
@@ -4,18 +4,24 @@ const ClipboardConfirmation = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
+const gtk = @import("gtk");
+const adw = @import("adw");
+const gobject = @import("gobject");
+const gio = @import("gio");
+
const apprt = @import("../../apprt.zig");
const CoreSurface = @import("../../Surface.zig");
const App = @import("App.zig");
const View = @import("View.zig");
-const c = @import("c.zig").c;
+const Builder = @import("Builder.zig");
+const adwaita = @import("adwaita.zig");
const log = std.log.scoped(.gtk);
-app: *App,
-window: *c.GtkWindow,
-view: PrimaryView,
+const DialogType = if (adwaita.versionAtLeast(1, 5, 0)) adw.AlertDialog else adw.MessageDialog;
+app: *App,
+dialog: *DialogType,
data: [:0]u8,
core_surface: *CoreSurface,
pending_req: apprt.ClipboardRequest,
@@ -57,201 +63,92 @@ fn init(
core_surface: *CoreSurface,
request: apprt.ClipboardRequest,
) !void {
- // Create the window
- const window = c.gtk_window_new();
- const gtk_window: *c.GtkWindow = @ptrCast(window);
- errdefer c.gtk_window_destroy(gtk_window);
- c.gtk_window_set_title(gtk_window, titleText(request));
- c.gtk_window_set_default_size(gtk_window, 550, 275);
- c.gtk_window_set_resizable(gtk_window, 0);
- c.gtk_widget_add_css_class(@ptrCast(@alignCast(gtk_window)), "window");
- c.gtk_widget_add_css_class(@ptrCast(@alignCast(gtk_window)), "clipboard-confirmation-window");
- _ = c.g_signal_connect_data(
- window,
- "destroy",
- c.G_CALLBACK(>kDestroy),
- self,
- null,
- c.G_CONNECT_DEFAULT,
- );
+ var builder = switch (DialogType) {
+ adw.AlertDialog => switch (request) {
+ .osc_52_read => Builder.init("ccw-osc-52-write-15", .blp),
+ .osc_52_write => Builder.init("ccw-osc-52-write-15", .blp),
+ .paste => Builder.init("ccw-paste-15", .blp),
+ },
+ adw.MessageDialog => switch (request) {
+ .osc_52_read => Builder.init("ccw-osc-52-write-12", .ui),
+ .osc_52_write => Builder.init("ccw-osc-52-write-12", .ui),
+ .paste => Builder.init("ccw-paste-12", .ui),
+ },
+ else => unreachable,
+ };
+ builder.deinit();
- // Set some state
+ const dialog = builder.getObject(DialogType, "clipboard_confirmation_window").?;
+
+ const copy = try app.core_app.alloc.dupeZ(u8, data);
+ errdefer app.core_app.alloc.free(copy);
self.* = .{
.app = app,
- .window = gtk_window,
- .view = undefined,
- .data = try app.core_app.alloc.dupeZ(u8, data),
+ .dialog = dialog,
+ .data = copy,
.core_surface = core_surface,
.pending_req = request,
};
- // Show the window
- const view = try PrimaryView.init(self, data);
- self.view = view;
- c.gtk_window_set_child(@ptrCast(window), view.root);
- _ = c.gtk_widget_grab_focus(view.buttons.cancel_button);
+ const text_view = builder.getObject(gtk.TextView, "text_view").?;
- c.gtk_widget_show(window);
+ const buffer = gtk.TextBuffer.new(null);
+ errdefer buffer.unref();
+ buffer.insertAtCursor(copy.ptr, @intCast(copy.len));
+ text_view.setBuffer(buffer);
- // Block the main window from input.
- // This will auto-revert when the window is closed.
- c.gtk_window_set_modal(gtk_window, 1);
+ switch (DialogType) {
+ adw.AlertDialog => {
+ const parent: ?*gtk.Widget = widget: {
+ const window = core_surface.rt_surface.container.window() orelse break :widget null;
+ break :widget @ptrCast(@alignCast(window.window));
+ };
+
+ dialog.choose(parent, null, gtkChoose, self);
+ },
+ adw.MessageDialog => {
+ if (adwaita.versionAtLeast(1, 3, 0)) {
+ dialog.choose(null, gtkChoose, self);
+ } else {
+ _ = adw.MessageDialog.signals.response.connect(
+ dialog,
+ *ClipboardConfirmation,
+ gtkResponse,
+ self,
+ .{},
+ );
+ dialog.as(gtk.Widget).show();
+ }
+ },
+ else => unreachable,
+ }
}
-fn gtkDestroy(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
- const self: *ClipboardConfirmation = @ptrCast(@alignCast(ud orelse return));
- self.destroy();
-}
-
-const PrimaryView = struct {
- root: *c.GtkWidget,
- text: *c.GtkTextView,
- buttons: ButtonsView,
-
- pub fn init(root: *ClipboardConfirmation, data: []const u8) !PrimaryView {
- // All our widgets
- const label = c.gtk_label_new(promptText(root.pending_req));
- const buf = unsafeBuffer(data);
- defer c.g_object_unref(buf);
- const buttons = try ButtonsView.init(root);
- const text_scroll = c.gtk_scrolled_window_new();
- errdefer c.g_object_unref(text_scroll);
- const text = c.gtk_text_view_new_with_buffer(buf);
- errdefer c.g_object_unref(text);
- c.gtk_scrolled_window_set_child(@ptrCast(text_scroll), text);
-
- // Create our view
- const view = try View.init(&.{
- .{ .name = "label", .widget = label },
- .{ .name = "text", .widget = text_scroll },
- .{ .name = "buttons", .widget = buttons.root },
- }, &vfl);
- errdefer view.deinit();
-
- // We can do additional settings once the layout is setup
- c.gtk_label_set_wrap(@ptrCast(label), 1);
- c.gtk_text_view_set_editable(@ptrCast(text), 0);
- c.gtk_text_view_set_cursor_visible(@ptrCast(text), 0);
- c.gtk_text_view_set_top_margin(@ptrCast(text), 8);
- c.gtk_text_view_set_bottom_margin(@ptrCast(text), 8);
- c.gtk_text_view_set_left_margin(@ptrCast(text), 8);
- c.gtk_text_view_set_right_margin(@ptrCast(text), 8);
- c.gtk_text_view_set_monospace(@ptrCast(text), 1);
-
- return .{ .root = view.root, .text = @ptrCast(text), .buttons = buttons };
- }
-
- /// Returns the GtkTextBuffer for the data that was unsafe.
- fn unsafeBuffer(data: []const u8) *c.GtkTextBuffer {
- const buf = c.gtk_text_buffer_new(null);
- errdefer c.g_object_unref(buf);
-
- c.gtk_text_buffer_insert_at_cursor(buf, data.ptr, @intCast(data.len));
-
- return buf;
- }
-
- const vfl = [_][*:0]const u8{
- "H:|-8-[label]-8-|",
- "H:|[text]|",
- "H:|[buttons]|",
- "V:|[label(<=80)][text(>=100)]-[buttons]-|",
- };
-};
-
-const ButtonsView = struct {
- root: *c.GtkWidget,
- confirm_button: *c.GtkWidget,
- cancel_button: *c.GtkWidget,
-
- pub fn init(root: *ClipboardConfirmation) !ButtonsView {
- const cancel_text, const confirm_text = switch (root.pending_req) {
- .paste => .{ "Cancel", "Paste" },
- .osc_52_read, .osc_52_write => .{ "Deny", "Allow" },
- };
-
- const cancel_button = c.gtk_button_new_with_label(cancel_text);
- errdefer c.g_object_unref(cancel_button);
-
- const confirm_button = c.gtk_button_new_with_label(confirm_text);
- errdefer c.g_object_unref(confirm_button);
-
- c.gtk_widget_add_css_class(confirm_button, "destructive-action");
- c.gtk_widget_add_css_class(cancel_button, "suggested-action");
-
- // Create our view
- const view = try View.init(&.{
- .{ .name = "cancel", .widget = cancel_button },
- .{ .name = "confirm", .widget = confirm_button },
- }, &vfl);
-
- // Signals
- _ = c.g_signal_connect_data(
- cancel_button,
- "clicked",
- c.G_CALLBACK(>kCancelClick),
- root,
- null,
- c.G_CONNECT_DEFAULT,
- );
- _ = c.g_signal_connect_data(
- confirm_button,
- "clicked",
- c.G_CALLBACK(>kConfirmClick),
- root,
- null,
- c.G_CONNECT_DEFAULT,
- );
-
- return .{ .root = view.root, .confirm_button = confirm_button, .cancel_button = cancel_button };
- }
-
- fn gtkCancelClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
- const self: *ClipboardConfirmation = @ptrCast(@alignCast(ud));
- c.gtk_window_destroy(@ptrCast(self.window));
- }
-
- fn gtkConfirmClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
- // Requeue the paste with force.
- const self: *ClipboardConfirmation = @ptrCast(@alignCast(ud));
+fn gtkChoose(dialog_: ?*gobject.Object, result: *gio.AsyncResult, ud: ?*anyopaque) callconv(.C) void {
+ const dialog = gobject.ext.cast(DialogType, dialog_.?).?;
+ const self: *ClipboardConfirmation = @ptrCast(@alignCast(ud.?));
+ const response = dialog.chooseFinish(result);
+ if (std.mem.orderZ(u8, response, "ok") == .eq) {
self.core_surface.completeClipboardRequest(
self.pending_req,
self.data,
true,
) catch |err| {
- std.log.err("Failed to requeue clipboard request: {}", .{err});
+ log.err("Failed to requeue clipboard request: {}", .{err});
};
-
- c.gtk_window_destroy(@ptrCast(self.window));
}
-
- const vfl = [_][*:0]const u8{
- "H:[cancel]-8-[confirm]-8-|",
- };
-};
-
-/// The title of the window, based on the reason the prompt is being shown.
-fn titleText(req: apprt.ClipboardRequest) [:0]const u8 {
- return switch (req) {
- .paste => "Warning: Potentially Unsafe Paste",
- .osc_52_read, .osc_52_write => "Authorize Clipboard Access",
- };
+ self.destroy();
}
-/// The text to display in the prompt window, based on the reason the prompt
-/// is being shown.
-fn promptText(req: apprt.ClipboardRequest) [:0]const u8 {
- return switch (req) {
- .paste =>
- \\Pasting this text into the terminal may be dangerous as it looks like some commands may be executed.
- ,
- .osc_52_read =>
- \\An application is attempting to read from the clipboard.
- \\The current clipboard contents are shown below.
- ,
- .osc_52_write =>
- \\An application is attempting to write to the clipboard.
- \\The content to write is shown below.
- ,
- };
+fn gtkResponse(_: *DialogType, response: [*:0]u8, self: *ClipboardConfirmation) callconv(.C) void {
+ if (std.mem.orderZ(u8, response, "ok") == .eq) {
+ self.core_surface.completeClipboardRequest(
+ self.pending_req,
+ self.data,
+ true,
+ ) catch |err| {
+ log.err("Failed to requeue clipboard request: {}", .{err});
+ };
+ }
+ self.destroy();
}
diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig
index 09d1c5a90..06a1576d3 100644
--- a/src/apprt/gtk/Surface.zig
+++ b/src/apprt/gtk/Surface.zig
@@ -8,6 +8,7 @@ const adw = @import("adw");
const gtk = @import("gtk");
const gio = @import("gio");
const gobject = @import("gobject");
+
const Allocator = std.mem.Allocator;
const build_config = @import("../../build_config.zig");
const build_options = @import("build_options");
diff --git a/src/apprt/gtk/gresource.zig b/src/apprt/gtk/gresource.zig
index 4bd08ed0a..32995c924 100644
--- a/src/apprt/gtk/gresource.zig
+++ b/src/apprt/gtk/gresource.zig
@@ -53,7 +53,11 @@ const icons = [_]struct {
},
};
-pub const ui_files = [_][]const u8{};
+pub const ui_files = [_][]const u8{
+ "ccw-osc-52-read-12",
+ "ccw-osc-52-write-12",
+ "ccw-paste-12",
+};
pub const VersionedBlueprint = struct {
major: u16,
@@ -66,6 +70,9 @@ pub const blueprint_files = [_]VersionedBlueprint{
.{ .major = 1, .minor = 5, .micro = 0, .name = "prompt-title-dialog" },
.{ .major = 1, .minor = 0, .micro = 0, .name = "menu-surface-context_menu" },
.{ .major = 1, .minor = 0, .micro = 0, .name = "menu-window-titlebar_menu" },
+ .{ .major = 1, .minor = 5, .micro = 0, .name = "ccw-osc-52-read-15" },
+ .{ .major = 1, .minor = 5, .micro = 0, .name = "ccw-osc-52-write-15" },
+ .{ .major = 1, .minor = 5, .micro = 0, .name = "ccw-paste-15" },
};
pub fn main() !void {
diff --git a/src/apprt/gtk/ui/ccw-osc-52-read-12.blp b/src/apprt/gtk/ui/ccw-osc-52-read-12.blp
new file mode 100644
index 000000000..a2ee10e34
--- /dev/null
+++ b/src/apprt/gtk/ui/ccw-osc-52-read-12.blp
@@ -0,0 +1,23 @@
+using Gtk 4.0;
+using Adw 1;
+translation-domain "com.mitchellh.ghostty";
+
+Adw.MessageDialog clipboard_confirmation_window {
+ heading: _("Authorize Clipboard Access");
+ body: _("An application is attempting to read from the clipboard. The current clipboard contents are shown below.");
+
+ responses [
+ cancel: _("Deny") suggested,
+ ok: _("Allow") destructive
+ ]
+
+ default-response: "cancel";
+ close-response: "cancel";
+
+ extra-child: ScrolledWindow {
+ width-request: 500;
+ height-request: 250;
+
+ TextView text_view {}
+ };
+}
diff --git a/src/apprt/gtk/ui/ccw-osc-52-read-12.ui b/src/apprt/gtk/ui/ccw-osc-52-read-12.ui
new file mode 100644
index 000000000..3d8743221
--- /dev/null
+++ b/src/apprt/gtk/ui/ccw-osc-52-read-12.ui
@@ -0,0 +1,28 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/apprt/gtk/ui/ccw-osc-52-read-15.blp b/src/apprt/gtk/ui/ccw-osc-52-read-15.blp
new file mode 100644
index 000000000..a1230b9c6
--- /dev/null
+++ b/src/apprt/gtk/ui/ccw-osc-52-read-15.blp
@@ -0,0 +1,23 @@
+using Gtk 4.0;
+using Adw 1;
+translation-domain "com.mitchellh.ghostty";
+
+Adw.AlertDialog clipboard_confirmation_window {
+ heading: _("Authorize Clipboard Access");
+ body: _("An application is attempting to read from the clipboard. The current clipboard contents are shown below.");
+
+ responses [
+ cancel: _("Deny") suggested,
+ ok: _("Allow") destructive
+ ]
+
+ default-response: "cancel";
+ close-response: "cancel";
+
+ extra-child: ScrolledWindow {
+ width-request: 500;
+ height-request: 250;
+
+ TextView text_view {}
+ };
+}
diff --git a/src/apprt/gtk/ui/ccw-osc-52-write-12.blp b/src/apprt/gtk/ui/ccw-osc-52-write-12.blp
new file mode 100644
index 000000000..97368cc21
--- /dev/null
+++ b/src/apprt/gtk/ui/ccw-osc-52-write-12.blp
@@ -0,0 +1,23 @@
+using Gtk 4.0;
+using Adw 1;
+translation-domain "com.mitchellh.ghostty";
+
+Adw.MessageDialog clipboard_confirmation_window {
+ heading: _("Authorize Clipboard Access");
+ body: _("An application is attempting to write to the clipboard. The current clipboard contents are shown below.");
+
+ responses [
+ cancel: _("Deny") suggested,
+ ok: _("Allow") destructive
+ ]
+
+ default-response: "cancel";
+ close-response: "cancel";
+
+ extra-child: ScrolledWindow {
+ width-request: 500;
+ height-request: 250;
+
+ TextView text_view {}
+ };
+}
diff --git a/src/apprt/gtk/ui/ccw-osc-52-write-12.ui b/src/apprt/gtk/ui/ccw-osc-52-write-12.ui
new file mode 100644
index 000000000..3febbd558
--- /dev/null
+++ b/src/apprt/gtk/ui/ccw-osc-52-write-12.ui
@@ -0,0 +1,28 @@
+
+
+
+
+
+ Authorize Clipboard Access
+ An application is attempting to write to the clipboard. The current clipboard contents are shown below.
+
+ Deny
+ Allow
+
+ cancel
+ cancel
+
+
+ 500
+ 250
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/apprt/gtk/ui/ccw-osc-52-write-15.blp b/src/apprt/gtk/ui/ccw-osc-52-write-15.blp
new file mode 100644
index 000000000..df36b9153
--- /dev/null
+++ b/src/apprt/gtk/ui/ccw-osc-52-write-15.blp
@@ -0,0 +1,23 @@
+using Gtk 4.0;
+using Adw 1;
+translation-domain "com.mitchellh.ghostty";
+
+Adw.AlertDialog clipboard_confirmation_window {
+ heading: _("Authorize Clipboard Access");
+ body: _("An application is attempting to write to the clipboard. The current clipboard contents are shown below.");
+
+ responses [
+ cancel: _("Deny") suggested,
+ ok: _("Allow") destructive
+ ]
+
+ default-response: "cancel";
+ close-response: "cancel";
+
+ extra-child: ScrolledWindow {
+ width-request: 500;
+ height-request: 250;
+
+ TextView text_view {}
+ };
+}
diff --git a/src/apprt/gtk/ui/ccw-paste-12.blp b/src/apprt/gtk/ui/ccw-paste-12.blp
new file mode 100644
index 000000000..3a530f56d
--- /dev/null
+++ b/src/apprt/gtk/ui/ccw-paste-12.blp
@@ -0,0 +1,23 @@
+using Gtk 4.0;
+using Adw 1;
+translation-domain "com.mitchellh.ghostty";
+
+Adw.MessageDialog clipboard_confirmation_window {
+ heading: _("Warning: Potentially Unsafe Paste");
+ body: _("Pasting this text into the terminal may be dangerous as it looks like some commands may be executed.");
+
+ responses [
+ cancel: _("Cancel") suggested,
+ ok: _("Paste") destructive
+ ]
+
+ default-response: "cancel";
+ close-response: "cancel";
+
+ extra-child: ScrolledWindow {
+ width-request: 500;
+ height-request: 250;
+
+ TextView text_view {}
+ };
+}
diff --git a/src/apprt/gtk/ui/ccw-paste-12.ui b/src/apprt/gtk/ui/ccw-paste-12.ui
new file mode 100644
index 000000000..e2f852f7f
--- /dev/null
+++ b/src/apprt/gtk/ui/ccw-paste-12.ui
@@ -0,0 +1,28 @@
+
+
+
+
+
+ Warning: Potentially Unsafe Paste
+ Pasting this text into the terminal may be dangerous as it looks like some commands may be executed.
+
+ Cancel
+ Paste
+
+ cancel
+ cancel
+
+
+ 500
+ 250
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/apprt/gtk/ui/ccw-paste-15.blp b/src/apprt/gtk/ui/ccw-paste-15.blp
new file mode 100644
index 000000000..8703071a6
--- /dev/null
+++ b/src/apprt/gtk/ui/ccw-paste-15.blp
@@ -0,0 +1,23 @@
+using Gtk 4.0;
+using Adw 1;
+translation-domain "com.mitchellh.ghostty";
+
+Adw.AlertDialog clipboard_confirmation_window {
+ heading: _("Warning: Potentially Unsafe Paste");
+ body: _("Pasting this text into the terminal may be dangerous as it looks like some commands may be executed.");
+
+ responses [
+ cancel: _("Cancel") suggested,
+ ok: _("Paste") destructive
+ ]
+
+ default-response: "cancel";
+ close-response: "cancel";
+
+ extra-child: ScrolledWindow {
+ width-request: 500;
+ height-request: 250;
+
+ TextView text_view {}
+ };
+}