macos: show secure input overlay when it is enabled

This commit is contained in:
Mitchell Hashimoto
2024-09-19 16:24:19 -07:00
parent c0e0eff468
commit c3d6356a87
5 changed files with 109 additions and 5 deletions

View File

@@ -12,7 +12,7 @@ import OSLog
// it. You have to yield secure input on application deactivation (because
// it'll affect other apps) and reacquire on reactivation, and every enable
// needs to be balanced with a disable.
class SecureInput {
class SecureInput : ObservableObject {
static let shared = SecureInput()
private static let logger = Logger(
@@ -31,7 +31,7 @@ class SecureInput {
private var scoped: [ObjectIdentifier: Bool] = [:]
// This is set to true when we've successfully called EnableSecureInput.
private var enabled: Bool = false
@Published private(set) var enabled: Bool = false
// This is true if we want to enable secure input. We want to enable
// secure input if its enabled globally or any of the scoped objects are

View File

@@ -0,0 +1,67 @@
import SwiftUI
struct SecureInputOverlay: View {
// Animations
@State private var shadowAngle: Angle = .degrees(0)
@State private var shadowWidth: CGFloat = 6
// Popover explainer text
@State private var isPopover = false
var body: some View {
VStack {
HStack {
Spacer()
Image(systemName: "lock.shield.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25)
.foregroundColor(.primary)
.padding(5)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(.background)
.innerShadow(
using: RoundedRectangle(cornerRadius: 12),
stroke: AngularGradient(
gradient: Gradient(colors: [.cyan, .blue, .yellow, .blue, .cyan]),
center: .center,
angle: shadowAngle
),
width: shadowWidth
)
)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color.gray, lineWidth: 1)
)
.onTapGesture {
isPopover = true
}
.padding(.top, 10)
.padding(.trailing, 10)
.popover(isPresented: $isPopover, arrowEdge: .bottom) {
Text("""
Secure Input is active. Secure Input is a macOS security feature that
prevents applications from reading keyboard events. Ghostty turns
this on manually if `Ghostty > Secure Keyboard Entry` is enabled or
automatically when at a password prompt.
""")
.padding(.all)
}
}
Spacer()
}
.onAppear {
withAnimation(Animation.linear(duration: 2).repeatForever(autoreverses: false)) {
shadowAngle = .degrees(360)
}
withAnimation(Animation.linear(duration: 2).repeatForever(autoreverses: true)) {
shadowWidth = 12
}
}
}
}

View File

@@ -52,6 +52,9 @@ extension Ghostty {
// True if we're hovering over the left URL view, so we can show it on the right.
@State private var isHoveringURLLeft: Bool = false
// Observe SecureInput to detect when its enabled
@ObservedObject private var secureInput = SecureInput.shared
@EnvironmentObject private var ghostty: Ghostty.App
var body: some View {
@@ -197,6 +200,14 @@ extension Ghostty {
}
}
// If we have secure input enabled and we're the focused surface and window
// then we want to show the secure input overlay.
if (secureInput.enabled &&
surfaceFocus &&
windowFocus) {
SecureInputOverlay()
}
// If our surface is not healthy, then we render an error view over it.
if (!surfaceView.healthy) {
Rectangle().fill(ghostty.config.backgroundColor)

View File

@@ -0,0 +1,18 @@
import SwiftUI
extension View {
func innerShadow<S: Shape, ST: ShapeStyle>(
using shape: S = Rectangle(),
stroke: ST = Color.black,
width: CGFloat = 6,
blur: CGFloat = 6
) -> some View {
return self
.overlay(
shape
.stroke(stroke, lineWidth: width)
.blur(radius: blur)
.mask(shape)
)
}
}