mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +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 */; };
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
||||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
||||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
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 */; };
|
A5CDF1912AAF9A5800513312 /* ConfigurationErrors.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5CDF1902AAF9A5800513312 /* ConfigurationErrors.xib */; };
|
||||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CDF1922AAF9E0800513312 /* ConfigurationErrorsController.swift */; };
|
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CDF1922AAF9E0800513312 /* ConfigurationErrorsController.swift */; };
|
||||||
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CDF1942AAFA19600513312 /* ConfigurationErrorsView.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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
A5CDF1942AAFA19600513312 /* ConfigurationErrorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationErrorsView.swift; sourceTree = "<group>"; };
|
||||||
@ -214,6 +218,7 @@
|
|||||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||||
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
||||||
|
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
||||||
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */,
|
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */,
|
||||||
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */,
|
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */,
|
||||||
A5CEAFDA29B8005900646FDA /* SplitView */,
|
A5CEAFDA29B8005900646FDA /* SplitView */,
|
||||||
@ -302,6 +307,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A57D79262C9C8798001D522E /* SecureInput.swift */,
|
A57D79262C9C8798001D522E /* SecureInput.swift */,
|
||||||
|
A5CC36122C9CD729004D6760 /* SecureInputOverlay.swift */,
|
||||||
);
|
);
|
||||||
path = "Secure Input";
|
path = "Secure Input";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -507,6 +513,7 @@
|
|||||||
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
||||||
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */,
|
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */,
|
||||||
A596309C2AEE1C9E00D64628 /* TerminalController.swift in Sources */,
|
A596309C2AEE1C9E00D64628 /* TerminalController.swift in Sources */,
|
||||||
|
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
|
||||||
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
||||||
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
|
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
|
||||||
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
||||||
@ -532,6 +539,7 @@
|
|||||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||||
|
A5CC36132C9CD72D004D6760 /* SecureInputOverlay.swift in Sources */,
|
||||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
||||||
A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */,
|
A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */,
|
||||||
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */,
|
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */,
|
||||||
@ -646,7 +654,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||||
MARKETING_VERSION = 0.1;
|
MARKETING_VERSION = 0.1;
|
||||||
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.ghostty;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.ghostty;
|
||||||
@ -797,7 +805,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||||
MARKETING_VERSION = 0.1;
|
MARKETING_VERSION = 0.1;
|
||||||
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.ghostty;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.ghostty;
|
||||||
@ -836,7 +844,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||||
MARKETING_VERSION = 0.1;
|
MARKETING_VERSION = 0.1;
|
||||||
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.ghostty;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mitchellh.ghostty;
|
||||||
|
@ -12,7 +12,7 @@ import OSLog
|
|||||||
// it. You have to yield secure input on application deactivation (because
|
// it. You have to yield secure input on application deactivation (because
|
||||||
// it'll affect other apps) and reacquire on reactivation, and every enable
|
// it'll affect other apps) and reacquire on reactivation, and every enable
|
||||||
// needs to be balanced with a disable.
|
// needs to be balanced with a disable.
|
||||||
class SecureInput {
|
class SecureInput : ObservableObject {
|
||||||
static let shared = SecureInput()
|
static let shared = SecureInput()
|
||||||
|
|
||||||
private static let logger = Logger(
|
private static let logger = Logger(
|
||||||
@ -31,7 +31,7 @@ class SecureInput {
|
|||||||
private var scoped: [ObjectIdentifier: Bool] = [:]
|
private var scoped: [ObjectIdentifier: Bool] = [:]
|
||||||
|
|
||||||
// This is set to true when we've successfully called EnableSecureInput.
|
// 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
|
// 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
|
// 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.
|
// True if we're hovering over the left URL view, so we can show it on the right.
|
||||||
@State private var isHoveringURLLeft: Bool = false
|
@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
|
@EnvironmentObject private var ghostty: Ghostty.App
|
||||||
|
|
||||||
var body: some View {
|
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 our surface is not healthy, then we render an error view over it.
|
||||||
if (!surfaceView.healthy) {
|
if (!surfaceView.healthy) {
|
||||||
Rectangle().fill(ghostty.config.backgroundColor)
|
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