diff --git a/MonitorControl/AppDelegate.swift b/MonitorControl/AppDelegate.swift index a98b343..2f3ba47 100644 --- a/MonitorControl/AppDelegate.swift +++ b/MonitorControl/AppDelegate.swift @@ -25,11 +25,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate { var monitorItems: [NSMenuItem] = [] var displays: [Display] = [] - var sliderHandlers: [SliderHandler] = [] - - var defaultDisplay: Display! = nil - var defaultBrightnessSlider: NSSlider! = nil - var defaultVolumeSlider: NSSlider! = nil let step = 100/16 @@ -89,17 +84,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate { // MARK: - Menu func clearDisplays() { - defaultDisplay = nil - defaultBrightnessSlider = nil - defaultVolumeSlider = nil - for monitor in monitorItems { statusMenu.removeItem(monitor) } monitorItems = [] displays = [] - sliderHandlers = [] } func updateDisplays() { @@ -122,8 +112,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate { let name = Utils.getDisplayName(forEdid: edid) let serial = Utils.getDisplaySerial(forEdid: edid) - let display = Display(identifier: id, name: name, serial: serial, isEnabled: true) - displays.append(display) + let display = Display.init(id, name: name, serial: serial) let monitorSubMenu = NSMenu() let brightnessSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu, @@ -134,21 +123,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate { forDisplay: display, command: AUDIO_SPEAKER_VOLUME, title: NSLocalizedString("Volume", comment: "Shown in menu")) - sliderHandlers.append(brightnessSliderHandler) - sliderHandlers.append(volumeSliderHandler) - - let isDefaultDisplay = defaultDisplay == nil - let defaultMonitorSelectButtom = NSButton(frame: NSRect(x: 25, y: 0, width: 200, height: 25)) - defaultMonitorSelectButtom.title = isDefaultDisplay ? NSLocalizedString("Default", comment: "Shown in menu") : NSLocalizedString("Set as default", comment: "Shown in menu") - defaultMonitorSelectButtom.bezelStyle = NSButton.BezelStyle.rounded - defaultMonitorSelectButtom.isEnabled = !isDefaultDisplay - - let defaultMonitorView = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 25)) - defaultMonitorView.addSubview(defaultMonitorSelectButtom) - - let defaultMonitorItem = NSMenuItem() - defaultMonitorItem.view = defaultMonitorView - monitorSubMenu.addItem(defaultMonitorItem) + display.brightnessSliderHandler = brightnessSliderHandler + display.volumeSliderHandler = volumeSliderHandler + displays.append(display) let monitorMenuItem = NSMenuItem() monitorMenuItem.title = "\(name)" @@ -156,16 +133,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate { monitorItems.append(monitorMenuItem) statusMenu.insertItem(monitorMenuItem, at: displays.count - 1) - - if isDefaultDisplay { - defaultDisplay = display - defaultBrightnessSlider = brightnessSliderHandler.slider - defaultVolumeSlider = volumeSliderHandler.slider - } } } - if defaultDisplay == nil { + if displays.count == 0 { // If no DDC capable display was detected let item = NSMenuItem() item.title = NSLocalizedString("No supported display found", comment: "Shown in menu") @@ -178,60 +149,32 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate { // MARK: - Media Key Tap delegate func handle(mediaKey: MediaKey, event: KeyEvent) { - - var command = BRIGHTNESS + guard let currentDisplay = Utils.getCurrentDisplay(from: displays) else { return } var rel = 0 - var slider = self.defaultBrightnessSlider switch mediaKey { case .brightnessUp: rel = +self.step + let value = currentDisplay.calcNewValue(for: BRIGHTNESS, withRel: rel) + currentDisplay.setBrightness(to: value) case .brightnessDown: rel = -self.step + let value = currentDisplay.calcNewValue(for: BRIGHTNESS, withRel: rel) + currentDisplay.setBrightness(to: value) case .mute: - rel = -100 - command = AUDIO_SPEAKER_VOLUME - slider = self.defaultVolumeSlider + currentDisplay.mute() case .volumeUp: rel = +self.step - command = AUDIO_SPEAKER_VOLUME - slider = self.defaultVolumeSlider + let value = currentDisplay.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: rel) + currentDisplay.setVolume(to: value) case .volumeDown: rel = -self.step - command = AUDIO_SPEAKER_VOLUME - slider = self.defaultVolumeSlider + let value = currentDisplay.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: rel) + currentDisplay.setVolume(to: value) default: return } - let k = "\(command)-\(self.defaultDisplay.serial)" - let value = max(0, min(100, prefs.integer(forKey: k) + rel)) - prefs.setValue(value, forKey: k) - prefs.synchronize() - - if let slider = slider { - slider.intValue = Int32(value) - } - - Utils.ddcctl(monitor: self.defaultDisplay.identifier, command: command, value: value) - - // OSD - if let manager = OSDManager.sharedManager() as? OSDManager { - var osdImage: Int64 = 1 // Brightness Image - if command == AUDIO_SPEAKER_VOLUME { - osdImage = 3 // Speaker image - if value == 0 { - osdImage = 4 // Mute speaker - } - } - manager.showImage(osdImage, - onDisplayID: self.defaultDisplay.identifier, - priority: 0x1f4, - msecUntilFade: 2000, - filledChiclets: UInt32(value/self.step), - totalChiclets: UInt32(100/self.step), - locked: false) - } } } diff --git a/MonitorControl/Objects/Display.swift b/MonitorControl/Objects/Display.swift index 8346c1b..8d772af 100644 --- a/MonitorControl/Objects/Display.swift +++ b/MonitorControl/Objects/Display.swift @@ -9,9 +9,86 @@ import Cocoa /// A display -struct Display { - var identifier: CGDirectDisplayID - var name: String - var serial: String - var isEnabled: Bool = true +class Display { + let identifier: CGDirectDisplayID + let name: String + let serial: String + var isEnabled: Bool + var isMuted: Bool = false + var brightnessSliderHandler: SliderHandler? + var volumeSliderHandler: SliderHandler? + + 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 = UserDefaults.standard.integer(forKey: "\(AUDIO_SPEAKER_VOLUME)-\(identifier)") + isMuted = false + } else { + isMuted = true + } + + Utils.ddcctl(monitor: identifier, command: AUDIO_SPEAKER_VOLUME, value: value) + if let slider = volumeSliderHandler?.slider { + slider.intValue = Int32(value) + } + showOsd(command: AUDIO_SPEAKER_VOLUME, value: value) + } + + func setVolume(to value: Int) { + if value > 0 { + isMuted = false + } + + Utils.ddcctl(monitor: identifier, command: AUDIO_SPEAKER_VOLUME, value: value) + if let slider = volumeSliderHandler?.slider { + slider.intValue = Int32(value) + } + showOsd(command: AUDIO_SPEAKER_VOLUME, value: value) + saveValue(value, for: AUDIO_SPEAKER_VOLUME) + } + + func setBrightness(to value: Int) { + Utils.ddcctl(monitor: identifier, command: BRIGHTNESS, value: value) + if let slider = brightnessSliderHandler?.slider { + slider.intValue = Int32(value) + } + showOsd(command: BRIGHTNESS, value: value) + saveValue(value, for: BRIGHTNESS) + } + + func calcNewValue(for command: Int32, withRel rel: Int) -> Int { + let currentValue = UserDefaults.standard.integer(forKey: "\(command)-\(identifier)") + return max(0, min(100, currentValue + rel)) + } + + func saveValue(_ value: Int, for command: Int32) { + UserDefaults.standard.set(value, forKey: "\(command)-\(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) + } + } } diff --git a/MonitorControl/Objects/SliderHandler.swift b/MonitorControl/Objects/SliderHandler.swift index 119ab77..fefc97d 100644 --- a/MonitorControl/Objects/SliderHandler.swift +++ b/MonitorControl/Objects/SliderHandler.swift @@ -32,8 +32,6 @@ class SliderHandler { } Utils.ddcctl(monitor: display.identifier, command: command, value: value) - - prefs.setValue(value, forKey: "\(command)-\(display.serial)") - prefs.synchronize() + prefs.setValue(value, forKey: "\(command)-\(display.identifier)") } } diff --git a/MonitorControl/Prefs/DisplayPrefsViewController.swift b/MonitorControl/Prefs/DisplayPrefsViewController.swift index c44478e..01dd539 100644 --- a/MonitorControl/Prefs/DisplayPrefsViewController.swift +++ b/MonitorControl/Prefs/DisplayPrefsViewController.swift @@ -53,7 +53,7 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController 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(identifier: id, name: "Mac built-in Display", serial: "", isEnabled: false) + let display = Display(id, name: "Mac built-in Display", serial: "", isEnabled: false) displays.append(display) continue } @@ -71,7 +71,7 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController isEnabled = enabled } - let display = Display(identifier: id, name: name, serial: serial, isEnabled: isEnabled) + let display = Display(id, name: name, serial: serial, isEnabled: isEnabled) displays.append(display) } } diff --git a/MonitorControl/Utils.swift b/MonitorControl/Utils.swift index 1e279b0..1e81bee 100644 --- a/MonitorControl/Utils.swift +++ b/MonitorControl/Utils.swift @@ -147,6 +147,21 @@ class Utils: NSObject { 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 + } + } + /// UserDefault Keys for the app prefs enum PrefKeys: String { /// Was the app launched once