From dd3d026db3e7eea5817b7520186567f981cad8ad Mon Sep 17 00:00:00 2001 From: Joni Van Roost Date: Wed, 28 Aug 2019 12:17:35 +0200 Subject: [PATCH] Add Advanced Preferences panel (#97) - Removed whitelist which caused some issues for some people #105 - Added `hide OSD` option to `AdvancedPrefsViewController` - Added `Longer Delay` option to `AdvancedPrefsViewController` - Added `PollingMode` and `PollingCount` to `AdvancedPrefsViewController` (should help #37) - Added option to reset all preferences to `AdvancedPrefsViewController` See the wiki for more info: https://github.com/the0neyouseek/MonitorControl/wiki/Advanced-Preferences --- .swiftlint.yml | 2 + Cartfile.resolved | 4 +- MonitorControl.xcodeproj/project.pbxproj | 90 +++-- MonitorControl/AppDelegate.swift | 56 +-- MonitorControl/Display.swift | 68 +++- .../Extensions/Display+Extension.swift | 7 + .../Extensions/Display+Whitelist.swift | 29 -- .../Extensions/NSNotification+Extension.swift | 5 + MonitorControl/Manager/DisplayManager.swift | 35 ++ .../Support/OnlyIntegerValueFormatter.swift | 15 + MonitorControl/Support/PollingMode.swift | 24 ++ MonitorControl/Support/Utils.swift | 20 +- MonitorControl/UI/Base.lproj/Main.storyboard | 334 ++++++++++++++++++ .../UI/{ => Cells}/ButtonCellView.swift | 0 .../UI/{ => Cells}/FriendlyNameCellView.swift | 1 - MonitorControl/UI/Cells/HideOsdCellView.swift | 25 ++ .../UI/Cells/LongerDelayCellView.swift | 41 +++ .../UI/Cells/PollingCountCellView.swift | 24 ++ .../UI/Cells/PollingModeCellView.swift | 31 ++ .../AdvancedPrefsViewController.swift | 132 +++++++ .../DisplayPrefsViewController.swift | 28 +- .../KeysPrefsViewController.swift | 5 +- .../MainPrefsViewController.swift | 26 +- 23 files changed, 894 insertions(+), 108 deletions(-) create mode 100644 MonitorControl/Extensions/Display+Extension.swift delete mode 100644 MonitorControl/Extensions/Display+Whitelist.swift create mode 100644 MonitorControl/Manager/DisplayManager.swift create mode 100644 MonitorControl/Support/OnlyIntegerValueFormatter.swift create mode 100644 MonitorControl/Support/PollingMode.swift rename MonitorControl/UI/{ => Cells}/ButtonCellView.swift (100%) rename MonitorControl/UI/{ => Cells}/FriendlyNameCellView.swift (95%) create mode 100644 MonitorControl/UI/Cells/HideOsdCellView.swift create mode 100644 MonitorControl/UI/Cells/LongerDelayCellView.swift create mode 100644 MonitorControl/UI/Cells/PollingCountCellView.swift create mode 100644 MonitorControl/UI/Cells/PollingModeCellView.swift create mode 100644 MonitorControl/View Controllers/AdvancedPrefsViewController.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index 056c607..a6dc814 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -7,3 +7,5 @@ excluded: - Carthage type_body_length: 500 file_length: 500 +cyclomatic_complexity: + ignores_case_statements: true \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved index f2ff341..12b03d0 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "reitermarkus/DDC.swift" "5c03666e17a1a850892c08a5db6f4a208f762f26" -github "rnine/AMCoreAudio" "3.3" +github "reitermarkus/DDC.swift" "f8e7dc7f2fea41ec4e27672fdbc7051be218ad02" +github "rnine/AMCoreAudio" "3.3.1" github "shpakovski/MASPreferences" "1.3" github "the0neyouseek/MediaKeyTap" "3.1.0" diff --git a/MonitorControl.xcodeproj/project.pbxproj b/MonitorControl.xcodeproj/project.pbxproj index 99d1f8e..c837fca 100644 --- a/MonitorControl.xcodeproj/project.pbxproj +++ b/MonitorControl.xcodeproj/project.pbxproj @@ -26,8 +26,16 @@ 28D1DE15227FD006004CB494 /* DDC.framework.dSYM in [Carthage] Copy Framework Debug Symbols */ = {isa = PBXBuildFile; fileRef = 28D1DE11227FD006004CB494 /* DDC.framework.dSYM */; }; 56754EAF1D9A4016007BCDC5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */; }; 56754EB11D9A4016007BCDC5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 56754EB01D9A4016007BCDC5 /* Assets.xcassets */; }; - 8C0E20562296ABBA000CBF15 /* NSNotification+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C0E20552296ABBA000CBF15 /* NSNotification+Extension.swift */; }; - F01B067922822141008E64DB /* Display+Whitelist.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01B067822822141008E64DB /* Display+Whitelist.swift */; }; + 6C0CCB26228F4F720037D2C5 /* AdvancedPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C0CCB25228F4F720037D2C5 /* AdvancedPrefsViewController.swift */; }; + 6C20466C23153E4F00859767 /* Display+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C20466B23153E4F00859767 /* Display+Extension.swift */; }; + 6C2EA1CD228F644B00060E3F /* OnlyIntegerValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C2EA1CC228F644B00060E3F /* OnlyIntegerValueFormatter.swift */; }; + 6C2EA1CF228F7DFB00060E3F /* PollingMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C2EA1CE228F7DFB00060E3F /* PollingMode.swift */; }; + 6C85EFDA22C941B000227EA1 /* DisplayManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C85EFD922C941B000227EA1 /* DisplayManager.swift */; }; + 6C85EFDD22CBAA8F00227EA1 /* PollingModeCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C85EFDC22CBAA8F00227EA1 /* PollingModeCellView.swift */; }; + 6C85EFDF22CBB54100227EA1 /* PollingCountCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C85EFDE22CBB54100227EA1 /* PollingCountCellView.swift */; }; + 6C85EFE122CC00AD00227EA1 /* NSNotification+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C85EFE022CC00AD00227EA1 /* NSNotification+Extension.swift */; }; + 6CCB278622D5315200619B05 /* HideOsdCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CCB278522D5315200619B05 /* HideOsdCellView.swift */; }; + 6CD444C322D4FBB8005BFD3D /* LongerDelayCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD444C222D4FBB8005BFD3D /* LongerDelayCellView.swift */; }; F01B0699228221B7008E64DB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F01B0680228221B6008E64DB /* Localizable.strings */; }; F01B069A228221B7008E64DB /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01B0683228221B6008E64DB /* Utils.swift */; }; F01B069E228221B7008E64DB /* ButtonCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01B068E228221B6008E64DB /* ButtonCellView.swift */; }; @@ -106,8 +114,16 @@ 56754EAB1D9A4016007BCDC5 /* MonitorControl.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MonitorControl.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = ""; }; 56754EB01D9A4016007BCDC5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 8C0E20552296ABBA000CBF15 /* NSNotification+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNotification+Extension.swift"; sourceTree = ""; }; - F01B067822822141008E64DB /* Display+Whitelist.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Display+Whitelist.swift"; sourceTree = ""; }; + 6C0CCB25228F4F720037D2C5 /* AdvancedPrefsViewController.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = AdvancedPrefsViewController.swift; sourceTree = ""; tabWidth = 4; }; + 6C20466B23153E4F00859767 /* Display+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Display+Extension.swift"; sourceTree = ""; }; + 6C2EA1CC228F644B00060E3F /* OnlyIntegerValueFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnlyIntegerValueFormatter.swift; sourceTree = ""; }; + 6C2EA1CE228F7DFB00060E3F /* PollingMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollingMode.swift; sourceTree = ""; }; + 6C85EFD922C941B000227EA1 /* DisplayManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayManager.swift; sourceTree = ""; }; + 6C85EFDC22CBAA8F00227EA1 /* PollingModeCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollingModeCellView.swift; sourceTree = ""; }; + 6C85EFDE22CBB54100227EA1 /* PollingCountCellView.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = PollingCountCellView.swift; sourceTree = ""; tabWidth = 4; }; + 6C85EFE022CC00AD00227EA1 /* NSNotification+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSNotification+Extension.swift"; sourceTree = ""; }; + 6CCB278522D5315200619B05 /* HideOsdCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideOsdCellView.swift; sourceTree = ""; }; + 6CD444C222D4FBB8005BFD3D /* LongerDelayCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongerDelayCellView.swift; sourceTree = ""; }; F01B0681228221B6008E64DB /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; F01B0682228221B6008E64DB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; F01B0683228221B6008E64DB /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; @@ -179,10 +195,10 @@ isa = PBXGroup; children = ( 2894D9B72280B30500DF58DA /* CGDirectDisplayID+Extension.swift */, - F01B067822822141008E64DB /* Display+Whitelist.swift */, 28D1DDEC227FB8F2004CB494 /* EDID+Extension.swift */, 28D1DDF1227FBE71004CB494 /* NSScreen+Extension.swift */, - 8C0E20552296ABBA000CBF15 /* NSNotification+Extension.swift */, + 6C85EFE022CC00AD00227EA1 /* NSNotification+Extension.swift */, + 6C20466B23153E4F00859767 /* Display+Extension.swift */, ); path = Extensions; sourceTree = ""; @@ -210,6 +226,8 @@ 56754EAD1D9A4016007BCDC5 /* MonitorControl */ = { isa = PBXGroup; children = ( + F01B0686228221B6008E64DB /* Info.plist */, + 6C85EFD622C74B0E00227EA1 /* Manager */, 56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */, 56754EB01D9A4016007BCDC5 /* Assets.xcassets */, F03A8DF11FFBAA6F0034DC27 /* Display.swift */, @@ -221,13 +239,35 @@ path = MonitorControl; sourceTree = ""; }; + 6C85EFD622C74B0E00227EA1 /* Manager */ = { + isa = PBXGroup; + children = ( + 6C85EFD922C941B000227EA1 /* DisplayManager.swift */, + ); + path = Manager; + sourceTree = ""; + }; + 6C85EFDB22CBA77600227EA1 /* Cells */ = { + isa = PBXGroup; + children = ( + F03FE4BF228DF62A001F59A4 /* FriendlyNameCellView.swift */, + F01B068E228221B6008E64DB /* ButtonCellView.swift */, + 6C85EFDC22CBAA8F00227EA1 /* PollingModeCellView.swift */, + 6C85EFDE22CBB54100227EA1 /* PollingCountCellView.swift */, + 6CD444C222D4FBB8005BFD3D /* LongerDelayCellView.swift */, + 6CCB278522D5315200619B05 /* HideOsdCellView.swift */, + ); + path = Cells; + sourceTree = ""; + }; F01B067F228221B6008E64DB /* Support */ = { isa = PBXGroup; children = ( F01B0685228221B6008E64DB /* Bridging-Header.h */, - F01B0686228221B6008E64DB /* Info.plist */, F01B0680228221B6008E64DB /* Localizable.strings */, F01B0683228221B6008E64DB /* Utils.swift */, + 6C2EA1CC228F644B00060E3F /* OnlyIntegerValueFormatter.swift */, + 6C2EA1CE228F7DFB00060E3F /* PollingMode.swift */, ); path = Support; sourceTree = ""; @@ -235,8 +275,7 @@ F01B0687228221B6008E64DB /* UI */ = { isa = PBXGroup; children = ( - F03FE4BF228DF62A001F59A4 /* FriendlyNameCellView.swift */, - F01B068E228221B6008E64DB /* ButtonCellView.swift */, + 6C85EFDB22CBA77600227EA1 /* Cells */, F01B0690228221B7008E64DB /* Main.storyboard */, F01B0692228221B7008E64DB /* MainMenu.xib */, F01B068F228221B7008E64DB /* SliderHandler.swift */, @@ -250,6 +289,7 @@ F0445D3F200259C10025AE82 /* DisplayPrefsViewController.swift */, F0445D3B200254FA0025AE82 /* KeysPrefsViewController.swift */, F0445D3720023E710025AE82 /* MainPrefsViewController.swift */, + 6C0CCB25228F4F720037D2C5 /* AdvancedPrefsViewController.swift */, ); path = "View Controllers"; sourceTree = ""; @@ -320,13 +360,13 @@ TargetAttributes = { 56754EAA1D9A4016007BCDC5 = { CreatedOnToolsVersion = 8.0; - DevelopmentTeam = KGY56RWR9A; + DevelopmentTeam = CYC8C8R4K9; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; F06792E6200A73460066C438 = { CreatedOnToolsVersion = 9.2; - DevelopmentTeam = KGY56RWR9A; + DevelopmentTeam = CYC8C8R4K9; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; @@ -454,18 +494,26 @@ buildActionMask = 2147483647; files = ( 56754EAF1D9A4016007BCDC5 /* AppDelegate.swift in Sources */, + 6C85EFE122CC00AD00227EA1 /* NSNotification+Extension.swift in Sources */, + 6C85EFDF22CBB54100227EA1 /* PollingCountCellView.swift in Sources */, + 6C0CCB26228F4F720037D2C5 /* AdvancedPrefsViewController.swift in Sources */, F01B069E228221B7008E64DB /* ButtonCellView.swift in Sources */, + 6C2EA1CD228F644B00060E3F /* OnlyIntegerValueFormatter.swift in Sources */, 2894D9B82280B30500DF58DA /* CGDirectDisplayID+Extension.swift in Sources */, + 6CCB278622D5315200619B05 /* HideOsdCellView.swift in Sources */, + 6CD444C322D4FBB8005BFD3D /* LongerDelayCellView.swift in Sources */, F03FE4C0228DF62B001F59A4 /* FriendlyNameCellView.swift in Sources */, - F01B067922822141008E64DB /* Display+Whitelist.swift in Sources */, + 6C2EA1CF228F7DFB00060E3F /* PollingMode.swift in Sources */, F03A8DF21FFBAA6F0034DC27 /* Display.swift in Sources */, F0445D40200259C10025AE82 /* DisplayPrefsViewController.swift in Sources */, - 8C0E20562296ABBA000CBF15 /* NSNotification+Extension.swift in Sources */, 28D1DDF0227FBD99004CB494 /* EDID+Extension.swift in Sources */, + 6C85EFDD22CBAA8F00227EA1 /* PollingModeCellView.swift in Sources */, F0445D3D200254FA0025AE82 /* KeysPrefsViewController.swift in Sources */, + 6C20466C23153E4F00859767 /* Display+Extension.swift in Sources */, F0445D3820023E710025AE82 /* MainPrefsViewController.swift in Sources */, 28D1DDF2227FBE71004CB494 /* NSScreen+Extension.swift in Sources */, F01B069F228221B7008E64DB /* SliderHandler.swift in Sources */, + 6C85EFDA22C941B000227EA1 /* DisplayManager.swift in Sources */, F01B069A228221B7008E64DB /* Utils.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -641,8 +689,8 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 560; - DEVELOPMENT_TEAM = KGY56RWR9A; + CURRENT_PROJECT_VERSION = 570; + DEVELOPMENT_TEAM = CYC8C8R4K9; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -668,8 +716,8 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 560; - DEVELOPMENT_TEAM = KGY56RWR9A; + CURRENT_PROJECT_VERSION = 570; + DEVELOPMENT_TEAM = CYC8C8R4K9; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -699,8 +747,8 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 560; - DEVELOPMENT_TEAM = KGY56RWR9A; + CURRENT_PROJECT_VERSION = 570; + DEVELOPMENT_TEAM = CYC8C8R4K9; ENABLE_HARDENED_RUNTIME = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = MonitorControlHelper/Info.plist; @@ -724,8 +772,8 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 560; - DEVELOPMENT_TEAM = KGY56RWR9A; + CURRENT_PROJECT_VERSION = 570; + DEVELOPMENT_TEAM = CYC8C8R4K9; ENABLE_HARDENED_RUNTIME = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = MonitorControlHelper/Info.plist; diff --git a/MonitorControl/AppDelegate.swift b/MonitorControl/AppDelegate.swift index 89157b0..86e9fa6 100644 --- a/MonitorControl/AppDelegate.swift +++ b/MonitorControl/AppDelegate.swift @@ -17,10 +17,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) var monitorItems: [NSMenuItem] = [] - var displays: [Display] = [] let step = 100 / 16 + var displayManager: DisplayManager? var mediaKeyTap: MediaKeyTap? var prefsController: NSWindowController? @@ -29,7 +29,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_: Notification) { app = self - self.setupLayout() + self.displayManager = DisplayManager() + self.setupViewControllers() self.subscribeEventListeners() self.startOrRestartMediaKeyTap() self.statusItem.image = NSImage(named: "status") @@ -40,11 +41,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.updateDisplays() } - func applicationWillTerminate(_: Notification) { - AMCoreAudio.NotificationCenter.defaultCenter.unsubscribe(self, eventType: AudioHardwareEvent.self) - DistributedNotificationCenter.default().removeObserver(self.accessibilityObserver as Any, name: .accessibilityApi, object: nil) - } - @IBAction func quitClicked(_: AnyObject) { NSApplication.shared.terminate(self) } @@ -59,7 +55,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { /// 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) @@ -83,7 +78,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } self.monitorItems = [] - self.displays = [] + self.displayManager?.clearDisplays() } func updateDisplays() { @@ -94,7 +89,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { if screen.isBuiltin { return false } - return DDC(for: screen.displayID)?.edid() != nil } @@ -128,8 +122,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { if let edid = ddc?.edid() { let name = Utils.getDisplayName(forEdid: edid) + let isEnabled = (prefs.object(forKey: "\(id)-state") as? Bool) ?? true - let display = Display(id, name: name, isBuiltin: screen.isBuiltin) + let display = Display(id, name: name, isBuiltin: screen.isBuiltin, isEnabled: isEnabled) let monitorSubMenu: NSMenu = asSubMenu ? NSMenu() : self.statusMenu @@ -153,7 +148,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { display.volumeSliderHandler = volumeSliderHandler display.brightnessSliderHandler = brightnessSliderHandler - self.displays.append(display) + self.displayManager?.addDisplay(display: display) let monitorMenuItem = NSMenuItem() monitorMenuItem.title = "\(display.getFriendlyName())" @@ -166,27 +161,39 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } - private func setupLayout() { + private func setupViewControllers() { let storyboard: NSStoryboard = NSStoryboard(name: "Main", bundle: Bundle.main) + let mainPrefsVc = storyboard.instantiateController(withIdentifier: "MainPrefsVC") + let keyPrefsVc = storyboard.instantiateController(withIdentifier: "KeysPrefsVC") + let displayPrefsVc = storyboard.instantiateController(withIdentifier: "DisplayPrefsVC") + let advancedPrefsVc = storyboard.instantiateController(withIdentifier: "AdvancedPrefsVC") let views = [ - storyboard.instantiateController(withIdentifier: "MainPrefsVC"), - storyboard.instantiateController(withIdentifier: "KeysPrefsVC"), - storyboard.instantiateController(withIdentifier: "DisplayPrefsVC"), + mainPrefsVc, + keyPrefsVc, + displayPrefsVc, + advancedPrefsVc, ] prefsController = MASPreferencesWindowController(viewControllers: views, title: NSLocalizedString("Preferences", comment: "Shown in Preferences window")) + if let displayPrefs = displayPrefsVc as? DisplayPrefsViewController { + displayPrefs.displayManager = self.displayManager + } + if let advancedPrefs = advancedPrefsVc as? AdvancedPrefsViewController { + advancedPrefs.displayManager = self.displayManager + } } 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) - NotificationCenter.default.addObserver(self, selector: #selector(handleFriendlyNameChanged), name: NSNotification.Name(Utils.PrefKeys.friendlyName.rawValue), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(handleListenForChanged), name: .listenFor, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(handleShowContrastChanged), name: .showContrast, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(handleFriendlyNameChanged), name: .friendlyName, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(handlePreferenceReset), name: .preferenceReset, object: nil) // subscribe Audio output detector (AMCoreAudio) AMCoreAudio.NotificationCenter.defaultCenter.subscribe(self, eventType: AudioHardwareEvent.self, dispatchQueue: DispatchQueue.main) // listen for accessibility status changes - self.accessibilityObserver = DistributedNotificationCenter.default().addObserver(forName: .accessibilityApi, object: nil, queue: nil) { _ in + _ = DistributedNotificationCenter.default().addObserver(forName: .accessibilityApi, object: nil, queue: nil) { _ in DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.startOrRestartMediaKeyTap() } @@ -198,9 +205,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { extension AppDelegate: MediaKeyTapDelegate { func handle(mediaKey: MediaKey, event _: KeyEvent?, modifiers: NSEvent.ModifierFlags?) { + let displays = self.displayManager?.getDisplays() ?? [Display]() guard let currentDisplay = Utils.getCurrentDisplay(from: displays) else { return } - let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? self.displays : [currentDisplay] + let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? displays : [currentDisplay] let isSmallIncrement = modifiers?.isSuperset(of: NSEvent.ModifierFlags([.shift, .option])) ?? false for display in allDisplays { @@ -241,6 +249,12 @@ extension AppDelegate: MediaKeyTapDelegate { self.updateDisplays() } + @objc func handlePreferenceReset() { + self.setDefaultPrefs() + self.updateDisplays() + self.startOrRestartMediaKeyTap() + } + private func startOrRestartMediaKeyTap() { var keys: [MediaKey] diff --git a/MonitorControl/Display.swift b/MonitorControl/Display.swift index be99c0d..39d6b3a 100644 --- a/MonitorControl/Display.swift +++ b/MonitorControl/Display.swift @@ -14,6 +14,26 @@ class Display { var contrastSliderHandler: SliderHandler? var ddc: DDC? + var hideOsd: Bool { + get { + return self.prefs.bool(forKey: "hideOsd-\(self.identifier)") + } + set { + self.prefs.set(newValue, forKey: "hideOsd-\(self.identifier)") + os_log("Set `hideOsd` to: %{public}@", type: .info, String(newValue)) + } + } + + var needsLongerDelay: Bool { + get { + return self.prefs.object(forKey: "longerDelay-\(self.identifier)") as? Bool ?? false + } + set { + self.prefs.set(newValue, forKey: "longerDelay-\(self.identifier)") + os_log("Set `needsLongerDisplay` to: %{public}@", type: .info, String(newValue)) + } + } + private let prefs = UserDefaults.standard private var audioPlayer: AVAudioPlayer? @@ -67,7 +87,9 @@ class Display { } if let slider = volumeSliderHandler?.slider { - slider.intValue = Int32(value) + DispatchQueue.main.async { + slider.intValue = Int32(value) + } } } @@ -90,7 +112,9 @@ class Display { } if let slider = volumeSliderHandler?.slider { - slider.intValue = Int32(value) + DispatchQueue.main.async { + slider.intValue = Int32(value) + } } self.saveValue(value, for: .audioSpeakerVolume) @@ -161,6 +185,46 @@ class Display { return self.prefs.string(forKey: "friendlyName-\(self.identifier)") ?? self.name } + func setPollingMode(_ value: Int) { + self.prefs.set(String(value), forKey: "pollingMode-\(self.identifier)") + } + + /* + Polling Modes: + 0 -> .none -> 0 tries + 1 -> .minimal -> 5 tries + 2 -> .normal -> 10 tries + 3 -> .heavy -> 100 tries + 4 -> .custom -> $pollingCount tries + */ + func getPollingMode() -> Int { + // Reading as string so we don't get "0" as the default value + return Int(self.prefs.string(forKey: "pollingMode-\(self.identifier)") ?? "2") ?? 2 + } + + func getPollingCount() -> Int { + let selectedMode = self.getPollingMode() + switch selectedMode { + case 0: + return PollingMode.none.value + case 1: + return PollingMode.minimal.value + case 2: + return PollingMode.normal.value + case 3: + return PollingMode.heavy.value + case 4: + let val = self.prefs.integer(forKey: "pollingCount-\(self.identifier)") + return PollingMode.custom(value: val).value + default: + return 0 + } + } + + func setPollingCount(_ value: Int) { + self.prefs.set(value, forKey: "pollingCount-\(self.identifier)") + } + private func showOsd(command: DDC.Command, value: Int, isSmallIncrement: Bool = false) { guard let manager = OSDManager.sharedManager() as? OSDManager else { return diff --git a/MonitorControl/Extensions/Display+Extension.swift b/MonitorControl/Extensions/Display+Extension.swift new file mode 100644 index 0000000..dbff1ca --- /dev/null +++ b/MonitorControl/Extensions/Display+Extension.swift @@ -0,0 +1,7 @@ +import Foundation + +extension Display: Equatable { + static func == (lhs: Display, rhs: Display) -> Bool { + return lhs.identifier == rhs.identifier + } +} diff --git a/MonitorControl/Extensions/Display+Whitelist.swift b/MonitorControl/Extensions/Display+Whitelist.swift deleted file mode 100644 index 43f5c29..0000000 --- a/MonitorControl/Extensions/Display+Whitelist.swift +++ /dev/null @@ -1,29 +0,0 @@ -extension Display { - enum WhitelistReason { - case longerDelay - case hideOsd - } - - static let whitelist: [UInt32: [UInt32: [WhitelistReason]]] = [ - 7789: [ - 30460: [.hideOsd, .longerDelay], // LG 38UC99-W over DisplayPort - 30459: [.hideOsd, .longerDelay], // LG 38UC99-W over HDMI - ], - ] - - var hideOsd: Bool { - guard let vendor = self.identifier.vendorNumber, let model = self.identifier.modelNumber else { - return false - } - - return Display.whitelist[vendor]?[model]?.contains(.hideOsd) ?? false - } - - var needsLongerDelay: Bool { - guard let vendor = self.identifier.vendorNumber, let model = self.identifier.modelNumber else { - return false - } - - return Display.whitelist[vendor]?[model]?.contains(.longerDelay) ?? false - } -} diff --git a/MonitorControl/Extensions/NSNotification+Extension.swift b/MonitorControl/Extensions/NSNotification+Extension.swift index fec8d83..19c6579 100644 --- a/MonitorControl/Extensions/NSNotification+Extension.swift +++ b/MonitorControl/Extensions/NSNotification+Extension.swift @@ -2,4 +2,9 @@ import Cocoa extension NSNotification.Name { static let accessibilityApi = NSNotification.Name(rawValue: "com.apple.accessibility.api") + static let listenFor = NSNotification.Name(rawValue: Utils.PrefKeys.listenFor.rawValue) + static let showContrast = NSNotification.Name(rawValue: Utils.PrefKeys.showContrast.rawValue) + static let friendlyName = NSNotification.Name(rawValue: Utils.PrefKeys.friendlyName.rawValue) + static let preferenceReset = NSNotification.Name(rawValue: Utils.PrefKeys.preferenceReset.rawValue) + static let displayListUpdate = NSNotification.Name(rawValue: Utils.PrefKeys.displayListUpdate.rawValue) } diff --git a/MonitorControl/Manager/DisplayManager.swift b/MonitorControl/Manager/DisplayManager.swift new file mode 100644 index 0000000..383dd97 --- /dev/null +++ b/MonitorControl/Manager/DisplayManager.swift @@ -0,0 +1,35 @@ +import Foundation + +class DisplayManager { + private var displays: [Display] { + didSet { + NotificationCenter.default.post(name: Notification.Name(Utils.PrefKeys.displayListUpdate.rawValue), object: nil) + } + } + + init() { + self.displays = [] + } + + func updateDisplays(displays: [Display]) { + self.displays = displays + } + + func getDisplays() -> [Display] { + return self.displays + } + + func addDisplay(display: Display) { + self.displays.append(display) + } + + func updateDisplay(display updatedDisplay: Display) { + if let indexToUpdate = self.displays.firstIndex(of: updatedDisplay) { + self.displays[indexToUpdate] = updatedDisplay + } + } + + func clearDisplays() { + self.displays = [] + } +} diff --git a/MonitorControl/Support/OnlyIntegerValueFormatter.swift b/MonitorControl/Support/OnlyIntegerValueFormatter.swift new file mode 100644 index 0000000..0b2acf8 --- /dev/null +++ b/MonitorControl/Support/OnlyIntegerValueFormatter.swift @@ -0,0 +1,15 @@ +import Cocoa + +class OnlyIntegerValueFormatter: NumberFormatter { + override func isPartialStringValid(_ partialString: String, newEditingString _: AutoreleasingUnsafeMutablePointer?, errorDescription _: AutoreleasingUnsafeMutablePointer?) -> Bool { + if partialString.isEmpty { + return true + } + + if partialString.count > 3 { + return false + } + + return Int(partialString) != nil + } +} diff --git a/MonitorControl/Support/PollingMode.swift b/MonitorControl/Support/PollingMode.swift new file mode 100644 index 0000000..30d2716 --- /dev/null +++ b/MonitorControl/Support/PollingMode.swift @@ -0,0 +1,24 @@ +import Foundation + +enum PollingMode { + case none + case minimal + case normal + case heavy + case custom(value: Int) + + var value: Int { + switch self { + case .none: + return 0 + case .minimal: + return 5 + case .normal: + return 10 + case .heavy: + return 100 + case let .custom(val): + return val + } + } +} diff --git a/MonitorControl/Support/Utils.swift b/MonitorControl/Support/Utils.swift index 8456000..d8f1e60 100644 --- a/MonitorControl/Support/Utils.swift +++ b/MonitorControl/Support/Utils.swift @@ -1,6 +1,7 @@ import Cocoa import DDC import os.log +import ServiceManagement class Utils: NSObject { // MARK: - Menu @@ -56,7 +57,12 @@ class Utils: NSObject { os_log("Display does not support enabling DDC application report.", type: .debug) } - values = display.ddc?.read(command: command, tries: 10, minReplyDelay: delay) + let tries = UInt(display.getPollingCount()) + os_log("Polling %{public}@ times", type: .info, String(tries)) + + if tries != 0 { + values = display.ddc?.read(command: command, tries: tries, minReplyDelay: delay) + } let (currentValue, maxValue) = values ?? (UInt16(display.getValue(for: command)), UInt16(display.getMaxValue(for: command))) @@ -94,6 +100,12 @@ class Utils: NSObject { return } + static func setStartAtLogin(enabled: Bool) { + let identifier = "\(Bundle.main.bundleIdentifier!)Helper" as CFString + SMLoginItemSetEnabled(identifier, enabled) + os_log("Toggle start at login state: %{public}@", type: .info, enabled ? "on" : "off") + } + static func getSystemPreferences() -> [String: AnyObject]? { var propertyListFormat = PropertyListSerialization.PropertyListFormat.xml let plistPath = NSString(string: "~/Library/Preferences/.GlobalPreferences.plist").expandingTildeInPath @@ -154,6 +166,12 @@ class Utils: NSObject { /// Friendly name changed case friendlyName + + /// Prefs Reset + case preferenceReset + + /// Used for notification when displays are updated in DisplayManager + case displayListUpdate } /// Keys for the value of listenFor option diff --git a/MonitorControl/UI/Base.lproj/Main.storyboard b/MonitorControl/UI/Base.lproj/Main.storyboard index 3ee70ec..d809ed8 100644 --- a/MonitorControl/UI/Base.lproj/Main.storyboard +++ b/MonitorControl/UI/Base.lproj/Main.storyboard @@ -473,5 +473,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Warning ⚠️ +Changing some of these setting may cause system freezes or unexpected behaviour. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MonitorControl/UI/ButtonCellView.swift b/MonitorControl/UI/Cells/ButtonCellView.swift similarity index 100% rename from MonitorControl/UI/ButtonCellView.swift rename to MonitorControl/UI/Cells/ButtonCellView.swift diff --git a/MonitorControl/UI/FriendlyNameCellView.swift b/MonitorControl/UI/Cells/FriendlyNameCellView.swift similarity index 95% rename from MonitorControl/UI/FriendlyNameCellView.swift rename to MonitorControl/UI/Cells/FriendlyNameCellView.swift index e2ee87e..ad1866a 100644 --- a/MonitorControl/UI/FriendlyNameCellView.swift +++ b/MonitorControl/UI/Cells/FriendlyNameCellView.swift @@ -3,7 +3,6 @@ import os.log class FriendlyNameCellView: NSTableCellView { var display: Display? - let prefs = UserDefaults.standard override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) diff --git a/MonitorControl/UI/Cells/HideOsdCellView.swift b/MonitorControl/UI/Cells/HideOsdCellView.swift new file mode 100644 index 0000000..5c983e5 --- /dev/null +++ b/MonitorControl/UI/Cells/HideOsdCellView.swift @@ -0,0 +1,25 @@ +import Cocoa +import os.log + +class HideOsdCellView: NSTableCellView { + @IBOutlet var button: NSButton! + var display: Display? + let prefs = UserDefaults.standard + + override func draw(_ dirtyRect: NSRect) { + super.draw(dirtyRect) + } + + @IBAction func buttonToggled(_ sender: NSButton) { + if let display = display { + switch sender.state { + case .on: + display.hideOsd = true + case .off: + display.hideOsd = false + default: + break + } + } + } +} diff --git a/MonitorControl/UI/Cells/LongerDelayCellView.swift b/MonitorControl/UI/Cells/LongerDelayCellView.swift new file mode 100644 index 0000000..857b3aa --- /dev/null +++ b/MonitorControl/UI/Cells/LongerDelayCellView.swift @@ -0,0 +1,41 @@ +import Cocoa +import os.log + +class LongerDelayCellView: NSTableCellView { + @IBOutlet var button: NSButton! + var display: Display? + let prefs = UserDefaults.standard + + override func draw(_ dirtyRect: NSRect) { + super.draw(dirtyRect) + } + + @IBAction func buttonToggled(_ sender: NSButton) { + if let display = self.display { + switch sender.state { + case .on: + let alert: NSAlert = NSAlert() + alert.messageText = NSLocalizedString("Enable Longer Delay?", comment: "Shown in the alert dialog") + alert.informativeText = NSLocalizedString("Are you sure you want to enable a longer delay? Doing so may freeze your system and require a restart. Start at login will be disabled as a safety measure.", comment: "Shown in the alert dialog") + alert.addButton(withTitle: NSLocalizedString("Yes", comment: "Shown in the alert dialog")) + alert.addButton(withTitle: NSLocalizedString("No", comment: "Shown in the alert dialog")) + alert.alertStyle = NSAlert.Style.critical + + if let window = self.window { + alert.beginSheetModal(for: window, completionHandler: { modalResponse in + if modalResponse == NSApplication.ModalResponse.alertFirstButtonReturn { + Utils.setStartAtLogin(enabled: false) + display.needsLongerDelay = true + } else { + sender.state = .off + } + }) + } + case .off: + display.needsLongerDelay = false + default: + break + } + } + } +} diff --git a/MonitorControl/UI/Cells/PollingCountCellView.swift b/MonitorControl/UI/Cells/PollingCountCellView.swift new file mode 100644 index 0000000..de857c7 --- /dev/null +++ b/MonitorControl/UI/Cells/PollingCountCellView.swift @@ -0,0 +1,24 @@ +import Cocoa +import os.log + +class PollingCountCellView: NSTableCellView { + var display: Display? + + @IBAction func valueChanged(_ sender: NSTextField) { + if let display = display { + let newValue = sender.stringValue + let originalValue = "\(display.getPollingCount())" + + if newValue.isEmpty { + self.textField?.stringValue = originalValue + } + + if newValue != originalValue, + !newValue.isEmpty, + let newValue = Int(newValue) { + display.setPollingCount(newValue) + os_log("Value changed for polling count: %{public}@", type: .info, "from `\(originalValue)` to `\(newValue)`") + } + } + } +} diff --git a/MonitorControl/UI/Cells/PollingModeCellView.swift b/MonitorControl/UI/Cells/PollingModeCellView.swift new file mode 100644 index 0000000..0d800dd --- /dev/null +++ b/MonitorControl/UI/Cells/PollingModeCellView.swift @@ -0,0 +1,31 @@ +import Cocoa +import os.log + +/* + menu tags: + 0: none + 1: minimal + 2: normal + 3: heavy + 4: custom + We use these tags as a way to mark selection + */ +class PollingModeCellView: NSTableCellView { + var display: Display? + @IBOutlet var pollingModeMenu: NSPopUpButtonCell! + + var didChangePollingMode: ((_ pollingModeInt: Int) -> Void)? + + @IBAction func valueChanged(_ sender: NSPopUpButton) { + if let display = display { + let newValue = sender.selectedTag() + let originalValue = display.getPollingMode() + + if newValue != originalValue { + display.setPollingMode(newValue) + self.didChangePollingMode?(newValue) + os_log("Value changed for polling count: %{public}@", type: .info, "from `\(originalValue)` to `\(newValue)`") + } + } + } +} diff --git a/MonitorControl/View Controllers/AdvancedPrefsViewController.swift b/MonitorControl/View Controllers/AdvancedPrefsViewController.swift new file mode 100644 index 0000000..70912ce --- /dev/null +++ b/MonitorControl/View Controllers/AdvancedPrefsViewController.swift @@ -0,0 +1,132 @@ +import Cocoa +import DDC +import MASPreferences +import os.log + +class AdvancedPrefsViewController: NSViewController, MASPreferencesViewController, NSTableViewDataSource, NSTableViewDelegate { + var viewIdentifier: String = "Advanced" + var toolbarItemLabel: String? = NSLocalizedString("Advanced", comment: "Shown in the main prefs window") + var toolbarItemImage: NSImage? = NSImage(named: NSImage.advancedName) + let prefs = UserDefaults.standard + + var displays: [Display] = [] + var displayManager: DisplayManager? + + enum DisplayColumn: Int { + case friendlyName + case identifier + case pollingMode + case pollingCount + case longerDelay + case hideOsd + } + + @IBOutlet var displayList: NSTableView! + + override func viewDidLoad() { + super.viewDidLoad() + NotificationCenter.default.addObserver(self, selector: #selector(self.loadDisplayList), name: .displayListUpdate, object: nil) + self.loadDisplayList() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @IBAction func resetPrefsClicked(_: NSButton) { + let alert: NSAlert = NSAlert() + alert.messageText = NSLocalizedString("Reset Preferences?", comment: "Shown in the alert dialog") + alert.informativeText = NSLocalizedString("Are you sure you want to reset all preferences?", comment: "Shown in the alert dialog") + alert.addButton(withTitle: NSLocalizedString("Yes", comment: "Shown in the alert dialog")) + alert.addButton(withTitle: NSLocalizedString("No", comment: "Shown in the alert dialog")) + alert.alertStyle = NSAlert.Style.warning + + if let window = self.view.window { + alert.beginSheetModal(for: window, completionHandler: { modalResponse in + if modalResponse == NSApplication.ModalResponse.alertFirstButtonReturn { + if let bundleID = Bundle.main.bundleIdentifier { + UserDefaults.standard.removePersistentDomain(forName: bundleID) + NotificationCenter.default.post(name: Notification.Name(Utils.PrefKeys.preferenceReset.rawValue), object: nil) + os_log("Resetting all preferences.") + } + } + }) + } + } + + @IBAction func helpClicked(_: NSButton) { + if let url = URL(string: "https://github.com/the0neyouseek/MonitorControl/wiki/Advanced-Preferences") { + NSWorkspace.shared.open(url) + } + } + + @objc func loadDisplayList() { + if let displays = displayManager?.getDisplays() { + self.displays = displays + self.displayList.reloadData() + } + } + + func numberOfRows(in _: NSTableView) -> Int { + return self.displays.count + } + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + guard let tableColumn = tableColumn, + let columnIndex = tableView.tableColumns.firstIndex(of: tableColumn), + let column = DisplayColumn(rawValue: columnIndex) else { + return nil + } + let display = self.displays[row] + let pollingMode = display.getPollingMode() + + switch column { + case .pollingMode: + if let cell = tableView.makeView(withIdentifier: tableColumn.identifier, owner: nil) as? PollingModeCellView { + cell.display = display + cell.pollingModeMenu.selectItem(withTag: pollingMode) + cell.didChangePollingMode = { _ in + // if the polling mode changed, reload the row so we can enable/disable the PollingCount field + tableView.reloadData(forRowIndexes: [row], columnIndexes: [DisplayColumn.pollingCount.rawValue]) + } + return cell + } + case .pollingCount: + if let cell = tableView.makeView(withIdentifier: tableColumn.identifier, owner: nil) as? PollingCountCellView { + cell.textField?.stringValue = "\(display.getPollingCount())" + cell.display = display + cell.textField?.isEnabled = pollingMode == 4 + return cell + } + case .longerDelay: + if let cell = tableView.makeView(withIdentifier: tableColumn.identifier, owner: nil) as? LongerDelayCellView { + cell.button.state = display.needsLongerDelay ? .on : .off + cell.display = display + return cell + } + case .hideOsd: + if let cell = tableView.makeView(withIdentifier: tableColumn.identifier, owner: nil) as? HideOsdCellView { + cell.button.state = display.hideOsd ? .on : .off + cell.display = display + return cell + } + default: + if let cell = tableView.makeView(withIdentifier: tableColumn.identifier, owner: nil) as? NSTableCellView { + cell.textField?.stringValue = self.getText(for: column, with: display) + return cell + } + } + return nil + } + + private func getText(for column: DisplayColumn, with display: Display) -> String { + switch column { + case .friendlyName: + return display.getFriendlyName() + case .identifier: + return "\(display.identifier)" + default: + return "" + } + } +} diff --git a/MonitorControl/View Controllers/DisplayPrefsViewController.swift b/MonitorControl/View Controllers/DisplayPrefsViewController.swift index b929dd0..7ae03c8 100644 --- a/MonitorControl/View Controllers/DisplayPrefsViewController.swift +++ b/MonitorControl/View Controllers/DisplayPrefsViewController.swift @@ -10,6 +10,8 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController let prefs = UserDefaults.standard var displays: [Display] = [] + var displayManager: DisplayManager? + enum DisplayColumn: Int { case checkbox case ddc @@ -25,12 +27,19 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController override func viewDidLoad() { super.viewDidLoad() - - self.allScreens.state = self.prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? .on : .off - + NotificationCenter.default.addObserver(self, selector: #selector(self.loadDisplayList), name: .displayListUpdate, object: nil) self.loadDisplayList() } + override func viewWillAppear() { + super.viewWillAppear() + self.allScreens.state = self.prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? .on : .off + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + @IBAction func allScreensTouched(_ sender: NSButton) { switch sender.state { case .on: @@ -47,17 +56,10 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController // MARK: - Table datasource - func loadDisplayList() { - for screen in NSScreen.screens { - let id = screen.displayID - - let name = screen.displayName ?? NSLocalizedString("Unknown", comment: "Unknown display name") - let isEnabled = (prefs.object(forKey: "\(id)-state") as? Bool) ?? true - - let display = Display(id, name: name, isBuiltin: screen.isBuiltin, isEnabled: isEnabled) - self.displays.append(display) + @objc func loadDisplayList() { + if let displays = self.displayManager?.getDisplays() { + self.displays = displays } - self.displayList.reloadData() } diff --git a/MonitorControl/View Controllers/KeysPrefsViewController.swift b/MonitorControl/View Controllers/KeysPrefsViewController.swift index 88e654b..eac40df 100644 --- a/MonitorControl/View Controllers/KeysPrefsViewController.swift +++ b/MonitorControl/View Controllers/KeysPrefsViewController.swift @@ -10,9 +10,8 @@ class KeysPrefsViewController: NSViewController, MASPreferencesViewController { @IBOutlet var listenFor: NSPopUpButton! - override func viewDidLoad() { - super.viewDidLoad() - + override func viewWillAppear() { + super.viewWillAppear() self.listenFor.selectItem(at: self.prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue)) } diff --git a/MonitorControl/View Controllers/MainPrefsViewController.swift b/MonitorControl/View Controllers/MainPrefsViewController.swift index 7c62487..e7a5596 100644 --- a/MonitorControl/View Controllers/MainPrefsViewController.swift +++ b/MonitorControl/View Controllers/MainPrefsViewController.swift @@ -14,32 +14,28 @@ class MainPrefsViewController: NSViewController, MASPreferencesViewController { @IBOutlet var showContrastSlider: NSButton! @IBOutlet var lowerContrast: NSButton! - @available(macOS, deprecated: 10.10) override func viewDidLoad() { super.viewDidLoad() - - let startAtLogin = (SMCopyAllJobDictionaries(kSMDomainUserLaunchd).takeRetainedValue() as? [[String: AnyObject]])?.first { $0["Label"] as? String == "\(Bundle.main.bundleIdentifier!)Helper" }?["OnDemand"] as? Bool ?? false - - self.startAtLogin.state = startAtLogin ? .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() } - @IBAction func startAtLoginClicked(_ sender: NSButton) { - let identifier = "\(Bundle.main.bundleIdentifier!)Helper" as CFString + @available(macOS, deprecated: 10.10) + override func viewWillAppear() { + super.viewWillAppear() + let startAtLogin = (SMCopyAllJobDictionaries(kSMDomainUserLaunchd).takeRetainedValue() as? [[String: AnyObject]])?.first { $0["Label"] as? String == "\(Bundle.main.bundleIdentifier!)Helper" }?["OnDemand"] as? Bool ?? false + self.startAtLogin.state = startAtLogin ? .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 + } + @IBAction func startAtLoginClicked(_ sender: NSButton) { switch sender.state { case .on: - SMLoginItemSetEnabled(identifier, true) + Utils.setStartAtLogin(enabled: true) case .off: - SMLoginItemSetEnabled(identifier, false) + Utils.setStartAtLogin(enabled: false) default: break } - - #if DEBUG - os_log("Toggle start at login state: %{public}@", type: .info, sender.state == .on ? "on" : "off") - #endif } @IBAction func showContrastSliderClicked(_ sender: NSButton) {