Project upgrade, stability improvements, new features (#59)
PR #59 with enhancements and fixes (Changes done by @JoniVR): - Code migration to **Swift 4.2** - **Fixed** the **MonitorControl scheme** which was gone for me - **Updated the screenshots and readme** - **Added a feature that detects the default sound output device and only intercepts when an external display is set as default output**, so Airpods, internal macbook volume and other non-monitor devices will be ignored (#48, #15) - **Fixed app unresponsiveness and high CPU on launch** (might be related to #50, #37, please do keep in mind that on initial launch there will still be a little unresponsiveness because the underlying framework - called [ddcctl](https://github.com/kfix/ddcctl) - attempts to read your volume settings 10 times) - Added a **version and build number** to the main preferences panel:  - Added a build script that increases the build number on each run. Version number still needs to be set manually. - refactored some parts of the code to make it a bit more readable.
BIN
.github/menudisplay.png
vendored
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
.github/menugeneral.png
vendored
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
.github/menukeys.png
vendored
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
.github/menulet-outdated.png
vendored
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
.github/menulet.png
vendored
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 157 KiB |
BIN
.github/osd.jpg
vendored
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
.github/osd.png
vendored
|
Before Width: | Height: | Size: 2.3 KiB |
|
|
@ -12,12 +12,12 @@
|
|||
56754EAF1D9A4016007BCDC5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */; };
|
||||
56754EB11D9A4016007BCDC5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 56754EB01D9A4016007BCDC5 /* Assets.xcassets */; };
|
||||
56754EB41D9A4016007BCDC5 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56754EB21D9A4016007BCDC5 /* MainMenu.xib */; };
|
||||
6C778D5A21E91060000A4D5F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6C778D5821E91060000A4D5F /* Main.storyboard */; };
|
||||
9A19D3B73485870616B6D4E0 /* Pods_MonitorControl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398F482D5C8816B29F16AAEB /* Pods_MonitorControl.framework */; };
|
||||
F03A8DF21FFBAA6F0034DC27 /* Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03A8DF11FFBAA6F0034DC27 /* Display.swift */; };
|
||||
F0445D3820023E710025AE82 /* MainPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0445D3720023E710025AE82 /* MainPrefsViewController.swift */; };
|
||||
F0445D3D200254FA0025AE82 /* KeysPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0445D3B200254FA0025AE82 /* KeysPrefsViewController.swift */; };
|
||||
F0445D40200259C10025AE82 /* DisplayPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0445D3F200259C10025AE82 /* DisplayPrefsViewController.swift */; };
|
||||
F0445D41200282E60025AE82 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F0445D43200282E60025AE82 /* Main.storyboard */; };
|
||||
F0445D4D200294AB0025AE82 /* ButtonCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0445D4C200294AB0025AE82 /* ButtonCellView.swift */; };
|
||||
F06792EA200A73460066C438 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F06792E9200A73460066C438 /* AppDelegate.swift */; };
|
||||
F06792F6200A745F0066C438 /* MonitorControlHelper.app in [Login] Copy Helper to start at Login */ = {isa = PBXBuildFile; fileRef = F06792E7200A73460066C438 /* MonitorControlHelper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
|
|
@ -56,13 +56,13 @@
|
|||
56754EB01D9A4016007BCDC5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
56754EB31D9A4016007BCDC5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
56754EB51D9A4016007BCDC5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
6C778D5921E91060000A4D5F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
6C778D5E21E910A2000A4D5F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
6C778D5F21E910A6000A4D5F /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
F03A8DF11FFBAA6F0034DC27 /* Display.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Display.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>"; };
|
||||
F0445D3F200259C10025AE82 /* DisplayPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayPrefsViewController.swift; sourceTree = "<group>"; };
|
||||
F0445D42200282E60025AE82 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
F0445D45200282EB0025AE82 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
F0445D47200282F80025AE82 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
F0445D49200285690025AE82 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
F0445D4B2002856C0025AE82 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
F0445D4C200294AB0025AE82 /* ButtonCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonCellView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -149,7 +149,7 @@
|
|||
children = (
|
||||
56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */,
|
||||
F091C9B71F6EA79B0096FD65 /* Utils.swift */,
|
||||
F0445D43200282E60025AE82 /* Main.storyboard */,
|
||||
6C778D5821E91060000A4D5F /* Main.storyboard */,
|
||||
56754EB21D9A4016007BCDC5 /* MainMenu.xib */,
|
||||
F0445D3620023D5B0025AE82 /* Prefs */,
|
||||
F091C9B41F6EA6180096FD65 /* Objects */,
|
||||
|
|
@ -247,6 +247,7 @@
|
|||
56754EA91D9A4016007BCDC5 /* Resources */,
|
||||
9DD5968596EFAF0E2EB56496 /* [CP] Embed Pods Frameworks */,
|
||||
F06792F5200A73FA0066C438 /* [Login] Copy Helper to start at Login */,
|
||||
6C778D4D21E90DA1000A4D5F /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -286,13 +287,14 @@
|
|||
TargetAttributes = {
|
||||
56754EAA1D9A4016007BCDC5 = {
|
||||
CreatedOnToolsVersion = 8.0;
|
||||
DevelopmentTeam = KGY56RWR9A;
|
||||
LastSwiftMigration = 0900;
|
||||
DevelopmentTeam = CYC8C8R4K9;
|
||||
LastSwiftMigration = 1010;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
F06792E6200A73460066C438 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
DevelopmentTeam = KGY56RWR9A;
|
||||
DevelopmentTeam = CYC8C8R4K9;
|
||||
LastSwiftMigration = 1010;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
|
|
@ -323,8 +325,8 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
56754EB11D9A4016007BCDC5 /* Assets.xcassets in Resources */,
|
||||
F0445D41200282E60025AE82 /* Main.storyboard in Resources */,
|
||||
F0EB972F1F6ED7C800686D2A /* Localizable.strings in Resources */,
|
||||
6C778D5A21E91060000A4D5F /* Main.storyboard in Resources */,
|
||||
55359E3B1E2737EC002671BC /* ddcctl.sh in Resources */,
|
||||
56754EB41D9A4016007BCDC5 /* MainMenu.xib in Resources */,
|
||||
);
|
||||
|
|
@ -340,6 +342,23 @@
|
|||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
6C778D4D21E90DA1000A4D5F /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nbuildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${INFOPLIST_FILE}\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${INFOPLIST_FILE}\"\n\n";
|
||||
};
|
||||
9DD5968596EFAF0E2EB56496 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -347,11 +366,13 @@
|
|||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-MonitorControl/Pods-MonitorControl-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/AMCoreAudio/AMCoreAudio.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/MASPreferences/MASPreferences.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/MediaKeyTap/MediaKeyTap.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AMCoreAudio.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MASPreferences.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MediaKeyTap.framework",
|
||||
);
|
||||
|
|
@ -432,12 +453,12 @@
|
|||
name = MainMenu.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F0445D43200282E60025AE82 /* Main.storyboard */ = {
|
||||
6C778D5821E91060000A4D5F /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F0445D42200282E60025AE82 /* Base */,
|
||||
F0445D45200282EB0025AE82 /* fr */,
|
||||
F0445D47200282F80025AE82 /* en */,
|
||||
6C778D5921E91060000A4D5F /* Base */,
|
||||
6C778D5E21E910A2000A4D5F /* en */,
|
||||
6C778D5F21E910A6000A4D5F /* fr */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -600,14 +621,14 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = KGY56RWR9A;
|
||||
DEVELOPMENT_TEAM = CYC8C8R4K9;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/MonitorControl/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -619,14 +640,14 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = KGY56RWR9A;
|
||||
DEVELOPMENT_TEAM = CYC8C8R4K9;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/MonitorControl/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
@ -641,7 +662,7 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = KGY56RWR9A;
|
||||
DEVELOPMENT_TEAM = CYC8C8R4K9;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = MonitorControlHelper/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
|
|
@ -649,7 +670,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControlHelper;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -664,7 +685,7 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = KGY56RWR9A;
|
||||
DEVELOPMENT_TEAM = CYC8C8R4K9;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = MonitorControlHelper/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
|
|
@ -672,7 +693,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControlHelper;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1010"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "56754EAA1D9A4016007BCDC5"
|
||||
BuildableName = "MonitorControl.app"
|
||||
BlueprintName = "MonitorControl"
|
||||
ReferencedContainer = "container:MonitorControl.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "56754EAA1D9A4016007BCDC5"
|
||||
BuildableName = "MonitorControl.app"
|
||||
BlueprintName = "MonitorControl"
|
||||
ReferencedContainer = "container:MonitorControl.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "56754EAA1D9A4016007BCDC5"
|
||||
BuildableName = "MonitorControl.app"
|
||||
BlueprintName = "MonitorControl"
|
||||
ReferencedContainer = "container:MonitorControl.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "56754EAA1D9A4016007BCDC5"
|
||||
BuildableName = "MonitorControl.app"
|
||||
BlueprintName = "MonitorControl"
|
||||
ReferencedContainer = "container:MonitorControl.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -11,12 +11,13 @@ import Cocoa
|
|||
import Foundation
|
||||
import MediaKeyTap
|
||||
import MASPreferences
|
||||
import AMCoreAudio
|
||||
|
||||
var app: AppDelegate! = nil
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@IBOutlet weak var statusMenu: NSMenu!
|
||||
@IBOutlet weak var window: NSWindow!
|
||||
|
|
@ -26,122 +27,97 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||
var monitorItems: [NSMenuItem] = []
|
||||
var displays: [Display] = []
|
||||
|
||||
let step = 100/16
|
||||
let step = 100/16
|
||||
|
||||
var mediaKeyTap: MediaKeyTap?
|
||||
var prefsController: NSWindowController?
|
||||
var mediaKeyTap: MediaKeyTap?
|
||||
var prefsController: NSWindowController?
|
||||
|
||||
var keysListenedFor: [MediaKey] = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown]
|
||||
var keysListenedFor: [MediaKey] = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown]
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
app = self
|
||||
|
||||
let listenFor = prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue)
|
||||
if listenFor == Utils.ListenForKeys.brightnessOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(2...4)
|
||||
} else if listenFor == Utils.ListenForKeys.volumeOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(0...1)
|
||||
}
|
||||
|
||||
mediaKeyTap = MediaKeyTap.init(delegate: self, for: keysListenedFor, observeBuiltIn: false)
|
||||
let storyboard: NSStoryboard = NSStoryboard.init(name: NSStoryboard.Name(rawValue: "Main"), bundle: Bundle.main)
|
||||
let views = [
|
||||
storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "MainPrefsVC")),
|
||||
storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "KeysPrefsVC")),
|
||||
storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "DisplayPrefsVC"))
|
||||
]
|
||||
prefsController = MASPreferencesWindowController(viewControllers: views, title: NSLocalizedString("Preferences", comment: "Shown in Preferences window"))
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleListenForChanged), name: NSNotification.Name.init(Utils.PrefKeys.listenFor.rawValue), object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleShowContrastChanged), name: NSNotification.Name.init(Utils.PrefKeys.showContrast.rawValue), object: nil)
|
||||
|
||||
statusItem.image = NSImage.init(named: NSImage.Name(rawValue: "status"))
|
||||
setupLayout()
|
||||
subscribeEventListeners()
|
||||
setVolumeKeysMode()
|
||||
statusItem.image = NSImage.init(named: "status")
|
||||
statusItem.menu = statusMenu
|
||||
|
||||
setDefaultPrefs()
|
||||
|
||||
setDefaultPrefs()
|
||||
Utils.acquirePrivileges()
|
||||
|
||||
CGDisplayRegisterReconfigurationCallback({_, _, _ in app.updateDisplays()}, nil)
|
||||
updateDisplays()
|
||||
|
||||
mediaKeyTap?.start()
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
}
|
||||
@IBAction func quitClicked(_ sender: AnyObject) {
|
||||
NSApplication.shared.terminate(self)
|
||||
}
|
||||
|
||||
@IBAction func quitClicked(_ sender: AnyObject) {
|
||||
NSApplication.shared.terminate(self)
|
||||
}
|
||||
@IBAction func prefsClicked(_ sender: AnyObject) {
|
||||
if let prefsController = prefsController {
|
||||
prefsController.showWindow(sender)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
prefsController.window?.makeKeyAndOrderFront(sender)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func prefsClicked(_ sender: AnyObject) {
|
||||
if let prefsController = prefsController {
|
||||
prefsController.showWindow(sender)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
prefsController.window?.makeKeyAndOrderFront(sender)
|
||||
}
|
||||
}
|
||||
/// Set the default prefs of the app
|
||||
func setDefaultPrefs() {
|
||||
let prefs = UserDefaults.standard
|
||||
if !prefs.bool(forKey: Utils.PrefKeys.appAlreadyLaunched.rawValue) {
|
||||
prefs.set(true, forKey: Utils.PrefKeys.appAlreadyLaunched.rawValue)
|
||||
|
||||
/// Set the default prefs of the app
|
||||
func setDefaultPrefs() {
|
||||
let prefs = UserDefaults.standard
|
||||
if !prefs.bool(forKey: Utils.PrefKeys.appAlreadyLaunched.rawValue) {
|
||||
prefs.set(true, forKey: Utils.PrefKeys.appAlreadyLaunched.rawValue)
|
||||
prefs.set(false, forKey: Utils.PrefKeys.startAtLogin.rawValue)
|
||||
|
||||
prefs.set(false, forKey: Utils.PrefKeys.startAtLogin.rawValue)
|
||||
prefs.set(false, forKey: Utils.PrefKeys.showContrast.rawValue)
|
||||
prefs.set(false, forKey: Utils.PrefKeys.lowerContrast.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
prefs.set(false, forKey: Utils.PrefKeys.showContrast.rawValue)
|
||||
prefs.set(false, forKey: Utils.PrefKeys.lowerContrast.rawValue)
|
||||
}
|
||||
}
|
||||
// MARK: - Menu
|
||||
func clearDisplays() {
|
||||
if statusMenu.items.count > 2 {
|
||||
var items: [NSMenuItem] = []
|
||||
for i in 0..<statusMenu.items.count - 2 {
|
||||
items.append(statusMenu.items[i])
|
||||
}
|
||||
|
||||
// MARK: - Menu
|
||||
for item in items {
|
||||
statusMenu.removeItem(item)
|
||||
}
|
||||
}
|
||||
|
||||
func clearDisplays() {
|
||||
if statusMenu.items.count > 2 {
|
||||
var items: [NSMenuItem] = []
|
||||
for i in 0..<statusMenu.items.count - 2 {
|
||||
items.append(statusMenu.items[i])
|
||||
}
|
||||
|
||||
for item in items {
|
||||
statusMenu.removeItem(item)
|
||||
}
|
||||
}
|
||||
|
||||
monitorItems = []
|
||||
displays = []
|
||||
}
|
||||
monitorItems = []
|
||||
displays = []
|
||||
}
|
||||
|
||||
func updateDisplays() {
|
||||
clearDisplays()
|
||||
clearDisplays()
|
||||
|
||||
var filteredScreens = NSScreen.screens.filter { screen -> Bool in
|
||||
if let id = screen.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as? CGDirectDisplayID {
|
||||
// Is Built In Screen (e.g. MBP/iMac Screen)
|
||||
if CGDisplayIsBuiltin(id) != 0 {
|
||||
return false
|
||||
}
|
||||
var filteredScreens = NSScreen.screens.filter { screen -> Bool in
|
||||
if let id = screen.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as? CGDirectDisplayID {
|
||||
// Is Built In Screen (e.g. MBP/iMac Screen)
|
||||
if CGDisplayIsBuiltin(id) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Does screen support EDID ?
|
||||
var edid = EDID()
|
||||
if !EDIDTest(id, &edid) {
|
||||
return false
|
||||
}
|
||||
// Does screen support EDID ?
|
||||
var edid = EDID()
|
||||
if !EDIDTest(id, &edid) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if filteredScreens.count == 1 {
|
||||
self.addScreenToMenu(screen: filteredScreens[0], asSubMenu: false)
|
||||
} else {
|
||||
for screen in filteredScreens {
|
||||
self.addScreenToMenu(screen: screen, asSubMenu: true)
|
||||
}
|
||||
}
|
||||
if filteredScreens.count == 1 {
|
||||
self.addScreenToMenu(screen: filteredScreens[0], asSubMenu: false)
|
||||
} else {
|
||||
for screen in filteredScreens {
|
||||
self.addScreenToMenu(screen: screen, asSubMenu: true)
|
||||
}
|
||||
}
|
||||
|
||||
if filteredScreens.count == 0 {
|
||||
// If no DDC capable display was detected
|
||||
|
|
@ -153,102 +129,175 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add a screen to the menu
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - screen: The screen to add
|
||||
/// - asSubMenu: Display in a sub menu or directly in menu
|
||||
private func addScreenToMenu(screen: NSScreen, asSubMenu: Bool) {
|
||||
if let id = screen.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as? CGDirectDisplayID {
|
||||
/// Add a screen to the menu
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - screen: The screen to add
|
||||
/// - asSubMenu: Display in a sub menu or directly in menu
|
||||
private func addScreenToMenu(screen: NSScreen, asSubMenu: Bool) {
|
||||
if let id = screen.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as? CGDirectDisplayID {
|
||||
|
||||
var edid = EDID()
|
||||
if EDIDTest(id, &edid) {
|
||||
let name = Utils.getDisplayName(forEdid: edid)
|
||||
let serial = Utils.getDisplaySerial(forEdid: edid)
|
||||
var edid = EDID()
|
||||
if EDIDTest(id, &edid) {
|
||||
let name = Utils.getDisplayName(forEdid: edid)
|
||||
let serial = Utils.getDisplaySerial(forEdid: edid)
|
||||
|
||||
let display = Display.init(id, name: name, serial: serial)
|
||||
let display = Display.init(id, name: name, serial: serial)
|
||||
|
||||
let monitorSubMenu: NSMenu = asSubMenu ? NSMenu() : statusMenu
|
||||
let volumeSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu,
|
||||
forDisplay: display,
|
||||
command: AUDIO_SPEAKER_VOLUME,
|
||||
title: NSLocalizedString("Volume", comment: "Shown in menu"))
|
||||
let brightnessSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu,
|
||||
forDisplay: display,
|
||||
command: BRIGHTNESS,
|
||||
title: NSLocalizedString("Brightness", comment: "Shown in menu"))
|
||||
if prefs.bool(forKey: Utils.PrefKeys.showContrast.rawValue) {
|
||||
let contrastSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu,
|
||||
forDisplay: display,
|
||||
command: CONTRAST,
|
||||
title: NSLocalizedString("Contrast", comment: "Shown in menu"))
|
||||
display.contrastSliderHandler = contrastSliderHandler
|
||||
}
|
||||
let monitorSubMenu: NSMenu = asSubMenu ? NSMenu() : statusMenu
|
||||
let volumeSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu,
|
||||
forDisplay: display,
|
||||
command: AUDIO_SPEAKER_VOLUME,
|
||||
title: NSLocalizedString("Volume", comment: "Shown in menu"))
|
||||
let brightnessSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu,
|
||||
forDisplay: display,
|
||||
command: BRIGHTNESS,
|
||||
title: NSLocalizedString("Brightness", comment: "Shown in menu"))
|
||||
if prefs.bool(forKey: Utils.PrefKeys.showContrast.rawValue) {
|
||||
let contrastSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu,
|
||||
forDisplay: display,
|
||||
command: CONTRAST,
|
||||
title: NSLocalizedString("Contrast", comment: "Shown in menu"))
|
||||
display.contrastSliderHandler = contrastSliderHandler
|
||||
}
|
||||
|
||||
display.volumeSliderHandler = volumeSliderHandler
|
||||
display.brightnessSliderHandler = brightnessSliderHandler
|
||||
displays.append(display)
|
||||
display.volumeSliderHandler = volumeSliderHandler
|
||||
display.brightnessSliderHandler = brightnessSliderHandler
|
||||
displays.append(display)
|
||||
|
||||
let monitorMenuItem = NSMenuItem()
|
||||
monitorMenuItem.title = "\(name)"
|
||||
if asSubMenu {
|
||||
monitorMenuItem.submenu = monitorSubMenu
|
||||
}
|
||||
let monitorMenuItem = NSMenuItem()
|
||||
monitorMenuItem.title = "\(name)"
|
||||
if asSubMenu {
|
||||
monitorMenuItem.submenu = monitorSubMenu
|
||||
}
|
||||
|
||||
monitorItems.append(monitorMenuItem)
|
||||
statusMenu.insertItem(monitorMenuItem, at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
monitorItems.append(monitorMenuItem)
|
||||
statusMenu.insertItem(monitorMenuItem, at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Media Key Tap delegate
|
||||
private func setupLayout() {
|
||||
let storyboard: NSStoryboard = NSStoryboard.init(name: "Main", bundle: Bundle.main)
|
||||
let views = [
|
||||
storyboard.instantiateController(withIdentifier: "MainPrefsVC"),
|
||||
storyboard.instantiateController(withIdentifier: "KeysPrefsVC"),
|
||||
storyboard.instantiateController(withIdentifier: "DisplayPrefsVC")
|
||||
]
|
||||
prefsController = MASPreferencesWindowController(viewControllers: views, title: NSLocalizedString("Preferences", comment: "Shown in Preferences window"))
|
||||
}
|
||||
|
||||
func handle(mediaKey: MediaKey, event: KeyEvent?) {
|
||||
guard let currentDisplay = Utils.getCurrentDisplay(from: displays) else { return }
|
||||
let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? displays : [currentDisplay]
|
||||
for display in allDisplays {
|
||||
if (prefs.object(forKey: "\(display.identifier)-state") as? Bool) ?? true {
|
||||
switch mediaKey {
|
||||
case .brightnessUp:
|
||||
let value = display.calcNewValue(for: BRIGHTNESS, withRel: +step)
|
||||
display.setBrightness(to: value)
|
||||
case .brightnessDown:
|
||||
let value = currentDisplay.calcNewValue(for: BRIGHTNESS, withRel: -step)
|
||||
display.setBrightness(to: value)
|
||||
case .mute:
|
||||
display.mute()
|
||||
case .volumeUp:
|
||||
let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: +step)
|
||||
display.setVolume(to: value)
|
||||
case .volumeDown:
|
||||
let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: -step)
|
||||
display.setVolume(to: value)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Prefs notification
|
||||
|
||||
@objc func handleListenForChanged() {
|
||||
let listenFor = prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue)
|
||||
keysListenedFor = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown]
|
||||
if listenFor == Utils.ListenForKeys.brightnessOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(2...4)
|
||||
} else if listenFor == Utils.ListenForKeys.volumeOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(0...1)
|
||||
}
|
||||
|
||||
mediaKeyTap?.stop()
|
||||
mediaKeyTap = MediaKeyTap.init(delegate: self, for: keysListenedFor, observeBuiltIn: false)
|
||||
mediaKeyTap?.start()
|
||||
}
|
||||
|
||||
@objc func handleShowContrastChanged() {
|
||||
self.updateDisplays()
|
||||
}
|
||||
private func subscribeEventListeners() {
|
||||
// subscribe KeyTap event listener
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleListenForChanged), name: NSNotification.Name.init(Utils.PrefKeys.listenFor.rawValue), object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleShowContrastChanged), name: NSNotification.Name.init(Utils.PrefKeys.showContrast.rawValue), object: nil)
|
||||
|
||||
// subscribe Audio output detector (AMCoreAudio)
|
||||
NotificationCenter.defaultCenter.subscribe(self, eventType: AudioHardwareEvent.self, dispatchQueue: DispatchQueue.main)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Media Key Tap delegate
|
||||
extension AppDelegate: MediaKeyTapDelegate {
|
||||
|
||||
func handle(mediaKey: MediaKey, event: KeyEvent?) {
|
||||
|
||||
guard let currentDisplay = Utils.getCurrentDisplay(from: displays) else { return }
|
||||
let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? displays : [currentDisplay]
|
||||
|
||||
for display in allDisplays {
|
||||
if (prefs.object(forKey: "\(display.identifier)-state") as? Bool) ?? true {
|
||||
switch mediaKey {
|
||||
case .brightnessUp:
|
||||
let value = display.calcNewValue(for: BRIGHTNESS, withRel: +step)
|
||||
display.setBrightness(to: value)
|
||||
case .brightnessDown:
|
||||
let value = currentDisplay.calcNewValue(for: BRIGHTNESS, withRel: -step)
|
||||
display.setBrightness(to: value)
|
||||
case .mute:
|
||||
display.mute()
|
||||
case .volumeUp:
|
||||
let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: +step)
|
||||
display.setVolume(to: value)
|
||||
case .volumeDown:
|
||||
let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: -step)
|
||||
display.setVolume(to: value)
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Prefs notification
|
||||
@objc func handleListenForChanged() {
|
||||
readKeyListenPreferences()
|
||||
setKeysToListenFor()
|
||||
}
|
||||
|
||||
@objc func handleShowContrastChanged() {
|
||||
self.updateDisplays()
|
||||
}
|
||||
|
||||
private func setKeysToListenFor() {
|
||||
mediaKeyTap?.stop()
|
||||
mediaKeyTap = MediaKeyTap.init(delegate: self, for: keysListenedFor, observeBuiltIn: false)
|
||||
mediaKeyTap?.start()
|
||||
}
|
||||
|
||||
private func readKeyListenPreferences() {
|
||||
let listenFor = prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue)
|
||||
keysListenedFor = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown]
|
||||
if listenFor == Utils.ListenForKeys.brightnessOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(2...4)
|
||||
} else if listenFor == Utils.ListenForKeys.volumeOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(0...1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AppDelegate: EventSubscriber {
|
||||
|
||||
/**
|
||||
Fires off when a change in default audio device is detected.
|
||||
*/
|
||||
func eventReceiver(_ event: Event) {
|
||||
|
||||
switch event {
|
||||
case let event as AudioHardwareEvent:
|
||||
switch event {
|
||||
case .defaultOutputDeviceChanged(let audioDevice):
|
||||
#if DEBUG
|
||||
print("Default output device changed to \(audioDevice)")
|
||||
print("Can device set its own volume? \(audioDevice.canSetVirtualMasterVolume(direction: .playback))")
|
||||
#endif
|
||||
setVolumeKeysMode()
|
||||
default: break
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
We check if the current default audio output device can change the volume,
|
||||
if not, we know for sure that we don't need to interact with it.
|
||||
*/
|
||||
func setVolumeKeysMode() {
|
||||
|
||||
readKeyListenPreferences()
|
||||
|
||||
if let defaultOutputDevice = AudioDevice.defaultOutputDevice() {
|
||||
if defaultOutputDevice.canSetVirtualMasterVolume(direction: .playback) {
|
||||
// Remove volume related keys
|
||||
let keysToDelete: [MediaKey] = [.volumeUp, .volumeDown, .mute]
|
||||
keysListenedFor = keysListenedFor.filter({ !keysToDelete.contains($0) })
|
||||
} else {
|
||||
// load keys to listen to from prefs like normal
|
||||
readKeyListenPreferences()
|
||||
}
|
||||
}
|
||||
setKeysToListenFor()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,85 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Main Prefs View Controller-->
|
||||
<scene sceneID="Bdy-Ug-m8G">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="MainPrefsVC" id="HNb-aq-vnV" customClass="MainPrefsViewController" customModule="MonitorControl" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="iuQ-z6-BGz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="161"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8KQ-j9-ddv">
|
||||
<rect key="frame" x="18" y="112" width="92" height="29"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="General" id="ocE-Cc-2bi">
|
||||
<font key="font" metaFont="systemBold" size="24"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dZG-M6-fsQ">
|
||||
<rect key="frame" x="18" y="86" width="199" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Start MonitorControl at Login" bezelStyle="regularSquare" imagePosition="left" inset="2" id="UTh-SV-vAQ">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="startAtLoginClicked:" target="HNb-aq-vnV" id="OrA-9Y-N8S"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7YI-Uo-3fM">
|
||||
<rect key="frame" x="18" y="52" width="177" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Show a slider for contrast" bezelStyle="regularSquare" imagePosition="left" inset="2" id="8cS-Fg-fKy">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="showContrastSliderClicked:" target="HNb-aq-vnV" id="9BB-1N-fjo"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rYO-48-phB">
|
||||
<rect key="frame" x="18" y="18" width="215" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Lower Contrast after Brightness" bezelStyle="regularSquare" imagePosition="left" inset="2" id="JDl-l4-s8k">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="lowerContrastClicked:" target="HNb-aq-vnV" id="hki-Wc-jOF"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="dZG-M6-fsQ" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="8QT-EO-uBJ"/>
|
||||
<constraint firstItem="rYO-48-phB" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="Cyy-k9-19a"/>
|
||||
<constraint firstItem="8KQ-j9-ddv" firstAttribute="top" secondItem="iuQ-z6-BGz" secondAttribute="top" constant="20" id="Fwh-JQ-1Cx"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="7YI-Uo-3fM" secondAttribute="trailing" constant="20" id="Jtd-pc-orO"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="rYO-48-phB" secondAttribute="trailing" constant="20" id="XEL-Kb-iUl"/>
|
||||
<constraint firstAttribute="bottom" secondItem="rYO-48-phB" secondAttribute="bottom" constant="20" id="YZb-sI-14r"/>
|
||||
<constraint firstItem="7YI-Uo-3fM" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="YhE-2k-Edh"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="dZG-M6-fsQ" secondAttribute="trailing" constant="20" id="c5h-vI-6Dg"/>
|
||||
<constraint firstItem="7YI-Uo-3fM" firstAttribute="top" secondItem="dZG-M6-fsQ" secondAttribute="bottom" constant="20" id="cdD-7n-hpx"/>
|
||||
<constraint firstItem="dZG-M6-fsQ" firstAttribute="top" secondItem="8KQ-j9-ddv" secondAttribute="bottom" constant="10" id="dPJ-ev-Y3O"/>
|
||||
<constraint firstItem="rYO-48-phB" firstAttribute="top" secondItem="7YI-Uo-3fM" secondAttribute="bottom" constant="20" id="fF5-Zz-fCl"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="dZG-M6-fsQ" secondAttribute="bottom" constant="20" id="gVa-4Q-fPP"/>
|
||||
<constraint firstItem="8KQ-j9-ddv" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="ii0-uV-Ylg"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="8KQ-j9-ddv" secondAttribute="trailing" constant="20" id="qVn-hz-dRV"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="lowerContrast" destination="rYO-48-phB" id="hSm-0P-Uur"/>
|
||||
<outlet property="showContrastSlider" destination="7YI-Uo-3fM" id="bPT-Jq-Lfc"/>
|
||||
<outlet property="startAtLogin" destination="dZG-M6-fsQ" id="Rwg-dp-vIj"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="ALN-AB-CU5" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="156" y="227"/>
|
||||
</scene>
|
||||
<!--Keys Prefs View Controller-->
|
||||
<scene sceneID="DmR-ia-4qL">
|
||||
<objects>
|
||||
|
|
@ -89,7 +15,7 @@
|
|||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fUf-UM-yW3">
|
||||
<rect key="frame" x="85" y="17" width="196" height="26"/>
|
||||
<rect key="frame" x="85" y="17" width="197" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Both Brightness & Volume" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Vr4-xb-B4o" id="DkZ-as-YDS">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
|
|
@ -177,7 +103,7 @@
|
|||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="" width="49" minWidth="40" maxWidth="1000" id="8U8-ec-Zbv">
|
||||
<tableColumn width="49" minWidth="40" maxWidth="1000" id="8U8-ec-Zbv">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Enabled">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
|
@ -208,7 +134,7 @@
|
|||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="" width="186" minWidth="40" maxWidth="1000" id="CHc-s5-4MN">
|
||||
<tableColumn width="186" minWidth="40" maxWidth="1000" id="CHc-s5-4MN">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Display Name">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
|
@ -241,7 +167,7 @@
|
|||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="" width="114" minWidth="10" maxWidth="3.4028234663852886e+38" id="dgp-q7-cBK">
|
||||
<tableColumn width="114" minWidth="10" maxWidth="3.4028234663852886e+38" id="dgp-q7-cBK">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Display Id">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
|
@ -282,11 +208,11 @@
|
|||
</tableView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="UHx-W7-xSv">
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="UHx-W7-xSv">
|
||||
<rect key="frame" x="-100" y="-100" width="358" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="HBR-36-UaL">
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="HBR-36-UaL">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
|
|
@ -328,5 +254,90 @@
|
|||
</objects>
|
||||
<point key="canvasLocation" x="1116" y="234"/>
|
||||
</scene>
|
||||
<!--Main Prefs View Controller-->
|
||||
<scene sceneID="zAg-r8-WQ5">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="MainPrefsVC" id="BGD-tY-Myx" customClass="MainPrefsViewController" customModule="MonitorControl" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="COE-Oc-gZs">
|
||||
<rect key="frame" x="0.0" y="0.0" width="404" height="208"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="siR-fL-v2a">
|
||||
<rect key="frame" x="18" y="159" width="92" height="29"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="General" id="ENU-js-huy">
|
||||
<font key="font" metaFont="systemBold" size="24"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9Gu-aU-Td2">
|
||||
<rect key="frame" x="18" y="86" width="199" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Start MonitorControl at Login" bezelStyle="regularSquare" imagePosition="left" inset="2" id="j72-NF-zsW">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="startAtLoginClicked:" target="BGD-tY-Myx" id="QvH-5O-Lck"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xGa-qG-9ut">
|
||||
<rect key="frame" x="18" y="52" width="177" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Show a slider for contrast" bezelStyle="regularSquare" imagePosition="left" inset="2" id="xSI-8W-Xd0">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="showContrastSliderClicked:" target="BGD-tY-Myx" id="Jek-jL-YMn"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i5K-Cd-wxm">
|
||||
<rect key="frame" x="18" y="18" width="215" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Lower Contrast after Brightness" bezelStyle="regularSquare" imagePosition="left" inset="2" id="fhy-Er-0aI">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="lowerContrastClicked:" target="BGD-tY-Myx" id="iTd-hI-jbv"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="tMg-qE-zTW">
|
||||
<rect key="frame" x="18" y="122" width="99" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="MonitorControl " id="mBs-6m-13Q">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="i5K-Cd-wxm" secondAttribute="trailing" constant="20" id="7p3-HQ-gLl"/>
|
||||
<constraint firstItem="xGa-qG-9ut" firstAttribute="top" secondItem="9Gu-aU-Td2" secondAttribute="bottom" constant="20" id="DOL-i0-bCI"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="9Gu-aU-Td2" secondAttribute="trailing" constant="20" id="Gft-KX-rdy"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="xGa-qG-9ut" secondAttribute="trailing" constant="20" id="HDL-zX-2dq"/>
|
||||
<constraint firstItem="9Gu-aU-Td2" firstAttribute="leading" secondItem="COE-Oc-gZs" secondAttribute="leading" constant="20" id="MwQ-M8-u18"/>
|
||||
<constraint firstItem="xGa-qG-9ut" firstAttribute="leading" secondItem="COE-Oc-gZs" secondAttribute="leading" constant="20" id="NgS-ga-iNo"/>
|
||||
<constraint firstItem="i5K-Cd-wxm" firstAttribute="top" secondItem="xGa-qG-9ut" secondAttribute="bottom" constant="20" id="R2I-Ko-ZJ9"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="siR-fL-v2a" secondAttribute="trailing" constant="20" id="Srs-vY-IQB"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="tMg-qE-zTW" secondAttribute="trailing" constant="20" id="cf5-i9-1VX"/>
|
||||
<constraint firstItem="siR-fL-v2a" firstAttribute="leading" secondItem="COE-Oc-gZs" secondAttribute="leading" constant="20" id="eXs-op-WJj"/>
|
||||
<constraint firstItem="tMg-qE-zTW" firstAttribute="top" secondItem="siR-fL-v2a" secondAttribute="bottom" constant="20" id="gY5-7g-9Zb"/>
|
||||
<constraint firstItem="siR-fL-v2a" firstAttribute="top" secondItem="COE-Oc-gZs" secondAttribute="top" constant="20" id="iTz-r7-NFb"/>
|
||||
<constraint firstItem="i5K-Cd-wxm" firstAttribute="leading" secondItem="COE-Oc-gZs" secondAttribute="leading" constant="20" id="jWI-DQ-WdT"/>
|
||||
<constraint firstItem="tMg-qE-zTW" firstAttribute="leading" secondItem="COE-Oc-gZs" secondAttribute="leading" constant="20" id="l4b-at-imk"/>
|
||||
<constraint firstItem="9Gu-aU-Td2" firstAttribute="top" secondItem="tMg-qE-zTW" secondAttribute="bottom" constant="20" id="mre-Oy-gJw"/>
|
||||
<constraint firstAttribute="bottom" secondItem="i5K-Cd-wxm" secondAttribute="bottom" constant="20" id="xxz-YT-Uma"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="lowerContrast" destination="i5K-Cd-wxm" id="sE8-Ra-JSn"/>
|
||||
<outlet property="showContrastSlider" destination="xGa-qG-9ut" id="AzL-sx-Z8Q"/>
|
||||
<outlet property="startAtLogin" destination="9Gu-aU-Td2" id="Tyx-Ub-Cyf"/>
|
||||
<outlet property="versionLabel" destination="tMg-qE-zTW" id="31N-s8-URL"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="JDw-du-OST" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="162" y="218"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2.1</string>
|
||||
<string>1.3.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>45</string>
|
||||
<string>48</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController
|
|||
|
||||
var viewIdentifier: String = "Display"
|
||||
var toolbarItemLabel: String? = NSLocalizedString("Display", comment: "Shown in the main prefs window")
|
||||
var toolbarItemImage: NSImage? = NSImage.init(named: .computer)
|
||||
var toolbarItemImage: NSImage? = NSImage.init(named: NSImage.computerName)
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
var displays: [Display] = []
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class KeysPrefsViewController: NSViewController, MASPreferencesViewController {
|
|||
|
||||
var viewIdentifier: String = "Keys"
|
||||
var toolbarItemLabel: String? = NSLocalizedString("Keys", comment: "Shown in the main prefs window")
|
||||
var toolbarItemImage: NSImage? = NSImage.init(named: NSImage.Name.init("KeyboardPref"))
|
||||
var toolbarItemImage: NSImage? = NSImage.init(named: "KeyboardPref")
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
@IBOutlet var listenFor: NSPopUpButton!
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@ class MainPrefsViewController: NSViewController, MASPreferencesViewController {
|
|||
|
||||
var viewIdentifier: String = "Main"
|
||||
var toolbarItemLabel: String? = NSLocalizedString("General", comment: "Shown in the main prefs window")
|
||||
var toolbarItemImage: NSImage? = NSImage.init(named: .preferencesGeneral)
|
||||
var toolbarItemImage: NSImage? = NSImage.init(named: NSImage.preferencesGeneralName)
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
@IBOutlet var startAtLogin: NSButton!
|
||||
|
||||
@IBOutlet weak var versionLabel: NSTextField!
|
||||
@IBOutlet var startAtLogin: NSButton!
|
||||
@IBOutlet var showContrastSlider: NSButton!
|
||||
@IBOutlet var lowerContrast: NSButton!
|
||||
|
||||
|
|
@ -27,6 +29,7 @@ class MainPrefsViewController: NSViewController, MASPreferencesViewController {
|
|||
startAtLogin.state = prefs.bool(forKey: Utils.PrefKeys.startAtLogin.rawValue) ? .on : .off
|
||||
showContrastSlider.state = prefs.bool(forKey: Utils.PrefKeys.showContrast.rawValue) ? .on : .off
|
||||
lowerContrast.state = prefs.bool(forKey: Utils.PrefKeys.lowerContrast.rawValue) ? .on : .off
|
||||
setVersionNumber()
|
||||
}
|
||||
|
||||
@IBAction func startAtLoginClicked(_ sender: NSButton) {
|
||||
|
|
@ -75,4 +78,10 @@ class MainPrefsViewController: NSViewController, MASPreferencesViewController {
|
|||
print("Toggle lower contrast after brightness state -> \(sender.state == .on ? "on" : "off")")
|
||||
#endif
|
||||
}
|
||||
|
||||
fileprivate func setVersionNumber() {
|
||||
let versionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") ?? "unknown"
|
||||
let buildNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") ?? "unknown"
|
||||
versionLabel.stringValue = "version \(versionNumber) build \(buildNumber)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,13 +112,9 @@ class Utils: NSObject {
|
|||
DispatchQueue.global(qos: .background).async {
|
||||
var val: Int?
|
||||
|
||||
for _ in 0...100 {
|
||||
if let res = getCommand(command, fromMonitor: display.identifier) {
|
||||
val = res
|
||||
break
|
||||
}
|
||||
usleep(40000)
|
||||
}
|
||||
if let res = getCommand(command, fromMonitor: display.identifier) {
|
||||
val = res
|
||||
}
|
||||
|
||||
if let val = val {
|
||||
display.saveValue(val, for: command)
|
||||
|
|
@ -128,7 +124,6 @@ class Utils: NSObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
|
|
|
|||
1
Podfile
|
|
@ -7,4 +7,5 @@ target 'MonitorControl' do
|
|||
|
||||
pod 'MediaKeyTap', :git => 'https://github.com/the0neyouseek/MediaKeyTap.git'
|
||||
pod 'MASPreferences'
|
||||
pod 'AMCoreAudio', '~> 3.2'
|
||||
end
|
||||
|
|
|
|||
12
Podfile.lock
|
|
@ -1,13 +1,16 @@
|
|||
PODS:
|
||||
- AMCoreAudio (3.2.1)
|
||||
- MASPreferences (1.3)
|
||||
- MediaKeyTap (2.1.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- AMCoreAudio (~> 3.2)
|
||||
- MASPreferences
|
||||
- MediaKeyTap (from `https://github.com/the0neyouseek/MediaKeyTap.git`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/CocoaPods/Specs.git:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- AMCoreAudio
|
||||
- MASPreferences
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
|
|
@ -16,13 +19,14 @@ EXTERNAL SOURCES:
|
|||
|
||||
CHECKOUT OPTIONS:
|
||||
MediaKeyTap:
|
||||
:commit: 2ff6e582c0c03b735d24d0800f4e43611d316e93
|
||||
:commit: 3722ad54585d931977af8152a9555e832f4000f6
|
||||
:git: https://github.com/the0neyouseek/MediaKeyTap.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AMCoreAudio: 7fa6b718dc93acc29f849d60c3ad680ae1bf07b5
|
||||
MASPreferences: c08b8622dd17b47da87669e741efd7c92e970e8c
|
||||
MediaKeyTap: b652877e9ae2d52ca4f5310fa5152945ad3f0798
|
||||
|
||||
PODFILE CHECKSUM: efa14c79ecdfeaf061bbc8ed950ee540d77f987a
|
||||
PODFILE CHECKSUM: 1b20d86a04731d82d4e8f346646d2b6b9d2db778
|
||||
|
||||
COCOAPODS: 1.5.0
|
||||
COCOAPODS: 1.5.3
|
||||
|
|
|
|||
63
README.md
|
|
@ -1,13 +1,45 @@
|
|||
# MonitorControl
|
||||
<h1 align="center"> MonitorControl </h1>
|
||||
|
||||
Control your external monitor brightness, contrast or volume directly from a menulet or with keyboard native keys
|
||||
<!-- subtext -->
|
||||
<div align="center">
|
||||
Control your external monitor brightness, contrast or volume directly from a menulet or with keyboard native keys.
|
||||
</div>
|
||||
|
||||

|
||||
<br/>
|
||||
|
||||
*Bonus: Using keyboard keys display the native osd :*
|
||||
<!-- shields -->
|
||||
<div align="center">
|
||||
<!-- downloads -->
|
||||
<a href="https://github.com/the0neyouseek/MonitorControl/releases">
|
||||
<img src="https://img.shields.io/github/downloads/the0neyouseek/MonitorControl/total.svg" alt="downloads"/>
|
||||
</a>
|
||||
<!-- version -->
|
||||
<a href="https://github.com/the0neyouseek/MonitorControl/releases/latest">
|
||||
<img src="https://img.shields.io/github/release/the0neyouseek/MonitorControl.svg" alt="latest version"/>
|
||||
</a>
|
||||
<!-- license -->
|
||||
<a href="https://github.com/the0neyouseek/MonitorControl/blob/master/License.txt">
|
||||
<img src="https://img.shields.io/github/license/the0neyouseek/MonitorControl.svg" alt="license"/>
|
||||
</a>
|
||||
<!-- platform -->
|
||||
<a href="https://github.com/the0neyouseek/MonitorControl">
|
||||
<img src="https://img.shields.io/badge/platform-macOS-lightgrey.svg" alt="platform"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||

|
||||
<br/>
|
||||
|
||||
<div align="center">
|
||||
<img src="./.github/menulet.png" alt="menulet screenshot"/>
|
||||
<br/><br/>
|
||||
<img src="./.github/menugeneral.png" width="299" alt="general screenshot"/><img src="./.github/menukeys.png" width="299" alt="keys screenshot"/><img src="./.github/menudisplay.png" width="299" alt="display screenshot"/>
|
||||
|
||||
<br/>
|
||||
|
||||
*Bonus: Using keyboard keys displays the native osd*
|
||||
|
||||
<img src="./.github/osd.jpg" width="500" align="center" alt="osd screenshot"/>
|
||||
</div>
|
||||
|
||||
## Download
|
||||
|
||||
|
|
@ -15,24 +47,21 @@ Go to [Release](https://github.com/the0neyouseek/MonitorControl/releases/latest)
|
|||
|
||||
## How to help
|
||||
|
||||
Open [issues](./issues) if you have a question, an enhancement to suggest or a bug you've found. If you want you can fork the code yourself and submit a pull request to improve the app.
|
||||
Open [issues](https://github.com/the0neyouseek/MonitorControl/issues) if you have a question, an enhancement to suggest or a bug you've found. If you want you can fork the code yourself and submit a pull request to improve the app.
|
||||
|
||||
## How to build
|
||||
|
||||
### Required
|
||||
|
||||
- XCode
|
||||
- Xcode
|
||||
- [Cocoapods](https://cocoapods.org/)
|
||||
- [SwiftLint](https://github.com/realm/SwiftLint)
|
||||
|
||||
Download the [zip](https://github.com/the0neyouseek/MonitorControl/archive/master.zip) directly or clone the project somewhere with git
|
||||
- [Swiftlint](https://github.com/realm/SwiftLint)
|
||||
|
||||
Clone the project
|
||||
```sh
|
||||
$ git clone https://github.com/the0neyouseek/MonitorControl.git
|
||||
git clone https://github.com/the0neyouseek/MonitorControl.git --recurse-submodules
|
||||
```
|
||||
|
||||
Then download the dependencies with Cocoapods
|
||||
|
||||
```sh
|
||||
$ pod install
|
||||
```
|
||||
|
|
@ -42,13 +71,17 @@ You're all set ! Now open the `MonitorControl.xcworkspace` with Xcode
|
|||
### Third party dependencies
|
||||
|
||||
- [MediaKeyTap](https://github.com/the0neyouseek/MediaKeyTap)
|
||||
- [MASPreferences](https://github.com/shpakovski/MASPreferences)
|
||||
- [ddcctl](https://github.com/kfix/ddcctl)
|
||||
- [AMCoreAudio](https://github.com/rnine/AMCoreAudio)
|
||||
|
||||
## Support
|
||||
- macOS Sierra (`10.12`) and up.
|
||||
- Works with monitors comptaible with [@kfix/ddcctl](https://github.com/kfix/ddcctl)
|
||||
- Works with monitors compatible with [@kfix/ddcctl](https://github.com/kfix/ddcctl)
|
||||
|
||||
## Thanks
|
||||
- [@bluejamesbond](https://github.com/bluejamesbond/) (Original developer)
|
||||
- [@Tyilo](https://github.com/Tyilo/) (Fork)
|
||||
- [@Bensge](https://github.com/Bensge/) - (Used some code from his project [NativeDisplayBrightness](https://github.com/Bensge/NativeDisplayBrightness))
|
||||
- [@nhurden](https://github.com/nhurden/) (For the original MediaKeyTap)
|
||||
- [@nhurden](https://github.com/nhurden/) (For the original MediaKeyTap)
|
||||
- [@kfix](https://github.com/kfix/ddcctl) (For ddcctl)
|
||||
|
|
|
|||