mirror of
https://github.com/MonitorControl/MonitorControl.git
synced 2026-05-15 14:15:55 -06:00
Implement ctrl + brightness for controlling internal display, Respect enabled option for internal display (#175)
* Refactor display loading logic * Split `Display` into `InternalDisplay` and `ExternalDisplay` * Add functions for controlling internal display brightness * Update MediaKeyTap dependency, Implement ctrl modifier for internal display * Fix `keyRepeatTimer` issue with multiple displays while holding down a MediaKey
This commit is contained in:
parent
c984a8c343
commit
16837f20c5
21 changed files with 309 additions and 206 deletions
2
Cartfile
2
Cartfile
|
|
@ -1,4 +1,4 @@
|
|||
github "the0neyouseek/MediaKeyTap"
|
||||
github "the0neyouseek/MediaKeyTap" "master"
|
||||
github "reitermarkus/DDC.swift" "master"
|
||||
github "rnine/AMCoreAudio"
|
||||
github "shpakovski/MASPreferences"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
github "reitermarkus/DDC.swift" "41e7c49b0450033c5349ca1cf5234a26ebc011b8"
|
||||
github "reitermarkus/DDC.swift" "1763870c94c555ff93878caaec8235fd3a9a429d"
|
||||
github "rnine/AMCoreAudio" "3.3.1"
|
||||
github "shpakovski/MASPreferences" "1.3"
|
||||
github "the0neyouseek/MediaKeyTap" "3.1.0"
|
||||
github "the0neyouseek/MediaKeyTap" "4314a361486c2907956756748939c61f460241bd"
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
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 */; };
|
||||
6CBFE27A23DB266000D1BC41 /* Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBFE27923DB266000D1BC41 /* Display.swift */; };
|
||||
6CBFE27C23DB27A200D1BC41 /* InternalDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBFE27B23DB27A200D1BC41 /* InternalDisplay.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 */; };
|
||||
|
|
@ -42,7 +44,7 @@
|
|||
F01B069F228221B7008E64DB /* SliderHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01B068F228221B7008E64DB /* SliderHandler.swift */; };
|
||||
F01B06A0228221B7008E64DB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F01B0690228221B7008E64DB /* Main.storyboard */; };
|
||||
F01B06A1228221B7008E64DB /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F01B0692228221B7008E64DB /* MainMenu.xib */; };
|
||||
F03A8DF21FFBAA6F0034DC27 /* Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03A8DF11FFBAA6F0034DC27 /* Display.swift */; };
|
||||
F03A8DF21FFBAA6F0034DC27 /* ExternalDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03A8DF11FFBAA6F0034DC27 /* ExternalDisplay.swift */; };
|
||||
F03FE4C0228DF62B001F59A4 /* FriendlyNameCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03FE4BF228DF62A001F59A4 /* FriendlyNameCellView.swift */; };
|
||||
F0445D3820023E710025AE82 /* MainPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0445D3720023E710025AE82 /* MainPrefsViewController.swift */; };
|
||||
F0445D3D200254FA0025AE82 /* KeysPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0445D3B200254FA0025AE82 /* KeysPrefsViewController.swift */; };
|
||||
|
|
@ -125,6 +127,8 @@
|
|||
6CAD134E23624CC1009BD53F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
6CAD134F23624CC1009BD53F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
6CAD135023624CC1009BD53F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
6CBFE27923DB266000D1BC41 /* Display.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Display.swift; sourceTree = "<group>"; };
|
||||
6CBFE27B23DB27A200D1BC41 /* InternalDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalDisplay.swift; sourceTree = "<group>"; };
|
||||
6CCB278522D5315200619B05 /* HideOsdCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideOsdCellView.swift; sourceTree = "<group>"; };
|
||||
6CD444C222D4FBB8005BFD3D /* LongerDelayCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongerDelayCellView.swift; sourceTree = "<group>"; };
|
||||
B0C4810623357CE500053F91 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
|
|
@ -146,7 +150,7 @@
|
|||
F01B06A522822215008E64DB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
F01B06A622822217008E64DB /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
F01B06A72282221B008E64DB /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
F03A8DF11FFBAA6F0034DC27 /* Display.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Display.swift; sourceTree = "<group>"; };
|
||||
F03A8DF11FFBAA6F0034DC27 /* ExternalDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalDisplay.swift; sourceTree = "<group>"; };
|
||||
F03FE4BF228DF62A001F59A4 /* FriendlyNameCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FriendlyNameCellView.swift; sourceTree = "<group>"; };
|
||||
F0445D3720023E710025AE82 /* MainPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainPrefsViewController.swift; sourceTree = "<group>"; };
|
||||
F0445D3B200254FA0025AE82 /* KeysPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeysPrefsViewController.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -232,11 +236,11 @@
|
|||
56754EAD1D9A4016007BCDC5 /* MonitorControl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6C6C34F423DB25BF00C0E9CB /* Model */,
|
||||
F01B0686228221B6008E64DB /* Info.plist */,
|
||||
6C85EFD622C74B0E00227EA1 /* Manager */,
|
||||
56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */,
|
||||
56754EB01D9A4016007BCDC5 /* Assets.xcassets */,
|
||||
F03A8DF11FFBAA6F0034DC27 /* Display.swift */,
|
||||
28D1DDEB227FB8E9004CB494 /* Extensions */,
|
||||
F01B067F228221B6008E64DB /* Support */,
|
||||
F01B0687228221B6008E64DB /* UI */,
|
||||
|
|
@ -245,6 +249,16 @@
|
|||
path = MonitorControl;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6C6C34F423DB25BF00C0E9CB /* Model */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F03A8DF11FFBAA6F0034DC27 /* ExternalDisplay.swift */,
|
||||
6CBFE27923DB266000D1BC41 /* Display.swift */,
|
||||
6CBFE27B23DB27A200D1BC41 /* InternalDisplay.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6C85EFD622C74B0E00227EA1 /* Manager */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -512,7 +526,9 @@
|
|||
6CD444C322D4FBB8005BFD3D /* LongerDelayCellView.swift in Sources */,
|
||||
F03FE4C0228DF62B001F59A4 /* FriendlyNameCellView.swift in Sources */,
|
||||
6C2EA1CF228F7DFB00060E3F /* PollingMode.swift in Sources */,
|
||||
F03A8DF21FFBAA6F0034DC27 /* Display.swift in Sources */,
|
||||
6CBFE27C23DB27A200D1BC41 /* InternalDisplay.swift in Sources */,
|
||||
6CBFE27A23DB266000D1BC41 /* Display.swift in Sources */,
|
||||
F03A8DF21FFBAA6F0034DC27 /* ExternalDisplay.swift in Sources */,
|
||||
F0445D40200259C10025AE82 /* DisplayPrefsViewController.swift in Sources */,
|
||||
28D1DDF0227FBD99004CB494 /* EDID+Extension.swift in Sources */,
|
||||
6C85EFDD22CBAA8F00227EA1 /* PollingModeCellView.swift in Sources */,
|
||||
|
|
@ -703,7 +719,7 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 570;
|
||||
CURRENT_PROJECT_VERSION = 631;
|
||||
DEVELOPMENT_TEAM = CYC8C8R4K9;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
|
@ -730,7 +746,7 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 570;
|
||||
CURRENT_PROJECT_VERSION = 631;
|
||||
DEVELOPMENT_TEAM = CYC8C8R4K9;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
|
@ -761,7 +777,7 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 570;
|
||||
CURRENT_PROJECT_VERSION = 631;
|
||||
DEVELOPMENT_TEAM = CYC8C8R4K9;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
|
@ -786,7 +802,7 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 570;
|
||||
CURRENT_PROJECT_VERSION = 631;
|
||||
DEVELOPMENT_TEAM = CYC8C8R4K9;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
|
|
|||
|
|
@ -77,87 +77,74 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
}
|
||||
|
||||
self.monitorItems = []
|
||||
self.displayManager?.clearDisplays()
|
||||
DisplayManager.shared.clearDisplays()
|
||||
}
|
||||
|
||||
func updateDisplays() {
|
||||
self.clearDisplays()
|
||||
|
||||
let filteredScreens = NSScreen.screens.filter { screen -> Bool in
|
||||
// Skip built-in displays.
|
||||
for screen in NSScreen.screens {
|
||||
let name = screen.displayName ?? NSLocalizedString("Unknown", comment: "Unknown display name")
|
||||
let id = screen.displayID
|
||||
let vendorNumber = screen.vendorNumber
|
||||
let modelNumber = screen.modelNumber
|
||||
let display: Display
|
||||
if screen.isBuiltin {
|
||||
return false
|
||||
display = InternalDisplay(id, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber)
|
||||
} else {
|
||||
display = ExternalDisplay(id, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber)
|
||||
}
|
||||
return DDC(for: screen.displayID)?.edid() != nil
|
||||
DisplayManager.shared.addDisplay(display: display)
|
||||
}
|
||||
|
||||
switch filteredScreens.count {
|
||||
case 0:
|
||||
// If no DDC capable display was detected
|
||||
let ddcDisplays = DisplayManager.shared.getDdcCapableDisplays()
|
||||
if ddcDisplays.count == 0 {
|
||||
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)
|
||||
self.statusMenu.insertItem(NSMenuItem.separator(), at: 1)
|
||||
default:
|
||||
os_log("The following supported displays were found:", type: .info)
|
||||
|
||||
for screen in filteredScreens {
|
||||
os_log(" - %{public}@", type: .info, "\(screen.displayName ?? NSLocalizedString("Unknown", comment: "Unknown display name")) (Vendor: \(screen.vendorNumber ?? 0), Model: \(screen.modelNumber ?? 0))")
|
||||
self.addScreenToMenu(screen: screen, asSubMenu: filteredScreens.count > 1)
|
||||
} else {
|
||||
for display in ddcDisplays {
|
||||
os_log("Supported display found: %{public}@", type: .info, "\(display.name) (Vendor: \(display.vendorNumber ?? 0), Model: \(display.modelNumber ?? 0))")
|
||||
self.addDisplayToMenu(display: display, asSubMenu: ddcDisplays.count > 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
let id = screen.displayID
|
||||
let ddc = DDC(for: id)
|
||||
private func addDisplayToMenu(display: ExternalDisplay, asSubMenu: Bool) {
|
||||
let monitorSubMenu: NSMenu = asSubMenu ? NSMenu() : self.statusMenu
|
||||
|
||||
if let edid = ddc?.edid() {
|
||||
let name = Utils.getDisplayName(forEdid: edid)
|
||||
let isEnabled = (prefs.object(forKey: "\(id)-state") as? Bool) ?? true
|
||||
self.statusMenu.insertItem(NSMenuItem.separator(), at: 0)
|
||||
|
||||
let display = Display(id, name: name, isBuiltin: screen.isBuiltin, isEnabled: isEnabled)
|
||||
|
||||
let monitorSubMenu: NSMenu = asSubMenu ? NSMenu() : self.statusMenu
|
||||
|
||||
self.statusMenu.insertItem(NSMenuItem.separator(), at: 0)
|
||||
|
||||
let volumeSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu,
|
||||
forDisplay: display,
|
||||
command: .audioSpeakerVolume,
|
||||
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.displayManager?.addDisplay(display: display)
|
||||
|
||||
let monitorMenuItem = NSMenuItem()
|
||||
monitorMenuItem.title = "\(display.getFriendlyName())"
|
||||
if asSubMenu {
|
||||
monitorMenuItem.submenu = monitorSubMenu
|
||||
}
|
||||
|
||||
self.monitorItems.append(monitorMenuItem)
|
||||
self.statusMenu.insertItem(monitorMenuItem, at: 0)
|
||||
let volumeSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu,
|
||||
forDisplay: display,
|
||||
command: .audioSpeakerVolume,
|
||||
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
|
||||
|
||||
let monitorMenuItem = NSMenuItem()
|
||||
monitorMenuItem.title = "\(display.getFriendlyName())"
|
||||
if asSubMenu {
|
||||
monitorMenuItem.submenu = monitorSubMenu
|
||||
}
|
||||
|
||||
self.monitorItems.append(monitorMenuItem)
|
||||
self.statusMenu.insertItem(monitorMenuItem, at: 0)
|
||||
}
|
||||
|
||||
private func setupViewControllers() {
|
||||
|
|
@ -173,12 +160,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
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() {
|
||||
|
|
@ -209,7 +190,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
} else if mediaKey == .volumeDown {
|
||||
return .volumeUp
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -218,6 +198,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
|
||||
extension AppDelegate: MediaKeyTapDelegate {
|
||||
func handle(mediaKey: MediaKey, event: KeyEvent?, modifiers: NSEvent.ModifierFlags?) {
|
||||
let isSmallIncrement = modifiers?.isSuperset(of: NSEvent.ModifierFlags([.shift, .option])) ?? false
|
||||
|
||||
// control internal display when holding ctrl modifier
|
||||
let isControlModifier = modifiers?.isSuperset(of: NSEvent.ModifierFlags([.control])) ?? false
|
||||
if isControlModifier, mediaKey == .brightnessUp || mediaKey == .brightnessDown {
|
||||
if let internalDisplay = DisplayManager.shared.getBuiltInDisplay() as? InternalDisplay {
|
||||
internalDisplay.stepBrightness(isUp: mediaKey == .brightnessUp, isSmallIncrement: isSmallIncrement)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let oppositeKey: MediaKey? = self.oppositeMediaKey(mediaKey: mediaKey)
|
||||
let isRepeat = event?.keyRepeat ?? false
|
||||
|
||||
|
|
@ -229,42 +220,40 @@ extension AppDelegate: MediaKeyTapDelegate {
|
|||
if isRepeat {
|
||||
return
|
||||
}
|
||||
|
||||
mediaKeyTimer.invalidate()
|
||||
}
|
||||
|
||||
let displays = self.displayManager?.getDisplays() ?? [Display]()
|
||||
guard let currentDisplay = Utils.getCurrentDisplay(from: displays) else { return }
|
||||
let displays = DisplayManager.shared.getAllDisplays()
|
||||
guard let currentDisplay = DisplayManager.shared.getCurrentDisplay() else { return }
|
||||
|
||||
let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? displays : [currentDisplay]
|
||||
let isSmallIncrement = modifiers?.isSuperset(of: NSEvent.ModifierFlags([.shift, .option])) ?? false
|
||||
|
||||
// Introduce a small delay to handle the media key being held down
|
||||
let delay = isRepeat ? 0.05 : 0
|
||||
|
||||
for display in allDisplays {
|
||||
if (prefs.object(forKey: "\(display.identifier)-state") as? Bool) ?? true {
|
||||
self.keyRepeatTimers[mediaKey] = Timer.scheduledTimer(withTimeInterval: delay, repeats: false, block: { _ in
|
||||
for display in allDisplays where display.isEnabled {
|
||||
switch mediaKey {
|
||||
case .brightnessUp, .brightnessDown:
|
||||
self.keyRepeatTimers[mediaKey] = Timer.scheduledTimer(withTimeInterval: delay, repeats: false, block: { _ in
|
||||
let osdValue = display.calcNewValue(for: .brightness, isUp: mediaKey == .brightnessUp, isSmallIncrement: isSmallIncrement)
|
||||
display.setBrightness(to: osdValue)
|
||||
})
|
||||
display.stepBrightness(isUp: mediaKey == .brightnessUp, isSmallIncrement: isSmallIncrement)
|
||||
case .mute:
|
||||
// The mute key should not respond to press + hold
|
||||
if !isRepeat {
|
||||
display.toggleMute()
|
||||
// mute only matters for external displays
|
||||
if let display = display as? ExternalDisplay {
|
||||
display.toggleMute()
|
||||
}
|
||||
}
|
||||
case .volumeUp, .volumeDown:
|
||||
self.keyRepeatTimers[mediaKey] = Timer.scheduledTimer(withTimeInterval: delay, repeats: false, block: { _ in
|
||||
let osdValue = display.calcNewValue(for: .audioSpeakerVolume, isUp: mediaKey == .volumeUp, isSmallIncrement: isSmallIncrement)
|
||||
display.setVolume(to: osdValue)
|
||||
})
|
||||
// volume only matters for external displays
|
||||
if let display = display as? ExternalDisplay {
|
||||
display.stepVolume(isUp: mediaKey == .volumeUp, isSmallIncrement: isSmallIncrement)
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - Prefs notification
|
||||
|
|
@ -304,7 +293,6 @@ extension AppDelegate: MediaKeyTapDelegate {
|
|||
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()
|
||||
|
|
@ -312,16 +300,13 @@ extension AppDelegate: MediaKeyTapDelegate {
|
|||
}
|
||||
|
||||
extension AppDelegate: EventSubscriber {
|
||||
/**
|
||||
Fires off when the default audio device changes.
|
||||
*/
|
||||
/// Fires off when the default audio device changes.
|
||||
func eventReceiver(_ event: Event) {
|
||||
if case let .defaultOutputDeviceChanged(audioDevice)? = event as? AudioHardwareEvent {
|
||||
#if DEBUG
|
||||
os_log("Default output device changed to “%{public}@”.", type: .info, audioDevice.name)
|
||||
os_log("Can device set its own volume? %{public}@", type: .info, audioDevice.canSetVirtualMasterVolume(direction: .playback).description)
|
||||
#endif
|
||||
|
||||
self.startOrRestartMediaKeyTap()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>574</string>
|
||||
<string>638</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
class DisplayManager {
|
||||
public static let shared = DisplayManager()
|
||||
|
||||
private var displays: [Display] {
|
||||
didSet {
|
||||
NotificationCenter.default.post(name: Notification.Name(Utils.PrefKeys.displayListUpdate.rawValue), object: nil)
|
||||
|
|
@ -15,10 +17,29 @@ class DisplayManager {
|
|||
self.displays = displays
|
||||
}
|
||||
|
||||
func getDisplays() -> [Display] {
|
||||
func getAllDisplays() -> [Display] {
|
||||
return self.displays
|
||||
}
|
||||
|
||||
func getDdcCapableDisplays() -> [ExternalDisplay] {
|
||||
return self.displays.compactMap { (display) -> ExternalDisplay? in
|
||||
if let externalDisplay = display as? ExternalDisplay, externalDisplay.ddc != nil {
|
||||
return externalDisplay
|
||||
} else { return nil }
|
||||
}
|
||||
}
|
||||
|
||||
func getBuiltInDisplay() -> Display? {
|
||||
return self.displays.first { $0 is InternalDisplay }
|
||||
}
|
||||
|
||||
func getCurrentDisplay() -> Display? {
|
||||
guard let mainDisplayID = NSScreen.main?.displayID else {
|
||||
return nil
|
||||
}
|
||||
return self.displays.first { $0.identifier == mainDisplayID }
|
||||
}
|
||||
|
||||
func addDisplay(display: Display) {
|
||||
self.displays.append(display)
|
||||
}
|
||||
|
|
|
|||
70
MonitorControl/Model/Display.swift
Normal file
70
MonitorControl/Model/Display.swift
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// Display.swift
|
||||
// MonitorControl
|
||||
//
|
||||
// Created by Joni Van Roost on 24/01/2020.
|
||||
// Copyright © 2020 Guillaume Broder. All rights reserved.
|
||||
//
|
||||
|
||||
import DDC
|
||||
import Foundation
|
||||
|
||||
class Display {
|
||||
internal let identifier: CGDirectDisplayID
|
||||
internal let name: String
|
||||
internal var vendorNumber: UInt32?
|
||||
internal var modelNumber: UInt32?
|
||||
internal var isEnabled: Bool {
|
||||
get {
|
||||
return self.prefs.object(forKey: "\(self.identifier)-state") as? Bool ?? true
|
||||
}
|
||||
set {
|
||||
self.prefs.set(newValue, forKey: "\(self.identifier)-state")
|
||||
}
|
||||
}
|
||||
|
||||
private let prefs = UserDefaults.standard
|
||||
|
||||
internal init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?) {
|
||||
self.identifier = identifier
|
||||
self.name = name
|
||||
self.vendorNumber = vendorNumber
|
||||
self.modelNumber = modelNumber
|
||||
}
|
||||
|
||||
func stepBrightness(isUp _: Bool, isSmallIncrement _: Bool) {}
|
||||
|
||||
func setFriendlyName(_ value: String) {
|
||||
self.prefs.set(value, forKey: "friendlyName-\(self.identifier)")
|
||||
}
|
||||
|
||||
func getFriendlyName() -> String {
|
||||
return self.prefs.string(forKey: "friendlyName-\(self.identifier)") ?? self.name
|
||||
}
|
||||
|
||||
func showOsd(command: DDC.Command, value: Int, maxValue: Int = 100) {
|
||||
guard let manager = OSDManager.sharedManager() as? OSDManager else {
|
||||
return
|
||||
}
|
||||
|
||||
var osdImage: Int64!
|
||||
switch command {
|
||||
case .brightness:
|
||||
osdImage = 1 // Brightness Image
|
||||
case .audioSpeakerVolume:
|
||||
osdImage = 3 // Speaker image
|
||||
case .audioMuteScreenBlank:
|
||||
osdImage = 4 // Mute image
|
||||
default:
|
||||
osdImage = 1
|
||||
}
|
||||
|
||||
manager.showImage(osdImage,
|
||||
onDisplayID: self.identifier,
|
||||
priority: 0x1F4,
|
||||
msecUntilFade: 1000,
|
||||
filledChiclets: UInt32(value),
|
||||
totalChiclets: UInt32(maxValue),
|
||||
locked: false)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,16 +3,14 @@ import Cocoa
|
|||
import DDC
|
||||
import os.log
|
||||
|
||||
class Display {
|
||||
let identifier: CGDirectDisplayID
|
||||
let name: String
|
||||
let isBuiltin: Bool
|
||||
var isEnabled: Bool
|
||||
class ExternalDisplay: Display {
|
||||
var brightnessSliderHandler: SliderHandler?
|
||||
var volumeSliderHandler: SliderHandler?
|
||||
var contrastSliderHandler: SliderHandler?
|
||||
var ddc: DDC?
|
||||
|
||||
private let prefs = UserDefaults.standard
|
||||
|
||||
var hideOsd: Bool {
|
||||
get {
|
||||
return self.prefs.bool(forKey: "hideOsd-\(self.identifier)")
|
||||
|
|
@ -33,17 +31,12 @@ class Display {
|
|||
}
|
||||
}
|
||||
|
||||
private let prefs = UserDefaults.standard
|
||||
private var audioPlayer: AVAudioPlayer?
|
||||
|
||||
private let osdChicletBoxes: Float = 16
|
||||
|
||||
init(_ identifier: CGDirectDisplayID, name: String, isBuiltin: Bool, isEnabled: Bool = true) {
|
||||
self.identifier = identifier
|
||||
self.name = name
|
||||
self.isEnabled = isBuiltin ? false : isEnabled
|
||||
override init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?) {
|
||||
super.init(identifier, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber)
|
||||
self.ddc = DDC(for: identifier)
|
||||
self.isBuiltin = isBuiltin
|
||||
}
|
||||
|
||||
// On some displays, the display's OSD overlaps the macOS OSD,
|
||||
|
|
@ -97,7 +90,7 @@ class Display {
|
|||
|
||||
if !fromVolumeSlider {
|
||||
self.hideDisplayOsd()
|
||||
self.showOsd(command: .audioSpeakerVolume, value: volumeOSDValue)
|
||||
self.showOsd(command: volumeOSDValue > 0 ? .audioSpeakerVolume : .audioMuteScreenBlank, value: volumeOSDValue)
|
||||
|
||||
if volumeOSDValue > 0 {
|
||||
self.playVolumeChangedSound()
|
||||
|
|
@ -109,8 +102,9 @@ class Display {
|
|||
}
|
||||
}
|
||||
|
||||
func setVolume(to volumeOSDValue: Int) {
|
||||
func stepVolume(isUp: Bool, isSmallIncrement: Bool) {
|
||||
var muteValue: Int?
|
||||
let volumeOSDValue = self.calcNewValue(for: .audioSpeakerVolume, isUp: isUp, isSmallIncrement: isSmallIncrement)
|
||||
let volumeDDCValue = UInt16(volumeOSDValue)
|
||||
|
||||
if self.isMuted(), volumeOSDValue > 0 {
|
||||
|
|
@ -134,7 +128,6 @@ class Display {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
self.saveValue(muteValue, for: .audioMuteScreenBlank)
|
||||
}
|
||||
|
||||
|
|
@ -154,7 +147,8 @@ class Display {
|
|||
}
|
||||
}
|
||||
|
||||
func setBrightness(to osdValue: Int) {
|
||||
override func stepBrightness(isUp: Bool, isSmallIncrement: Bool) {
|
||||
let osdValue = Int(self.calcNewValue(for: .brightness, isUp: isUp, isSmallIncrement: isSmallIncrement))
|
||||
let isAlreadySet = osdValue == self.getValue(for: .brightness)
|
||||
let ddcValue = UInt16(osdValue)
|
||||
|
||||
|
|
@ -234,18 +228,17 @@ class Display {
|
|||
let filledChicletBoxes = self.osdChicletBoxes * (Float(currentValue) / Float(self.getMaxValue(for: command)))
|
||||
|
||||
var nextFilledChicletBoxes: Float
|
||||
var fillecChicletBoxesRel: Float = isUp ? 1 : -1
|
||||
var filledChicletBoxesRel: Float = isUp ? 1 : -1
|
||||
|
||||
// This is a workaround to ensure that if the user has set the value using a small step (that is, the current chiclet box isn't completely filled,
|
||||
// the next regular up or down step will only fill or empty that chiclet, and not the next one as well - it only really works because the max value is 100
|
||||
if (isUp && ceil(filledChicletBoxes) - filledChicletBoxes > 0.15) || (!isUp && filledChicletBoxes - floor(filledChicletBoxes) > 0.15) {
|
||||
fillecChicletBoxesRel = 0
|
||||
filledChicletBoxesRel = 0
|
||||
}
|
||||
|
||||
nextFilledChicletBoxes = isUp ? ceil(filledChicletBoxes + fillecChicletBoxesRel) : floor(filledChicletBoxes + fillecChicletBoxesRel)
|
||||
nextFilledChicletBoxes = isUp ? ceil(filledChicletBoxes + filledChicletBoxesRel) : floor(filledChicletBoxes + filledChicletBoxesRel)
|
||||
nextValue = Int(Float(self.getMaxValue(for: command)) * (nextFilledChicletBoxes / self.osdChicletBoxes))
|
||||
}
|
||||
|
||||
return max(0, min(self.getMaxValue(for: command), Int(nextValue)))
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +256,6 @@ class Display {
|
|||
|
||||
func getMaxValue(for command: DDC.Command) -> Int {
|
||||
let max = self.prefs.integer(forKey: "max-\(command.rawValue)-\(self.identifier)")
|
||||
|
||||
return max == 0 ? 100 : max
|
||||
}
|
||||
|
||||
|
|
@ -275,14 +267,6 @@ class Display {
|
|||
self.prefs.set(value, forKey: "restore-\(command.rawValue)-\(self.identifier)")
|
||||
}
|
||||
|
||||
func setFriendlyName(_ value: String) {
|
||||
self.prefs.set(value, forKey: "friendlyName-\(self.identifier)")
|
||||
}
|
||||
|
||||
func getFriendlyName() -> String {
|
||||
return self.prefs.string(forKey: "friendlyName-\(self.identifier)") ?? self.name
|
||||
}
|
||||
|
||||
func setPollingMode(_ value: Int) {
|
||||
self.prefs.set(String(value), forKey: "pollingMode-\(self.identifier)")
|
||||
}
|
||||
|
|
@ -327,26 +311,8 @@ class Display {
|
|||
return isSmallIncrement ? 1 : Int(floor(Float(self.getMaxValue(for: command)) / self.osdChicletBoxes))
|
||||
}
|
||||
|
||||
private func showOsd(command: DDC.Command, value: Int) {
|
||||
guard let manager = OSDManager.sharedManager() as? OSDManager else {
|
||||
return
|
||||
}
|
||||
|
||||
var osdImage: Int64 = 1 // Brightness Image
|
||||
if command == .audioSpeakerVolume {
|
||||
osdImage = 3 // Speaker image
|
||||
if self.isMuted() {
|
||||
osdImage = 4 // Mute speaker
|
||||
}
|
||||
}
|
||||
|
||||
manager.showImage(osdImage,
|
||||
onDisplayID: self.identifier,
|
||||
priority: 0x1F4,
|
||||
msecUntilFade: 1000,
|
||||
filledChiclets: UInt32(value),
|
||||
totalChiclets: UInt32(self.getMaxValue(for: command)),
|
||||
locked: false)
|
||||
override func showOsd(command: DDC.Command, value: Int, maxValue _: Int = 100) {
|
||||
super.showOsd(command: command, value: value, maxValue: self.getMaxValue(for: command))
|
||||
}
|
||||
|
||||
private func supportsMuteCommand() -> Bool {
|
||||
85
MonitorControl/Model/InternalDisplay.swift
Normal file
85
MonitorControl/Model/InternalDisplay.swift
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// InternalDisplay.swift
|
||||
// MonitorControl
|
||||
//
|
||||
// Created by Joni Van Roost on 24/01/2020.
|
||||
// Copyright © 2020 Guillaume Broder. All rights reserved.
|
||||
//
|
||||
// Most of the code in this file was sourced from:
|
||||
// https://github.com/fnesveda/ExternalDisplayBrightness
|
||||
// all credit goes to @fnesveda
|
||||
|
||||
import Foundation
|
||||
|
||||
class InternalDisplay: Display {
|
||||
// the queue for dispatching display operations, so they're not performed directly and concurrently
|
||||
private var displayQueue: DispatchQueue
|
||||
|
||||
override init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?) {
|
||||
self.displayQueue = DispatchQueue(label: String("displayQueue-\(identifier)"))
|
||||
super.init(identifier, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber)
|
||||
}
|
||||
|
||||
func calcNewBrightness(isUp: Bool, isSmallIncrement: Bool) -> Float {
|
||||
var step: Float = (isUp ? 1 : -1) / 16.0
|
||||
let delta = step / 4
|
||||
if isSmallIncrement {
|
||||
step = delta
|
||||
}
|
||||
return min(max(0, ceil((self.getBrightness() + delta) / step) * step), 1)
|
||||
}
|
||||
|
||||
public func getBrightness() -> Float {
|
||||
self.displayQueue.sync {
|
||||
Float(type(of: self).CoreDisplayGetUserBrightness?(self.identifier) ?? 0.5)
|
||||
}
|
||||
}
|
||||
|
||||
override func stepBrightness(isUp: Bool, isSmallIncrement: Bool) {
|
||||
let value = self.calcNewBrightness(isUp: isUp, isSmallIncrement: isSmallIncrement)
|
||||
self.displayQueue.sync {
|
||||
type(of: self).CoreDisplaySetUserBrightness?(self.identifier, Double(value))
|
||||
type(of: self).DisplayServicesBrightnessChanged?(self.identifier, Double(value))
|
||||
self.showOsd(command: .brightness, value: Int(value * 64), maxValue: 64)
|
||||
}
|
||||
}
|
||||
|
||||
// notifies the system that the brightness of a specified display has changed (to update System Preferences etc.)
|
||||
// unfortunately Apple doesn't provide a public API for this, so we have to manually extract the function from the DisplayServices framework
|
||||
private static var DisplayServicesBrightnessChanged: ((CGDirectDisplayID, Double) -> Void)? {
|
||||
let displayServicesPath = CFURLCreateWithString(kCFAllocatorDefault, "/System/Library/PrivateFrameworks/DisplayServices.framework" as CFString, nil)
|
||||
if let displayServicesBundle = CFBundleCreate(kCFAllocatorDefault, displayServicesPath) {
|
||||
if let funcPointer = CFBundleGetFunctionPointerForName(displayServicesBundle, "DisplayServicesBrightnessChanged" as CFString) {
|
||||
typealias DSBCFunctionType = @convention(c) (UInt32, Double) -> Void
|
||||
return unsafeBitCast(funcPointer, to: DSBCFunctionType.self)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// reads the brightness of a display through the CoreDisplay framework
|
||||
// unfortunately Apple doesn't provide a public API for this, so we have to manually extract the function from the CoreDisplay framework
|
||||
private static var CoreDisplayGetUserBrightness: ((CGDirectDisplayID) -> Double)? {
|
||||
let coreDisplayPath = CFURLCreateWithString(kCFAllocatorDefault, "/System/Library/Frameworks/CoreDisplay.framework" as CFString, nil)
|
||||
if let coreDisplayBundle = CFBundleCreate(kCFAllocatorDefault, coreDisplayPath) {
|
||||
if let funcPointer = CFBundleGetFunctionPointerForName(coreDisplayBundle, "CoreDisplay_Display_GetUserBrightness" as CFString) {
|
||||
typealias CDGUBFunctionType = @convention(c) (UInt32) -> Double
|
||||
return unsafeBitCast(funcPointer, to: CDGUBFunctionType.self)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sets the brightness of a display through the CoreDisplay framework
|
||||
// unfortunately Apple doesn't provide a public API for this, so we have to manually extract the function from the CoreDisplay framework
|
||||
private static var CoreDisplaySetUserBrightness: ((CGDirectDisplayID, Double) -> Void)? {
|
||||
let coreDisplayPath = CFURLCreateWithString(kCFAllocatorDefault, "/System/Library/Frameworks/CoreDisplay.framework" as CFString, nil)
|
||||
if let coreDisplayBundle = CFBundleCreate(kCFAllocatorDefault, coreDisplayPath) {
|
||||
if let funcPointer = CFBundleGetFunctionPointerForName(coreDisplayBundle, "CoreDisplay_Display_SetUserBrightness" as CFString) {
|
||||
typealias CDSUBFunctionType = @convention(c) (UInt32, Double) -> Void
|
||||
return unsafeBitCast(funcPointer, to: CDSUBFunctionType.self)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ class Utils: NSObject {
|
|||
/// - command: Command (Brightness/Volume/...)
|
||||
/// - title: Title of the slider
|
||||
/// - Returns: An `NSSlider` slider
|
||||
static func addSliderMenuItem(toMenu menu: NSMenu, forDisplay display: Display, command: DDC.Command, title: String) -> SliderHandler {
|
||||
static func addSliderMenuItem(toMenu menu: NSMenu, forDisplay display: ExternalDisplay, command: DDC.Command, title: String) -> SliderHandler {
|
||||
let item = NSMenuItem()
|
||||
|
||||
let handler = SliderHandler(display: display, command: command)
|
||||
|
|
@ -131,28 +131,6 @@ class Utils: NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Display Infos
|
||||
|
||||
/// Get the name of a display
|
||||
///
|
||||
/// - Parameter edid: the EDID of a display
|
||||
/// - Returns: a string
|
||||
static func getDisplayName(forEdid edid: EDID) -> String {
|
||||
return edid.displayName() ?? NSLocalizedString("Unknown", comment: "Unknown display name")
|
||||
}
|
||||
|
||||
/// 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? {
|
||||
guard let mainDisplayID = NSScreen.main?.displayID else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return displays.first { $0.identifier == mainDisplayID }
|
||||
}
|
||||
|
||||
// MARK: - Enums
|
||||
|
||||
/// UserDefault Keys for the app prefs
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import os.log
|
|||
class ButtonCellView: NSTableCellView {
|
||||
@IBOutlet var button: NSButton!
|
||||
var display: Display?
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
|
|
@ -12,17 +11,10 @@ class ButtonCellView: NSTableCellView {
|
|||
|
||||
@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
|
||||
}
|
||||
|
||||
let isEnabled = sender.state == .on
|
||||
display.isEnabled = isEnabled
|
||||
#if DEBUG
|
||||
os_log("Toggle enabled display state: %{public}@", type: .info, sender.state == .on ? "on" : "off")
|
||||
os_log("Toggle enabled display state: %{public}@", type: .info, isEnabled ? "on" : "off")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ class FriendlyNameCellView: NSTableCellView {
|
|||
!newValue.isEmpty {
|
||||
display.setFriendlyName(newValue)
|
||||
NotificationCenter.default.post(name: Notification.Name(Utils.PrefKeys.friendlyName.rawValue), object: nil)
|
||||
|
||||
#if DEBUG
|
||||
os_log("Value changed for friendly name: %{public}@", type: .info, "from `\(originalValue)` to `\(newValue)`")
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import os.log
|
|||
|
||||
class HideOsdCellView: NSTableCellView {
|
||||
@IBOutlet var button: NSButton!
|
||||
var display: Display?
|
||||
var display: ExternalDisplay?
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import os.log
|
|||
|
||||
class LongerDelayCellView: NSTableCellView {
|
||||
@IBOutlet var button: NSButton!
|
||||
var display: Display?
|
||||
var display: ExternalDisplay?
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import Cocoa
|
|||
import os.log
|
||||
|
||||
class PollingCountCellView: NSTableCellView {
|
||||
var display: Display?
|
||||
var display: ExternalDisplay?
|
||||
|
||||
@IBAction func valueChanged(_ sender: NSTextField) {
|
||||
if let display = display {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import os.log
|
|||
We use these tags as a way to mark selection
|
||||
*/
|
||||
class PollingModeCellView: NSTableCellView {
|
||||
var display: Display?
|
||||
var display: ExternalDisplay?
|
||||
@IBOutlet var pollingModeMenu: NSPopUpButtonCell!
|
||||
|
||||
var didChangePollingMode: ((_ pollingModeInt: Int) -> Void)?
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import DDC
|
|||
|
||||
class SliderHandler {
|
||||
var slider: NSSlider?
|
||||
var display: Display
|
||||
var display: ExternalDisplay
|
||||
let cmd: DDC.Command
|
||||
|
||||
public init(display: Display, command: DDC.Command) {
|
||||
public init(display: ExternalDisplay, command: DDC.Command) {
|
||||
self.display = display
|
||||
self.cmd = command
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ class AdvancedPrefsViewController: NSViewController, MASPreferencesViewControlle
|
|||
var toolbarItemImage: NSImage? = NSImage(named: NSImage.advancedName)
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
var displays: [Display] = []
|
||||
var displayManager: DisplayManager?
|
||||
var displays: [ExternalDisplay] = []
|
||||
|
||||
enum DisplayColumn: Int {
|
||||
case friendlyName
|
||||
|
|
@ -61,10 +60,8 @@ class AdvancedPrefsViewController: NSViewController, MASPreferencesViewControlle
|
|||
}
|
||||
|
||||
@objc func loadDisplayList() {
|
||||
if let displays = displayManager?.getDisplays() {
|
||||
self.displays = displays
|
||||
self.displayList.reloadData()
|
||||
}
|
||||
self.displays = DisplayManager.shared.getDdcCapableDisplays()
|
||||
self.displayList.reloadData()
|
||||
}
|
||||
|
||||
func numberOfRows(in _: NSTableView) -> Int {
|
||||
|
|
@ -119,7 +116,7 @@ class AdvancedPrefsViewController: NSViewController, MASPreferencesViewControlle
|
|||
return nil
|
||||
}
|
||||
|
||||
private func getText(for column: DisplayColumn, with display: Display) -> String {
|
||||
private func getText(for column: DisplayColumn, with display: ExternalDisplay) -> String {
|
||||
switch column {
|
||||
case .friendlyName:
|
||||
return display.getFriendlyName()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController
|
|||
let prefs = UserDefaults.standard
|
||||
|
||||
var displays: [Display] = []
|
||||
var displayManager: DisplayManager?
|
||||
|
||||
enum DisplayColumn: Int {
|
||||
case checkbox
|
||||
|
|
@ -57,9 +56,7 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController
|
|||
// MARK: - Table datasource
|
||||
|
||||
@objc func loadDisplayList() {
|
||||
if let displays = self.displayManager?.getDisplays() {
|
||||
self.displays = displays
|
||||
}
|
||||
self.displays = DisplayManager.shared.getAllDisplays()
|
||||
self.displayList.reloadData()
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +79,6 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController
|
|||
if let cell = tableView.makeView(withIdentifier: tableColumn.identifier, owner: nil) as? ButtonCellView {
|
||||
cell.display = display
|
||||
cell.button.state = display.isEnabled ? .on : .off
|
||||
cell.button.isEnabled = !display.isBuiltin
|
||||
return cell
|
||||
}
|
||||
case .ddc:
|
||||
|
|
|
|||
|
|
@ -17,11 +17,9 @@ class KeysPrefsViewController: NSViewController, MASPreferencesViewController {
|
|||
|
||||
@IBAction func listenForChanged(_ sender: NSPopUpButton) {
|
||||
self.prefs.set(sender.selectedTag(), forKey: Utils.PrefKeys.listenFor.rawValue)
|
||||
|
||||
#if DEBUG
|
||||
os_log("Toggle keys listened for state state: %{public}@", type: .info, sender.selectedItem?.title ?? "")
|
||||
#endif
|
||||
|
||||
NotificationCenter.default.post(name: Notification.Name(Utils.PrefKeys.listenFor.rawValue), object: nil)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>574</string>
|
||||
<string>638</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue