chore: import upstream snapshot with attribution
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import Carbon
|
||||
import Cocoa
|
||||
import OSLog
|
||||
|
||||
// Manages the secure keyboard input state. Secure keyboard input is an old Carbon
|
||||
// API still in use by applications such as Webkit. From the old Carbon docs:
|
||||
// "When secure event input mode is enabled, keyboard input goes only to the
|
||||
// application with keyboard focus and is not echoed to other applications that
|
||||
// might be using the event monitor target to watch keyboard input."
|
||||
//
|
||||
// Secure input is global and stateful so you need a singleton class to manage
|
||||
// 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: ObservableObject {
|
||||
static let shared = SecureInput()
|
||||
|
||||
private static let logger = Logger(
|
||||
subsystem: Bundle.main.bundleIdentifier!,
|
||||
category: String(describing: SecureInput.self)
|
||||
)
|
||||
|
||||
// True if you want to enable secure input globally.
|
||||
var global: Bool = false {
|
||||
didSet {
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
// The scoped objects and whether they're currently in focus.
|
||||
private var scoped: [ObjectIdentifier: Bool] = [:]
|
||||
|
||||
// This is set to true when we've successfully called EnableSecureInput.
|
||||
@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
|
||||
// in focus.
|
||||
private var desired: Bool {
|
||||
global || scoped.contains(where: { $0.value })
|
||||
}
|
||||
|
||||
private init() {
|
||||
// Add notifications for application active/resign so we can disable
|
||||
// secure input. This is only useful for global enabling of secure
|
||||
// input.
|
||||
let center = NotificationCenter.default
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onDidResignActive(notification:)),
|
||||
name: NSApplication.didResignActiveNotification,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onDidBecomeActive(notification:)),
|
||||
name: NSApplication.didBecomeActiveNotification,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
|
||||
// Reset our state so that we can ensure we set the proper secure input
|
||||
// system state
|
||||
scoped.removeAll()
|
||||
global = false
|
||||
apply()
|
||||
}
|
||||
|
||||
// Add a scoped object that has secure input enabled. The focused value will
|
||||
// determine if the object currently has focus. This is used so that secure
|
||||
// input is only enabled while the object is focused.
|
||||
func setScoped(_ object: ObjectIdentifier, focused: Bool) {
|
||||
scoped[object] = focused
|
||||
apply()
|
||||
}
|
||||
|
||||
// Remove a scoped object completely.
|
||||
func removeScoped(_ object: ObjectIdentifier) {
|
||||
scoped[object] = nil
|
||||
apply()
|
||||
}
|
||||
|
||||
private func apply() {
|
||||
// If we aren't active then we don't do anything. The become/resign
|
||||
// active notifications will handle applying for us.
|
||||
guard NSApp.isActive else { return }
|
||||
|
||||
// We only need to apply if we're not in our desired state
|
||||
guard enabled != desired else { return }
|
||||
|
||||
let err: OSStatus
|
||||
if enabled {
|
||||
err = DisableSecureEventInput()
|
||||
} else {
|
||||
err = EnableSecureEventInput()
|
||||
}
|
||||
if err == noErr {
|
||||
enabled = desired
|
||||
Self.logger.debug("secure input state=\(self.enabled, privacy: .public)")
|
||||
return
|
||||
}
|
||||
|
||||
Self.logger.warning("secure input apply failed err=\(err, privacy: .public)")
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc private func onDidBecomeActive(notification: NSNotification) {
|
||||
// We only want to re-enable if we're not already enabled and we
|
||||
// desire to be enabled.
|
||||
guard !enabled && desired else { return }
|
||||
let err = EnableSecureEventInput()
|
||||
if err == noErr {
|
||||
enabled = true
|
||||
Self.logger.debug("secure input enabled on activation")
|
||||
return
|
||||
}
|
||||
|
||||
Self.logger.warning("secure input apply failed err=\(err, privacy: .public)")
|
||||
}
|
||||
|
||||
@objc private func onDidResignActive(notification: NSNotification) {
|
||||
// We only want to disable if we're enabled.
|
||||
guard enabled else { return }
|
||||
let err = DisableSecureEventInput()
|
||||
if err == noErr {
|
||||
enabled = false
|
||||
Self.logger.debug("secure input disabled on deactivation")
|
||||
return
|
||||
}
|
||||
|
||||
Self.logger.warning("secure input apply failed err=\(err, privacy: .public)")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user