diff --git a/LICENSE b/LICENSE index 9665631..07b0628 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Nico Verbruggen +Copyright (c) 2019-2022 Nico Verbruggen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index fcf4701..22958eb 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -11,6 +11,16 @@ 5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; }; 54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; }; 54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; }; + 54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */; }; + 54D9E0B327E4F51E003B9AD9 /* HotKeysController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */; }; + 54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AD27E4F51E003B9AD9 /* Key.swift */; }; + 54D9E0B527E4F51E003B9AD9 /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AD27E4F51E003B9AD9 /* Key.swift */; }; + 54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */; }; + 54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */; }; + 54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */; }; + 54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */; }; + 54D9E0BA27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */; }; + 54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */; }; 54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; }; 54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; }; 54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; }; @@ -112,7 +122,6 @@ C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; }; C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; - C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; }; C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49E171E27A5736E00787921 /* PMServicesView.swift */; }; @@ -140,7 +149,6 @@ C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; }; C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; }; C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; }; - C4C1019627C659B7001FACC2 /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4C1019527C659B7001FACC2 /* HotKey */; }; C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; }; C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; }; C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; }; @@ -231,6 +239,13 @@ 5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = ""; }; 5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; 54B48B5E275F66AE006D90C5 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = ""; }; + 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKeysController.swift; sourceTree = ""; }; + 54D9E0AD27E4F51E003B9AD9 /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = ""; }; + 54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKey.swift; sourceTree = ""; }; + 54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyCombo.swift; sourceTree = ""; }; + 54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModifierFlagsExtension.swift; sourceTree = ""; }; + 54D9E0BF27E4F5D9003B9AD9 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + 54D9E0C027E4F5E9003B9AD9 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SelectPreferenceView.xib; sourceTree = ""; }; 54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxPreferenceView.swift; sourceTree = ""; }; 54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HotkeyPreferenceView.xib; sourceTree = ""; }; @@ -362,7 +377,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C4998F0626175E7200B2526E /* HotKey in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -370,7 +384,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C4C1019627C659B7001FACC2 /* HotKey in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -405,6 +418,27 @@ path = PHP; sourceTree = ""; }; + 54D9E0AB27E4F502003B9AD9 /* HotKey */ = { + isa = PBXGroup; + children = ( + 54D9E0BF27E4F5D9003B9AD9 /* LICENSE */, + 54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */, + 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */, + 54D9E0AD27E4F51E003B9AD9 /* Key.swift */, + 54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */, + 54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */, + ); + path = HotKey; + sourceTree = ""; + }; + 54D9E0BE27E4F5C0003B9AD9 /* Vendor */ = { + isa = PBXGroup; + children = ( + 54D9E0AB27E4F502003B9AD9 /* HotKey */, + ); + path = Vendor; + sourceTree = ""; + }; 54FCFD28276C88C0004CE748 /* Views */ = { isa = PBXGroup; children = ( @@ -471,6 +505,7 @@ C4F8C0A522D4FA41002EFE61 /* README.md */, C4E713562570150F00007428 /* SECURITY.md */, C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */, + 54D9E0C027E4F5E9003B9AD9 /* LICENSE */, C4E713572570151400007428 /* docs */, C41C1B3522B0097F00E7CF16 /* phpmon */, C4F7807A25D7F84B000DBC97 /* phpmon-tests */, @@ -493,6 +528,7 @@ children = ( C4B5853A2770FE2500DA4FBE /* Common */, C41E181722CB61EB0072CF09 /* Domain */, + 54D9E0BE27E4F5C0003B9AD9 /* Vendor */, C41C1B3F22B0098000E7CF16 /* Info.plist */, C4232EE42612526500158FC6 /* Credits.html */, C41C1B4022B0098000E7CF16 /* phpmon.entitlements */, @@ -807,7 +843,6 @@ ); name = "PHP Monitor"; packageProductDependencies = ( - C4998F0526175E7200B2526E /* HotKey */, ); productName = phpmon; productReference = C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */; @@ -828,7 +863,6 @@ ); name = "phpmon-tests"; packageProductDependencies = ( - C4C1019527C659B7001FACC2 /* HotKey */, ); productName = "phpmon-tests"; productReference = C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */; @@ -862,7 +896,6 @@ ); mainGroup = C41C1B2A22B0097F00E7CF16; packageReferences = ( - C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */, ); productRefGroup = C41C1B3422B0097F00E7CF16 /* Products */; projectDirPath = ""; @@ -955,6 +988,7 @@ C415D3B72770F294005EF286 /* Actions.swift in Sources */, C4AC51FC27E27F47008528CA /* SiteListKindCell.swift in Sources */, C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */, + 54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */, C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */, 54B48B5F275F66AE006D90C5 /* Application.swift in Sources */, C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, @@ -963,9 +997,11 @@ C4811D2422D70A4700B5F6B3 /* App.swift in Sources */, C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */, C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */, + 54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */, 5420395F2613607600FB00FA /* Preferences.swift in Sources */, C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */, 54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */, + 54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */, C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */, C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */, C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */, @@ -998,9 +1034,11 @@ C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */, C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, + 54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */, C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */, C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */, C44067F927E2585E0045BD4E /* SiteListTypeCell.swift in Sources */, + 54D9E0BA27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */, C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */, C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */, C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */, @@ -1020,6 +1058,7 @@ 54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */, C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */, C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */, + 54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */, C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */, 54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */, C415D3B82770F294005EF286 /* Actions.swift in Sources */, @@ -1029,6 +1068,7 @@ C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */, C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */, C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */, + 54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */, C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */, C4D5CFCB27E0F9CD00035329 /* NginxConfigParser.swift in Sources */, C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */, @@ -1040,6 +1080,7 @@ C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, C4F319C927B034A500AFF46F /* Stats.swift in Sources */, C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */, + 54D9E0B527E4F51E003B9AD9 /* Key.swift in Sources */, C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */, C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */, C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */, @@ -1049,6 +1090,7 @@ C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */, C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */, C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, + 54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */, C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */, C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */, C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, @@ -1068,6 +1110,7 @@ C417DC75277614690015E6EE /* Helpers.swift in Sources */, C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */, C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, + 54D9E0B327E4F51E003B9AD9 /* HotKeysController.swift in Sources */, C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */, C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */, @@ -1372,30 +1415,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/soffes/HotKey"; - requirement = { - kind = upToNextMinorVersion; - minimumVersion = 0.1.3; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - C4998F0526175E7200B2526E /* HotKey */ = { - isa = XCSwiftPackageProductDependency; - package = C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */; - productName = HotKey; - }; - C4C1019527C659B7001FACC2 /* HotKey */ = { - isa = XCSwiftPackageProductDependency; - package = C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */; - productName = HotKey; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = C41C1B2B22B0097F00E7CF16 /* Project object */; } diff --git a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme index cb237ab..b4996e7 100644 --- a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme +++ b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme @@ -42,15 +42,16 @@ + allowLocationSimulation = "YES" + consoleMode = "1"> Void + + // MARK: - Properties + + let identifier = UUID() + + public let keyCombo: KeyCombo + public var keyDownHandler: Handler? + public var keyUpHandler: Handler? + public var isPaused = false { + didSet { + if isPaused { + HotKeysController.unregister(self) + } else { + HotKeysController.register(self) + } + } + } + + // MARK: - Initializers + + public init(keyCombo: KeyCombo, keyDownHandler: Handler? = nil, keyUpHandler: Handler? = nil) { + self.keyCombo = keyCombo + self.keyDownHandler = keyDownHandler + self.keyUpHandler = keyUpHandler + + HotKeysController.register(self) + } + + public convenience init(carbonKeyCode: UInt32, carbonModifiers: UInt32, keyDownHandler: Handler? = nil, keyUpHandler: Handler? = nil) { + let keyCombo = KeyCombo(carbonKeyCode: carbonKeyCode, carbonModifiers: carbonModifiers) + self.init(keyCombo: keyCombo, keyDownHandler: keyDownHandler, keyUpHandler: keyUpHandler) + } + + public convenience init(key: Key, modifiers: NSEvent.ModifierFlags, keyDownHandler: Handler? = nil, keyUpHandler: Handler? = nil) { + let keyCombo = KeyCombo(key: key, modifiers: modifiers) + self.init(keyCombo: keyCombo, keyDownHandler: keyDownHandler, keyUpHandler: keyUpHandler) + } + + deinit { + HotKeysController.unregister(self) + } +} diff --git a/phpmon/Vendor/HotKey/HotKeysController.swift b/phpmon/Vendor/HotKey/HotKeysController.swift new file mode 100644 index 0000000..7a5cb63 --- /dev/null +++ b/phpmon/Vendor/HotKey/HotKeysController.swift @@ -0,0 +1,185 @@ +import Carbon + +final class HotKeysController { + + // MARK: - Types + + final class HotKeyBox { + let identifier: UUID + weak var hotKey: HotKey? + let carbonHotKeyID: UInt32 + var carbonEventHotKey: EventHotKeyRef? + + init(hotKey: HotKey, carbonHotKeyID: UInt32) { + self.identifier = hotKey.identifier + self.hotKey = hotKey + self.carbonHotKeyID = carbonHotKeyID + } + } + + // MARK: - Properties + + static var hotKeys = [UInt32: HotKeyBox]() + static private var hotKeysCount: UInt32 = 0 + + static let eventHotKeySignature: UInt32 = { + let string = "SSHk" + var result: FourCharCode = 0 + for char in string.utf16 { + result = (result << 8) + FourCharCode(char) + } + return result + }() + + private static let eventSpec = [ + EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyPressed)), + EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyReleased)) + ] + + private static var eventHandler: EventHandlerRef? + + // MARK: - Registration + + static func register(_ hotKey: HotKey) { + // Don't register an already registered HotKey + if hotKeys.values.first(where: { $0.identifier == hotKey.identifier }) != nil { + return + } + + // Increment the count which will become out next ID + hotKeysCount += 1 + + // Create a box for our metadata and weak HotKey + let box = HotKeyBox(hotKey: hotKey, carbonHotKeyID: hotKeysCount) + hotKeys[box.carbonHotKeyID] = box + + // Register with the system + var eventHotKey: EventHotKeyRef? + let hotKeyID = EventHotKeyID(signature: eventHotKeySignature, id: box.carbonHotKeyID) + let registerError = RegisterEventHotKey( + hotKey.keyCombo.carbonKeyCode, + hotKey.keyCombo.carbonModifiers, + hotKeyID, + GetEventDispatcherTarget(), + 0, + &eventHotKey + ) + + // Ensure registration worked + guard registerError == noErr, eventHotKey != nil else { + return + } + + // Store the event so we can unregister it later + box.carbonEventHotKey = eventHotKey + + // Setup the event handler if needed + updateEventHandler() + } + + static func unregister(_ hotKey: HotKey) { + // Find the box + guard let box = self.box(for: hotKey) else { + return + } + + // Unregister the hot key + UnregisterEventHotKey(box.carbonEventHotKey) + + // Destroy the box + box.hotKey = nil + hotKeys.removeValue(forKey: box.carbonHotKeyID) + } + + + // MARK: - Events + + static func handleCarbonEvent(_ event: EventRef?) -> OSStatus { + // Ensure we have an event + guard let event = event else { + return OSStatus(eventNotHandledErr) + } + + // Get the hot key ID from the event + var hotKeyID = EventHotKeyID() + let error = GetEventParameter( + event, + UInt32(kEventParamDirectObject), + UInt32(typeEventHotKeyID), + nil, + MemoryLayout.size, + nil, + &hotKeyID + ) + + if error != noErr { + return error + } + + // Ensure we have a HotKey registered for this ID + guard hotKeyID.signature == eventHotKeySignature, + let hotKey = self.hotKey(for: hotKeyID.id) + else { + return OSStatus(eventNotHandledErr) + } + + // Call the handler + switch GetEventKind(event) { + case UInt32(kEventHotKeyPressed): + if !hotKey.isPaused, let handler = hotKey.keyDownHandler { + handler() + return noErr + } + case UInt32(kEventHotKeyReleased): + if !hotKey.isPaused, let handler = hotKey.keyUpHandler { + handler() + return noErr + } + default: + break + } + + return OSStatus(eventNotHandledErr) + } + + private static func updateEventHandler() { + if hotKeysCount == 0 || eventHandler != nil { + return + } + + // Register for key down and key up + let eventSpec = [ + EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyPressed)), + EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyReleased)) + ] + + // Install the handler + InstallEventHandler(GetEventDispatcherTarget(), hotKeyEventHandler, 2, eventSpec, nil, &eventHandler) + } + + + // MARK: - Querying + + private static func hotKey(for carbonHotKeyID: UInt32) -> HotKey? { + if let hotKey = hotKeys[carbonHotKeyID]?.hotKey { + return hotKey + } + + hotKeys.removeValue(forKey: carbonHotKeyID) + return nil + } + + private static func box(for hotKey: HotKey) -> HotKeyBox? { + for box in hotKeys.values { + if box.identifier == hotKey.identifier { + return box + } + } + + return nil + } +} + +private func hotKeyEventHandler(eventHandlerCall: EventHandlerCallRef?, event: EventRef?, userData: UnsafeMutableRawPointer?) -> OSStatus { + return HotKeysController.handleCarbonEvent(event) +} diff --git a/phpmon/Vendor/HotKey/Key.swift b/phpmon/Vendor/HotKey/Key.swift new file mode 100644 index 0000000..083339a --- /dev/null +++ b/phpmon/Vendor/HotKey/Key.swift @@ -0,0 +1,497 @@ +import Carbon + +public enum Key { + + // MARK: - Letters + case a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z + + // MARK: - Numbers + case zero, one, two, three, four, five, six, seven, eight, nine + + // MARK: - Symbols + case period, quote, rightBracket, semicolon, slash, backslash, comma, equal, grave, leftBracket, minus + + // MARK: - Whitespace + case space, tab, `return` + + // MARK: - Modifiers + case command, rightCommand, option, rightOption, control, rightControl, shift, rightShift, function, capsLock + + // MARK: - Navigation + case pageUp, pageDown, home, end, upArrow, rightArrow, downArrow, leftArrow + + // MARK: - Functions + case f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20 + + // MARK: - Keypad + case keypad0, keypad1, keypad2, keypad3, keypad4, keypad5, keypad6, keypad7, keypad8, keypad9, + keypadClear, keypadDecimal, keypadDivide, keypadEnter, keypadEquals, keypadMinus, keypadMultiply, keypadPlus + + // MARK: - Misc + case escape, delete, forwardDelete, help, volumeUp, volumeDown, mute + + // MARK: - Initializers + + public init?(string: String) { + switch string.lowercased() { + case "a": self = .a + case "s": self = .s + case "d": self = .d + case "f": self = .f + case "h": self = .h + case "g": self = .g + case "z": self = .z + case "x": self = .x + case "c": self = .c + case "v": self = .v + case "b": self = .b + case "q": self = .q + case "w": self = .w + case "e": self = .e + case "r": self = .r + case "y": self = .y + case "t": self = .t + case "one", "1": self = .one + case "two", "2": self = .two + case "three", "3": self = .three + case "four", "4": self = .four + case "six", "6": self = .six + case "five", "5": self = .five + case "equal", "=": self = .equal + case "nine", "9": self = .nine + case "seven", "7": self = .seven + case "minus", "-": self = .minus + case "eight", "8": self = .eight + case "zero", "0": self = .zero + case "rightBracket", "]": self = .rightBracket + case "o": self = .o + case "u": self = .u + case "leftBracket", "[": self = .leftBracket + case "i": self = .i + case "p": self = .p + case "l": self = .l + case "j": self = .j + case "quote", "\"": self = .quote + case "k": self = .k + case "semicolon", ";": self = .semicolon + case "backslash", "\\": self = .backslash + case "comma", ",": self = .comma + case "slash", "/": self = .slash + case "n": self = .n + case "m": self = .m + case "period", ".": self = .period + case "grave", "`", "ˋ", "`": self = .grave + case "keypaddecimal": self = .keypadDecimal + case "keypadmultiply": self = .keypadMultiply + case "keypadplus": self = .keypadPlus + case "keypadclear", "⌧": self = .keypadClear + case "keypaddivide": self = .keypadDivide + case "keypadenter": self = .keypadEnter + case "keypadminus": self = .keypadMinus + case "keypadequals": self = .keypadEquals + case "keypad0": self = .keypad0 + case "keypad1": self = .keypad1 + case "keypad2": self = .keypad2 + case "keypad3": self = .keypad3 + case "keypad4": self = .keypad4 + case "keypad5": self = .keypad5 + case "keypad6": self = .keypad6 + case "keypad7": self = .keypad7 + case "keypad8": self = .keypad8 + case "keypad9": self = .keypad9 + case "return", "\r", "↩︎", "⏎", "⮐": self = .return + case "tab", "\t", "⇥": self = .tab + case "space", " ", "␣": self = .space + case "delete", "⌫": self = .delete + case "escape", "⎋": self = .escape + case "command", "⌘", "": self = .command + case "shift", "⇧": self = .shift + case "capslock", "⇪": self = .capsLock + case "option", "⌥": self = .option + case "control", "⌃": self = .control + case "rightcommand": self = .rightCommand + case "rightshift": self = .rightShift + case "rightoption": self = .rightOption + case "rightcontrol": self = .rightControl + case "function", "fn": self = .function + case "f17", "F17": self = .f17 + case "volumeup", "🔊": self = .volumeUp + case "volumedown", "🔉": self = .volumeDown + case "mute", "🔇": self = .mute + case "f18", "F18": self = .f18 + case "f19", "F19": self = .f19 + case "f20", "F20": self = .f20 + case "f5", "F5": self = .f5 + case "f6", "F6": self = .f6 + case "f7", "F7": self = .f7 + case "f3", "F3": self = .f3 + case "f8", "F8": self = .f8 + case "f9", "F9": self = .f9 + case "f11", "F11": self = .f11 + case "f13", "F13": self = .f13 + case "f16", "F16": self = .f16 + case "f14", "F14": self = .f14 + case "f10", "F10": self = .f10 + case "f12", "F12": self = .f12 + case "f15", "F15": self = .f15 + case "help", "?⃝": self = .help + case "home", "↖": self = .home + case "pageup", "⇞": self = .pageUp + case "forwarddelete", "⌦": self = .forwardDelete + case "f4", "F4": self = .f4 + case "end", "↘": self = .end + case "f2", "F2": self = .f2 + case "pagedown", "⇟": self = .pageDown + case "f1", "F1": self = .f1 + case "leftarrow", "←": self = .leftArrow + case "rightarrow", "→": self = .rightArrow + case "downarrow", "↓": self = .downArrow + case "uparrow", "↑": self = .upArrow + default: return nil + } + } + + public init?(carbonKeyCode: UInt32) { + switch carbonKeyCode { + case UInt32(kVK_ANSI_A): self = .a + case UInt32(kVK_ANSI_S): self = .s + case UInt32(kVK_ANSI_D): self = .d + case UInt32(kVK_ANSI_F): self = .f + case UInt32(kVK_ANSI_H): self = .h + case UInt32(kVK_ANSI_G): self = .g + case UInt32(kVK_ANSI_Z): self = .z + case UInt32(kVK_ANSI_X): self = .x + case UInt32(kVK_ANSI_C): self = .c + case UInt32(kVK_ANSI_V): self = .v + case UInt32(kVK_ANSI_B): self = .b + case UInt32(kVK_ANSI_Q): self = .q + case UInt32(kVK_ANSI_W): self = .w + case UInt32(kVK_ANSI_E): self = .e + case UInt32(kVK_ANSI_R): self = .r + case UInt32(kVK_ANSI_Y): self = .y + case UInt32(kVK_ANSI_T): self = .t + case UInt32(kVK_ANSI_1): self = .one + case UInt32(kVK_ANSI_2): self = .two + case UInt32(kVK_ANSI_3): self = .three + case UInt32(kVK_ANSI_4): self = .four + case UInt32(kVK_ANSI_6): self = .six + case UInt32(kVK_ANSI_5): self = .five + case UInt32(kVK_ANSI_Equal): self = .equal + case UInt32(kVK_ANSI_9): self = .nine + case UInt32(kVK_ANSI_7): self = .seven + case UInt32(kVK_ANSI_Minus): self = .minus + case UInt32(kVK_ANSI_8): self = .eight + case UInt32(kVK_ANSI_0): self = .zero + case UInt32(kVK_ANSI_RightBracket): self = .rightBracket + case UInt32(kVK_ANSI_O): self = .o + case UInt32(kVK_ANSI_U): self = .u + case UInt32(kVK_ANSI_LeftBracket): self = .leftBracket + case UInt32(kVK_ANSI_I): self = .i + case UInt32(kVK_ANSI_P): self = .p + case UInt32(kVK_ANSI_L): self = .l + case UInt32(kVK_ANSI_J): self = .j + case UInt32(kVK_ANSI_Quote): self = .quote + case UInt32(kVK_ANSI_K): self = .k + case UInt32(kVK_ANSI_Semicolon): self = .semicolon + case UInt32(kVK_ANSI_Backslash): self = .backslash + case UInt32(kVK_ANSI_Comma): self = .comma + case UInt32(kVK_ANSI_Slash): self = .slash + case UInt32(kVK_ANSI_N): self = .n + case UInt32(kVK_ANSI_M): self = .m + case UInt32(kVK_ANSI_Period): self = .period + case UInt32(kVK_ANSI_Grave): self = .grave + case UInt32(kVK_ANSI_KeypadDecimal): self = .keypadDecimal + case UInt32(kVK_ANSI_KeypadMultiply): self = .keypadMultiply + case UInt32(kVK_ANSI_KeypadPlus): self = .keypadPlus + case UInt32(kVK_ANSI_KeypadClear): self = .keypadClear + case UInt32(kVK_ANSI_KeypadDivide): self = .keypadDivide + case UInt32(kVK_ANSI_KeypadEnter): self = .keypadEnter + case UInt32(kVK_ANSI_KeypadMinus): self = .keypadMinus + case UInt32(kVK_ANSI_KeypadEquals): self = .keypadEquals + case UInt32(kVK_ANSI_Keypad0): self = .keypad0 + case UInt32(kVK_ANSI_Keypad1): self = .keypad1 + case UInt32(kVK_ANSI_Keypad2): self = .keypad2 + case UInt32(kVK_ANSI_Keypad3): self = .keypad3 + case UInt32(kVK_ANSI_Keypad4): self = .keypad4 + case UInt32(kVK_ANSI_Keypad5): self = .keypad5 + case UInt32(kVK_ANSI_Keypad6): self = .keypad6 + case UInt32(kVK_ANSI_Keypad7): self = .keypad7 + case UInt32(kVK_ANSI_Keypad8): self = .keypad8 + case UInt32(kVK_ANSI_Keypad9): self = .keypad9 + case UInt32(kVK_Return): self = .`return` + case UInt32(kVK_Tab): self = .tab + case UInt32(kVK_Space): self = .space + case UInt32(kVK_Delete): self = .delete + case UInt32(kVK_Escape): self = .escape + case UInt32(kVK_Command): self = .command + case UInt32(kVK_Shift): self = .shift + case UInt32(kVK_CapsLock): self = .capsLock + case UInt32(kVK_Option): self = .option + case UInt32(kVK_Control): self = .control + case UInt32(kVK_RightCommand): self = .rightCommand + case UInt32(kVK_RightShift): self = .rightShift + case UInt32(kVK_RightOption): self = .rightOption + case UInt32(kVK_RightControl): self = .rightControl + case UInt32(kVK_Function): self = .function + case UInt32(kVK_F17): self = .f17 + case UInt32(kVK_VolumeUp): self = .volumeUp + case UInt32(kVK_VolumeDown): self = .volumeDown + case UInt32(kVK_Mute): self = .mute + case UInt32(kVK_F18): self = .f18 + case UInt32(kVK_F19): self = .f19 + case UInt32(kVK_F20): self = .f20 + case UInt32(kVK_F5): self = .f5 + case UInt32(kVK_F6): self = .f6 + case UInt32(kVK_F7): self = .f7 + case UInt32(kVK_F3): self = .f3 + case UInt32(kVK_F8): self = .f8 + case UInt32(kVK_F9): self = .f9 + case UInt32(kVK_F11): self = .f11 + case UInt32(kVK_F13): self = .f13 + case UInt32(kVK_F16): self = .f16 + case UInt32(kVK_F14): self = .f14 + case UInt32(kVK_F10): self = .f10 + case UInt32(kVK_F12): self = .f12 + case UInt32(kVK_F15): self = .f15 + case UInt32(kVK_Help): self = .help + case UInt32(kVK_Home): self = .home + case UInt32(kVK_PageUp): self = .pageUp + case UInt32(kVK_ForwardDelete): self = .forwardDelete + case UInt32(kVK_F4): self = .f4 + case UInt32(kVK_End): self = .end + case UInt32(kVK_F2): self = .f2 + case UInt32(kVK_PageDown): self = .pageDown + case UInt32(kVK_F1): self = .f1 + case UInt32(kVK_LeftArrow): self = .leftArrow + case UInt32(kVK_RightArrow): self = .rightArrow + case UInt32(kVK_DownArrow): self = .downArrow + case UInt32(kVK_UpArrow): self = .upArrow + default: return nil + } + } + + public var carbonKeyCode: UInt32 { + switch self { + case .a: return UInt32(kVK_ANSI_A) + case .s: return UInt32(kVK_ANSI_S) + case .d: return UInt32(kVK_ANSI_D) + case .f: return UInt32(kVK_ANSI_F) + case .h: return UInt32(kVK_ANSI_H) + case .g: return UInt32(kVK_ANSI_G) + case .z: return UInt32(kVK_ANSI_Z) + case .x: return UInt32(kVK_ANSI_X) + case .c: return UInt32(kVK_ANSI_C) + case .v: return UInt32(kVK_ANSI_V) + case .b: return UInt32(kVK_ANSI_B) + case .q: return UInt32(kVK_ANSI_Q) + case .w: return UInt32(kVK_ANSI_W) + case .e: return UInt32(kVK_ANSI_E) + case .r: return UInt32(kVK_ANSI_R) + case .y: return UInt32(kVK_ANSI_Y) + case .t: return UInt32(kVK_ANSI_T) + case .one: return UInt32(kVK_ANSI_1) + case .two: return UInt32(kVK_ANSI_2) + case .three: return UInt32(kVK_ANSI_3) + case .four: return UInt32(kVK_ANSI_4) + case .six: return UInt32(kVK_ANSI_6) + case .five: return UInt32(kVK_ANSI_5) + case .equal: return UInt32(kVK_ANSI_Equal) + case .nine: return UInt32(kVK_ANSI_9) + case .seven: return UInt32(kVK_ANSI_7) + case .minus: return UInt32(kVK_ANSI_Minus) + case .eight: return UInt32(kVK_ANSI_8) + case .zero: return UInt32(kVK_ANSI_0) + case .rightBracket: return UInt32(kVK_ANSI_RightBracket) + case .o: return UInt32(kVK_ANSI_O) + case .u: return UInt32(kVK_ANSI_U) + case .leftBracket: return UInt32(kVK_ANSI_LeftBracket) + case .i: return UInt32(kVK_ANSI_I) + case .p: return UInt32(kVK_ANSI_P) + case .l: return UInt32(kVK_ANSI_L) + case .j: return UInt32(kVK_ANSI_J) + case .quote: return UInt32(kVK_ANSI_Quote) + case .k: return UInt32(kVK_ANSI_K) + case .semicolon: return UInt32(kVK_ANSI_Semicolon) + case .backslash: return UInt32(kVK_ANSI_Backslash) + case .comma: return UInt32(kVK_ANSI_Comma) + case .slash: return UInt32(kVK_ANSI_Slash) + case .n: return UInt32(kVK_ANSI_N) + case .m: return UInt32(kVK_ANSI_M) + case .period: return UInt32(kVK_ANSI_Period) + case .grave: return UInt32(kVK_ANSI_Grave) + case .keypadDecimal: return UInt32(kVK_ANSI_KeypadDecimal) + case .keypadMultiply: return UInt32(kVK_ANSI_KeypadMultiply) + case .keypadPlus: return UInt32(kVK_ANSI_KeypadPlus) + case .keypadClear: return UInt32(kVK_ANSI_KeypadClear) + case .keypadDivide: return UInt32(kVK_ANSI_KeypadDivide) + case .keypadEnter: return UInt32(kVK_ANSI_KeypadEnter) + case .keypadMinus: return UInt32(kVK_ANSI_KeypadMinus) + case .keypadEquals: return UInt32(kVK_ANSI_KeypadEquals) + case .keypad0: return UInt32(kVK_ANSI_Keypad0) + case .keypad1: return UInt32(kVK_ANSI_Keypad1) + case .keypad2: return UInt32(kVK_ANSI_Keypad2) + case .keypad3: return UInt32(kVK_ANSI_Keypad3) + case .keypad4: return UInt32(kVK_ANSI_Keypad4) + case .keypad5: return UInt32(kVK_ANSI_Keypad5) + case .keypad6: return UInt32(kVK_ANSI_Keypad6) + case .keypad7: return UInt32(kVK_ANSI_Keypad7) + case .keypad8: return UInt32(kVK_ANSI_Keypad8) + case .keypad9: return UInt32(kVK_ANSI_Keypad9) + case .`return`: return UInt32(kVK_Return) + case .tab: return UInt32(kVK_Tab) + case .space: return UInt32(kVK_Space) + case .delete: return UInt32(kVK_Delete) + case .escape: return UInt32(kVK_Escape) + case .command: return UInt32(kVK_Command) + case .shift: return UInt32(kVK_Shift) + case .capsLock: return UInt32(kVK_CapsLock) + case .option: return UInt32(kVK_Option) + case .control: return UInt32(kVK_Control) + case .rightCommand: return UInt32(kVK_RightCommand) + case .rightShift: return UInt32(kVK_RightShift) + case .rightOption: return UInt32(kVK_RightOption) + case .rightControl: return UInt32(kVK_RightControl) + case .function: return UInt32(kVK_Function) + case .f17: return UInt32(kVK_F17) + case .volumeUp: return UInt32(kVK_VolumeUp) + case .volumeDown: return UInt32(kVK_VolumeDown) + case .mute: return UInt32(kVK_Mute) + case .f18: return UInt32(kVK_F18) + case .f19: return UInt32(kVK_F19) + case .f20: return UInt32(kVK_F20) + case .f5: return UInt32(kVK_F5) + case .f6: return UInt32(kVK_F6) + case .f7: return UInt32(kVK_F7) + case .f3: return UInt32(kVK_F3) + case .f8: return UInt32(kVK_F8) + case .f9: return UInt32(kVK_F9) + case .f11: return UInt32(kVK_F11) + case .f13: return UInt32(kVK_F13) + case .f16: return UInt32(kVK_F16) + case .f14: return UInt32(kVK_F14) + case .f10: return UInt32(kVK_F10) + case .f12: return UInt32(kVK_F12) + case .f15: return UInt32(kVK_F15) + case .help: return UInt32(kVK_Help) + case .home: return UInt32(kVK_Home) + case .pageUp: return UInt32(kVK_PageUp) + case .forwardDelete: return UInt32(kVK_ForwardDelete) + case .f4: return UInt32(kVK_F4) + case .end: return UInt32(kVK_End) + case .f2: return UInt32(kVK_F2) + case .pageDown: return UInt32(kVK_PageDown) + case .f1: return UInt32(kVK_F1) + case .leftArrow: return UInt32(kVK_LeftArrow) + case .rightArrow: return UInt32(kVK_RightArrow) + case .downArrow: return UInt32(kVK_DownArrow) + case .upArrow: return UInt32(kVK_UpArrow) + } + } + +} + +extension Key: CustomStringConvertible { + public var description: String { + switch self { + case .a: return "A" + case .s: return "S" + case .d: return "D" + case .f: return "F" + case .h: return "H" + case .g: return "G" + case .z: return "Z" + case .x: return "X" + case .c: return "C" + case .v: return "V" + case .b: return "B" + case .q: return "Q" + case .w: return "W" + case .e: return "E" + case .r: return "R" + case .y: return "Y" + case .t: return "T" + case .one, .keypad1: return "1" + case .two, .keypad2: return "2" + case .three, .keypad3: return "3" + case .four, .keypad4: return "4" + case .six, .keypad6: return "6" + case .five, .keypad5: return "5" + case .equal: return "=" + case .nine, .keypad9: return "9" + case .seven, .keypad7: return "7" + case .minus: return "-" + case .eight, .keypad8: return "8" + case .zero, .keypad0: return "0" + case .rightBracket: return "]" + case .o: return "O" + case .u: return "U" + case .leftBracket: return "[" + case .i: return "I" + case .p: return "P" + case .l: return "L" + case .j: return "J" + case .quote: return "\"" + case .k: return "K" + case .semicolon: return ";" + case .backslash: return "\\" + case .comma: return "," + case .slash: return "/" + case .n: return "N" + case .m: return "M" + case .period: return "." + case .grave: return "`" + case .keypadDecimal: return "." + case .keypadMultiply: return "𝗑" + case .keypadPlus: return "+" + case .keypadClear: return "⌧" + case .keypadDivide: return "/" + case .keypadEnter: return "↩︎" + case .keypadMinus: return "-" + case .keypadEquals: return "=" + case .`return`: return "↩︎" + case .tab: return "⇥" + case .space: return "␣" + case .delete: return "⌫" + case .escape: return "⎋" + case .command, .rightCommand: return "⌘" + case .shift, .rightShift: return "⇧" + case .capsLock: return "⇪" + case .option, .rightOption: return "⌥" + case .control, .rightControl: return "⌃" + case .function: return "fn" + case .f17: return "F17" + case .volumeUp: return "🔊" + case .volumeDown: return "🔉" + case .mute: return "🔇" + case .f18: return "F18" + case .f19: return "F19" + case .f20: return "F20" + case .f5: return "F5" + case .f6: return "F6" + case .f7: return "F7" + case .f3: return "F3" + case .f8: return "F8" + case .f9: return "F9" + case .f11: return "F11" + case .f13: return "F13" + case .f16: return "F16" + case .f14: return "F14" + case .f10: return "F10" + case .f12: return "F12" + case .f15: return "F15" + case .help: return "?⃝" + case .home: return "↖" + case .pageUp: return "⇞" + case .forwardDelete: return "⌦" + case .f4: return "F4" + case .end: return "↘" + case .f2: return "F2" + case .pageDown: return "⇟" + case .f1: return "F1" + case .leftArrow: return "←" + case .rightArrow: return "→" + case .downArrow: return "↓" + case .upArrow: return "↑" + } + } +} diff --git a/phpmon/Vendor/HotKey/KeyCombo.swift b/phpmon/Vendor/HotKey/KeyCombo.swift new file mode 100644 index 0000000..b5b3957 --- /dev/null +++ b/phpmon/Vendor/HotKey/KeyCombo.swift @@ -0,0 +1,82 @@ +import AppKit + +public struct KeyCombo: Equatable { + + // MARK: - Properties + + public var carbonKeyCode: UInt32 + public var carbonModifiers: UInt32 + + public var key: Key? { + get { + return Key(carbonKeyCode: carbonKeyCode) + } + + set { + carbonKeyCode = newValue?.carbonKeyCode ?? 0 + } + } + + public var modifiers: NSEvent.ModifierFlags { + get { + return NSEvent.ModifierFlags(carbonFlags: carbonModifiers) + } + + set { + carbonModifiers = newValue.carbonFlags + } + } + + public var isValid: Bool { + return carbonKeyCode >= 0 + } + + // MARK: - Initializers + + public init(carbonKeyCode: UInt32, carbonModifiers: UInt32 = 0) { + self.carbonKeyCode = carbonKeyCode + self.carbonModifiers = carbonModifiers + } + + public init(key: Key, modifiers: NSEvent.ModifierFlags = []) { + self.carbonKeyCode = key.carbonKeyCode + self.carbonModifiers = modifiers.carbonFlags + } + + // MARK: - Converting Keys + + public static func carbonKeyCodeToString(_ carbonKeyCode: UInt32) -> String? { + return nil + } +} + +extension KeyCombo { + public var dictionary: [String: Any] { + return [ + "keyCode": Int(carbonKeyCode), + "modifiers": Int(carbonModifiers) + ] + } + + public init?(dictionary: [String: Any]) { + guard let keyCode = dictionary["keyCode"] as? Int, + let modifiers = dictionary["modifiers"] as? Int + else { + return nil + } + + self.init(carbonKeyCode: UInt32(keyCode), carbonModifiers: UInt32(modifiers)) + } +} + +extension KeyCombo: CustomStringConvertible { + public var description: String { + var output = modifiers.description + + if let keyDescription = key?.description { + output += keyDescription + } + + return output + } +} diff --git a/phpmon/Vendor/HotKey/LICENSE b/phpmon/Vendor/HotKey/LICENSE new file mode 100644 index 0000000..ae1aac2 --- /dev/null +++ b/phpmon/Vendor/HotKey/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2017–2019 Sam Soffes, http://soff.es + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/phpmon/Vendor/HotKey/ModifierFlagsExtension.swift b/phpmon/Vendor/HotKey/ModifierFlagsExtension.swift new file mode 100644 index 0000000..8ab6613 --- /dev/null +++ b/phpmon/Vendor/HotKey/ModifierFlagsExtension.swift @@ -0,0 +1,70 @@ +import AppKit +import Carbon + +extension NSEvent.ModifierFlags { + public var carbonFlags: UInt32 { + var carbonFlags: UInt32 = 0 + + if contains(.command) { + carbonFlags |= UInt32(cmdKey) + } + + if contains(.option) { + carbonFlags |= UInt32(optionKey) + } + + if contains(.control) { + carbonFlags |= UInt32(controlKey) + } + + if contains(.shift) { + carbonFlags |= UInt32(shiftKey) + } + + return carbonFlags + } + + public init(carbonFlags: UInt32) { + self.init() + + if carbonFlags & UInt32(cmdKey) == UInt32(cmdKey) { + insert(.command) + } + + if carbonFlags & UInt32(optionKey) == UInt32(optionKey) { + insert(.option) + } + + if carbonFlags & UInt32(controlKey) == UInt32(controlKey) { + insert(.control) + } + + if carbonFlags & UInt32(shiftKey) == UInt32(shiftKey) { + insert(.shift) + } + } +} + +extension NSEvent.ModifierFlags: CustomStringConvertible { + public var description: String { + var output = "" + + if contains(.control) { + output += Key.control.description + } + + if contains(.option) { + output += Key.option.description + } + + if contains(.shift) { + output += Key.shift.description + } + + if contains(.command) { + output += Key.command.description + } + + return output + } +}