diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index a46491ae6..daeaea583 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -508,12 +508,12 @@ pub fn performAction( .quit_timer => self.quitTimer(value), .prompt_title => try self.promptTitle(target), .toggle_quick_terminal => return try self.toggleQuickTerminal(), + .secure_input => self.setSecureInput(target, value), // Unimplemented .close_all_windows, .toggle_visibility, .cell_size, - .secure_input, .key_sequence, .render_inspector, .renderer_health, @@ -1415,6 +1415,15 @@ fn newWindow(self: *App, parent_: ?*CoreSurface) !void { window.present(); } +fn setSecureInput(_: *App, target: apprt.Target, value: apprt.action.SecureInput) void { + switch (target) { + .app => {}, + .surface => |surface| { + surface.rt_surface.setSecureInput(value); + }, + } +} + fn quit(self: *App) void { // If we're already not running, do nothing. if (!self.running) return; diff --git a/src/apprt/gtk/ClipboardConfirmationWindow.zig b/src/apprt/gtk/ClipboardConfirmationWindow.zig index 632f64fa3..9260d1c7b 100644 --- a/src/apprt/gtk/ClipboardConfirmationWindow.zig +++ b/src/apprt/gtk/ClipboardConfirmationWindow.zig @@ -25,12 +25,17 @@ dialog: *DialogType, data: [:0]u8, core_surface: *CoreSurface, pending_req: apprt.ClipboardRequest, +text_view: *gtk.TextView, +text_view_scroll: *gtk.ScrolledWindow, +reveal_button: *gtk.Button, +hide_button: *gtk.Button, pub fn create( app: *App, data: []const u8, core_surface: *CoreSurface, request: apprt.ClipboardRequest, + is_secure_input: bool, ) !void { if (app.clipboard_confirmation_window != null) return error.WindowAlreadyExists; @@ -43,6 +48,7 @@ pub fn create( data, core_surface, request, + is_secure_input, ); app.clipboard_confirmation_window = self; @@ -62,6 +68,7 @@ fn init( data: []const u8, core_surface: *CoreSurface, request: apprt.ClipboardRequest, + is_secure_input: bool, ) !void { var builder = switch (DialogType) { adw.AlertDialog => switch (request) { @@ -79,6 +86,10 @@ fn init( defer builder.deinit(); const dialog = builder.getObject(DialogType, "clipboard_confirmation_window").?; + const text_view = builder.getObject(gtk.TextView, "text_view").?; + const reveal_button = builder.getObject(gtk.Button, "reveal_button").?; + const hide_button = builder.getObject(gtk.Button, "hide_button").?; + const text_view_scroll = builder.getObject(gtk.ScrolledWindow, "text_view_scroll").?; const copy = try app.core_app.alloc.dupeZ(u8, data); errdefer app.core_app.alloc.free(copy); @@ -88,15 +99,39 @@ fn init( .data = copy, .core_surface = core_surface, .pending_req = request, + .text_view = text_view, + .text_view_scroll = text_view_scroll, + .reveal_button = reveal_button, + .hide_button = hide_button, }; - const text_view = builder.getObject(gtk.TextView, "text_view").?; - const buffer = gtk.TextBuffer.new(null); errdefer buffer.unref(); buffer.insertAtCursor(copy.ptr, @intCast(copy.len)); text_view.setBuffer(buffer); + if (is_secure_input) { + text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(false)); + self.text_view.as(gtk.Widget).addCssClass("blurred"); + + self.reveal_button.as(gtk.Widget).setVisible(@intFromBool(true)); + + _ = gtk.Button.signals.clicked.connect( + reveal_button, + *ClipboardConfirmation, + gtkRevealButtonClicked, + self, + .{}, + ); + _ = gtk.Button.signals.clicked.connect( + hide_button, + *ClipboardConfirmation, + gtkHideButtonClicked, + self, + .{}, + ); + } + switch (DialogType) { adw.AlertDialog => { const parent: ?*gtk.Widget = widget: { @@ -152,3 +187,19 @@ fn gtkResponse(_: *DialogType, response: [*:0]u8, self: *ClipboardConfirmation) } self.destroy(); } + +fn gtkRevealButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.C) void { + self.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(true)); + self.text_view.as(gtk.Widget).removeCssClass("blurred"); + + self.hide_button.as(gtk.Widget).setVisible(@intFromBool(true)); + self.reveal_button.as(gtk.Widget).setVisible(@intFromBool(false)); +} + +fn gtkHideButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.C) void { + self.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(false)); + self.text_view.as(gtk.Widget).addCssClass("blurred"); + + self.hide_button.as(gtk.Widget).setVisible(@intFromBool(false)); + self.reveal_button.as(gtk.Widget).setVisible(@intFromBool(true)); +} diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index be2dfdc6f..9795762ea 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -308,6 +308,9 @@ context_menu: Menu(Surface, "context_menu", false), /// True when we have a precision scroll in progress precision_scroll: bool = false, +/// Flag indicating whether the surface is in secure input mode. +is_secure_input: bool = false, + /// The state of the key event while we're doing IM composition. /// See gtkKeyPressed for detailed descriptions. pub const IMKeyEvent = enum { @@ -1163,6 +1166,7 @@ pub fn setClipboardString( val, &self.core_surface, .{ .osc_52_write = clipboard_type }, + self.is_secure_input, ) catch |window_err| { log.err("failed to create clipboard confirmation window err={}", .{window_err}); }; @@ -1211,6 +1215,7 @@ fn gtkClipboardRead( str, &self.core_surface, req.state, + self.is_secure_input, ) catch |window_err| { log.err("failed to create clipboard confirmation window err={}", .{window_err}); }; @@ -2231,6 +2236,7 @@ fn doPaste(self: *Surface, data: [:0]const u8) void { data, &self.core_surface, .paste, + self.is_secure_input, ) catch |window_err| { log.err("failed to create clipboard confirmation window err={}", .{window_err}); }; @@ -2323,3 +2329,11 @@ fn gtkPromptTitleResponse(source_object: ?*gobject.Object, result: *gio.AsyncRes } } } + +pub fn setSecureInput(self: *Surface, value: apprt.action.SecureInput) void { + switch (value) { + .on => self.is_secure_input = true, + .off => self.is_secure_input = false, + .toggle => self.is_secure_input = !self.is_secure_input, + } +} diff --git a/src/apprt/gtk/style.css b/src/apprt/gtk/style.css index 75d020a06..ecaef6b33 100644 --- a/src/apprt/gtk/style.css +++ b/src/apprt/gtk/style.css @@ -63,3 +63,13 @@ window.ssd.no-border-radius { margin: 0; padding: 0; } + +.clipboard-content-view { + filter: blur(0px); + transition: filter 0.3s ease; +} + +.clipboard-content-view.blurred { + filter: blur(5px); + transition: filter 0.3s ease; +} diff --git a/src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp b/src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp index eafa92e1b..c76b69884 100644 --- a/src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp +++ b/src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp @@ -14,18 +14,58 @@ Adw.MessageDialog clipboard_confirmation_window { default-response: "cancel"; close-response: "cancel"; - extra-child: ScrolledWindow { - width-request: 500; - height-request: 250; + extra-child: Overlay { + styles [ + "osd" + ] - TextView text_view { - cursor-visible: false; - editable: false; - monospace: true; - top-margin: 8; - left-margin: 8; - bottom-margin: 8; - right-margin: 8; + ScrolledWindow text_view_scroll { + width-request: 500; + height-request: 250; + + TextView text_view { + cursor-visible: false; + editable: false; + monospace: true; + top-margin: 8; + left-margin: 8; + bottom-margin: 8; + right-margin: 8; + + styles [ + "clipboard-content-view" + ] + } + } + + [overlay] + Button reveal_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + Image { + icon-name: "view-reveal-symbolic"; + } + } + + [overlay] + Button hide_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + styles [ + "opaque" + ] + + Image { + icon-name: "view-conceal-symbolic"; + } } }; } diff --git a/src/apprt/gtk/ui/1.2/ccw-osc-52-read.ui b/src/apprt/gtk/ui/1.2/ccw-osc-52-read.ui index 9c89835a0..82512e3a2 100644 --- a/src/apprt/gtk/ui/1.2/ccw-osc-52-read.ui +++ b/src/apprt/gtk/ui/1.2/ccw-osc-52-read.ui @@ -7,27 +7,68 @@ corresponding .blp file and regenerate this file with blueprint-compiler. - Authorize Clipboard Access - An application is attempting to read from the clipboard. The current clipboard contents are shown below. + Authorize Clipboard Access + An application is attempting to read from the clipboard. The current clipboard contents are shown below. - Deny - Allow + Deny + Allow cancel cancel - - 500 - 250 + + - - false - false - true - 8 - 8 - 8 - 8 + + 500 + 250 + + + false + false + true + 8 + 8 + 8 + 8 + + + + + + + + false + 2 + 1 + 12 + 12 + + + view-reveal-symbolic + + + + + + + false + 2 + 1 + 12 + 12 + + + + view-conceal-symbolic + + diff --git a/src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp b/src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp index ecd58929b..529a2fc52 100644 --- a/src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp +++ b/src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp @@ -14,18 +14,58 @@ Adw.MessageDialog clipboard_confirmation_window { default-response: "cancel"; close-response: "cancel"; - extra-child: ScrolledWindow { - width-request: 500; - height-request: 250; + extra-child: Overlay { + styles [ + "osd" + ] - TextView text_view { - cursor-visible: false; - editable: false; - monospace: true; - top-margin: 8; - left-margin: 8; - bottom-margin: 8; - right-margin: 8; + ScrolledWindow text_view_scroll { + width-request: 500; + height-request: 250; + + TextView text_view { + cursor-visible: false; + editable: false; + monospace: true; + top-margin: 8; + left-margin: 8; + bottom-margin: 8; + right-margin: 8; + + styles [ + "clipboard-content-view" + ] + } + } + + [overlay] + Button reveal_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + Image { + icon-name: "view-reveal-symbolic"; + } + } + + [overlay] + Button hide_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + styles [ + "opaque" + ] + + Image { + icon-name: "view-conceal-symbolic"; + } } }; } diff --git a/src/apprt/gtk/ui/1.2/ccw-osc-52-write.ui b/src/apprt/gtk/ui/1.2/ccw-osc-52-write.ui index a8904de0d..195fb1de1 100644 --- a/src/apprt/gtk/ui/1.2/ccw-osc-52-write.ui +++ b/src/apprt/gtk/ui/1.2/ccw-osc-52-write.ui @@ -7,27 +7,68 @@ corresponding .blp file and regenerate this file with blueprint-compiler. - Authorize Clipboard Access - An application is attempting to write to the clipboard. The current clipboard contents are shown below. + Authorize Clipboard Access + An application is attempting to write to the clipboard. The current clipboard contents are shown below. - Deny - Allow + Deny + Allow cancel cancel - - 500 - 250 + + - - false - false - true - 8 - 8 - 8 - 8 + + 500 + 250 + + + false + false + true + 8 + 8 + 8 + 8 + + + + + + + + false + 2 + 1 + 12 + 12 + + + view-reveal-symbolic + + + + + + + false + 2 + 1 + 12 + 12 + + + + view-conceal-symbolic + + diff --git a/src/apprt/gtk/ui/1.2/ccw-paste.blp b/src/apprt/gtk/ui/1.2/ccw-paste.blp index 916860368..3ca12e966 100644 --- a/src/apprt/gtk/ui/1.2/ccw-paste.blp +++ b/src/apprt/gtk/ui/1.2/ccw-paste.blp @@ -14,18 +14,58 @@ Adw.MessageDialog clipboard_confirmation_window { default-response: "cancel"; close-response: "cancel"; - extra-child: ScrolledWindow { - width-request: 500; - height-request: 250; + extra-child: Overlay { + styles [ + "osd" + ] - TextView text_view { - cursor-visible: false; - editable: false; - monospace: true; - top-margin: 8; - left-margin: 8; - bottom-margin: 8; - right-margin: 8; + ScrolledWindow text_view_scroll { + width-request: 500; + height-request: 250; + + TextView text_view { + cursor-visible: false; + editable: false; + monospace: true; + top-margin: 8; + left-margin: 8; + bottom-margin: 8; + right-margin: 8; + + styles [ + "clipboard-content-view" + ] + } + } + + [overlay] + Button reveal_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + Image { + icon-name: "view-reveal-symbolic"; + } + } + + [overlay] + Button hide_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + styles [ + "opaque" + ] + + Image { + icon-name: "view-conceal-symbolic"; + } } }; } diff --git a/src/apprt/gtk/ui/1.2/ccw-paste.ui b/src/apprt/gtk/ui/1.2/ccw-paste.ui index 33e75b3e8..342c767e6 100644 --- a/src/apprt/gtk/ui/1.2/ccw-paste.ui +++ b/src/apprt/gtk/ui/1.2/ccw-paste.ui @@ -7,27 +7,68 @@ corresponding .blp file and regenerate this file with blueprint-compiler. - Warning: Potentially Unsafe Paste - Pasting this text into the terminal may be dangerous as it looks like some commands may be executed. + 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 + Paste cancel cancel - - 500 - 250 + + - - false - false - true - 8 - 8 - 8 - 8 + + 500 + 250 + + + false + false + true + 8 + 8 + 8 + 8 + + + + + + + + false + 2 + 1 + 12 + 12 + + + view-reveal-symbolic + + + + + + + false + 2 + 1 + 12 + 12 + + + + view-conceal-symbolic + + diff --git a/src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp b/src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp index 3319bc597..60fca5b00 100644 --- a/src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp +++ b/src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp @@ -14,18 +14,58 @@ Adw.AlertDialog clipboard_confirmation_window { default-response: "cancel"; close-response: "cancel"; - extra-child: ScrolledWindow { - width-request: 500; - height-request: 250; + extra-child: Overlay { + styles [ + "osd" + ] - TextView text_view { - cursor-visible: false; - editable: false; - monospace: true; - top-margin: 8; - left-margin: 8; - bottom-margin: 8; - right-margin: 8; + ScrolledWindow text_view_scroll { + width-request: 500; + height-request: 250; + + TextView text_view { + cursor-visible: false; + editable: false; + monospace: true; + top-margin: 8; + left-margin: 8; + bottom-margin: 8; + right-margin: 8; + + styles [ + "clipboard-content-view" + ] + } + } + + [overlay] + Button reveal_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + Image { + icon-name: "view-reveal-symbolic"; + } + } + + [overlay] + Button hide_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + styles [ + "opaque" + ] + + Image { + icon-name: "view-conceal-symbolic"; + } } }; } diff --git a/src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp b/src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp index 8372f0ccb..7ef17def0 100644 --- a/src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp +++ b/src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp @@ -14,18 +14,58 @@ Adw.AlertDialog clipboard_confirmation_window { default-response: "cancel"; close-response: "cancel"; - extra-child: ScrolledWindow { - width-request: 500; - height-request: 250; + extra-child: Overlay { + styles [ + "osd" + ] - TextView text_view { - cursor-visible: false; - editable: false; - monospace: true; - top-margin: 8; - left-margin: 8; - bottom-margin: 8; - right-margin: 8; + ScrolledWindow text_view_scroll { + width-request: 500; + height-request: 250; + + TextView text_view { + cursor-visible: false; + editable: false; + monospace: true; + top-margin: 8; + left-margin: 8; + bottom-margin: 8; + right-margin: 8; + + styles [ + "clipboard-content-view" + ] + } + } + + [overlay] + Button reveal_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + Image { + icon-name: "view-reveal-symbolic"; + } + } + + [overlay] + Button hide_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + styles [ + "opaque" + ] + + Image { + icon-name: "view-conceal-symbolic"; + } } }; } diff --git a/src/apprt/gtk/ui/1.5/ccw-paste.blp b/src/apprt/gtk/ui/1.5/ccw-paste.blp index 57aaabfd1..57539fb44 100644 --- a/src/apprt/gtk/ui/1.5/ccw-paste.blp +++ b/src/apprt/gtk/ui/1.5/ccw-paste.blp @@ -10,22 +10,62 @@ Adw.AlertDialog clipboard_confirmation_window { cancel: _("Cancel") suggested, ok: _("Paste") destructive ] - + default-response: "cancel"; close-response: "cancel"; - - extra-child: ScrolledWindow { - width-request: 500; - height-request: 250; - TextView text_view { - cursor-visible: false; - editable: false; - monospace: true; - top-margin: 8; - left-margin: 8; - bottom-margin: 8; - right-margin: 8; + extra-child: Overlay { + styles [ + "osd" + ] + + ScrolledWindow text_view_scroll { + width-request: 500; + height-request: 250; + + TextView text_view { + cursor-visible: false; + editable: false; + monospace: true; + top-margin: 8; + left-margin: 8; + bottom-margin: 8; + right-margin: 8; + + styles [ + "clipboard-content-view" + ] + } + } + + [overlay] + Button reveal_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + Image { + icon-name: "view-reveal-symbolic"; + } + } + + [overlay] + Button hide_button { + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + styles [ + "opaque" + ] + + Image { + icon-name: "view-conceal-symbolic"; + } } }; }