diff --git a/.github/menulet.png b/.github/menulet.png
new file mode 100644
index 0000000..36ab2c1
Binary files /dev/null and b/.github/menulet.png differ
diff --git a/.github/osd.png b/.github/osd.png
new file mode 100644
index 0000000..584ad26
Binary files /dev/null and b/.github/osd.png differ
diff --git a/License.txt b/License.txt
new file mode 100644
index 0000000..f9e5824
--- /dev/null
+++ b/License.txt
@@ -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.
\ No newline at end of file
diff --git a/MonitorControl.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MonitorControl.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index ac9a2f9..0000000
--- a/MonitorControl.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/MonitorControl.OSX.xcodeproj/xcuserdata/mkurian.xcuserdatad/xcschemes/xcschememanagement.plist b/MonitorControl.OSX.xcodeproj/xcuserdata/mkurian.xcuserdatad/xcschemes/xcschememanagement.plist
deleted file mode 100644
index cfd5bfa..0000000
--- a/MonitorControl.OSX.xcodeproj/xcuserdata/mkurian.xcuserdatad/xcschemes/xcschememanagement.plist
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
- SchemeUserState
-
- MonitorControl.OSX.xcscheme
-
- orderHint
- 0
-
-
- SuppressBuildableAutocreation
-
- 56754EAA1D9A4016007BCDC5
-
- primary
-
-
-
-
-
diff --git a/MonitorControl.OSX/Bridging-Header.h b/MonitorControl.OSX/Bridging-Header.h
deleted file mode 100644
index 8c4e0fa..0000000
--- a/MonitorControl.OSX/Bridging-Header.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#import
-#include "../ddcctl/DDC.h"
diff --git a/MonitorControl.OSX.xcodeproj/project.pbxproj b/MonitorControl.xcodeproj/project.pbxproj
similarity index 55%
rename from MonitorControl.OSX.xcodeproj/project.pbxproj
rename to MonitorControl.xcodeproj/project.pbxproj
index 31fe48a..d4d679e 100644
--- a/MonitorControl.OSX.xcodeproj/project.pbxproj
+++ b/MonitorControl.xcodeproj/project.pbxproj
@@ -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 = ""; };
55359E371E2737EC002671BC /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; };
55359E381E2737EC002671BC /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
- 55359E3E1E27380B002671BC /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; };
- 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 = ""; };
+ 56754EAB1D9A4016007BCDC5 /* MonitorControl.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MonitorControl.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 56754EAE1D9A4016007BCDC5 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = ""; };
56754EB01D9A4016007BCDC5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
56754EB31D9A4016007BCDC5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
56754EB51D9A4016007BCDC5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ F091C9B21F6EA6110096FD65 /* SliderHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderHandler.swift; sourceTree = ""; };
+ F091C9B71F6EA79B0096FD65 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; };
+ F091C9B91F6EB43B0096FD65 /* fr */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = fr; path = fr.lproj/MainMenu.strings; sourceTree = ""; };
+ F091C9C21F6EB8660096FD65 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
+ F091C9C31F6EB8720096FD65 /* fr */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; };
+ F091C9C41F6EBA5A0096FD65 /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; };
+ F0A987D51F77A823009B603D /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = en; path = en.lproj/MainMenu.strings; sourceTree = ""; };
+ F0A987D61F77B290009B603D /* OSD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OSD.framework; sourceTree = ""; };
+ F0A987DA1F77B404009B603D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = ""; };
+ F0A987DC1F77B404009B603D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
+ F0A987DD1F77B404009B603D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ F0A987DF1F77B404009B603D /* SliderHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderHandler.swift; sourceTree = ""; };
+ F0A987E11F77B404009B603D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ F0A987E21F77B404009B603D /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; };
+ F0A987E31F77B404009B603D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainMenu.strings; sourceTree = ""; };
+ F0A987E41F77B404009B603D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; };
+ F0A987E51F77B404009B603D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ F0A987E61F77B404009B603D /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; };
+ F0A987E71F77B404009B603D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
/* 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 = "";
};
56754EAC1D9A4016007BCDC5 /* Products */ = {
isa = PBXGroup;
children = (
- 56754EAB1D9A4016007BCDC5 /* MonitorControl.OSX.app */,
+ 56754EAB1D9A4016007BCDC5 /* MonitorControl.app */,
);
name = Products;
sourceTree = "";
};
- 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 = "";
+ };
+ F091C9B41F6EA6180096FD65 /* Objects */ = {
+ isa = PBXGroup;
+ children = (
+ F091C9B21F6EA6110096FD65 /* SliderHandler.swift */,
+ );
+ path = Objects;
+ sourceTree = "";
+ };
+ F0A987D71F77B404009B603D /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ F0A987D81F77B404009B603D /* MonitorControl */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 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 = "";
+ };
+ F0A987DE1F77B404009B603D /* Objects */ = {
+ isa = PBXGroup;
+ children = (
+ F0A987DF1F77B404009B603D /* SliderHandler.swift */,
+ );
+ path = Objects;
sourceTree = "";
};
/* 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 = "";
+ };
+ F091C9C11F6EB8660096FD65 /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F091C9C21F6EB8660096FD65 /* en */,
+ F091C9C31F6EB8720096FD65 /* fr */,
+ );
+ name = Localizable.strings;
+ sourceTree = "";
+ };
+ F0A987D91F77B404009B603D /* MainMenu.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F0A987DA1F77B404009B603D /* en */,
+ F0A987E31F77B404009B603D /* fr */,
+ );
+ name = MainMenu.strings;
+ sourceTree = "";
+ };
+ F0A987DB1F77B404009B603D /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F0A987DC1F77B404009B603D /* en */,
+ F0A987E41F77B404009B603D /* fr */,
+ );
+ name = Localizable.strings;
+ sourceTree = "";
+ };
+ F0A987E01F77B404009B603D /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F0A987E11F77B404009B603D /* Base */,
);
name = MainMenu.xib;
sourceTree = "";
@@ -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 */,
diff --git a/MonitorControl.OSX/AppDelegate.swift b/MonitorControl/AppDelegate.swift
similarity index 61%
rename from MonitorControl.OSX/AppDelegate.swift
rename to MonitorControl/AppDelegate.swift
index 6a2d692..ccce703 100644
--- a/MonitorControl.OSX/AppDelegate.swift
+++ b/MonitorControl/AppDelegate.swift
@@ -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)
+ // Keyboard shortcut only for main screen
+ let currentDisplayId = NSScreen.main?.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as! CGDirectDisplayID
+ if (self.defaultDisplay.id != currentDisplayId) {
+ return
+ }
- if !flags.contains(NSEventModifierFlags.command) {
- return
- }
- flags.subtract(NSEventModifierFlags.command)
+ // Brightness -> Shift + Control + Alt + Command + (Up/Down)
+ // Volume -> Shift + Control + Alt + Command + (Left/Right)
+ // Mute -> Minus
- var rel = 0
- if event.keyCode == 27 {
- rel = -5
- } else if event.keyCode == 24 {
- rel = +5
- } else {
+ // 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
+
+ // 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)
@@ -161,14 +151,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
slider.minValue = 0
slider.maxValue = 100
slider.integerValue = prefs.integer(forKey: "\(command)-\(display.serial)")
- slider.action = #selector(SliderHandler.valueChanged)
+ slider.action = #selector(SliderHandler.valueChanged)
view.addSubview(label)
- view.addSubview(labelKeyCode)
view.addSubview(slider)
item.view = view
-
+
menu.addItem(item)
menu.addItem(NSMenuItem.separator())
@@ -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,19 +235,19 @@ 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)
}
}
-
+
func acquirePrivileges() {
let options: NSDictionary = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true]
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: "")
}
}
diff --git a/MonitorControl.OSX/Assets.xcassets/AppIcon.appiconset/Contents.json b/MonitorControl/Assets.xcassets/AppIcon.appiconset/Contents.json
similarity index 100%
rename from MonitorControl.OSX/Assets.xcassets/AppIcon.appiconset/Contents.json
rename to MonitorControl/Assets.xcassets/AppIcon.appiconset/Contents.json
diff --git a/MonitorControl/Assets.xcassets/Contents.json b/MonitorControl/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/MonitorControl/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/MonitorControl/Assets.xcassets/status.imageset/Contents.json b/MonitorControl/Assets.xcassets/status.imageset/Contents.json
new file mode 100644
index 0000000..719188e
--- /dev/null
+++ b/MonitorControl/Assets.xcassets/status.imageset/Contents.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/MonitorControl/Assets.xcassets/status.imageset/status.png b/MonitorControl/Assets.xcassets/status.imageset/status.png
new file mode 100644
index 0000000..44e7552
Binary files /dev/null and b/MonitorControl/Assets.xcassets/status.imageset/status.png differ
diff --git a/MonitorControl/Assets.xcassets/status.imageset/status@2x.png b/MonitorControl/Assets.xcassets/status.imageset/status@2x.png
new file mode 100644
index 0000000..cd07318
Binary files /dev/null and b/MonitorControl/Assets.xcassets/status.imageset/status@2x.png differ
diff --git a/MonitorControl.OSX/Base.lproj/MainMenu.xib b/MonitorControl/Base.lproj/MainMenu.xib
similarity index 83%
rename from MonitorControl.OSX/Base.lproj/MainMenu.xib
rename to MonitorControl/Base.lproj/MainMenu.xib
index 4aaf92c..d55f9e9 100644
--- a/MonitorControl.OSX/Base.lproj/MainMenu.xib
+++ b/MonitorControl/Base.lproj/MainMenu.xib
@@ -1,8 +1,8 @@
-
-
+
+
-
+
@@ -12,7 +12,7 @@
-
+
diff --git a/MonitorControl/Bridging-Header.h b/MonitorControl/Bridging-Header.h
new file mode 100644
index 0000000..7d73d33
--- /dev/null
+++ b/MonitorControl/Bridging-Header.h
@@ -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
+#include "../ddcctl/DDC.h"
+#import
+
+#endif /* Bridging_Header_h */
diff --git a/MonitorControl.OSX/Info.plist b/MonitorControl/Info.plist
similarity index 88%
rename from MonitorControl.OSX/Info.plist
rename to MonitorControl/Info.plist
index 814050e..2fe6225 100644
--- a/MonitorControl.OSX/Info.plist
+++ b/MonitorControl/Info.plist
@@ -20,15 +20,17 @@
1.0
CFBundleVersion
1
+ LSApplicationCategoryType
+ public.app-category.utilities
LSMinimumSystemVersion
$(MACOSX_DEPLOYMENT_TARGET)
+ LSUIElement
+
NSHumanReadableCopyright
- Copyright © 2016 Mathew Kurian. All rights reserved.
+ MIT Licensed. 2017.
NSMainNibFile
MainMenu
NSPrincipalClass
NSApplication
- LSUIElement
-
diff --git a/MonitorControl/Objects/SliderHandler.swift b/MonitorControl/Objects/SliderHandler.swift
new file mode 100644
index 0000000..c470e8e
--- /dev/null
+++ b/MonitorControl/Objects/SliderHandler.swift
@@ -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()
+ }
+}
diff --git a/MonitorControl/Utils.swift b/MonitorControl/Utils.swift
new file mode 100644
index 0000000..15ad8db
--- /dev/null
+++ b/MonitorControl/Utils.swift
@@ -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
+ }
+}
diff --git a/MonitorControl/en.lproj/Localizable.strings b/MonitorControl/en.lproj/Localizable.strings
new file mode 100644
index 0000000..02e8298
Binary files /dev/null and b/MonitorControl/en.lproj/Localizable.strings differ
diff --git a/MonitorControl/en.lproj/MainMenu.strings b/MonitorControl/en.lproj/MainMenu.strings
new file mode 100644
index 0000000..bcd1e86
--- /dev/null
+++ b/MonitorControl/en.lproj/MainMenu.strings
@@ -0,0 +1,3 @@
+
+/* Class = "NSMenuItem"; title = "Quit"; ObjectID = "JTa-2I-AsI"; */
+"JTa-2I-AsI.title" = "Quit";
diff --git a/MonitorControl/fr.lproj/Localizable.strings b/MonitorControl/fr.lproj/Localizable.strings
new file mode 100644
index 0000000..61c7556
Binary files /dev/null and b/MonitorControl/fr.lproj/Localizable.strings differ
diff --git a/MonitorControl/fr.lproj/MainMenu.strings b/MonitorControl/fr.lproj/MainMenu.strings
new file mode 100644
index 0000000..41b2039
--- /dev/null
+++ b/MonitorControl/fr.lproj/MainMenu.strings
@@ -0,0 +1,3 @@
+
+/* Class = "NSMenuItem"; title = "Quit"; ObjectID = "JTa-2I-AsI"; */
+"JTa-2I-AsI.title" = "Quitter";
diff --git a/OSD.framework/Headers/OSDManager.h b/OSD.framework/Headers/OSDManager.h
new file mode 100644
index 0000000..a067a8d
--- /dev/null
+++ b/OSD.framework/Headers/OSDManager.h
@@ -0,0 +1,21 @@
+#import "OSDUIHelperProtocol.h"
+
+@class NSXPCConnection;
+
+@interface OSDManager : NSObject
+{
+ id _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 remoteObjectProxy; // @dynamic remoteObjectProxy;
+
+@end
diff --git a/OSD.framework/Headers/OSDUIHelperProtocol.h b/OSD.framework/Headers/OSDUIHelperProtocol.h
new file mode 100644
index 0000000..a1246c0
--- /dev/null
+++ b/OSD.framework/Headers/OSDUIHelperProtocol.h
@@ -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
+
diff --git a/OSD.framework/OSD b/OSD.framework/OSD
new file mode 120000
index 0000000..ee6bfa0
--- /dev/null
+++ b/OSD.framework/OSD
@@ -0,0 +1 @@
+Versions/Current/OSD
\ No newline at end of file
diff --git a/OSD.framework/Resources b/OSD.framework/Resources
new file mode 120000
index 0000000..953ee36
--- /dev/null
+++ b/OSD.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/OSD.framework/Versions/A/OSD b/OSD.framework/Versions/A/OSD
new file mode 100755
index 0000000..58813ae
Binary files /dev/null and b/OSD.framework/Versions/A/OSD differ
diff --git a/OSD.framework/Versions/A/Resources/Info.plist b/OSD.framework/Versions/A/Resources/Info.plist
new file mode 100644
index 0000000..824b8c0
--- /dev/null
+++ b/OSD.framework/Versions/A/Resources/Info.plist
@@ -0,0 +1,46 @@
+
+
+
+
+ BuildMachineOSBuild
+ 16B2657
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ OSD
+ CFBundleIdentifier
+ com.apple.OSD
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ OSD
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ CFBundleVersion
+ 1
+ DTCompiler
+ com.apple.compilers.llvm.clang.1_0
+ DTPlatformBuild
+ 9L173x
+ DTPlatformVersion
+ GM
+ DTSDKBuild
+ 17A317
+ DTSDKName
+ macosx10.13internal
+ DTXcode
+ 0900
+ DTXcodeBuild
+ 9L173x
+ NSHumanReadableCopyright
+ Copyright © 2015 Apple Inc. All rights reserved.
+
+
diff --git a/OSD.framework/Versions/A/Resources/version.plist b/OSD.framework/Versions/A/Resources/version.plist
new file mode 100644
index 0000000..a387863
--- /dev/null
+++ b/OSD.framework/Versions/A/Resources/version.plist
@@ -0,0 +1,18 @@
+
+
+
+
+ BuildAliasOf
+ OSDFramework
+ BuildVersion
+ 487
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ ProjectName
+ OSDFramework
+ SourceVersion
+ 27000000000000
+
+
diff --git a/OSD.framework/Versions/A/_CodeSignature/CodeResources b/OSD.framework/Versions/A/_CodeSignature/CodeResources
new file mode 100644
index 0000000..cd4347d
--- /dev/null
+++ b/OSD.framework/Versions/A/_CodeSignature/CodeResources
@@ -0,0 +1,139 @@
+
+
+
+
+ files
+
+ Resources/Info.plist
+
+ bTy7OXKIr2tY7ToPw28ekz1xUXU=
+
+ Resources/version.plist
+
+ d0I/dBV8v16urCBanZt9RaZvG1E=
+
+
+ files2
+
+ Resources/Info.plist
+
+ hash2
+
+ uEmRq0D23jBsIWK+0+UH3bCcn16eQdAwKWglrQbTfQc=
+
+
+ Resources/version.plist
+
+ hash2
+
+ f4xR2tymy1G7xEyxX1+yXJmSgOrrndsypu67avrQ8Ss=
+
+
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/OSD.framework/Versions/Current b/OSD.framework/Versions/Current
new file mode 120000
index 0000000..8c7e5a6
--- /dev/null
+++ b/OSD.framework/Versions/Current
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/OSD.framework/XPCServices b/OSD.framework/XPCServices
new file mode 120000
index 0000000..99c46ea
--- /dev/null
+++ b/OSD.framework/XPCServices
@@ -0,0 +1 @@
+Versions/Current/XPCServices
\ No newline at end of file
diff --git a/README.md b/README.md
index 2371d87..fa9ae5f 100644
--- a/README.md
+++ b/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))
\ No newline at end of file