mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 17:26:09 +03:00
macos: show secure input overlay when it is enabled
This commit is contained in:
@ -58,6 +58,8 @@
|
||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||
A5CC36132C9CD72D004D6760 /* SecureInputOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CC36122C9CD729004D6760 /* SecureInputOverlay.swift */; };
|
||||
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CC36142C9CDA03004D6760 /* View+Extension.swift */; };
|
||||
A5CDF1912AAF9A5800513312 /* ConfigurationErrors.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5CDF1902AAF9A5800513312 /* ConfigurationErrors.xib */; };
|
||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CDF1922AAF9E0800513312 /* ConfigurationErrorsController.swift */; };
|
||||
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CDF1942AAFA19600513312 /* ConfigurationErrorsView.swift */; };
|
||||
@ -124,6 +126,8 @@
|
||||
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
||||
A5CC36122C9CD729004D6760 /* SecureInputOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureInputOverlay.swift; sourceTree = "<group>"; };
|
||||
A5CC36142C9CDA03004D6760 /* View+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extension.swift"; sourceTree = "<group>"; };
|
||||
A5CDF1902AAF9A5800513312 /* ConfigurationErrors.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConfigurationErrors.xib; sourceTree = "<group>"; };
|
||||
A5CDF1922AAF9E0800513312 /* ConfigurationErrorsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationErrorsController.swift; sourceTree = "<group>"; };
|
||||
A5CDF1942AAFA19600513312 /* ConfigurationErrorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationErrorsView.swift; sourceTree = "<group>"; };
|
||||
@ -214,6 +218,7 @@
|
||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
||||
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
||||
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */,
|
||||
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */,
|
||||
A5CEAFDA29B8005900646FDA /* SplitView */,
|
||||
@ -302,6 +307,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A57D79262C9C8798001D522E /* SecureInput.swift */,
|
||||
A5CC36122C9CD729004D6760 /* SecureInputOverlay.swift */,
|
||||
);
|
||||
path = "Secure Input";
|
||||
sourceTree = "<group>";
|
||||
@ -507,6 +513,7 @@
|
||||
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
||||
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */,
|
||||
A596309C2AEE1C9E00D64628 /* TerminalController.swift in Sources */,
|
||||
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
|
||||
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
||||
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
|
||||
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
||||
@ -532,6 +539,7 @@
|
||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||
A5CC36132C9CD72D004D6760 /* SecureInputOverlay.swift in Sources */,
|
||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
||||
A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */,
|
||||
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */,
|
||||
@ -646,7 +654,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 0.1;
|
||||
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.ghostty;
|
||||
@ -797,7 +805,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 0.1;
|
||||
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.ghostty;
|
||||
@ -836,7 +844,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 0.1;
|
||||
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.ghostty;
|
||||
|
@ -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
|
||||
|
67
macos/Sources/Features/Secure Input/SecureInputOverlay.swift
Normal file
67
macos/Sources/Features/Secure Input/SecureInputOverlay.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
18
macos/Sources/Helpers/View+Extension.swift
Normal file
18
macos/Sources/Helpers/View+Extension.swift
Normal 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)
|
||||
)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user