@@ -63,6 +63,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
6363
6464 func applicationDidFinishLaunching( _ aNotification: Notification ) {
6565 print ( " CapsLockSwitcher: Did Finish Launching " )
66+
67+ // Set the override
68+ manageCapsLockDelayOverride ( setOverride: true )
69+
6670 // Check permissions initially - prompt might not work on modern OS anyway
6771 let hasPermissions = checkAccessibilityPermissions ( promptUserIfNeeded: false )
6872
@@ -100,6 +104,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
100104
101105 func applicationWillTerminate( _ aNotification: Notification ) {
102106 print ( " CapsLockSwitcher: Will Terminate " )
107+ manageCapsLockDelayOverride ( setOverride: false ) // Reset the override
103108 destroyEventTap ( ) // Clean up the event tap
104109 // Remove status item (optional, system usually does it)
105110 if let item = statusItem {
@@ -739,7 +744,48 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
739744
740745
741746 // MARK: - Helper Functions
747+ /// Manages the system-level CapsLockDelayOverride using hidutil.
748+ /// - Parameter setOverride: If true, sets the delay override to 0. If false, attempts to reset the override
749+ private func manageCapsLockDelayOverride( setOverride: Bool ) {
750+ // Use "0" to set the override, "200" to reset it to default.
751+ let propertyValue = setOverride ? " 0 " : " 200 "
752+ let jsonString = " { \" CapsLockDelayOverride \" : \( propertyValue) } "
753+ let actionDescription = setOverride ? " set CapsLockDelayOverride to 0 " : " reset CapsLockDelayOverride "
754+
755+ print ( " CapsLockSwitcher: Attempting to \( actionDescription) ... " )
756+
757+ let process = Process ( )
758+ // Ensure the path to hidutil is correct. /usr/bin/ is standard.
759+ process. executableURL = URL ( fileURLWithPath: " /usr/bin/hidutil " )
760+ process. arguments = [ " property " , " --set " , jsonString]
761+
762+ // Optional: Capture output/errors for debugging
763+ let pipe = Pipe ( )
764+ process. standardOutput = pipe
765+ process. standardError = pipe
766+
767+ do {
768+ try process. run ( )
769+ process. waitUntilExit ( ) // Wait for the command to complete
770+
771+ let data = pipe. fileHandleForReading. readDataToEndOfFile ( )
772+ if let output = String ( data: data, encoding: . utf8) , !output. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty {
773+ // Log output only if it's not empty to avoid noise
774+ print ( " hidutil output: \( output. trimmingCharacters ( in: . whitespacesAndNewlines) ) " )
775+ }
742776
777+ if process. terminationStatus == 0 {
778+ print ( " CapsLockSwitcher: Successfully \( actionDescription) . " )
779+ } else {
780+ // Log a warning if the command finished with an error status
781+ print ( " Warning: hidutil command finished with status \( process. terminationStatus) . \( actionDescription) might have failed. " )
782+ }
783+ } catch {
784+ // Log an error if process.run() itself throws (e.g., command not found)
785+ print ( " Error executing hidutil to \( actionDescription) : \( error. localizedDescription) " )
786+ }
787+ }
788+
743789 /// Handles cleanup and UI update when permission loss is detected, run on main thread.
744790 private func handleSuspectedPermissionLoss( ) {
745791 // Ensure we are on the main thread for UI updates and state changes
0 commit comments