mirror of
https://github.com/MonitorControl/MonitorControl.git
synced 2026-05-15 14:15:55 -06:00
🎉 1.0
- Code rework - Native OSD - Change default shortcuts for problems with apps - Codesign app Signed-off-by: Guillaume Broder <iamnotheoneyouseek@gmail.com>
This commit is contained in:
parent
38137e84e9
commit
036a29d4bc
33 changed files with 689 additions and 158 deletions
BIN
.github/menulet.png
vendored
Normal file
BIN
.github/menulet.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
.github/osd.png
vendored
Normal file
BIN
.github/osd.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
21
License.txt
Normal file
21
License.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright © 2017
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:MonitorControl.OSX.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>MonitorControl.OSX.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>56754EAA1D9A4016007BCDC5</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#include "../ddcctl/DDC.h"
|
||||
|
|
@ -12,6 +12,10 @@
|
|||
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 */; };
|
||||
F091C9B31F6EA6110096FD65 /* SliderHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F091C9B21F6EA6110096FD65 /* SliderHandler.swift */; };
|
||||
F091C9B81F6EA79B0096FD65 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F091C9B71F6EA79B0096FD65 /* Utils.swift */; };
|
||||
F0A987E81F77B40E009B603D /* OSD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0A987D61F77B290009B603D /* OSD.framework */; };
|
||||
F0EB972F1F6ED7C800686D2A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F091C9C11F6EB8660096FD65 /* Localizable.strings */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
|
|
@ -21,12 +25,30 @@
|
|||
55359E361E2737EC002671BC /* ddcctl.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = ddcctl.sh; sourceTree = "<group>"; };
|
||||
55359E371E2737EC002671BC /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
|
||||
55359E381E2737EC002671BC /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
55359E3E1E27380B002671BC /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
56754EAB1D9A4016007BCDC5 /* MonitorControl.OSX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MonitorControl.OSX.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
56754EAB1D9A4016007BCDC5 /* MonitorControl.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MonitorControl.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
F091C9B21F6EA6110096FD65 /* SliderHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderHandler.swift; sourceTree = "<group>"; };
|
||||
F091C9B71F6EA79B0096FD65 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
|
||||
F091C9B91F6EB43B0096FD65 /* fr */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = fr; path = fr.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
F091C9C21F6EB8660096FD65 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F091C9C31F6EB8720096FD65 /* fr */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F091C9C41F6EBA5A0096FD65 /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
F0A987D51F77A823009B603D /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
F0A987D61F77B290009B603D /* OSD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OSD.framework; sourceTree = "<group>"; };
|
||||
F0A987DA1F77B404009B603D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
F0A987DC1F77B404009B603D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F0A987DD1F77B404009B603D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
F0A987DF1F77B404009B603D /* SliderHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderHandler.swift; sourceTree = "<group>"; };
|
||||
F0A987E11F77B404009B603D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
F0A987E21F77B404009B603D /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
|
||||
F0A987E31F77B404009B603D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
F0A987E41F77B404009B603D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
F0A987E51F77B404009B603D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
F0A987E61F77B404009B603D /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
F0A987E71F77B404009B603D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -34,6 +56,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F0A987E81F77B40E009B603D /* OSD.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -56,38 +79,83 @@
|
|||
56754EA21D9A4016007BCDC5 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
56754EAD1D9A4016007BCDC5 /* MonitorControl.OSX */,
|
||||
56754EAD1D9A4016007BCDC5 /* MonitorControl */,
|
||||
F0A987D61F77B290009B603D /* OSD.framework */,
|
||||
55359E321E2737EC002671BC /* ddcctl */,
|
||||
56754EAC1D9A4016007BCDC5 /* Products */,
|
||||
F0A987D71F77B404009B603D /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
56754EAC1D9A4016007BCDC5 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
56754EAB1D9A4016007BCDC5 /* MonitorControl.OSX.app */,
|
||||
56754EAB1D9A4016007BCDC5 /* MonitorControl.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
56754EAD1D9A4016007BCDC5 /* MonitorControl.OSX */ = {
|
||||
56754EAD1D9A4016007BCDC5 /* MonitorControl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */,
|
||||
F091C9B71F6EA79B0096FD65 /* Utils.swift */,
|
||||
F091C9B41F6EA6180096FD65 /* Objects */,
|
||||
F091C9C41F6EBA5A0096FD65 /* Bridging-Header.h */,
|
||||
56754EB01D9A4016007BCDC5 /* Assets.xcassets */,
|
||||
56754EB21D9A4016007BCDC5 /* MainMenu.xib */,
|
||||
F091C9C11F6EB8660096FD65 /* Localizable.strings */,
|
||||
56754EB51D9A4016007BCDC5 /* Info.plist */,
|
||||
55359E3E1E27380B002671BC /* Bridging-Header.h */,
|
||||
);
|
||||
path = MonitorControl.OSX;
|
||||
path = MonitorControl;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F091C9B41F6EA6180096FD65 /* Objects */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F091C9B21F6EA6110096FD65 /* SliderHandler.swift */,
|
||||
);
|
||||
path = Objects;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F0A987D71F77B404009B603D /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F0A987D81F77B404009B603D /* MonitorControl */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F0A987D81F77B404009B603D /* MonitorControl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F0A987D91F77B404009B603D /* MainMenu.strings */,
|
||||
F0A987DB1F77B404009B603D /* Localizable.strings */,
|
||||
F0A987DD1F77B404009B603D /* Assets.xcassets */,
|
||||
F0A987DE1F77B404009B603D /* Objects */,
|
||||
F0A987E01F77B404009B603D /* MainMenu.xib */,
|
||||
F0A987E21F77B404009B603D /* Utils.swift */,
|
||||
F0A987E51F77B404009B603D /* AppDelegate.swift */,
|
||||
F0A987E61F77B404009B603D /* Bridging-Header.h */,
|
||||
F0A987E71F77B404009B603D /* Info.plist */,
|
||||
);
|
||||
path = MonitorControl;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F0A987DE1F77B404009B603D /* Objects */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F0A987DF1F77B404009B603D /* SliderHandler.swift */,
|
||||
);
|
||||
path = Objects;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
56754EAA1D9A4016007BCDC5 /* MonitorControl.OSX */ = {
|
||||
56754EAA1D9A4016007BCDC5 /* MonitorControl */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 56754EB81D9A4016007BCDC5 /* Build configuration list for PBXNativeTarget "MonitorControl.OSX" */;
|
||||
buildConfigurationList = 56754EB81D9A4016007BCDC5 /* Build configuration list for PBXNativeTarget "MonitorControl" */;
|
||||
buildPhases = (
|
||||
56754EA71D9A4016007BCDC5 /* Sources */,
|
||||
56754EA81D9A4016007BCDC5 /* Frameworks */,
|
||||
|
|
@ -97,9 +165,9 @@
|
|||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = MonitorControl.OSX;
|
||||
name = MonitorControl;
|
||||
productName = MonitorControl.OSX;
|
||||
productReference = 56754EAB1D9A4016007BCDC5 /* MonitorControl.OSX.app */;
|
||||
productReference = 56754EAB1D9A4016007BCDC5 /* MonitorControl.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
|
@ -109,29 +177,31 @@
|
|||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0800;
|
||||
LastUpgradeCheck = 0820;
|
||||
LastUpgradeCheck = 0900;
|
||||
ORGANIZATIONNAME = "Mathew Kurian";
|
||||
TargetAttributes = {
|
||||
56754EAA1D9A4016007BCDC5 = {
|
||||
CreatedOnToolsVersion = 8.0;
|
||||
LastSwiftMigration = 0900;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 56754EA61D9A4016007BCDC5 /* Build configuration list for PBXProject "MonitorControl.OSX" */;
|
||||
buildConfigurationList = 56754EA61D9A4016007BCDC5 /* Build configuration list for PBXProject "MonitorControl" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
fr,
|
||||
);
|
||||
mainGroup = 56754EA21D9A4016007BCDC5;
|
||||
productRefGroup = 56754EAC1D9A4016007BCDC5 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
56754EAA1D9A4016007BCDC5 /* MonitorControl.OSX */,
|
||||
56754EAA1D9A4016007BCDC5 /* MonitorControl */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
|
@ -142,6 +212,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
56754EB11D9A4016007BCDC5 /* Assets.xcassets in Resources */,
|
||||
F0EB972F1F6ED7C800686D2A /* Localizable.strings in Resources */,
|
||||
55359E3B1E2737EC002671BC /* ddcctl.sh in Resources */,
|
||||
56754EB41D9A4016007BCDC5 /* MainMenu.xib in Resources */,
|
||||
);
|
||||
|
|
@ -154,8 +225,10 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F091C9B31F6EA6110096FD65 /* SliderHandler.swift in Sources */,
|
||||
56754EAF1D9A4016007BCDC5 /* AppDelegate.swift in Sources */,
|
||||
55359E391E2737EC002671BC /* DDC.c in Sources */,
|
||||
F091C9B81F6EA79B0096FD65 /* Utils.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -166,6 +239,43 @@
|
|||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
56754EB31D9A4016007BCDC5 /* Base */,
|
||||
F091C9B91F6EB43B0096FD65 /* fr */,
|
||||
F0A987D51F77A823009B603D /* en */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F091C9C11F6EB8660096FD65 /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F091C9C21F6EB8660096FD65 /* en */,
|
||||
F091C9C31F6EB8720096FD65 /* fr */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F0A987D91F77B404009B603D /* MainMenu.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F0A987DA1F77B404009B603D /* en */,
|
||||
F0A987E31F77B404009B603D /* fr */,
|
||||
);
|
||||
name = MainMenu.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F0A987DB1F77B404009B603D /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F0A987DC1F77B404009B603D /* en */,
|
||||
F0A987E41F77B404009B603D /* fr */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F0A987E01F77B404009B603D /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F0A987E11F77B404009B603D /* Base */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -177,12 +287,15 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
|
|
@ -190,7 +303,11 @@
|
|||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVES = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
|
|
@ -200,6 +317,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/**";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
|
@ -227,12 +345,15 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
|
|
@ -240,7 +361,11 @@
|
|||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVES = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
|
|
@ -250,6 +375,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/**";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
|
@ -269,13 +395,18 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = MonitorControl.OSX/Info.plist;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/**";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/MonitorControl/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "bluejamesbond.MonitorControl-OSX";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl.OSX/Bridging-Header.h";
|
||||
SWIFT_VERSION = 3.0;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -283,20 +414,25 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = MonitorControl.OSX/Info.plist;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/**";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/MonitorControl/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "bluejamesbond.MonitorControl-OSX";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl.OSX/Bridging-Header.h";
|
||||
SWIFT_VERSION = 3.0;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
56754EA61D9A4016007BCDC5 /* Build configuration list for PBXProject "MonitorControl.OSX" */ = {
|
||||
56754EA61D9A4016007BCDC5 /* Build configuration list for PBXProject "MonitorControl" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
56754EB61D9A4016007BCDC5 /* Debug */,
|
||||
|
|
@ -305,7 +441,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
56754EB81D9A4016007BCDC5 /* Build configuration list for PBXNativeTarget "MonitorControl.OSX" */ = {
|
||||
56754EB81D9A4016007BCDC5 /* Build configuration list for PBXNativeTarget "MonitorControl" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
56754EB91D9A4016007BCDC5 /* Debug */,
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
//
|
||||
// AppDelegate.swift
|
||||
// MonitorControl.OSX
|
||||
// MonitorControl
|
||||
//
|
||||
// Created by Mathew Kurian on 9/26/16.
|
||||
// Copyright © 2016 Mathew Kurian. All rights reserved.
|
||||
// Last edited by Guillaume Broder on 9/17/2017
|
||||
// MIT Licensed. 2017.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
|
@ -18,47 +19,13 @@ struct Display {
|
|||
var app: AppDelegate! = nil
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
func ddcctl(monitor: CGDirectDisplayID, command: Int32, value: Int) {
|
||||
var wrcmd = DDCWriteCommand(control_id: UInt8(command), new_value: UInt8(value))
|
||||
DDCWrite(monitor, &wrcmd)
|
||||
print(value)
|
||||
}
|
||||
|
||||
class SliderHandler : NSObject {
|
||||
var display : Display
|
||||
var command : Int32 = 0
|
||||
|
||||
public init(display: Display, command: Int32) {
|
||||
self.display = display
|
||||
self.command = command
|
||||
}
|
||||
|
||||
func valueChanged(slider: NSSlider) {
|
||||
let snapInterval = 25
|
||||
let snapThreshold = 3
|
||||
|
||||
var value = slider.integerValue
|
||||
|
||||
let closest = (value + snapInterval / 2) / snapInterval * snapInterval
|
||||
if abs(closest - value) <= snapThreshold {
|
||||
value = closest
|
||||
slider.integerValue = value
|
||||
}
|
||||
|
||||
ddcctl(monitor: display.id, command: command, value: value)
|
||||
|
||||
prefs.setValue(value, forKey: "\(command)-\(display.serial)")
|
||||
prefs.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@IBOutlet weak var statusMenu: NSMenu!
|
||||
@IBOutlet weak var window: NSWindow!
|
||||
|
||||
let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)
|
||||
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||
|
||||
var monitorItems: [NSMenuItem] = []
|
||||
var displays: [Display] = []
|
||||
|
|
@ -68,14 +35,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
var defaultBrightnessSlider: NSSlider! = nil
|
||||
var defaultVolumeSlider: NSSlider! = nil
|
||||
|
||||
let step = 100/16;
|
||||
|
||||
@IBAction func quitClicked(_ sender: AnyObject) {
|
||||
NSApplication.shared().terminate(self)
|
||||
NSApplication.shared.terminate(self)
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
app = self
|
||||
|
||||
statusItem.title = "♨"
|
||||
statusItem.image = NSImage.init(named: NSImage.Name(rawValue: "status"))
|
||||
statusItem.menu = statusMenu
|
||||
|
||||
acquirePrivileges()
|
||||
|
|
@ -84,39 +53,63 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
updateDisplays()
|
||||
|
||||
NSEvent.addGlobalMonitorForEvents(
|
||||
matching: NSEventMask.keyDown, handler: {(event: NSEvent) in
|
||||
matching: NSEvent.EventTypeMask.keyDown, handler: {(event: NSEvent) in
|
||||
if self.defaultDisplay == nil {
|
||||
return
|
||||
}
|
||||
|
||||
let modifiers = NSEventModifierFlags.init(rawValue: NSEventModifierFlags.command.rawValue |
|
||||
NSEventModifierFlags.control.rawValue |
|
||||
NSEventModifierFlags.option.rawValue |
|
||||
NSEventModifierFlags.shift.rawValue)
|
||||
var flags = event.modifierFlags.intersection(modifiers)
|
||||
|
||||
if !flags.contains(NSEventModifierFlags.command) {
|
||||
// Keyboard shortcut only for main screen
|
||||
let currentDisplayId = NSScreen.main?.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as! CGDirectDisplayID
|
||||
if (self.defaultDisplay.id != currentDisplayId) {
|
||||
return
|
||||
}
|
||||
flags.subtract(NSEventModifierFlags.command)
|
||||
|
||||
// Brightness -> Shift + Control + Alt + Command + (Up/Down)
|
||||
// Volume -> Shift + Control + Alt + Command + (Left/Right)
|
||||
// Mute -> Minus
|
||||
|
||||
// Capture keys
|
||||
let modifiers = NSEvent.ModifierFlags.init(rawValue: NSEvent.ModifierFlags.shift.rawValue | NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.control.rawValue | NSEvent.ModifierFlags.option.rawValue)
|
||||
let flags = event.modifierFlags.intersection(modifiers)
|
||||
|
||||
// Only do something if all modifiers are active
|
||||
if !flags.contains(NSEvent.ModifierFlags.shift) || !flags.contains(NSEvent.ModifierFlags.command) || !flags.contains(NSEvent.ModifierFlags.control) || !flags.contains(NSEvent.ModifierFlags.option) {
|
||||
return
|
||||
}
|
||||
|
||||
var brightnessRel = 0
|
||||
var volumeRel = 0
|
||||
var rel = 0
|
||||
if event.keyCode == 27 {
|
||||
rel = -5
|
||||
} else if event.keyCode == 24 {
|
||||
rel = +5
|
||||
|
||||
// Down key
|
||||
if event.keyCode == Utils.key.keyDownArrow.rawValue {
|
||||
brightnessRel = -self.step
|
||||
// Up key
|
||||
} else if event.keyCode == Utils.key.keyUpArrow.rawValue {
|
||||
brightnessRel = +self.step
|
||||
// Left key
|
||||
} else if event.keyCode == Utils.key.keyLeftArrow.rawValue {
|
||||
volumeRel = -self.step
|
||||
// Right key
|
||||
} else if event.keyCode == Utils.key.keyRightArrow.rawValue {
|
||||
volumeRel = +self.step
|
||||
// M key
|
||||
} else if event.keyCode == Utils.key.keyMute.rawValue {
|
||||
volumeRel = -100
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
var command = Int32()
|
||||
var slider: NSSlider! = nil
|
||||
if flags == NSEventModifierFlags.option {
|
||||
if brightnessRel == 0 {
|
||||
command = AUDIO_SPEAKER_VOLUME
|
||||
slider = self.defaultVolumeSlider
|
||||
} else if flags == NSEventModifierFlags.shift {
|
||||
rel = volumeRel
|
||||
} else if volumeRel == 0 {
|
||||
command = BRIGHTNESS
|
||||
slider = self.defaultBrightnessSlider
|
||||
rel = brightnessRel
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
|
@ -128,30 +121,27 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
prefs.synchronize()
|
||||
slider.intValue = Int32(value)
|
||||
|
||||
ddcctl(monitor: self.defaultDisplay.id, command: command, value: value)
|
||||
Utils.ddcctl(monitor: self.defaultDisplay.id, command: command, value: value)
|
||||
|
||||
// OSD
|
||||
let manager : OSDManager = OSDManager.sharedManager() as! OSDManager
|
||||
var osdImage : Int = 1 // Brightness Image
|
||||
if brightnessRel == 0 {
|
||||
osdImage = 3 // Speaker image
|
||||
if value == 0 {
|
||||
osdImage = 4 // Mute speaker
|
||||
}
|
||||
}
|
||||
manager.showImage(Int64(osdImage), onDisplayID: self.defaultDisplay.id, priority: 0x1f4, msecUntilFade: 2000, filledChiclets: UInt32(value/self.step), totalChiclets: UInt32(100/self.step), locked: false)
|
||||
})
|
||||
}
|
||||
|
||||
func makeLabel(text: String, frame: NSRect) -> NSTextField {
|
||||
let label = NSTextField(frame: frame)
|
||||
label.stringValue = text
|
||||
label.isBordered = false
|
||||
label.isBezeled = false
|
||||
label.isEditable = false
|
||||
label.drawsBackground = false
|
||||
return label
|
||||
}
|
||||
|
||||
func addSliderItem(menu: NSMenu, isDefaultDisplay: Bool, display: Display, command: Int32, title: String, shortcut: String) -> NSSlider {
|
||||
func addSliderItem(menu: NSMenu, isDefaultDisplay: Bool, display: Display, command: Int32, title: String) -> NSSlider {
|
||||
let item = NSMenuItem()
|
||||
|
||||
let view = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 40))
|
||||
|
||||
let label = makeLabel(text: title, frame: NSRect(x: 20, y: 19, width: 130, height: 20))
|
||||
|
||||
let labelKeyCode = makeLabel(text: shortcut, frame: NSRect(x: 120, y: 19, width: 100, height: 20))
|
||||
labelKeyCode.isHidden = !isDefaultDisplay
|
||||
labelKeyCode.alignment = NSTextAlignment.right
|
||||
let label = Utils.makeLabel(text: title, frame: NSRect(x: 20, y: 19, width: 130, height: 20))
|
||||
|
||||
let handler = SliderHandler(display: display, command: command)
|
||||
sliderHandlers.append(handler)
|
||||
|
|
@ -164,7 +154,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
slider.action = #selector(SliderHandler.valueChanged)
|
||||
|
||||
view.addSubview(label)
|
||||
view.addSubview(labelKeyCode)
|
||||
view.addSubview(slider)
|
||||
|
||||
item.view = view
|
||||
|
|
@ -183,14 +172,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
for m in monitorItems {
|
||||
statusMenu.removeItem(m)
|
||||
}
|
||||
|
||||
monitorItems = []
|
||||
displays = []
|
||||
sliderHandlers = []
|
||||
|
||||
sleep(1)
|
||||
|
||||
for s in NSScreen.screens()! {
|
||||
let id = s.deviceDescription["NSScreenNumber"] as! CGDirectDisplayID
|
||||
for s in NSScreen.screens {
|
||||
let id = s.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as! CGDirectDisplayID
|
||||
if CGDisplayIsBuiltin(id) != 0 {
|
||||
continue
|
||||
}
|
||||
|
|
@ -211,17 +201,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
let monitorMenuItem = NSMenuItem()
|
||||
let monitorSubMenu = NSMenu()
|
||||
|
||||
let brightnessSlider = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: BRIGHTNESS, title: "Brightness", shortcut: "⇧⌘- / ⇧⌘+")
|
||||
let _ = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: CONTRAST, title: "Contrast", shortcut: "")
|
||||
let volumeSlider = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: AUDIO_SPEAKER_VOLUME, title: "Volume", shortcut: "⌥⌘- / ⌥⌘+")
|
||||
|
||||
let brightnessSlider = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: BRIGHTNESS, title: NSLocalizedString("Brightness", comment: "Sown in menu"))
|
||||
let _ = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: CONTRAST, title: NSLocalizedString("Contrast", comment: "Shown in menu"))
|
||||
let volumeSlider = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: AUDIO_SPEAKER_VOLUME, title: NSLocalizedString("Volume", comment: "Shown in menu"))
|
||||
|
||||
let defaultMonitorItem = NSMenuItem()
|
||||
let defaultMonitorView = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 25))
|
||||
|
||||
let defaultMonitorSelectButtom = NSButton(frame: NSRect(x: 25, y: 0, width: 200, height: 25))
|
||||
defaultMonitorSelectButtom.title = isDefaultDisplay ? "Default" : "Set as default"
|
||||
defaultMonitorSelectButtom.bezelStyle = NSRoundRectBezelStyle
|
||||
defaultMonitorSelectButtom.title = isDefaultDisplay ? NSLocalizedString("Default", comment: "Shown in menu") : NSLocalizedString("Set as default", comment: "Shown in menu")
|
||||
defaultMonitorSelectButtom.bezelStyle = NSButton.BezelStyle.rounded
|
||||
defaultMonitorSelectButtom.isEnabled = !isDefaultDisplay
|
||||
|
||||
defaultMonitorView.addSubview(defaultMonitorSelectButtom)
|
||||
|
|
@ -246,7 +235,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
if defaultDisplay == nil {
|
||||
// If no DDC capable display was detected
|
||||
let item = NSMenuItem()
|
||||
item.title = "No supported display found"
|
||||
item.title = NSLocalizedString("No supported display found", comment: "Shown in menu")
|
||||
item.isEnabled = false
|
||||
monitorItems.append(item)
|
||||
statusMenu.insertItem(item, at: 0)
|
||||
|
|
@ -258,7 +247,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
let accessibilityEnabled = AXIsProcessTrustedWithOptions(options)
|
||||
|
||||
if !accessibilityEnabled {
|
||||
print("You need to enable the keylogger in the System Prefrences")
|
||||
print(NSLocalizedString("You need to enable the keylogger in the System Prefrences for the keyboard shortcuts to work", comment: ""))
|
||||
}
|
||||
|
||||
return
|
||||
|
|
@ -293,10 +282,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
}
|
||||
|
||||
func getDisplayName(_ edid: EDID) -> String {
|
||||
return getDescriptorString(edid, 0xFC) ?? "Display"
|
||||
return getDescriptorString(edid, 0xFC) ?? NSLocalizedString("Display", comment: "")
|
||||
}
|
||||
|
||||
func getDisplaySerial(_ edid: EDID) -> String {
|
||||
return getDescriptorString(edid, 0xFF) ?? "Unknown"
|
||||
return getDescriptorString(edid, 0xFF) ?? NSLocalizedString("Unknown", comment: "")
|
||||
}
|
||||
}
|
||||
6
MonitorControl/Assets.xcassets/Contents.json
Normal file
6
MonitorControl/Assets.xcassets/Contents.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
25
MonitorControl/Assets.xcassets/status.imageset/Contents.json
vendored
Normal file
25
MonitorControl/Assets.xcassets/status.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "status.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "status@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
BIN
MonitorControl/Assets.xcassets/status.imageset/status.png
vendored
Normal file
BIN
MonitorControl/Assets.xcassets/status.imageset/status.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
MonitorControl/Assets.xcassets/status.imageset/status@2x.png
vendored
Normal file
BIN
MonitorControl/Assets.xcassets/status.imageset/status@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13196" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11201"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13196"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="LG_Monitor_Control_Menulet" customModuleProvider="target">
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="MonitorControl" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="quitClicked" destination="JTa-2I-AsI" id="pCf-S9-cfs"/>
|
||||
<outlet property="statusMenu" destination="lCi-vw-mwp" id="iBf-iw-RcA"/>
|
||||
16
MonitorControl/Bridging-Header.h
Normal file
16
MonitorControl/Bridging-Header.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Bridging-Header.h
|
||||
// MonitorControl
|
||||
//
|
||||
// Created by Guillaume BRODER on 17/09/2017.
|
||||
// MIT Licensed. 2017.
|
||||
//
|
||||
|
||||
#ifndef Bridging_Header_h
|
||||
#define Bridging_Header_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include "../ddcctl/DDC.h"
|
||||
#import <OSD/OSDManager.h>
|
||||
|
||||
#endif /* Bridging_Header_h */
|
||||
|
|
@ -20,15 +20,17 @@
|
|||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2016 Mathew Kurian. All rights reserved.</string>
|
||||
<string>MIT Licensed. 2017.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
37
MonitorControl/Objects/SliderHandler.swift
Normal file
37
MonitorControl/Objects/SliderHandler.swift
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// SliderHandler.swift
|
||||
// MonitorControl
|
||||
//
|
||||
// Created by Guillaume BRODER on 9/17/2017.
|
||||
// MIT Licensed. 2017.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class SliderHandler: NSObject {
|
||||
var display : Display
|
||||
var command : Int32 = 0
|
||||
|
||||
public init(display: Display, command: Int32) {
|
||||
self.display = display
|
||||
self.command = command
|
||||
}
|
||||
|
||||
@objc func valueChanged(slider: NSSlider) {
|
||||
let snapInterval = 25
|
||||
let snapThreshold = 3
|
||||
|
||||
var value = slider.integerValue
|
||||
|
||||
let closest = (value + snapInterval / 2) / snapInterval * snapInterval
|
||||
if abs(closest - value) <= snapThreshold {
|
||||
value = closest
|
||||
slider.integerValue = value
|
||||
}
|
||||
|
||||
Utils.ddcctl(monitor: display.id, command: command, value: value)
|
||||
|
||||
prefs.setValue(value, forKey: "\(command)-\(display.serial)")
|
||||
prefs.synchronize()
|
||||
}
|
||||
}
|
||||
55
MonitorControl/Utils.swift
Normal file
55
MonitorControl/Utils.swift
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// Utils.swift
|
||||
// MonitorControl
|
||||
//
|
||||
// Created by Guillaume BRODER on 9/17/2017.
|
||||
// MIT Licensed.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class Utils: NSObject {
|
||||
|
||||
/// Send command to ddcctl
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - monitor: The id of the Monitor to send the command to
|
||||
/// - command: The command to send
|
||||
/// - value: the value of the command
|
||||
static func ddcctl(monitor: CGDirectDisplayID, command: Int32, value: Int) {
|
||||
var wrcmd = DDCWriteCommand(control_id: UInt8(command), new_value: UInt8(value))
|
||||
DDCWrite(monitor, &wrcmd)
|
||||
print(value)
|
||||
}
|
||||
|
||||
/// Create a label
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - text: The text of the label
|
||||
/// - frame: The frame of the label
|
||||
/// - Returns: An `NSTextField` label
|
||||
static func makeLabel(text: String, frame: NSRect) -> NSTextField {
|
||||
let label = NSTextField(frame: frame)
|
||||
label.stringValue = text
|
||||
label.isBordered = false
|
||||
label.isBezeled = false
|
||||
label.isEditable = false
|
||||
label.drawsBackground = false
|
||||
return label
|
||||
}
|
||||
|
||||
|
||||
/// Enum for hardware independent keyCode
|
||||
///
|
||||
/// - keyLeftArrow: keyCode for the left arrow
|
||||
/// - keyRightArrow: keyCode for the right arrow
|
||||
/// - keyDownArrow: keyCode for the down arrow
|
||||
/// - keyUpArrow: keyCode for the up arrow
|
||||
enum key : Int {
|
||||
case keyLeftArrow = 123
|
||||
case keyRightArrow = 124
|
||||
case keyDownArrow = 125
|
||||
case keyUpArrow = 126
|
||||
case keyMute = 24
|
||||
}
|
||||
}
|
||||
BIN
MonitorControl/en.lproj/Localizable.strings
Normal file
BIN
MonitorControl/en.lproj/Localizable.strings
Normal file
Binary file not shown.
3
MonitorControl/en.lproj/MainMenu.strings
Normal file
3
MonitorControl/en.lproj/MainMenu.strings
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
/* Class = "NSMenuItem"; title = "Quit"; ObjectID = "JTa-2I-AsI"; */
|
||||
"JTa-2I-AsI.title" = "Quit";
|
||||
BIN
MonitorControl/fr.lproj/Localizable.strings
Normal file
BIN
MonitorControl/fr.lproj/Localizable.strings
Normal file
Binary file not shown.
3
MonitorControl/fr.lproj/MainMenu.strings
Normal file
3
MonitorControl/fr.lproj/MainMenu.strings
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
/* Class = "NSMenuItem"; title = "Quit"; ObjectID = "JTa-2I-AsI"; */
|
||||
"JTa-2I-AsI.title" = "Quitter";
|
||||
21
OSD.framework/Headers/OSDManager.h
Normal file
21
OSD.framework/Headers/OSDManager.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#import "OSDUIHelperProtocol.h"
|
||||
|
||||
@class NSXPCConnection;
|
||||
|
||||
@interface OSDManager : NSObject <OSDUIHelperProtocol>
|
||||
{
|
||||
id <OSDUIHelperProtocol> _proxyObject;
|
||||
NSXPCConnection *connection;
|
||||
}
|
||||
|
||||
+ (id)sharedManager;
|
||||
@property(retain) NSXPCConnection *connection; // @synthesize connection;
|
||||
- (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4;
|
||||
- (void)fadeClassicImageOnDisplay:(unsigned int)arg1;
|
||||
- (void)showImageAtPath:(id)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5;
|
||||
- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7;
|
||||
- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5;
|
||||
- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4;
|
||||
@property(readonly) id <OSDUIHelperProtocol> remoteObjectProxy; // @dynamic remoteObjectProxy;
|
||||
|
||||
@end
|
||||
11
OSD.framework/Headers/OSDUIHelperProtocol.h
Normal file
11
OSD.framework/Headers/OSDUIHelperProtocol.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
@class NSString;
|
||||
|
||||
@protocol OSDUIHelperProtocol
|
||||
- (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4;
|
||||
- (void)fadeClassicImageOnDisplay:(unsigned int)arg1;
|
||||
- (void)showImageAtPath:(NSString *)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5;
|
||||
- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7;
|
||||
- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5;
|
||||
- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4;
|
||||
@end
|
||||
|
||||
1
OSD.framework/OSD
Symbolic link
1
OSD.framework/OSD
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
Versions/Current/OSD
|
||||
1
OSD.framework/Resources
Symbolic link
1
OSD.framework/Resources
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
Versions/Current/Resources
|
||||
BIN
OSD.framework/Versions/A/OSD
Executable file
BIN
OSD.framework/Versions/A/OSD
Executable file
Binary file not shown.
46
OSD.framework/Versions/A/Resources/Info.plist
Normal file
46
OSD.framework/Versions/A/Resources/Info.plist
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>16B2657</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>OSD</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.OSD</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>OSD</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>9L173x</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>GM</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>17A317</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx10.13internal</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0900</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>9L173x</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015 Apple Inc. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
18
OSD.framework/Versions/A/Resources/version.plist
Normal file
18
OSD.framework/Versions/A/Resources/version.plist
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildAliasOf</key>
|
||||
<string>OSDFramework</string>
|
||||
<key>BuildVersion</key>
|
||||
<string>487</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>ProjectName</key>
|
||||
<string>OSDFramework</string>
|
||||
<key>SourceVersion</key>
|
||||
<string>27000000000000</string>
|
||||
</dict>
|
||||
</plist>
|
||||
139
OSD.framework/Versions/A/_CodeSignature/CodeResources
Normal file
139
OSD.framework/Versions/A/_CodeSignature/CodeResources
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>Resources/Info.plist</key>
|
||||
<data>
|
||||
bTy7OXKIr2tY7ToPw28ekz1xUXU=
|
||||
</data>
|
||||
<key>Resources/version.plist</key>
|
||||
<data>
|
||||
d0I/dBV8v16urCBanZt9RaZvG1E=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>Resources/Info.plist</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
uEmRq0D23jBsIWK+0+UH3bCcn16eQdAwKWglrQbTfQc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/version.plist</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
f4xR2tymy1G7xEyxX1+yXJmSgOrrndsypu67avrQ8Ss=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^Resources/</key>
|
||||
<true/>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^[^/]+$</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
1
OSD.framework/Versions/Current
Symbolic link
1
OSD.framework/Versions/Current
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
A
|
||||
1
OSD.framework/XPCServices
Symbolic link
1
OSD.framework/XPCServices
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
Versions/Current/XPCServices
|
||||
40
README.md
40
README.md
|
|
@ -1,10 +1,40 @@
|
|||
# MonitorControl.OSX
|
||||
A menu let to control your monitor (brightness, contrast, volume)
|
||||
# MonitorControl
|
||||
|
||||

|
||||
Control your external monitor brightness, contrast or volume directly from a menulet or with keyboard shortcuts :
|
||||
|
||||
Compatible with most Dell monitors and LG including 27UD68
|
||||
- Brightness: `⇧` + `⌃` + `⌥` + `⌘` + `↑/↓` (Shift + Control + Alt + Command + Up/Down arrows)
|
||||
- Volume: `⇧` + `⌃` + `⌥` + `⌘` + `←/→` (Shift + Control + Alt + Command + Left/Right arrows)
|
||||
- Mute: `⇧` + `⌃` + `⌥` + `⌘` + `-` (Shift + Control + Alt + Command + Minus)
|
||||
|
||||
(Ps. The keyboard shortcut only work for the default screen)
|
||||
|
||||

|
||||
|
||||
## Download
|
||||
|
||||
Go to [Release](./) and download the latest `.dmg`
|
||||
|
||||
## Brightness/Volume default key
|
||||
You can use [Karabiner Elements](https://github.com/tekezo/Karabiner-Elements/) to use the default mac key (`F1`, `F2` for brightness and `F10`, `F11`, `F12` for volume) with this set of custom rules : [Karabiner rules for MonitorControl](karabiner://karabiner/assets/complex_modifications/import?url=https%3A%2F%2Fraw.githubusercontent.com%2Fthe0neyouseek%2FMonitorControl%2Fmaster%2F.github%2Frules.json)
|
||||
|
||||
---
|
||||
|
||||
Powered by [@kfix/ddcctl](https://github.com/kfix/ddcctl)
|
||||
Bonus: Using keyboard shortcuts display the native osd :
|
||||
|
||||

|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Hande multiple screen for keyboard shortcut (Possibly the choice to have all screen brightness/volume increase/decrease at the same time or separatly)
|
||||
- [ ] Skip Karabiner use for keyboard shortcut
|
||||
- [ ] Option to start app at login
|
||||
- [ ] Add [SwiftLint](https://github.com/realm/SwiftLint)
|
||||
|
||||
## Support
|
||||
- macOS Sierra (`10.12`) and up.
|
||||
- Works with monitors comptaible with [@kfix/ddcctl](https://github.com/kfix/ddcctl)
|
||||
|
||||
## Thanks
|
||||
- @bluejamesbond (Original developer)
|
||||
- @Tyilo (Fork)
|
||||
- @Bensge - (Used some code from his project [NativeDisplayBrightness](https://github.com/Bensge/NativeDisplayBrightness))
|
||||
Loading…
Add table
Add a link
Reference in a new issue