From 659df65b034a69fb7f3107c7ef398da3ef82e762 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 5 May 2019 21:26:59 +0200 Subject: [PATCH 01/16] Update to Swift 5.0 and run `swiftformat`. --- .swift-version | 1 + .swiftformat | 5 + .swiftlint.yml | 1 + MonitorControl.xcodeproj/project.pbxproj | 32 +- .../xcschemes/MonitorControl.xcscheme | 2 +- .../xcshareddata/IDEWorkspaceChecks.plist | 4 +- MonitorControl/AppDelegate.swift | 474 +++++++++-------- MonitorControl/Info.plist | 2 +- MonitorControl/Objects/ButtonCellView.swift | 43 +- MonitorControl/Objects/Display.swift | 170 +++--- MonitorControl/Objects/SliderHandler.swift | 38 +- .../Prefs/DisplayPrefsViewController.swift | 211 ++++---- .../Prefs/KeysPrefsViewController.swift | 34 +- .../Prefs/MainPrefsViewController.swift | 120 +++-- MonitorControl/Utils.swift | 487 +++++++++--------- MonitorControlHelper/AppDelegate.swift | 28 +- MonitorControlHelper/Info.plist | 2 + Podfile | 10 +- Podfile.lock | 12 +- 19 files changed, 836 insertions(+), 840 deletions(-) create mode 100644 .swift-version create mode 100644 .swiftformat diff --git a/.swift-version b/.swift-version new file mode 100644 index 00000000..819e07a2 --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +5.0 diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 00000000..acfcba2f --- /dev/null +++ b/.swiftformat @@ -0,0 +1,5 @@ +--indent 2 +--ranges no-space +--self insert +--exponentcase lowercase +--exclude Pods diff --git a/.swiftlint.yml b/.swiftlint.yml index b156b01b..ca726242 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -2,6 +2,7 @@ disabled_rules: - line_length - function_body_length - identifier_name + - trailing_comma excluded: - Pods type_body_length: 500 diff --git a/MonitorControl.xcodeproj/project.pbxproj b/MonitorControl.xcodeproj/project.pbxproj index 7fe33595..5106c521 100644 --- a/MonitorControl.xcodeproj/project.pbxproj +++ b/MonitorControl.xcodeproj/project.pbxproj @@ -287,21 +287,21 @@ TargetAttributes = { 56754EAA1D9A4016007BCDC5 = { CreatedOnToolsVersion = 8.0; - DevelopmentTeam = CYC8C8R4K9; - LastSwiftMigration = 1010; + DevelopmentTeam = KJ5F4KTDFH; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; F06792E6200A73460066C438 = { CreatedOnToolsVersion = 9.2; - DevelopmentTeam = CYC8C8R4K9; - LastSwiftMigration = 1010; + DevelopmentTeam = KJ5F4KTDFH; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 56754EA61D9A4016007BCDC5 /* Build configuration list for PBXProject "MonitorControl" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -365,7 +365,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-MonitorControl/Pods-MonitorControl-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-MonitorControl/Pods-MonitorControl-frameworks.sh", "${BUILT_PRODUCTS_DIR}/AMCoreAudio/AMCoreAudio.framework", "${BUILT_PRODUCTS_DIR}/MASPreferences/MASPreferences.framework", "${BUILT_PRODUCTS_DIR}/MediaKeyTap/MediaKeyTap.framework", @@ -378,7 +378,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MonitorControl/Pods-MonitorControl-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MonitorControl/Pods-MonitorControl-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; C0EF20D28FC7408CBE89A686 /* [CP] Check Pods Manifest.lock */ = { @@ -621,14 +621,14 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = CYC8C8R4K9; + DEVELOPMENT_TEAM = KJ5F4KTDFH; INFOPLIST_FILE = "$(SRCROOT)/MonitorControl/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Bridging-Header.h"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -640,14 +640,14 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = CYC8C8R4K9; + DEVELOPMENT_TEAM = KJ5F4KTDFH; INFOPLIST_FILE = "$(SRCROOT)/MonitorControl/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Bridging-Header.h"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -662,15 +662,14 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = CYC8C8R4K9; + DEVELOPMENT_TEAM = KJ5F4KTDFH; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = MonitorControlHelper/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControlHelper; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -685,15 +684,14 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = CYC8C8R4K9; + DEVELOPMENT_TEAM = KJ5F4KTDFH; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = MonitorControlHelper/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControlHelper; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/MonitorControl.xcodeproj/xcshareddata/xcschemes/MonitorControl.xcscheme b/MonitorControl.xcodeproj/xcshareddata/xcschemes/MonitorControl.xcscheme index eaaeab23..657c698f 100644 --- a/MonitorControl.xcodeproj/xcshareddata/xcschemes/MonitorControl.xcscheme +++ b/MonitorControl.xcodeproj/xcshareddata/xcschemes/MonitorControl.xcscheme @@ -1,6 +1,6 @@ - IDEDidComputeMac32BitWarning - + IDEDidComputeMac32BitWarning + diff --git a/MonitorControl/AppDelegate.swift b/MonitorControl/AppDelegate.swift index e4b7de53..0e8ff3de 100644 --- a/MonitorControl/AppDelegate.swift +++ b/MonitorControl/AppDelegate.swift @@ -7,297 +7,295 @@ // MIT Licensed. 2017. // +import AMCoreAudio import Cocoa import Foundation -import MediaKeyTap import MASPreferences -import AMCoreAudio +import MediaKeyTap -var app: AppDelegate! = nil +var app: AppDelegate! let prefs = UserDefaults.standard @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { + @IBOutlet var statusMenu: NSMenu! + @IBOutlet var window: NSWindow! - @IBOutlet weak var statusMenu: NSMenu! - @IBOutlet weak var window: NSWindow! + let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) - let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) + var monitorItems: [NSMenuItem] = [] + var displays: [Display] = [] - var monitorItems: [NSMenuItem] = [] - var displays: [Display] = [] + let step = 100 / 16 - let step = 100/16 + var mediaKeyTap: MediaKeyTap? + var prefsController: NSWindowController? - var mediaKeyTap: MediaKeyTap? - var prefsController: NSWindowController? + var keysListenedFor: [MediaKey] = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown] - var keysListenedFor: [MediaKey] = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown] + func applicationDidFinishLaunching(_: Notification) { + app = self - func applicationDidFinishLaunching(_ aNotification: Notification) { - app = self + self.setupLayout() + self.subscribeEventListeners() + setVolumeKeysMode() + self.statusItem.image = NSImage(named: "status") + self.statusItem.menu = self.statusMenu + self.setDefaultPrefs() + Utils.acquirePrivileges() + CGDisplayRegisterReconfigurationCallback({ _, _, _ in app.updateDisplays() }, nil) + self.updateDisplays() + } - setupLayout() - subscribeEventListeners() - setVolumeKeysMode() - statusItem.image = NSImage.init(named: "status") - statusItem.menu = statusMenu - setDefaultPrefs() - Utils.acquirePrivileges() - CGDisplayRegisterReconfigurationCallback({_, _, _ in app.updateDisplays()}, nil) - updateDisplays() - } + @IBAction func quitClicked(_: AnyObject) { + NSApplication.shared.terminate(self) + } - @IBAction func quitClicked(_ sender: AnyObject) { - NSApplication.shared.terminate(self) + @IBAction func prefsClicked(_ sender: AnyObject) { + if let prefsController = prefsController { + prefsController.showWindow(sender) + NSApp.activate(ignoringOtherApps: true) + prefsController.window?.makeKeyAndOrderFront(sender) } + } - @IBAction func prefsClicked(_ sender: AnyObject) { - if let prefsController = prefsController { - prefsController.showWindow(sender) - NSApp.activate(ignoringOtherApps: true) - prefsController.window?.makeKeyAndOrderFront(sender) - } + /// Set the default prefs of the app + func setDefaultPrefs() { + let prefs = UserDefaults.standard + if !prefs.bool(forKey: Utils.PrefKeys.appAlreadyLaunched.rawValue) { + prefs.set(true, forKey: Utils.PrefKeys.appAlreadyLaunched.rawValue) + + prefs.set(false, forKey: Utils.PrefKeys.startAtLogin.rawValue) + + prefs.set(false, forKey: Utils.PrefKeys.showContrast.rawValue) + prefs.set(false, forKey: Utils.PrefKeys.lowerContrast.rawValue) } + } - /// Set the default prefs of the app - func setDefaultPrefs() { - let prefs = UserDefaults.standard - if !prefs.bool(forKey: Utils.PrefKeys.appAlreadyLaunched.rawValue) { - prefs.set(true, forKey: Utils.PrefKeys.appAlreadyLaunched.rawValue) + // MARK: - Menu - prefs.set(false, forKey: Utils.PrefKeys.startAtLogin.rawValue) + func clearDisplays() { + if self.statusMenu.items.count > 2 { + var items: [NSMenuItem] = [] + for i in 0.. 2 { - var items: [NSMenuItem] = [] - for i in 0.. Bool in - if let id = screen.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as? CGDirectDisplayID { - // Is Built In Screen (e.g. MBP/iMac Screen) - if CGDisplayIsBuiltin(id) != 0 { - return false - } - - // Does screen support EDID ? - var edid = EDID() - if !EDIDTest(id, &edid) { - return false - } - - return true - } - return false + var filteredScreens = NSScreen.screens.filter { screen -> Bool in + if let id = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID { + // Is Built In Screen (e.g. MBP/iMac Screen) + if CGDisplayIsBuiltin(id) != 0 { + return false } - if filteredScreens.count == 1 { - self.addScreenToMenu(screen: filteredScreens[0], asSubMenu: false) - } else { - for screen in filteredScreens { - self.addScreenToMenu(screen: screen, asSubMenu: true) - } + // Does screen support EDID ? + var edid = EDID() + if !EDIDTest(id, &edid) { + return false } - if filteredScreens.count == 0 { - // If no DDC capable display was detected - let item = NSMenuItem() - item.title = NSLocalizedString("No supported display found", comment: "Shown in menu") - item.isEnabled = false - monitorItems.append(item) - statusMenu.insertItem(item, at: 0) - } + return true + } + return false } - /// Add a screen to the menu - /// - /// - Parameters: - /// - screen: The screen to add - /// - asSubMenu: Display in a sub menu or directly in menu - private func addScreenToMenu(screen: NSScreen, asSubMenu: Bool) { - if let id = screen.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as? CGDirectDisplayID { - - var edid = EDID() - if EDIDTest(id, &edid) { - let name = Utils.getDisplayName(forEdid: edid) - let serial = Utils.getDisplaySerial(forEdid: edid) - - let display = Display.init(id, name: name, serial: serial) - - let monitorSubMenu: NSMenu = asSubMenu ? NSMenu() : statusMenu - let volumeSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu, - forDisplay: display, - command: AUDIO_SPEAKER_VOLUME, - title: NSLocalizedString("Volume", comment: "Shown in menu")) - let brightnessSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu, - forDisplay: display, - command: BRIGHTNESS, - title: NSLocalizedString("Brightness", comment: "Shown in menu")) - if prefs.bool(forKey: Utils.PrefKeys.showContrast.rawValue) { - let contrastSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu, - forDisplay: display, - command: CONTRAST, - title: NSLocalizedString("Contrast", comment: "Shown in menu")) - display.contrastSliderHandler = contrastSliderHandler - } - - display.volumeSliderHandler = volumeSliderHandler - display.brightnessSliderHandler = brightnessSliderHandler - displays.append(display) - - let monitorMenuItem = NSMenuItem() - monitorMenuItem.title = "\(name)" - if asSubMenu { - monitorMenuItem.submenu = monitorSubMenu - } - - monitorItems.append(monitorMenuItem) - statusMenu.insertItem(monitorMenuItem, at: 0) - } - } + if filteredScreens.count == 1 { + self.addScreenToMenu(screen: filteredScreens[0], asSubMenu: false) + } else { + for screen in filteredScreens { + self.addScreenToMenu(screen: screen, asSubMenu: true) + } } - private func setupLayout() { - let storyboard: NSStoryboard = NSStoryboard.init(name: "Main", bundle: Bundle.main) - let views = [ - storyboard.instantiateController(withIdentifier: "MainPrefsVC"), - storyboard.instantiateController(withIdentifier: "KeysPrefsVC"), - storyboard.instantiateController(withIdentifier: "DisplayPrefsVC") - ] - prefsController = MASPreferencesWindowController(viewControllers: views, title: NSLocalizedString("Preferences", comment: "Shown in Preferences window")) + if filteredScreens.count == 0 { + // If no DDC capable display was detected + let item = NSMenuItem() + item.title = NSLocalizedString("No supported display found", comment: "Shown in menu") + item.isEnabled = false + self.monitorItems.append(item) + self.statusMenu.insertItem(item, at: 0) } + } + + /// Add a screen to the menu + /// + /// - Parameters: + /// - screen: The screen to add + /// - asSubMenu: Display in a sub menu or directly in menu + private func addScreenToMenu(screen: NSScreen, asSubMenu: Bool) { + if let id = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID { + var edid = EDID() + if EDIDTest(id, &edid) { + let name = Utils.getDisplayName(forEdid: edid) + let serial = Utils.getDisplaySerial(forEdid: edid) + + let display = Display(id, name: name, serial: serial) + + let monitorSubMenu: NSMenu = asSubMenu ? NSMenu() : self.statusMenu + let volumeSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu, + forDisplay: display, + command: AUDIO_SPEAKER_VOLUME, + title: NSLocalizedString("Volume", comment: "Shown in menu")) + let brightnessSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu, + forDisplay: display, + command: BRIGHTNESS, + title: NSLocalizedString("Brightness", comment: "Shown in menu")) + if prefs.bool(forKey: Utils.PrefKeys.showContrast.rawValue) { + let contrastSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu, + forDisplay: display, + command: CONTRAST, + title: NSLocalizedString("Contrast", comment: "Shown in menu")) + display.contrastSliderHandler = contrastSliderHandler + } + + display.volumeSliderHandler = volumeSliderHandler + display.brightnessSliderHandler = brightnessSliderHandler + self.displays.append(display) - private func subscribeEventListeners() { - // subscribe KeyTap event listener - NotificationCenter.default.addObserver(self, selector: #selector(handleListenForChanged), name: NSNotification.Name.init(Utils.PrefKeys.listenFor.rawValue), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(handleShowContrastChanged), name: NSNotification.Name.init(Utils.PrefKeys.showContrast.rawValue), object: nil) + let monitorMenuItem = NSMenuItem() + monitorMenuItem.title = "\(name)" + if asSubMenu { + monitorMenuItem.submenu = monitorSubMenu + } - // subscribe Audio output detector (AMCoreAudio) - NotificationCenter.defaultCenter.subscribe(self, eventType: AudioHardwareEvent.self, dispatchQueue: DispatchQueue.main) + self.monitorItems.append(monitorMenuItem) + self.statusMenu.insertItem(monitorMenuItem, at: 0) + } } + } + + private func setupLayout() { + let storyboard: NSStoryboard = NSStoryboard(name: "Main", bundle: Bundle.main) + let views = [ + storyboard.instantiateController(withIdentifier: "MainPrefsVC"), + storyboard.instantiateController(withIdentifier: "KeysPrefsVC"), + storyboard.instantiateController(withIdentifier: "DisplayPrefsVC"), + ] + prefsController = MASPreferencesWindowController(viewControllers: views, title: NSLocalizedString("Preferences", comment: "Shown in Preferences window")) + } + + private func subscribeEventListeners() { + // subscribe KeyTap event listener + NotificationCenter.default.addObserver(self, selector: #selector(handleListenForChanged), name: NSNotification.Name(Utils.PrefKeys.listenFor.rawValue), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(handleShowContrastChanged), name: NSNotification.Name(Utils.PrefKeys.showContrast.rawValue), object: nil) + + // subscribe Audio output detector (AMCoreAudio) + NotificationCenter.defaultCenter.subscribe(self, eventType: AudioHardwareEvent.self, dispatchQueue: DispatchQueue.main) + } } // MARK: - Media Key Tap delegate -extension AppDelegate: MediaKeyTapDelegate { - func handle(mediaKey: MediaKey, event: KeyEvent?) { - - guard let currentDisplay = Utils.getCurrentDisplay(from: displays) else { return } - let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? displays : [currentDisplay] - - for display in allDisplays { - if (prefs.object(forKey: "\(display.identifier)-state") as? Bool) ?? true { - switch mediaKey { - case .brightnessUp: - let value = display.calcNewValue(for: BRIGHTNESS, withRel: +step) - display.setBrightness(to: value) - case .brightnessDown: - let value = currentDisplay.calcNewValue(for: BRIGHTNESS, withRel: -step) - display.setBrightness(to: value) - case .mute: - display.mute() - case .volumeUp: - let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: +step) - display.setVolume(to: value) - case .volumeDown: - let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: -step) - display.setVolume(to: value) - - default: - return - } - } +extension AppDelegate: MediaKeyTapDelegate { + func handle(mediaKey: MediaKey, event _: KeyEvent?) { + guard let currentDisplay = Utils.getCurrentDisplay(from: displays) else { return } + + let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? self.displays : [currentDisplay] + + for display in allDisplays { + if (prefs.object(forKey: "\(display.identifier)-state") as? Bool) ?? true { + switch mediaKey { + case .brightnessUp: + let value = display.calcNewValue(for: BRIGHTNESS, withRel: +self.step) + display.setBrightness(to: value) + case .brightnessDown: + let value = currentDisplay.calcNewValue(for: BRIGHTNESS, withRel: -self.step) + display.setBrightness(to: value) + case .mute: + display.mute() + case .volumeUp: + let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: +self.step) + display.setVolume(to: value) + case .volumeDown: + let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: -self.step) + display.setVolume(to: value) + + default: + return } - + } } + } - // MARK: - Prefs notification - @objc func handleListenForChanged() { - readKeyListenPreferences() - setKeysToListenFor() - } + // MARK: - Prefs notification - @objc func handleShowContrastChanged() { - self.updateDisplays() - } + @objc func handleListenForChanged() { + self.readKeyListenPreferences() + self.setKeysToListenFor() + } - private func setKeysToListenFor() { - mediaKeyTap?.stop() - mediaKeyTap = MediaKeyTap.init(delegate: self, for: keysListenedFor, observeBuiltIn: false) - mediaKeyTap?.start() - } + @objc func handleShowContrastChanged() { + self.updateDisplays() + } - private func readKeyListenPreferences() { - let listenFor = prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue) - keysListenedFor = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown] - if listenFor == Utils.ListenForKeys.brightnessOnlyKeys.rawValue { - keysListenedFor.removeSubrange(2...4) - } else if listenFor == Utils.ListenForKeys.volumeOnlyKeys.rawValue { - keysListenedFor.removeSubrange(0...1) - } + private func setKeysToListenFor() { + self.mediaKeyTap?.stop() + self.mediaKeyTap = MediaKeyTap(delegate: self, for: self.keysListenedFor, observeBuiltIn: false) + self.mediaKeyTap?.start() + } + + private func readKeyListenPreferences() { + let listenFor = prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue) + + self.keysListenedFor = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown] + + if listenFor == Utils.ListenForKeys.brightnessOnlyKeys.rawValue { + self.keysListenedFor.removeSubrange(2...4) + } else if listenFor == Utils.ListenForKeys.volumeOnlyKeys.rawValue { + self.keysListenedFor.removeSubrange(0...1) } + } } extension AppDelegate: EventSubscriber { - - /** - Fires off when a change in default audio device is detected. - */ - func eventReceiver(_ event: Event) { - - switch event { - case let event as AudioHardwareEvent: - switch event { - case .defaultOutputDeviceChanged(let audioDevice): - #if DEBUG - print("Default output device changed to \(audioDevice)") - print("Can device set its own volume? \(audioDevice.canSetVirtualMasterVolume(direction: .playback))") - #endif - setVolumeKeysMode() - default: break - } - default: break - } + /** + Fires off when a change in default audio device is detected. + */ + func eventReceiver(_ event: Event) { + switch event { + case let event as AudioHardwareEvent: + switch event { + case let .defaultOutputDeviceChanged(audioDevice): + #if DEBUG + print("Default output device changed to \(audioDevice)") + print("Can device set its own volume? \(audioDevice.canSetVirtualMasterVolume(direction: .playback))") + #endif + self.setVolumeKeysMode() + default: break + } + default: break } - - /** - We check if the current default audio output device can change the volume, - if not, we know for sure that we don't need to interact with it. - */ - func setVolumeKeysMode() { - - readKeyListenPreferences() - - if let defaultOutputDevice = AudioDevice.defaultOutputDevice() { - if defaultOutputDevice.canSetVirtualMasterVolume(direction: .playback) { - // Remove volume related keys - let keysToDelete: [MediaKey] = [.volumeUp, .volumeDown, .mute] - keysListenedFor = keysListenedFor.filter({ !keysToDelete.contains($0) }) - } else { - // load keys to listen to from prefs like normal - readKeyListenPreferences() - } - } - setKeysToListenFor() + } + + /** + We check if the current default audio output device can change the volume, + if not, we know for sure that we don't need to interact with it. + */ + func setVolumeKeysMode() { + self.readKeyListenPreferences() + + if let defaultOutputDevice = AudioDevice.defaultOutputDevice() { + if defaultOutputDevice.canSetVirtualMasterVolume(direction: .playback) { + // Remove volume related keys + let keysToDelete: [MediaKey] = [.volumeUp, .volumeDown, .mute] + keysListenedFor = self.keysListenedFor.filter { !keysToDelete.contains($0) } + } else { + // load keys to listen to from prefs like normal + self.readKeyListenPreferences() + } } + self.setKeysToListenFor() + } } diff --git a/MonitorControl/Info.plist b/MonitorControl/Info.plist index 874975af..4cf59c42 100644 --- a/MonitorControl/Info.plist +++ b/MonitorControl/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.3.0 CFBundleVersion - 48 + 57 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/MonitorControl/Objects/ButtonCellView.swift b/MonitorControl/Objects/ButtonCellView.swift index fa6aafc4..c4b575b3 100644 --- a/MonitorControl/Objects/ButtonCellView.swift +++ b/MonitorControl/Objects/ButtonCellView.swift @@ -9,29 +9,28 @@ import Cocoa class ButtonCellView: NSTableCellView { + @IBOutlet var button: NSButton! + var display: Display? + let prefs = UserDefaults.standard - @IBOutlet var button: NSButton! - var display: Display? - let prefs = UserDefaults.standard + override func draw(_ dirtyRect: NSRect) { + super.draw(dirtyRect) + } - override func draw(_ dirtyRect: NSRect) { - super.draw(dirtyRect) - } - - @IBAction func buttonToggled(_ sender: NSButton) { - if let display = display { - switch sender.state { - case .on: - prefs.set(true, forKey: "\(display.identifier)-state") - case .off: - prefs.set(false, forKey: "\(display.identifier)-state") - default: - break - } + @IBAction func buttonToggled(_ sender: NSButton) { + if let display = display { + switch sender.state { + case .on: + self.prefs.set(true, forKey: "\(display.identifier)-state") + case .off: + self.prefs.set(false, forKey: "\(display.identifier)-state") + default: + break + } - #if DEBUG - print("Toggle enabled display state -> \(sender.state == .on ? "on" : "off")") - #endif - } - } + #if DEBUG + print("Toggle enabled display state -> \(sender.state == .on ? "on" : "off")") + #endif + } + } } diff --git a/MonitorControl/Objects/Display.swift b/MonitorControl/Objects/Display.swift index cf0f9eb6..2a3942a3 100644 --- a/MonitorControl/Objects/Display.swift +++ b/MonitorControl/Objects/Display.swift @@ -10,100 +10,100 @@ import Cocoa /// A display class Display { - let identifier: CGDirectDisplayID - let name: String - let serial: String - var isEnabled: Bool - var isMuted: Bool = false - var brightnessSliderHandler: SliderHandler? - var volumeSliderHandler: SliderHandler? - var contrastSliderHandler: SliderHandler? + let identifier: CGDirectDisplayID + let name: String + let serial: String + var isEnabled: Bool + var isMuted: Bool = false + var brightnessSliderHandler: SliderHandler? + var volumeSliderHandler: SliderHandler? + var contrastSliderHandler: SliderHandler? - private let prefs = UserDefaults.standard + private let prefs = UserDefaults.standard - init(_ identifier: CGDirectDisplayID, name: String, serial: String, isEnabled: Bool = true) { - self.identifier = identifier - self.name = name - self.serial = serial - self.isEnabled = isEnabled - } + init(_ identifier: CGDirectDisplayID, name: String, serial: String, isEnabled: Bool = true) { + self.identifier = identifier + self.name = name + self.serial = serial + self.isEnabled = isEnabled + } - func mute() { - var value = 0 - if isMuted { - value = prefs.integer(forKey: "\(AUDIO_SPEAKER_VOLUME)-\(identifier)") - isMuted = false - } else { - isMuted = true - } + func mute() { + var value = 0 + if self.isMuted { + value = self.prefs.integer(forKey: "\(AUDIO_SPEAKER_VOLUME)-\(self.identifier)") + self.isMuted = false + } else { + self.isMuted = true + } - Utils.sendCommand(AUDIO_SPEAKER_VOLUME, toMonitor: identifier, withValue: value) - if let slider = volumeSliderHandler?.slider { - slider.intValue = Int32(value) - } - showOsd(command: AUDIO_SPEAKER_VOLUME, value: value) - } + Utils.sendCommand(AUDIO_SPEAKER_VOLUME, toMonitor: self.identifier, withValue: value) + if let slider = volumeSliderHandler?.slider { + slider.intValue = Int32(value) + } + self.showOsd(command: AUDIO_SPEAKER_VOLUME, value: value) + } - func setVolume(to value: Int) { - if value > 0 { - isMuted = false - } + func setVolume(to value: Int) { + if value > 0 { + self.isMuted = false + } - Utils.sendCommand(AUDIO_SPEAKER_VOLUME, toMonitor: identifier, withValue: value) - if let slider = volumeSliderHandler?.slider { - slider.intValue = Int32(value) - } - showOsd(command: AUDIO_SPEAKER_VOLUME, value: value) - saveValue(value, for: AUDIO_SPEAKER_VOLUME) - } + Utils.sendCommand(AUDIO_SPEAKER_VOLUME, toMonitor: self.identifier, withValue: value) + if let slider = volumeSliderHandler?.slider { + slider.intValue = Int32(value) + } + self.showOsd(command: AUDIO_SPEAKER_VOLUME, value: value) + self.saveValue(value, for: AUDIO_SPEAKER_VOLUME) + } - func setBrightness(to value: Int) { - if prefs.bool(forKey: Utils.PrefKeys.lowerContrast.rawValue) { - if value == 0 { - Utils.sendCommand(CONTRAST, toMonitor: identifier, withValue: value) - if let slider = contrastSliderHandler?.slider { - slider.intValue = Int32(value) - } - } else if prefs.integer(forKey: "\(BRIGHTNESS)-\(identifier)") == 0 { - let contrastValue = prefs.integer(forKey: "\(CONTRAST)-\(identifier)") - Utils.sendCommand(CONTRAST, toMonitor: identifier, withValue: contrastValue) - } - } + func setBrightness(to value: Int) { + if self.prefs.bool(forKey: Utils.PrefKeys.lowerContrast.rawValue) { + if value == 0 { + Utils.sendCommand(CONTRAST, toMonitor: self.identifier, withValue: value) + if let slider = contrastSliderHandler?.slider { + slider.intValue = Int32(value) + } + } else if self.prefs.integer(forKey: "\(BRIGHTNESS)-\(self.identifier)") == 0 { + let contrastValue = self.prefs.integer(forKey: "\(CONTRAST)-\(self.identifier)") + Utils.sendCommand(CONTRAST, toMonitor: self.identifier, withValue: contrastValue) + } + } - Utils.sendCommand(BRIGHTNESS, toMonitor: identifier, withValue: value) - if let slider = brightnessSliderHandler?.slider { - slider.intValue = Int32(value) - } - showOsd(command: BRIGHTNESS, value: value) - saveValue(value, for: BRIGHTNESS) - } + Utils.sendCommand(BRIGHTNESS, toMonitor: self.identifier, withValue: value) + if let slider = brightnessSliderHandler?.slider { + slider.intValue = Int32(value) + } + self.showOsd(command: BRIGHTNESS, value: value) + self.saveValue(value, for: BRIGHTNESS) + } - func calcNewValue(for command: Int32, withRel rel: Int) -> Int { - let currentValue = prefs.integer(forKey: "\(command)-\(identifier)") - return max(0, min(100, currentValue + rel)) - } + func calcNewValue(for command: Int32, withRel rel: Int) -> Int { + let currentValue = self.prefs.integer(forKey: "\(command)-\(self.identifier)") + return max(0, min(100, currentValue + rel)) + } - func saveValue(_ value: Int, for command: Int32) { - prefs.set(value, forKey: "\(command)-\(identifier)") - } + func saveValue(_ value: Int, for command: Int32) { + self.prefs.set(value, forKey: "\(command)-\(self.identifier)") + } - private func showOsd(command: Int32, value: Int) { - if let manager = OSDManager.sharedManager() as? OSDManager { - var osdImage: Int64 = 1 // Brightness Image - if command == AUDIO_SPEAKER_VOLUME { - osdImage = 3 // Speaker image - if isMuted { - osdImage = 4 // Mute speaker - } - } - let step = 100/16 - manager.showImage(osdImage, - onDisplayID: identifier, - priority: 0x1f4, - msecUntilFade: 2000, - filledChiclets: UInt32(value/step), - totalChiclets: UInt32(100/step), - locked: false) - } - } + private func showOsd(command: Int32, value: Int) { + if let manager = OSDManager.sharedManager() as? OSDManager { + var osdImage: Int64 = 1 // Brightness Image + if command == AUDIO_SPEAKER_VOLUME { + osdImage = 3 // Speaker image + if self.isMuted { + osdImage = 4 // Mute speaker + } + } + let step = 100 / 16 + manager.showImage(osdImage, + onDisplayID: self.identifier, + priority: 0x1F4, + msecUntilFade: 2000, + filledChiclets: UInt32(value / step), + totalChiclets: UInt32(100 / step), + locked: false) + } + } } diff --git a/MonitorControl/Objects/SliderHandler.swift b/MonitorControl/Objects/SliderHandler.swift index 6cddebcd..d9198db0 100644 --- a/MonitorControl/Objects/SliderHandler.swift +++ b/MonitorControl/Objects/SliderHandler.swift @@ -10,28 +10,28 @@ import Cocoa /// Handle the slider class SliderHandler { - var slider: NSSlider? - var display: Display - var command: Int32 = 0 + var slider: NSSlider? + var display: Display + var command: Int32 = 0 - public init(display: Display, command: Int32) { - self.display = display - self.command = command - } + public init(display: Display, command: Int32) { + self.display = display + self.command = command + } - @objc func valueChanged(slider: NSSlider) { - let snapInterval = 25 - let snapThreshold = 3 + @objc func valueChanged(slider: NSSlider) { + let snapInterval = 25 + let snapThreshold = 3 - var value = slider.integerValue + var value = slider.integerValue - let closest = (value + snapInterval / 2) / snapInterval * snapInterval - if abs(closest - value) <= snapThreshold { - value = closest - slider.integerValue = value - } + let closest = (value + snapInterval / 2) / snapInterval * snapInterval + if abs(closest - value) <= snapThreshold { + value = closest + slider.integerValue = value + } - Utils.sendCommand(command, toMonitor: display.identifier, withValue: value) - display.saveValue(value, for: command) - } + Utils.sendCommand(self.command, toMonitor: self.display.identifier, withValue: value) + self.display.saveValue(value, for: self.command) + } } diff --git a/MonitorControl/Prefs/DisplayPrefsViewController.swift b/MonitorControl/Prefs/DisplayPrefsViewController.swift index 3b933c7d..6025eda8 100644 --- a/MonitorControl/Prefs/DisplayPrefsViewController.swift +++ b/MonitorControl/Prefs/DisplayPrefsViewController.swift @@ -10,113 +10,112 @@ import Cocoa import MASPreferences class DisplayPrefsViewController: NSViewController, MASPreferencesViewController, NSTableViewDataSource, NSTableViewDelegate { + var viewIdentifier: String = "Display" + var toolbarItemLabel: String? = NSLocalizedString("Display", comment: "Shown in the main prefs window") + var toolbarItemImage: NSImage? = NSImage(named: NSImage.computerName) + let prefs = UserDefaults.standard + + var displays: [Display] = [] + enum DisplayCell: String { + case checkbox + case name + case identifier + } + + @IBOutlet var allScreens: NSButton! + @IBOutlet var displayList: NSTableView! + + override func viewDidLoad() { + super.viewDidLoad() + + self.allScreens.state = self.prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? .on : .off + + self.loadDisplayList() + } + + @IBAction func allScreensTouched(_ sender: NSButton) { + switch sender.state { + case .on: + self.prefs.set(true, forKey: Utils.PrefKeys.allScreens.rawValue) + case .off: + self.prefs.set(false, forKey: Utils.PrefKeys.allScreens.rawValue) + default: break + } - var viewIdentifier: String = "Display" - var toolbarItemLabel: String? = NSLocalizedString("Display", comment: "Shown in the main prefs window") - var toolbarItemImage: NSImage? = NSImage.init(named: NSImage.computerName) - let prefs = UserDefaults.standard - - var displays: [Display] = [] - enum DisplayCell: String { - case checkbox - case name - case identifier - } - - @IBOutlet var allScreens: NSButton! - @IBOutlet var displayList: NSTableView! - - override func viewDidLoad() { - super.viewDidLoad() - - allScreens.state = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? .on : .off - - loadDisplayList() + #if DEBUG + print("Toggle allScreens state -> \(sender.state == .on ? "on" : "off")") + #endif + } + + // MARK: - Table datasource + + func loadDisplayList() { + for screen in NSScreen.screens { + if let id = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID { + // Is Built In Screen (e.g. MBP/iMac Screen) + if CGDisplayIsBuiltin(id) != 0 { + let display = Display(id, name: "Mac built-in Display", serial: "", isEnabled: false) + displays.append(display) + continue + } + + // Does screen support EDID ? + var edid = EDID() + if !EDIDTest(id, &edid) { + continue + } + + let name = Utils.getDisplayName(forEdid: edid) + let serial = Utils.getDisplaySerial(forEdid: edid) + let isEnabled = (prefs.object(forKey: "\(id)-state") as? Bool) ?? true + + let display = Display(id, name: name, serial: serial, isEnabled: isEnabled) + displays.append(display) + } + } + self.displayList.reloadData() + } + + func numberOfRows(in _: NSTableView) -> Int { + return self.displays.count + } + + // MARK: - Table delegate + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + var cellType = DisplayCell.checkbox + var checked = false + var text = "" + let display = self.displays[row] + + if tableColumn == tableView.tableColumns[0] { + // Checkbox + checked = display.isEnabled + } else if tableColumn == tableView.tableColumns[1] { + // Name + text = display.name + cellType = DisplayCell.name + } else if tableColumn == tableView.tableColumns[2] { + // Identifier + text = "\(display.identifier)" + cellType = DisplayCell.identifier + } + if cellType == DisplayCell.checkbox { + if let cell = tableView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: nil) as? ButtonCellView { + cell.button.state = checked ? .on : .off + cell.display = display + if display.name == "Mac built-in Display" { + cell.button.isEnabled = false + } + return cell + } + } else { + if let cell = tableView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: nil) as? NSTableCellView { + cell.textField?.stringValue = text + return cell + } } - @IBAction func allScreensTouched(_ sender: NSButton) { - switch sender.state { - case .on: - prefs.set(true, forKey: Utils.PrefKeys.allScreens.rawValue) - case .off: - prefs.set(false, forKey: Utils.PrefKeys.allScreens.rawValue) - default: break - } - - #if DEBUG - print("Toggle allScreens state -> \(sender.state == .on ? "on" : "off")") - #endif - } - - // MARK: - Table datasource - - func loadDisplayList() { - for screen in NSScreen.screens { - if let id = screen.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as? CGDirectDisplayID { - // Is Built In Screen (e.g. MBP/iMac Screen) - if CGDisplayIsBuiltin(id) != 0 { - let display = Display(id, name: "Mac built-in Display", serial: "", isEnabled: false) - displays.append(display) - continue - } - - // Does screen support EDID ? - var edid = EDID() - if !EDIDTest(id, &edid) { - continue - } - - let name = Utils.getDisplayName(forEdid: edid) - let serial = Utils.getDisplaySerial(forEdid: edid) - let isEnabled = (prefs.object(forKey: "\(id)-state") as? Bool) ?? true - - let display = Display(id, name: name, serial: serial, isEnabled: isEnabled) - displays.append(display) - } - } - displayList.reloadData() - } - - func numberOfRows(in tableView: NSTableView) -> Int { - return displays.count - } - - // MARK: - Table delegate - - func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { - var cellType = DisplayCell.checkbox - var checked = false - var text = "" - let display = displays[row] - - if tableColumn == tableView.tableColumns[0] { - // Checkbox - checked = display.isEnabled - } else if tableColumn == tableView.tableColumns[1] { - // Name - text = display.name - cellType = DisplayCell.name - } else if tableColumn == tableView.tableColumns[2] { - // Identifier - text = "\(display.identifier)" - cellType = DisplayCell.identifier - } - if cellType == DisplayCell.checkbox { - if let cell = tableView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: nil) as? ButtonCellView { - cell.button.state = checked ? .on : .off - cell.display = display - if display.name == "Mac built-in Display" { - cell.button.isEnabled = false - } - return cell - } - } else { - if let cell = tableView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: nil) as? NSTableCellView { - cell.textField?.stringValue = text - return cell - } - } - - return nil - } + return nil + } } diff --git a/MonitorControl/Prefs/KeysPrefsViewController.swift b/MonitorControl/Prefs/KeysPrefsViewController.swift index 9c74dbfb..557297c1 100644 --- a/MonitorControl/Prefs/KeysPrefsViewController.swift +++ b/MonitorControl/Prefs/KeysPrefsViewController.swift @@ -10,28 +10,26 @@ import Cocoa import MASPreferences class KeysPrefsViewController: NSViewController, MASPreferencesViewController { + var viewIdentifier: String = "Keys" + var toolbarItemLabel: String? = NSLocalizedString("Keys", comment: "Shown in the main prefs window") + var toolbarItemImage: NSImage? = NSImage(named: "KeyboardPref") + let prefs = UserDefaults.standard - var viewIdentifier: String = "Keys" - var toolbarItemLabel: String? = NSLocalizedString("Keys", comment: "Shown in the main prefs window") - var toolbarItemImage: NSImage? = NSImage.init(named: "KeyboardPref") - let prefs = UserDefaults.standard + @IBOutlet var listenFor: NSPopUpButton! - @IBOutlet var listenFor: NSPopUpButton! + override func viewDidLoad() { + super.viewDidLoad() - override func viewDidLoad() { - super.viewDidLoad() + self.listenFor.selectItem(at: self.prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue)) + } - listenFor.selectItem(at: prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue)) - } + @IBAction func listenForChanged(_ sender: NSPopUpButton) { + self.prefs.set(sender.selectedTag(), forKey: Utils.PrefKeys.listenFor.rawValue) - @IBAction func listenForChanged(_ sender: NSPopUpButton) { - prefs.set(sender.selectedTag(), forKey: Utils.PrefKeys.listenFor.rawValue) - - #if DEBUG - print("Toggle keys listened for state state -> \(sender.selectedItem?.title ?? "")") - #endif - - NotificationCenter.default.post(name: Notification.Name.init(Utils.PrefKeys.listenFor.rawValue), object: nil) - } + #if DEBUG + print("Toggle keys listened for state state -> \(sender.selectedItem?.title ?? "")") + #endif + NotificationCenter.default.post(name: Notification.Name(Utils.PrefKeys.listenFor.rawValue), object: nil) + } } diff --git a/MonitorControl/Prefs/MainPrefsViewController.swift b/MonitorControl/Prefs/MainPrefsViewController.swift index 7805f86b..e372f47c 100644 --- a/MonitorControl/Prefs/MainPrefsViewController.swift +++ b/MonitorControl/Prefs/MainPrefsViewController.swift @@ -11,77 +11,75 @@ import MASPreferences import ServiceManagement class MainPrefsViewController: NSViewController, MASPreferencesViewController { + var viewIdentifier: String = "Main" + var toolbarItemLabel: String? = NSLocalizedString("General", comment: "Shown in the main prefs window") + var toolbarItemImage: NSImage? = NSImage(named: NSImage.preferencesGeneralName) + let prefs = UserDefaults.standard - var viewIdentifier: String = "Main" - var toolbarItemLabel: String? = NSLocalizedString("General", comment: "Shown in the main prefs window") - var toolbarItemImage: NSImage? = NSImage.init(named: NSImage.preferencesGeneralName) - let prefs = UserDefaults.standard + @IBOutlet var versionLabel: NSTextField! + @IBOutlet var startAtLogin: NSButton! + @IBOutlet var showContrastSlider: NSButton! + @IBOutlet var lowerContrast: NSButton! - - @IBOutlet weak var versionLabel: NSTextField! - @IBOutlet var startAtLogin: NSButton! - @IBOutlet var showContrastSlider: NSButton! - @IBOutlet var lowerContrast: NSButton! + override func viewDidLoad() { + super.viewDidLoad() - override func viewDidLoad() { - super.viewDidLoad() + self.startAtLogin.state = self.prefs.bool(forKey: Utils.PrefKeys.startAtLogin.rawValue) ? .on : .off + self.showContrastSlider.state = self.prefs.bool(forKey: Utils.PrefKeys.showContrast.rawValue) ? .on : .off + self.lowerContrast.state = self.prefs.bool(forKey: Utils.PrefKeys.lowerContrast.rawValue) ? .on : .off + self.setVersionNumber() + } - startAtLogin.state = prefs.bool(forKey: Utils.PrefKeys.startAtLogin.rawValue) ? .on : .off - showContrastSlider.state = prefs.bool(forKey: Utils.PrefKeys.showContrast.rawValue) ? .on : .off - lowerContrast.state = prefs.bool(forKey: Utils.PrefKeys.lowerContrast.rawValue) ? .on : .off - setVersionNumber() + @IBAction func startAtLoginClicked(_ sender: NSButton) { + let identifier = "me.guillaumeb.MonitorControlHelper" as CFString + switch sender.state { + case .on: + self.prefs.set(true, forKey: Utils.PrefKeys.startAtLogin.rawValue) + SMLoginItemSetEnabled(identifier, true) + case .off: + self.prefs.set(false, forKey: Utils.PrefKeys.startAtLogin.rawValue) + SMLoginItemSetEnabled(identifier, false) + default: break } - @IBAction func startAtLoginClicked(_ sender: NSButton) { - let identifier = "me.guillaumeb.MonitorControlHelper" as CFString - switch sender.state { - case .on: - prefs.set(true, forKey: Utils.PrefKeys.startAtLogin.rawValue) - SMLoginItemSetEnabled(identifier, true) - case .off: - prefs.set(false, forKey: Utils.PrefKeys.startAtLogin.rawValue) - SMLoginItemSetEnabled(identifier, false) - default: break - } + #if DEBUG + print("Toggle start at login state -> \(sender.state == .on ? "on" : "off")") + #endif + } - #if DEBUG - print("Toggle start at login state -> \(sender.state == .on ? "on" : "off")") - #endif - } - - @IBAction func showContrastSliderClicked(_ sender: NSButton) { - switch sender.state { - case .on: - prefs.set(true, forKey: Utils.PrefKeys.showContrast.rawValue) - case .off: - prefs.set(false, forKey: Utils.PrefKeys.showContrast.rawValue) - default: break - } + @IBAction func showContrastSliderClicked(_ sender: NSButton) { + switch sender.state { + case .on: + self.prefs.set(true, forKey: Utils.PrefKeys.showContrast.rawValue) + case .off: + self.prefs.set(false, forKey: Utils.PrefKeys.showContrast.rawValue) + default: break + } - #if DEBUG - print("Toggle show contrast slider state -> \(sender.state == .on ? "on" : "off")") - #endif + #if DEBUG + print("Toggle show contrast slider state -> \(sender.state == .on ? "on" : "off")") + #endif - NotificationCenter.default.post(name: Notification.Name.init(Utils.PrefKeys.showContrast.rawValue), object: nil) - } + NotificationCenter.default.post(name: Notification.Name(Utils.PrefKeys.showContrast.rawValue), object: nil) + } - @IBAction func lowerContrastClicked(_ sender: NSButton) { - switch sender.state { - case .on: - prefs.set(true, forKey: Utils.PrefKeys.lowerContrast.rawValue) - case .off: - prefs.set(false, forKey: Utils.PrefKeys.lowerContrast.rawValue) - default: break - } + @IBAction func lowerContrastClicked(_ sender: NSButton) { + switch sender.state { + case .on: + self.prefs.set(true, forKey: Utils.PrefKeys.lowerContrast.rawValue) + case .off: + self.prefs.set(false, forKey: Utils.PrefKeys.lowerContrast.rawValue) + default: break + } - #if DEBUG - print("Toggle lower contrast after brightness state -> \(sender.state == .on ? "on" : "off")") - #endif - } + #if DEBUG + print("Toggle lower contrast after brightness state -> \(sender.state == .on ? "on" : "off")") + #endif + } - fileprivate func setVersionNumber() { - let versionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") ?? "unknown" - let buildNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") ?? "unknown" - versionLabel.stringValue = "version \(versionNumber) build \(buildNumber)" - } + fileprivate func setVersionNumber() { + let versionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") ?? "unknown" + let buildNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") ?? "unknown" + self.versionLabel.stringValue = "version \(versionNumber) build \(buildNumber)" + } } diff --git a/MonitorControl/Utils.swift b/MonitorControl/Utils.swift index 87bf2cee..50c7e0d6 100644 --- a/MonitorControl/Utils.swift +++ b/MonitorControl/Utils.swift @@ -9,248 +9,247 @@ import Cocoa class Utils: NSObject { - private static func printCommandValue(_ command: Int32, _ value: Int) { - let cmdString: (Int32) -> String? = { - switch $0 { - case BRIGHTNESS: - return "Brightness" - case CONTRAST: - return "Contrast" - case AUDIO_SPEAKER_VOLUME: - return "Volume" - default: - return nil - } - } - - print("\(cmdString(command) ?? "N/A") value: \(value)") - } - - // MARK: - DDCCTL - - /// Send command to ddcctl - /// - /// - Parameters: - /// - command: The command to send - /// - monitor: The id of the Monitor to send the command to - /// - value: the value of the command - static func sendCommand(_ command: Int32, toMonitor monitor: CGDirectDisplayID, withValue value: Int) { - var wrcmd = DDCWriteCommand(control_id: UInt8(command), new_value: UInt8(value)) - DDCWrite(monitor, &wrcmd) - - #if DEBUG - printCommandValue(command, value) - #endif - } - - /// Get current value of ddcctl command - /// - /// - Parameters: - /// - command: The command to send - /// - monitor: The id of the monitor to send the command to - /// - Returns: the value of the command - static func getCommand(_ command: Int32, fromMonitor monitor: CGDirectDisplayID) -> Int? { - var readCmd = DDCReadCommand() - readCmd.control_id = UInt8(command) - readCmd.max_value = 0 - readCmd.current_value = 0 - DDCRead(monitor, &readCmd) - - #if DEBUG - printCommandValue(command, Int(readCmd.current_value)) - #endif - - return readCmd.success ? Int(readCmd.current_value) : nil - } - - // MARK: - Menu - - /// Create a label - /// - /// - Parameters: - /// - text: The text of the label - /// - frame: The frame of the label - /// - Returns: An `NSTextField` label - static func makeLabel(text: String, frame: NSRect) -> NSTextField { - let label = NSTextField(frame: frame) - label.stringValue = text - label.isBordered = false - label.isBezeled = false - label.isEditable = false - label.drawsBackground = false - return label - } - - /// Create a slider and add it to the menu - /// - /// - Parameters: - /// - menu: Menu containing the slider - /// - display: Display to control - /// - command: Command (Brightness/Volume/...) - /// - title: Title of the slider - /// - Returns: An `NSSlider` slider - static func addSliderMenuItem(toMenu menu: NSMenu, forDisplay display: Display, command: Int32, title: String) -> SliderHandler { - let item = NSMenuItem() - let view = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 40)) - let label = Utils.makeLabel(text: title, frame: NSRect(x: 20, y: 19, width: 130, height: 20)) - let handler = SliderHandler(display: display, command: command) - let slider = NSSlider(frame: NSRect(x: 20, y: 0, width: 200, height: 19)) - slider.target = handler - slider.minValue = 0 - slider.maxValue = 100 - slider.action = #selector(SliderHandler.valueChanged) - handler.slider = slider - - view.addSubview(label) - view.addSubview(slider) - - item.view = view - - menu.insertItem(item, at: 0) - menu.insertItem(NSMenuItem.separator(), at: 1) - - DispatchQueue.global(qos: .background).async { - var val: Int? - - if let res = getCommand(command, fromMonitor: display.identifier) { - val = res - } - - if let val = val { - display.saveValue(val, for: command) - - DispatchQueue.main.async { - slider.integerValue = val - } - } - } - return handler - } - - // MARK: - Utilities - - /// Acquire Privileges (Necessary to listen to keyboard event globally) - static func acquirePrivileges() { - let options: NSDictionary = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] - let accessibilityEnabled = AXIsProcessTrustedWithOptions(options) - - if !accessibilityEnabled { - let alert = NSAlert() - alert.addButton(withTitle: NSLocalizedString("Ok", comment: "Shown in the alert dialog")) - alert.messageText = NSLocalizedString("Shortcuts not available", comment: "Shown in the alert dialog") - alert.informativeText = NSLocalizedString("You need to enable MonitorControl in System Preferences > Security and Privacy > Accessibility for the keyboard shortcuts to work", comment: "Shown in the alert dialog") - alert.alertStyle = .warning - alert.runModal() - } - - return - } - - // MARK: - Display Infos - - /// Get the descriptor text - /// - /// - Parameter descriptor: the descriptor - /// - Returns: a string - static func getEdidString(_ descriptor: descriptor) -> String { - var result = "" - for (_, bitChar) in Mirror(reflecting: descriptor.text.data).children { - if let bitChar = bitChar as? Int8 { - let char = Character(UnicodeScalar(UInt8(bitPattern: bitChar))) - if char == "\0" || char == "\n" { - break - } - result.append(char) - } - } - return result - } - - /// Get the descriptors of a display from the Edid - /// - /// - Parameters: - /// - edid: the EDID of a display - /// - type: the type of descriptor - /// - Returns: a string if type of descriptor is found - static func getDescriptorString(_ edid: EDID, _ type: UInt8) -> String? { - for (_, descriptor) in Mirror(reflecting: edid.descriptors).children { - if let descriptor = descriptor as? descriptor { - if descriptor.text.type == UInt8(type) { - return getEdidString(descriptor) - } - } - } - - return nil - } - - /// Get the name of a display - /// - /// - Parameter edid: the EDID of a display - /// - Returns: a string - static func getDisplayName(forEdid edid: EDID) -> String { - return getDescriptorString(edid, 0xFC) ?? NSLocalizedString("Display", comment: "") - } - - /// Get the serial of a display - /// - /// - Parameter edid: the EDID of a display - /// - Returns: a string - static func getDisplaySerial(forEdid edid: EDID) -> String { - return getDescriptorString(edid, 0xFF) ?? NSLocalizedString("Unknown", comment: "") - } - - /// Get the main display from a list of display - /// - /// - Parameter displays: List of Display - /// - Returns: the main display or nil if not found - static func getCurrentDisplay(from displays: [Display]) -> Display? { - return displays.first { display -> Bool in - if let main = NSScreen.main { - if let id = main.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as? CGDirectDisplayID { - return display.identifier == id - } - } - return false - } - } - - // MARK: - Enums - - /// UserDefault Keys for the app prefs - enum PrefKeys: String { - /// Was the app launched once - case appAlreadyLaunched - - /// Does the app start at Login - case startAtLogin - - /// Does the app start when plugged to an external monitor - case startWhenExternal - - /// Keys listened for (Brightness/Volume) - case listenFor - - /// Show contrast sliders - case showContrast - - /// Lower contrast after brightness - case lowerContrast - - /// Change Brightness/Volume for all screens - case allScreens - } - - /// Keys for the value of listenFor option - enum ListenForKeys: Int { - /// Listen for Brightness and Volume keys - case brightnessAndVolumeKeys = 0 - - /// Listen for Brightness keys only - case brightnessOnlyKeys = 1 - - /// Listen for Volume keys only - case volumeOnlyKeys = 2 - } - + private static func printCommandValue(_ command: Int32, _ value: Int) { + let cmdString: (Int32) -> String? = { + switch $0 { + case BRIGHTNESS: + return "Brightness" + case CONTRAST: + return "Contrast" + case AUDIO_SPEAKER_VOLUME: + return "Volume" + default: + return nil + } + } + + print("\(cmdString(command) ?? "N/A") value: \(value)") + } + + // MARK: - DDCCTL + + /// Send command to ddcctl + /// + /// - Parameters: + /// - command: The command to send + /// - monitor: The id of the Monitor to send the command to + /// - value: the value of the command + static func sendCommand(_ command: Int32, toMonitor monitor: CGDirectDisplayID, withValue value: Int) { + var wrcmd = DDCWriteCommand(control_id: UInt8(command), new_value: UInt8(value)) + DDCWrite(monitor, &wrcmd) + + #if DEBUG + self.printCommandValue(command, value) + #endif + } + + /// Get current value of ddcctl command + /// + /// - Parameters: + /// - command: The command to send + /// - monitor: The id of the monitor to send the command to + /// - Returns: the value of the command + static func getCommand(_ command: Int32, fromMonitor monitor: CGDirectDisplayID) -> Int? { + var readCmd = DDCReadCommand() + readCmd.control_id = UInt8(command) + readCmd.max_value = 0 + readCmd.current_value = 0 + DDCRead(monitor, &readCmd) + + #if DEBUG + self.printCommandValue(command, Int(readCmd.current_value)) + #endif + + return readCmd.success ? Int(readCmd.current_value) : nil + } + + // MARK: - Menu + + /// Create a label + /// + /// - Parameters: + /// - text: The text of the label + /// - frame: The frame of the label + /// - Returns: An `NSTextField` label + static func makeLabel(text: String, frame: NSRect) -> NSTextField { + let label = NSTextField(frame: frame) + label.stringValue = text + label.isBordered = false + label.isBezeled = false + label.isEditable = false + label.drawsBackground = false + return label + } + + /// Create a slider and add it to the menu + /// + /// - Parameters: + /// - menu: Menu containing the slider + /// - display: Display to control + /// - command: Command (Brightness/Volume/...) + /// - title: Title of the slider + /// - Returns: An `NSSlider` slider + static func addSliderMenuItem(toMenu menu: NSMenu, forDisplay display: Display, command: Int32, title: String) -> SliderHandler { + let item = NSMenuItem() + let view = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 40)) + let label = Utils.makeLabel(text: title, frame: NSRect(x: 20, y: 19, width: 130, height: 20)) + let handler = SliderHandler(display: display, command: command) + let slider = NSSlider(frame: NSRect(x: 20, y: 0, width: 200, height: 19)) + slider.target = handler + slider.minValue = 0 + slider.maxValue = 100 + slider.action = #selector(SliderHandler.valueChanged) + handler.slider = slider + + view.addSubview(label) + view.addSubview(slider) + + item.view = view + + menu.insertItem(item, at: 0) + menu.insertItem(NSMenuItem.separator(), at: 1) + + DispatchQueue.global(qos: .background).async { + var val: Int? + + if let res = getCommand(command, fromMonitor: display.identifier) { + val = res + } + + if let val = val { + display.saveValue(val, for: command) + + DispatchQueue.main.async { + slider.integerValue = val + } + } + } + return handler + } + + // MARK: - Utilities + + /// Acquire Privileges (Necessary to listen to keyboard event globally) + static func acquirePrivileges() { + let options: NSDictionary = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] + let accessibilityEnabled = AXIsProcessTrustedWithOptions(options) + + if !accessibilityEnabled { + let alert = NSAlert() + alert.addButton(withTitle: NSLocalizedString("Ok", comment: "Shown in the alert dialog")) + alert.messageText = NSLocalizedString("Shortcuts not available", comment: "Shown in the alert dialog") + alert.informativeText = NSLocalizedString("You need to enable MonitorControl in System Preferences > Security and Privacy > Accessibility for the keyboard shortcuts to work", comment: "Shown in the alert dialog") + alert.alertStyle = .warning + alert.runModal() + } + + return + } + + // MARK: - Display Infos + + /// Get the descriptor text + /// + /// - Parameter descriptor: the descriptor + /// - Returns: a string + static func getEdidString(_ descriptor: descriptor) -> String { + var result = "" + for (_, bitChar) in Mirror(reflecting: descriptor.text.data).children { + if let bitChar = bitChar as? Int8 { + let char = Character(UnicodeScalar(UInt8(bitPattern: bitChar))) + if char == "\0" || char == "\n" { + break + } + result.append(char) + } + } + return result + } + + /// Get the descriptors of a display from the Edid + /// + /// - Parameters: + /// - edid: the EDID of a display + /// - type: the type of descriptor + /// - Returns: a string if type of descriptor is found + static func getDescriptorString(_ edid: EDID, _ type: UInt8) -> String? { + for (_, descriptor) in Mirror(reflecting: edid.descriptors).children { + if let descriptor = descriptor as? descriptor { + if descriptor.text.type == UInt8(type) { + return self.getEdidString(descriptor) + } + } + } + + return nil + } + + /// Get the name of a display + /// + /// - Parameter edid: the EDID of a display + /// - Returns: a string + static func getDisplayName(forEdid edid: EDID) -> String { + return self.getDescriptorString(edid, 0xFC) ?? NSLocalizedString("Display", comment: "") + } + + /// Get the serial of a display + /// + /// - Parameter edid: the EDID of a display + /// - Returns: a string + static func getDisplaySerial(forEdid edid: EDID) -> String { + return self.getDescriptorString(edid, 0xFF) ?? NSLocalizedString("Unknown", comment: "") + } + + /// Get the main display from a list of display + /// + /// - Parameter displays: List of Display + /// - Returns: the main display or nil if not found + static func getCurrentDisplay(from displays: [Display]) -> Display? { + return displays.first { display -> Bool in + if let main = NSScreen.main { + if let id = main.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID { + return display.identifier == id + } + } + return false + } + } + + // MARK: - Enums + + /// UserDefault Keys for the app prefs + enum PrefKeys: String { + /// Was the app launched once + case appAlreadyLaunched + + /// Does the app start at Login + case startAtLogin + + /// Does the app start when plugged to an external monitor + case startWhenExternal + + /// Keys listened for (Brightness/Volume) + case listenFor + + /// Show contrast sliders + case showContrast + + /// Lower contrast after brightness + case lowerContrast + + /// Change Brightness/Volume for all screens + case allScreens + } + + /// Keys for the value of listenFor option + enum ListenForKeys: Int { + /// Listen for Brightness and Volume keys + case brightnessAndVolumeKeys = 0 + + /// Listen for Brightness keys only + case brightnessOnlyKeys = 1 + + /// Listen for Volume keys only + case volumeOnlyKeys = 2 + } } diff --git a/MonitorControlHelper/AppDelegate.swift b/MonitorControlHelper/AppDelegate.swift index af74872b..f780987c 100644 --- a/MonitorControlHelper/AppDelegate.swift +++ b/MonitorControlHelper/AppDelegate.swift @@ -11,22 +11,22 @@ import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { - @IBOutlet weak var window: NSWindow! + @IBOutlet weak var window: NSWindow! - func applicationDidFinishLaunching(_ aNotification: Notification) { - let bundlePath = Bundle.main.bundlePath as NSString - var pathComponents = bundlePath.pathComponents - for _ in 0...4 { - pathComponents.removeLast() - } + func applicationDidFinishLaunching(_ aNotification: Notification) { + let bundlePath = Bundle.main.bundlePath as NSString + var pathComponents = bundlePath.pathComponents + for _ in 0...4 { + pathComponents.removeLast() + } - let path = NSString.path(withComponents: pathComponents) - NSWorkspace.shared.launchApplication(path) - NSApp.terminate(nil) - } + let path = NSString.path(withComponents: pathComponents) + NSWorkspace.shared.launchApplication(path) + NSApp.terminate(nil) + } - func applicationWillTerminate(_ aNotification: Notification) { - // Insert code here to tear down your application - } + func applicationWillTerminate(_ aNotification: Notification) { + // Insert code here to tear down your application + } } diff --git a/MonitorControlHelper/Info.plist b/MonitorControlHelper/Info.plist index e560b18d..d17b6dda 100644 --- a/MonitorControlHelper/Info.plist +++ b/MonitorControlHelper/Info.plist @@ -20,6 +20,8 @@ 1.0 CFBundleVersion 1 + LSApplicationCategoryType + public.app-category.utilities LSBackgroundOnly LSMinimumSystemVersion diff --git a/Podfile b/Podfile index bc304895..82f435b3 100644 --- a/Podfile +++ b/Podfile @@ -1,11 +1,9 @@ -# Podfile - platform :osx, '10.11' target 'MonitorControl' do - use_frameworks! + use_frameworks! - pod 'MediaKeyTap', :git => 'https://github.com/the0neyouseek/MediaKeyTap.git' - pod 'MASPreferences' - pod 'AMCoreAudio', '~> 3.2' + pod 'MediaKeyTap', :git => 'https://github.com/the0neyouseek/MediaKeyTap.git' + pod 'MASPreferences' + pod 'AMCoreAudio' end diff --git a/Podfile.lock b/Podfile.lock index 8cbe4ded..1c394826 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,10 +1,10 @@ PODS: - AMCoreAudio (3.2.1) - MASPreferences (1.3) - - MediaKeyTap (2.1.0) + - MediaKeyTap (2.2.1) DEPENDENCIES: - - AMCoreAudio (~> 3.2) + - AMCoreAudio - MASPreferences - MediaKeyTap (from `https://github.com/the0neyouseek/MediaKeyTap.git`) @@ -19,14 +19,14 @@ EXTERNAL SOURCES: CHECKOUT OPTIONS: MediaKeyTap: - :commit: 3722ad54585d931977af8152a9555e832f4000f6 + :commit: 10348e3a800b665914699045582192dc9aca23d5 :git: https://github.com/the0neyouseek/MediaKeyTap.git SPEC CHECKSUMS: AMCoreAudio: 7fa6b718dc93acc29f849d60c3ad680ae1bf07b5 MASPreferences: c08b8622dd17b47da87669e741efd7c92e970e8c - MediaKeyTap: b652877e9ae2d52ca4f5310fa5152945ad3f0798 + MediaKeyTap: 737434cc2459e5fb9385f7e8974939ee833cd8ec -PODFILE CHECKSUM: 1b20d86a04731d82d4e8f346646d2b6b9d2db778 +PODFILE CHECKSUM: f5029816b34b5fb96cc7ee27b5e921c1777cb93e -COCOAPODS: 1.5.3 +COCOAPODS: 1.6.1 From 6e07eca3e3b562fca175cc576a5b03eac1a73372 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Mon, 6 May 2019 00:58:05 +0200 Subject: [PATCH 02/16] Refactor `MediaKeyTap` start/restart. --- MonitorControl/AppDelegate.swift | 83 ++++++++++++-------------------- MonitorControl/Info.plist | 2 +- 2 files changed, 33 insertions(+), 52 deletions(-) diff --git a/MonitorControl/AppDelegate.swift b/MonitorControl/AppDelegate.swift index 0e8ff3de..23f0aea4 100644 --- a/MonitorControl/AppDelegate.swift +++ b/MonitorControl/AppDelegate.swift @@ -31,14 +31,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { var mediaKeyTap: MediaKeyTap? var prefsController: NSWindowController? - var keysListenedFor: [MediaKey] = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown] - func applicationDidFinishLaunching(_: Notification) { app = self self.setupLayout() self.subscribeEventListeners() - setVolumeKeysMode() + self.startOrRestartMediaKeyTap() self.statusItem.image = NSImage(named: "status") self.statusItem.menu = self.statusMenu self.setDefaultPrefs() @@ -47,6 +45,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.updateDisplays() } + func applicationWillTerminate(_ notification: Notification) { + AMCoreAudio.NotificationCenter.defaultCenter.unsubscribe(self, eventType: AudioHardwareEvent.self) + } + @IBAction func quitClicked(_: AnyObject) { NSApplication.shared.terminate(self) } @@ -192,7 +194,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.addObserver(self, selector: #selector(handleShowContrastChanged), name: NSNotification.Name(Utils.PrefKeys.showContrast.rawValue), object: nil) // subscribe Audio output detector (AMCoreAudio) - NotificationCenter.defaultCenter.subscribe(self, eventType: AudioHardwareEvent.self, dispatchQueue: DispatchQueue.main) + AMCoreAudio.NotificationCenter.defaultCenter.subscribe(self, eventType: AudioHardwareEvent.self, dispatchQueue: DispatchQueue.main) } } @@ -232,70 +234,49 @@ extension AppDelegate: MediaKeyTapDelegate { // MARK: - Prefs notification @objc func handleListenForChanged() { - self.readKeyListenPreferences() - self.setKeysToListenFor() + self.startOrRestartMediaKeyTap() } @objc func handleShowContrastChanged() { self.updateDisplays() } - private func setKeysToListenFor() { - self.mediaKeyTap?.stop() - self.mediaKeyTap = MediaKeyTap(delegate: self, for: self.keysListenedFor, observeBuiltIn: false) - self.mediaKeyTap?.start() - } - - private func readKeyListenPreferences() { - let listenFor = prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue) + private func startOrRestartMediaKeyTap() { + var keys: [MediaKey] - self.keysListenedFor = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown] + switch prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue) { + case Utils.ListenForKeys.brightnessOnlyKeys.rawValue: + keys = [.brightnessUp, .brightnessDown] + case Utils.ListenForKeys.volumeOnlyKeys.rawValue: + keys = [.mute, .volumeUp, .volumeDown] + default: + keys = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown] + } - if listenFor == Utils.ListenForKeys.brightnessOnlyKeys.rawValue { - self.keysListenedFor.removeSubrange(2...4) - } else if listenFor == Utils.ListenForKeys.volumeOnlyKeys.rawValue { - self.keysListenedFor.removeSubrange(0...1) + if let audioDevice = AudioDevice.defaultOutputDevice(), audioDevice.canSetVirtualMasterVolume(direction: .playback) { + // Remove volume related keys. + let keysToDelete: [MediaKey] = [.volumeUp, .volumeDown, .mute] + keys.removeAll { keysToDelete.contains($0) } } + + self.mediaKeyTap?.stop() + self.mediaKeyTap = MediaKeyTap(delegate: self, for: keys, observeBuiltIn: false) + self.mediaKeyTap?.start() } } extension AppDelegate: EventSubscriber { /** - Fires off when a change in default audio device is detected. + Fires off when the default audio device changes. */ func eventReceiver(_ event: Event) { - switch event { - case let event as AudioHardwareEvent: - switch event { - case let .defaultOutputDeviceChanged(audioDevice): - #if DEBUG - print("Default output device changed to \(audioDevice)") - print("Can device set its own volume? \(audioDevice.canSetVirtualMasterVolume(direction: .playback))") - #endif - self.setVolumeKeysMode() - default: break - } - default: break - } - } + if case let .defaultOutputDeviceChanged(audioDevice)? = event as? AudioHardwareEvent { + #if DEBUG + print("Default output device changed to “\(audioDevice.name)”.") + print("Can device set its own volume? \(audioDevice.canSetVirtualMasterVolume(direction: .playback))") + #endif - /** - We check if the current default audio output device can change the volume, - if not, we know for sure that we don't need to interact with it. - */ - func setVolumeKeysMode() { - self.readKeyListenPreferences() - - if let defaultOutputDevice = AudioDevice.defaultOutputDevice() { - if defaultOutputDevice.canSetVirtualMasterVolume(direction: .playback) { - // Remove volume related keys - let keysToDelete: [MediaKey] = [.volumeUp, .volumeDown, .mute] - keysListenedFor = self.keysListenedFor.filter { !keysToDelete.contains($0) } - } else { - // load keys to listen to from prefs like normal - self.readKeyListenPreferences() - } + self.startOrRestartMediaKeyTap() } - self.setKeysToListenFor() } } diff --git a/MonitorControl/Info.plist b/MonitorControl/Info.plist index 4cf59c42..c00e42d6 100644 --- a/MonitorControl/Info.plist +++ b/MonitorControl/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.3.0 CFBundleVersion - 57 + 81 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion From 05a35b057a8a1a035bfc16546d862451cb2abf7f Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Mon, 6 May 2019 01:11:25 +0200 Subject: [PATCH 03/16] Remove useless comments. --- MonitorControl/AppDelegate.swift | 9 --------- MonitorControl/Bridging-Header.h | 8 -------- MonitorControl/Objects/ButtonCellView.swift | 8 -------- MonitorControl/Objects/Display.swift | 9 --------- MonitorControl/Objects/SliderHandler.swift | 9 --------- MonitorControl/Prefs/DisplayPrefsViewController.swift | 8 -------- MonitorControl/Prefs/KeysPrefsViewController.swift | 8 -------- MonitorControl/Prefs/MainPrefsViewController.swift | 8 -------- MonitorControl/Utils.swift | 8 -------- 9 files changed, 75 deletions(-) diff --git a/MonitorControl/AppDelegate.swift b/MonitorControl/AppDelegate.swift index 23f0aea4..20df050d 100644 --- a/MonitorControl/AppDelegate.swift +++ b/MonitorControl/AppDelegate.swift @@ -1,12 +1,3 @@ -// -// AppDelegate.swift -// MonitorControl -// -// Created by Mathew Kurian on 9/26/16. -// Last edited by Guillaume Broder on 9/17/2017 -// MIT Licensed. 2017. -// - import AMCoreAudio import Cocoa import Foundation diff --git a/MonitorControl/Bridging-Header.h b/MonitorControl/Bridging-Header.h index 7d73d33e..1679979f 100644 --- a/MonitorControl/Bridging-Header.h +++ b/MonitorControl/Bridging-Header.h @@ -1,11 +1,3 @@ -// -// Bridging-Header.h -// MonitorControl -// -// Created by Guillaume BRODER on 17/09/2017. -// MIT Licensed. 2017. -// - #ifndef Bridging_Header_h #define Bridging_Header_h diff --git a/MonitorControl/Objects/ButtonCellView.swift b/MonitorControl/Objects/ButtonCellView.swift index c4b575b3..0b062750 100644 --- a/MonitorControl/Objects/ButtonCellView.swift +++ b/MonitorControl/Objects/ButtonCellView.swift @@ -1,11 +1,3 @@ -// -// ButtonCellView.swift -// MonitorControl -// -// Created by Guillaume BRODER on 07/01/2018. -// Copyright © 2018 Mathew Kurian. All rights reserved. -// - import Cocoa class ButtonCellView: NSTableCellView { diff --git a/MonitorControl/Objects/Display.swift b/MonitorControl/Objects/Display.swift index 2a3942a3..218e7349 100644 --- a/MonitorControl/Objects/Display.swift +++ b/MonitorControl/Objects/Display.swift @@ -1,14 +1,5 @@ -// -// Display.swift -// MonitorControl -// -// Created by Guillaume BRODER on 02/01/2018. -// MIT Licensed. -// - import Cocoa -/// A display class Display { let identifier: CGDirectDisplayID let name: String diff --git a/MonitorControl/Objects/SliderHandler.swift b/MonitorControl/Objects/SliderHandler.swift index d9198db0..01a77f66 100644 --- a/MonitorControl/Objects/SliderHandler.swift +++ b/MonitorControl/Objects/SliderHandler.swift @@ -1,14 +1,5 @@ -// -// SliderHandler.swift -// MonitorControl -// -// Created by Guillaume BRODER on 9/17/2017. -// MIT Licensed. 2017. -// - import Cocoa -/// Handle the slider class SliderHandler { var slider: NSSlider? var display: Display diff --git a/MonitorControl/Prefs/DisplayPrefsViewController.swift b/MonitorControl/Prefs/DisplayPrefsViewController.swift index 6025eda8..d63865db 100644 --- a/MonitorControl/Prefs/DisplayPrefsViewController.swift +++ b/MonitorControl/Prefs/DisplayPrefsViewController.swift @@ -1,11 +1,3 @@ -// -// DisplayPrefsViewController.swift -// MonitorControl -// -// Created by Guillaume BRODER on 07/01/2018. -// MIT Licensed. -// - import Cocoa import MASPreferences diff --git a/MonitorControl/Prefs/KeysPrefsViewController.swift b/MonitorControl/Prefs/KeysPrefsViewController.swift index 557297c1..0a639c27 100644 --- a/MonitorControl/Prefs/KeysPrefsViewController.swift +++ b/MonitorControl/Prefs/KeysPrefsViewController.swift @@ -1,11 +1,3 @@ -// -// KeysPrefsViewController.swift -// MonitorControl -// -// Created by Guillaume BRODER on 07/01/2018. -// MIT Licensed. -// - import Cocoa import MASPreferences diff --git a/MonitorControl/Prefs/MainPrefsViewController.swift b/MonitorControl/Prefs/MainPrefsViewController.swift index e372f47c..4c2c3be4 100644 --- a/MonitorControl/Prefs/MainPrefsViewController.swift +++ b/MonitorControl/Prefs/MainPrefsViewController.swift @@ -1,11 +1,3 @@ -// -// MainPrefsViewController.swift -// MonitorControl -// -// Created by Guillaume BRODER on 07/01/2018. -// MIT Licensed. -// - import Cocoa import MASPreferences import ServiceManagement diff --git a/MonitorControl/Utils.swift b/MonitorControl/Utils.swift index 50c7e0d6..4378dbbc 100644 --- a/MonitorControl/Utils.swift +++ b/MonitorControl/Utils.swift @@ -1,11 +1,3 @@ -// -// Utils.swift -// MonitorControl -// -// Created by Guillaume BRODER on 9/17/2017. -// MIT Licensed. -// - import Cocoa class Utils: NSObject { From 3a4942c52b1b0979c7e65b301c7513b1b944eb86 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Mon, 6 May 2019 01:14:58 +0200 Subject: [PATCH 04/16] Reorder files. --- MonitorControl.xcodeproj/project.pbxproj | 66 ++++++++----------- .../{Objects => }/ButtonCellView.swift | 0 MonitorControl/{Objects => }/Display.swift | 0 .../{Objects => }/SliderHandler.swift | 0 .../DisplayPrefsViewController.swift | 0 .../KeysPrefsViewController.swift | 0 .../MainPrefsViewController.swift | 0 7 files changed, 29 insertions(+), 37 deletions(-) rename MonitorControl/{Objects => }/ButtonCellView.swift (100%) rename MonitorControl/{Objects => }/Display.swift (100%) rename MonitorControl/{Objects => }/SliderHandler.swift (100%) rename MonitorControl/{Prefs => View Controllers}/DisplayPrefsViewController.swift (100%) rename MonitorControl/{Prefs => View Controllers}/KeysPrefsViewController.swift (100%) rename MonitorControl/{Prefs => View Controllers}/MainPrefsViewController.swift (100%) diff --git a/MonitorControl.xcodeproj/project.pbxproj b/MonitorControl.xcodeproj/project.pbxproj index 5106c521..d969ced1 100644 --- a/MonitorControl.xcodeproj/project.pbxproj +++ b/MonitorControl.xcodeproj/project.pbxproj @@ -125,13 +125,13 @@ 56754EA21D9A4016007BCDC5 = { isa = PBXGroup; children = ( - 56754EAD1D9A4016007BCDC5 /* MonitorControl */, - F0A987D61F77B290009B603D /* OSD.framework */, 55359E321E2737EC002671BC /* ddcctl */, - F06792E8200A73460066C438 /* MonitorControlHelper */, - 56754EAC1D9A4016007BCDC5 /* Products */, F0A987D71F77B404009B603D /* Frameworks */, + 56754EAD1D9A4016007BCDC5 /* MonitorControl */, + F06792E8200A73460066C438 /* MonitorControlHelper */, + F0A987D61F77B290009B603D /* OSD.framework */, EFFC2F3E35BEC9ACFA754137 /* Pods */, + 56754EAC1D9A4016007BCDC5 /* Products */, ); sourceTree = ""; }; @@ -148,15 +148,17 @@ isa = PBXGroup; children = ( 56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */, - F091C9B71F6EA79B0096FD65 /* Utils.swift */, - 6C778D5821E91060000A4D5F /* Main.storyboard */, - 56754EB21D9A4016007BCDC5 /* MainMenu.xib */, - F0445D3620023D5B0025AE82 /* Prefs */, - F091C9B41F6EA6180096FD65 /* Objects */, - F091C9C41F6EBA5A0096FD65 /* Bridging-Header.h */, 56754EB01D9A4016007BCDC5 /* Assets.xcassets */, - F091C9C11F6EB8660096FD65 /* Localizable.strings */, + F091C9C41F6EBA5A0096FD65 /* Bridging-Header.h */, + F0445D4C200294AB0025AE82 /* ButtonCellView.swift */, + F03A8DF11FFBAA6F0034DC27 /* Display.swift */, 56754EB51D9A4016007BCDC5 /* Info.plist */, + F091C9C11F6EB8660096FD65 /* Localizable.strings */, + 6C778D5821E91060000A4D5F /* Main.storyboard */, + 56754EB21D9A4016007BCDC5 /* MainMenu.xib */, + F091C9B21F6EA6110096FD65 /* SliderHandler.swift */, + F091C9B71F6EA79B0096FD65 /* Utils.swift */, + F0445D3620023D5B0025AE82 /* View Controllers */, ); path = MonitorControl; sourceTree = ""; @@ -170,14 +172,14 @@ name = Pods; sourceTree = ""; }; - F0445D3620023D5B0025AE82 /* Prefs */ = { + F0445D3620023D5B0025AE82 /* View Controllers */ = { isa = PBXGroup; children = ( - F0445D3720023E710025AE82 /* MainPrefsViewController.swift */, - F0445D3B200254FA0025AE82 /* KeysPrefsViewController.swift */, F0445D3F200259C10025AE82 /* DisplayPrefsViewController.swift */, + F0445D3B200254FA0025AE82 /* KeysPrefsViewController.swift */, + F0445D3720023E710025AE82 /* MainPrefsViewController.swift */, ); - path = Prefs; + path = "View Controllers"; sourceTree = ""; }; F06792E8200A73460066C438 /* MonitorControlHelper */ = { @@ -190,16 +192,6 @@ path = MonitorControlHelper; sourceTree = ""; }; - F091C9B41F6EA6180096FD65 /* Objects */ = { - isa = PBXGroup; - children = ( - F0445D4C200294AB0025AE82 /* ButtonCellView.swift */, - F091C9B21F6EA6110096FD65 /* SliderHandler.swift */, - F03A8DF11FFBAA6F0034DC27 /* Display.swift */, - ); - path = Objects; - sourceTree = ""; - }; F0A987D71F77B404009B603D /* Frameworks */ = { isa = PBXGroup; children = ( @@ -212,15 +204,15 @@ F0A987D81F77B404009B603D /* MonitorControl */ = { isa = PBXGroup; children = ( - F0A987D91F77B404009B603D /* MainMenu.strings */, - F0A987DB1F77B404009B603D /* Localizable.strings */, - F0A987DD1F77B404009B603D /* Assets.xcassets */, - F0A987DE1F77B404009B603D /* Objects */, - F0A987E01F77B404009B603D /* MainMenu.xib */, - F0A987E21F77B404009B603D /* Utils.swift */, F0A987E51F77B404009B603D /* AppDelegate.swift */, + F0A987DD1F77B404009B603D /* Assets.xcassets */, F0A987E61F77B404009B603D /* Bridging-Header.h */, F0A987E71F77B404009B603D /* Info.plist */, + F0A987DB1F77B404009B603D /* Localizable.strings */, + F0A987D91F77B404009B603D /* MainMenu.strings */, + F0A987E01F77B404009B603D /* MainMenu.xib */, + F0A987DE1F77B404009B603D /* Objects */, + F0A987E21F77B404009B603D /* Utils.swift */, ); path = MonitorControl; sourceTree = ""; @@ -325,9 +317,9 @@ buildActionMask = 2147483647; files = ( 56754EB11D9A4016007BCDC5 /* Assets.xcassets in Resources */, + 55359E3B1E2737EC002671BC /* ddcctl.sh in Resources */, F0EB972F1F6ED7C800686D2A /* Localizable.strings in Resources */, 6C778D5A21E91060000A4D5F /* Main.storyboard in Resources */, - 55359E3B1E2737EC002671BC /* ddcctl.sh in Resources */, 56754EB41D9A4016007BCDC5 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -420,15 +412,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F0445D40200259C10025AE82 /* DisplayPrefsViewController.swift in Sources */, - F03A8DF21FFBAA6F0034DC27 /* Display.swift in Sources */, - F091C9B31F6EA6110096FD65 /* SliderHandler.swift in Sources */, 56754EAF1D9A4016007BCDC5 /* AppDelegate.swift in Sources */, + F0445D4D200294AB0025AE82 /* ButtonCellView.swift in Sources */, 55359E391E2737EC002671BC /* DDC.c in Sources */, - F0445D3820023E710025AE82 /* MainPrefsViewController.swift in Sources */, + F03A8DF21FFBAA6F0034DC27 /* Display.swift in Sources */, + F0445D40200259C10025AE82 /* DisplayPrefsViewController.swift in Sources */, F0445D3D200254FA0025AE82 /* KeysPrefsViewController.swift in Sources */, + F0445D3820023E710025AE82 /* MainPrefsViewController.swift in Sources */, + F091C9B31F6EA6110096FD65 /* SliderHandler.swift in Sources */, F091C9B81F6EA79B0096FD65 /* Utils.swift in Sources */, - F0445D4D200294AB0025AE82 /* ButtonCellView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MonitorControl/Objects/ButtonCellView.swift b/MonitorControl/ButtonCellView.swift similarity index 100% rename from MonitorControl/Objects/ButtonCellView.swift rename to MonitorControl/ButtonCellView.swift diff --git a/MonitorControl/Objects/Display.swift b/MonitorControl/Display.swift similarity index 100% rename from MonitorControl/Objects/Display.swift rename to MonitorControl/Display.swift diff --git a/MonitorControl/Objects/SliderHandler.swift b/MonitorControl/SliderHandler.swift similarity index 100% rename from MonitorControl/Objects/SliderHandler.swift rename to MonitorControl/SliderHandler.swift diff --git a/MonitorControl/Prefs/DisplayPrefsViewController.swift b/MonitorControl/View Controllers/DisplayPrefsViewController.swift similarity index 100% rename from MonitorControl/Prefs/DisplayPrefsViewController.swift rename to MonitorControl/View Controllers/DisplayPrefsViewController.swift diff --git a/MonitorControl/Prefs/KeysPrefsViewController.swift b/MonitorControl/View Controllers/KeysPrefsViewController.swift similarity index 100% rename from MonitorControl/Prefs/KeysPrefsViewController.swift rename to MonitorControl/View Controllers/KeysPrefsViewController.swift diff --git a/MonitorControl/Prefs/MainPrefsViewController.swift b/MonitorControl/View Controllers/MainPrefsViewController.swift similarity index 100% rename from MonitorControl/Prefs/MainPrefsViewController.swift rename to MonitorControl/View Controllers/MainPrefsViewController.swift From 5156420876a19412a3d1dd6e3aa2855ae67e8e47 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Mon, 6 May 2019 01:41:24 +0200 Subject: [PATCH 05/16] Add German translation. --- MonitorControl.xcodeproj/project.pbxproj | 11 +++++ MonitorControl/Base.lproj/Main.storyboard | 11 +++-- MonitorControl/Info.plist | 2 +- .../MainPrefsViewController.swift | 2 +- MonitorControl/de.lproj/Localizable.strings | 41 ++++++++++++++++ MonitorControl/de.lproj/Main.strings | 48 +++++++++++++++++++ MonitorControl/de.lproj/MainMenu.strings | 6 +++ MonitorControl/en.lproj/Main.strings | 18 ------- MonitorControl/fr.lproj/Main.strings | 18 ------- 9 files changed, 114 insertions(+), 43 deletions(-) create mode 100644 MonitorControl/de.lproj/Localizable.strings create mode 100644 MonitorControl/de.lproj/Main.strings create mode 100644 MonitorControl/de.lproj/MainMenu.strings diff --git a/MonitorControl.xcodeproj/project.pbxproj b/MonitorControl.xcodeproj/project.pbxproj index d969ced1..7d16192a 100644 --- a/MonitorControl.xcodeproj/project.pbxproj +++ b/MonitorControl.xcodeproj/project.pbxproj @@ -42,6 +42,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 28D1DD78227FA927004CB494 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = ""; }; + 28D1DD79227FA927004CB494 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Main.strings; sourceTree = ""; }; + 28D1DD7A227FA927004CB494 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = ""; }; + 28D1DD7B227FA927004CB494 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + 28D1DD7C227FA939004CB494 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; 31E16D90527EBD3F8A12BE0B /* Pods-MonitorControl.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MonitorControl.release.xcconfig"; path = "Pods/Target Support Files/Pods-MonitorControl/Pods-MonitorControl.release.xcconfig"; sourceTree = ""; }; 398F482D5C8816B29F16AAEB /* Pods_MonitorControl.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MonitorControl.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 42B61ABC1D7907131330228A /* Pods-MonitorControl.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MonitorControl.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MonitorControl/Pods-MonitorControl.debug.xcconfig"; sourceTree = ""; }; @@ -299,6 +304,7 @@ en, Base, fr, + de, ); mainGroup = 56754EA21D9A4016007BCDC5; productRefGroup = 56754EAC1D9A4016007BCDC5 /* Products */; @@ -439,6 +445,7 @@ isa = PBXVariantGroup; children = ( 56754EB31D9A4016007BCDC5 /* Base */, + 28D1DD7A227FA927004CB494 /* de */, F0445D49200285690025AE82 /* en */, F0445D4B2002856C0025AE82 /* fr */, ); @@ -449,6 +456,7 @@ isa = PBXVariantGroup; children = ( 6C778D5921E91060000A4D5F /* Base */, + 28D1DD79227FA927004CB494 /* de */, 6C778D5E21E910A2000A4D5F /* en */, 6C778D5F21E910A6000A4D5F /* fr */, ); @@ -458,6 +466,7 @@ F091C9C11F6EB8660096FD65 /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( + 28D1DD7C227FA939004CB494 /* de */, F091C9C21F6EB8660096FD65 /* en */, F091C9C31F6EB8720096FD65 /* fr */, ); @@ -476,6 +485,7 @@ F0A987DB1F77B404009B603D /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( + 28D1DD7B227FA927004CB494 /* de */, F0A987DC1F77B404009B603D /* en */, F0A987E41F77B404009B603D /* fr */, ); @@ -486,6 +496,7 @@ isa = PBXVariantGroup; children = ( F0A987E11F77B404009B603D /* Base */, + 28D1DD78227FA927004CB494 /* de */, ); name = MainMenu.xib; sourceTree = ""; diff --git a/MonitorControl/Base.lproj/Main.storyboard b/MonitorControl/Base.lproj/Main.storyboard index 8c520638..2bb36547 100644 --- a/MonitorControl/Base.lproj/Main.storyboard +++ b/MonitorControl/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -207,6 +207,7 @@ +