diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09dfede --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ diff --git a/Info/Menu.png b/Info/Menu.png new file mode 100644 index 0000000..91bd275 Binary files /dev/null and b/Info/Menu.png differ diff --git a/Info/Settings.png b/Info/Settings.png new file mode 100644 index 0000000..196b540 Binary files /dev/null and b/Info/Settings.png differ diff --git a/Info/nativeUI.png b/Info/nativeUI.png new file mode 100644 index 0000000..e754be8 Binary files /dev/null and b/Info/nativeUI.png differ diff --git a/NativeDisplayBrightness.xcodeproj/project.pbxproj b/NativeDisplayBrightness.xcodeproj/project.pbxproj index ba50f8d..2c9cf16 100644 --- a/NativeDisplayBrightness.xcodeproj/project.pbxproj +++ b/NativeDisplayBrightness.xcodeproj/project.pbxproj @@ -3,32 +3,49 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 9DBE37511DB7990900ABE422 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DBE37501DB7990900ABE422 /* AppDelegate.m */; }; 9DBE37541DB7990900ABE422 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DBE37531DB7990900ABE422 /* main.m */; }; 9DBE37561DB7990900ABE422 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9DBE37551DB7990900ABE422 /* Assets.xcassets */; }; - 9DBE37591DB7990900ABE422 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9DBE37571DB7990900ABE422 /* MainMenu.xib */; }; - 9DBE37621DB7996100ABE422 /* DDC.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DBE37601DB7996100ABE422 /* DDC.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 9DBE37621DB7996100ABE422 /* DDC.c in Sources */ = {isa = PBXBuildFile; fileRef = 9DBE37601DB7996100ABE422 /* DDC.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + F8A8C8E7201F925800F49C15 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8A8C8E6201F925800F49C15 /* CoreBrightness.framework */; }; + F8A8C8EB201F98CF00F49C15 /* ColorTemperatureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A8C8E9201F98CF00F49C15 /* ColorTemperatureViewController.m */; }; + F8A8C8EC201F98CF00F49C15 /* ColorTemperatureViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F8A8C8EA201F98CF00F49C15 /* ColorTemperatureViewController.xib */; }; + F8DB0B58201DE52F00991F83 /* Readme.md in Resources */ = {isa = PBXBuildFile; fileRef = F8DB0B57201DE52F00991F83 /* Readme.md */; }; + F8DB0B61201DEAF300991F83 /* SettingsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DB0B5F201DEAF300991F83 /* SettingsWindowController.m */; }; + F8DB0B62201DEAF300991F83 /* SettingsWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F8DB0B60201DEAF300991F83 /* SettingsWindowController.xib */; }; + F8DB0B68201E008200991F83 /* BrightnessViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DB0B66201E008200991F83 /* BrightnessViewController.m */; }; + F8DB0B69201E008200991F83 /* BrightnessViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F8DB0B67201E008200991F83 /* BrightnessViewController.xib */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 9D1F75421DBD44310039345A /* OSD.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSD.h; sourceTree = ""; }; - 9D1F75431DBD48310039345A /* CoreGraphicsPriv.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreGraphicsPriv.h; sourceTree = ""; }; 9DBE374C1DB7990900ABE422 /* NativeDisplayBrightness.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NativeDisplayBrightness.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9DBE374F1DB7990900ABE422 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 9DBE37501DB7990900ABE422 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9DBE37531DB7990900ABE422 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 9DBE37551DB7990900ABE422 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 9DBE37581DB7990900ABE422 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 9DBE375A1DB7990900ABE422 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9DBE37601DB7996100ABE422 /* DDC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDC.m; sourceTree = ""; }; + 9DBE37601DB7996100ABE422 /* DDC.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DDC.c; sourceTree = ""; }; 9DBE37611DB7996100ABE422 /* DDC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDC.h; sourceTree = ""; }; 9DBE37631DB79A4000ABE422 /* BezelServices.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BezelServices.h; sourceTree = ""; }; - 9DBE37641DB7A87200ABE422 /* Readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Readme.md; sourceTree = ""; }; - 9DBE37661DB7A9D600ABE422 /* License.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = License.txt; sourceTree = ""; }; + 9DBE37661DB7A9D600ABE422 /* License.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = License.txt; path = NativeDisplayBrightness/License.txt; sourceTree = SOURCE_ROOT; }; + F8197A22201E818A0006516C /* CBBlueLightClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBBlueLightClient.h; sourceTree = ""; }; + F8A8C8E4201F7F1600F49C15 /* Config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Config.h; sourceTree = ""; }; + F8A8C8E6201F925800F49C15 /* CoreBrightness.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBrightness.framework; path = ../../../System/Library/PrivateFrameworks/CoreBrightness.framework; sourceTree = ""; }; + F8A8C8E8201F98CF00F49C15 /* ColorTemperatureViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ColorTemperatureViewController.h; sourceTree = ""; }; + F8A8C8E9201F98CF00F49C15 /* ColorTemperatureViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ColorTemperatureViewController.m; sourceTree = ""; }; + F8A8C8EA201F98CF00F49C15 /* ColorTemperatureViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ColorTemperatureViewController.xib; sourceTree = ""; }; + F8DB0B57201DE52F00991F83 /* Readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Readme.md; sourceTree = SOURCE_ROOT; }; + F8DB0B5E201DEAF300991F83 /* SettingsWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsWindowController.h; sourceTree = ""; }; + F8DB0B5F201DEAF300991F83 /* SettingsWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsWindowController.m; sourceTree = ""; }; + F8DB0B60201DEAF300991F83 /* SettingsWindowController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsWindowController.xib; sourceTree = ""; }; + F8DB0B65201E008200991F83 /* BrightnessViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BrightnessViewController.h; sourceTree = ""; }; + F8DB0B66201E008200991F83 /* BrightnessViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BrightnessViewController.m; sourceTree = ""; }; + F8DB0B67201E008200991F83 /* BrightnessViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BrightnessViewController.xib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -36,6 +53,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F8A8C8E7201F925800F49C15 /* CoreBrightness.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -47,6 +65,7 @@ children = ( 9DBE374E1DB7990900ABE422 /* NativeDisplayBrightness */, 9DBE374D1DB7990900ABE422 /* Products */, + F8A8C8E5201F925800F49C15 /* Frameworks */, ); sourceTree = ""; }; @@ -61,19 +80,10 @@ 9DBE374E1DB7990900ABE422 /* NativeDisplayBrightness */ = { isa = PBXGroup; children = ( - 9DBE374F1DB7990900ABE422 /* AppDelegate.h */, - 9DBE37501DB7990900ABE422 /* AppDelegate.m */, - 9DBE37551DB7990900ABE422 /* Assets.xcassets */, - 9DBE37571DB7990900ABE422 /* MainMenu.xib */, - 9DBE375A1DB7990900ABE422 /* Info.plist */, - 9DBE37601DB7996100ABE422 /* DDC.m */, - 9DBE37641DB7A87200ABE422 /* Readme.md */, - 9DBE37661DB7A9D600ABE422 /* License.txt */, - 9DBE37611DB7996100ABE422 /* DDC.h */, + F8DB0B54201DE41300991F83 /* Info */, + F8DB0B56201DE44A00991F83 /* Classes */, + F8DB0B55201DE43A00991F83 /* Resources */, 9DBE37521DB7990900ABE422 /* Supporting Files */, - 9DBE37631DB79A4000ABE422 /* BezelServices.h */, - 9D1F75421DBD44310039345A /* OSD.h */, - 9D1F75431DBD48310039345A /* CoreGraphicsPriv.h */, ); path = NativeDisplayBrightness; sourceTree = ""; @@ -81,11 +91,61 @@ 9DBE37521DB7990900ABE422 /* Supporting Files */ = { isa = PBXGroup; children = ( + 9DBE375A1DB7990900ABE422 /* Info.plist */, 9DBE37531DB7990900ABE422 /* main.m */, ); name = "Supporting Files"; sourceTree = ""; }; + F8A8C8E5201F925800F49C15 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F8A8C8E6201F925800F49C15 /* CoreBrightness.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + F8DB0B54201DE41300991F83 /* Info */ = { + isa = PBXGroup; + children = ( + F8DB0B57201DE52F00991F83 /* Readme.md */, + 9DBE37661DB7A9D600ABE422 /* License.txt */, + ); + name = Info; + sourceTree = ""; + }; + F8DB0B55201DE43A00991F83 /* Resources */ = { + isa = PBXGroup; + children = ( + 9DBE37551DB7990900ABE422 /* Assets.xcassets */, + ); + name = Resources; + sourceTree = ""; + }; + F8DB0B56201DE44A00991F83 /* Classes */ = { + isa = PBXGroup; + children = ( + F8A8C8E4201F7F1600F49C15 /* Config.h */, + 9DBE374F1DB7990900ABE422 /* AppDelegate.h */, + 9DBE37501DB7990900ABE422 /* AppDelegate.m */, + 9DBE37601DB7996100ABE422 /* DDC.c */, + 9DBE37611DB7996100ABE422 /* DDC.h */, + 9DBE37631DB79A4000ABE422 /* BezelServices.h */, + 9D1F75421DBD44310039345A /* OSD.h */, + F8197A22201E818A0006516C /* CBBlueLightClient.h */, + F8DB0B5E201DEAF300991F83 /* SettingsWindowController.h */, + F8DB0B5F201DEAF300991F83 /* SettingsWindowController.m */, + F8DB0B60201DEAF300991F83 /* SettingsWindowController.xib */, + F8DB0B65201E008200991F83 /* BrightnessViewController.h */, + F8DB0B66201E008200991F83 /* BrightnessViewController.m */, + F8DB0B67201E008200991F83 /* BrightnessViewController.xib */, + F8A8C8E8201F98CF00F49C15 /* ColorTemperatureViewController.h */, + F8A8C8E9201F98CF00F49C15 /* ColorTemperatureViewController.m */, + F8A8C8EA201F98CF00F49C15 /* ColorTemperatureViewController.xib */, + ); + name = Classes; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -112,8 +172,8 @@ 9DBE37441DB7990900ABE422 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; - ORGANIZATIONNAME = "Benno Krauss"; + LastUpgradeCheck = 1330; + ORGANIZATIONNAME = "Volodymyr Klymenko"; TargetAttributes = { 9DBE374B1DB7990900ABE422 = { CreatedOnToolsVersion = 8.0; @@ -122,8 +182,8 @@ }; }; buildConfigurationList = 9DBE37471DB7990900ABE422 /* Build configuration list for PBXProject "NativeDisplayBrightness" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -144,8 +204,11 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + F8A8C8EC201F98CF00F49C15 /* ColorTemperatureViewController.xib in Resources */, + F8DB0B58201DE52F00991F83 /* Readme.md in Resources */, + F8DB0B69201E008200991F83 /* BrightnessViewController.xib in Resources */, + F8DB0B62201DEAF300991F83 /* SettingsWindowController.xib in Resources */, 9DBE37561DB7990900ABE422 /* Assets.xcassets in Resources */, - 9DBE37591DB7990900ABE422 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -156,25 +219,17 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9DBE37621DB7996100ABE422 /* DDC.m in Sources */, + 9DBE37621DB7996100ABE422 /* DDC.c in Sources */, + F8DB0B68201E008200991F83 /* BrightnessViewController.m in Sources */, + F8A8C8EB201F98CF00F49C15 /* ColorTemperatureViewController.m in Sources */, 9DBE37541DB7990900ABE422 /* main.m in Sources */, + F8DB0B61201DEAF300991F83 /* SettingsWindowController.m in Sources */, 9DBE37511DB7990900ABE422 /* AppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXVariantGroup section */ - 9DBE37571DB7990900ABE422 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 9DBE37581DB7990900ABE422 /* Base */, - ); - name = MainMenu.xib; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ 9DBE375B1DB7990900ABE422 /* Debug */ = { isa = XCBuildConfiguration; @@ -267,9 +322,20 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 13; + DEVELOPMENT_TEAM = L3UA46DS39; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks"; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; INFOPLIST_FILE = NativeDisplayBrightness/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.12.4; + MARKETING_VERSION = 1.2; PRODUCT_BUNDLE_IDENTIFIER = com.bensge.NativeDisplayBrightness; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -279,9 +345,20 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 13; + DEVELOPMENT_TEAM = L3UA46DS39; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks"; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; INFOPLIST_FILE = NativeDisplayBrightness/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.12.4; + MARKETING_VERSION = 1.2; PRODUCT_BUNDLE_IDENTIFIER = com.bensge.NativeDisplayBrightness; PRODUCT_NAME = "$(TARGET_NAME)"; }; diff --git a/NativeDisplayBrightness.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/NativeDisplayBrightness.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/NativeDisplayBrightness.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/NativeDisplayBrightness.xcodeproj/xcuserdata/bennokrauss.xcuserdatad/xcschemes/NativeDisplayBrightness.xcscheme b/NativeDisplayBrightness.xcodeproj/xcshareddata/xcschemes/NativeDisplayBrightness.xcscheme similarity index 96% rename from NativeDisplayBrightness.xcodeproj/xcuserdata/bennokrauss.xcuserdatad/xcschemes/NativeDisplayBrightness.xcscheme rename to NativeDisplayBrightness.xcodeproj/xcshareddata/xcschemes/NativeDisplayBrightness.xcscheme index 39ec31b..a778ee1 100644 --- a/NativeDisplayBrightness.xcodeproj/xcuserdata/bennokrauss.xcuserdatad/xcschemes/NativeDisplayBrightness.xcscheme +++ b/NativeDisplayBrightness.xcodeproj/xcshareddata/xcschemes/NativeDisplayBrightness.xcscheme @@ -1,6 +1,6 @@ - - - - + + - - - - - - SchemeUserState - - NativeDisplayBrightness.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - 9DBE374B1DB7990900ABE422 - - primary - - - - - diff --git a/NativeDisplayBrightness/AppDelegate.h b/NativeDisplayBrightness/AppDelegate.h index 249e960..2f5ea2b 100644 --- a/NativeDisplayBrightness/AppDelegate.h +++ b/NativeDisplayBrightness/AppDelegate.h @@ -7,9 +7,50 @@ // #import +#import "Config.h" +#import "CBBlueLightClient.h" + +@class BrightnessViewController; +@class ColorTemperatureViewController; @interface AppDelegate : NSObject +@property (strong) NSDictionary *keys; + +@property (strong) NSStatusItem *statusBarIcon; +@property (strong) NSMenu *statusBarMenu; +@property (strong) BrightnessViewController *brightnessView; + +@property (strong) CBBlueLightClient *blueLight; +@property (strong) ColorTemperatureViewController *colorTemperatureView; +@property (strong) NSMenuItem *colorTemperatureMenu; + +//runtime variables +@property (assign) int currentBrightness; +@property (assign) BOOL supportsBlueLightReduction; + +//Settings values +@property (nonatomic, assign) BOOL multiMonitor; +@property (nonatomic, assign) BOOL smoothStep; +@property (nonatomic, assign) BOOL showBrightness; +@property (nonatomic, assign) int maxBrightness; + +@property (nonatomic, strong) NSString *increaseBrightnessKey; +@property (nonatomic, strong) NSString *decreaseBrightnessKey; + +@property (nonatomic, assign) BOOL adjustColorTemperature; +@property (nonatomic, assign) float colorTemperatureLimit; + +@property (nonatomic, strong) NSString *colorTemperatureLessWarmKey; +@property (nonatomic, strong) NSString *colorTemperatureMoreWarmKey; + ++(BOOL)loadSavedBrightness:(uint*) savedBrightness forDisplayID:(CGDirectDisplayID) displayID; + ++(void)changeMainScreenBrightness:(int) newBrightness; ++(void)changeMainScreenBrightnessWithStep:(int) deltaInSubsteps; + ++(void)changeScreenColorTemperature:(float) colorTemperature; ++(void)changeScreenColorTemperatureStep:(float) colorTemperatureStep; @end diff --git a/NativeDisplayBrightness/AppDelegate.m b/NativeDisplayBrightness/AppDelegate.m index 34d7721..3d220d1 100644 --- a/NativeDisplayBrightness/AppDelegate.m +++ b/NativeDisplayBrightness/AppDelegate.m @@ -11,41 +11,64 @@ #import "BezelServices.h" #import "OSD.h" #include -@import Carbon; - -#pragma mark - constants - -static NSString *brightnessValuePreferenceKey = @"brightness"; -static const float brightnessStep = 100/16.f; +#import "SettingsWindowController.h" +#import "BrightnessViewController.h" +#import "ColorTemperatureViewController.h" #pragma mark - variables -void *(*_BSDoGraphicWithMeterAndTimeout)(CGDirectDisplayID arg0, BSGraphic arg1, int arg2, float v, int timeout) = NULL; +static void *(*_BSDoGraphicWithMeterAndTimeout)(CGDirectDisplayID arg0, BSGraphic arg1, int arg2, float v, int timeout) = NULL; #pragma mark - functions -void set_control(CGDirectDisplayID cdisplay, uint control_id, uint new_value) +static BOOL set_control(CGDirectDisplayID display_id, uint control_id, uint new_value) { struct DDCWriteCommand command; command.control_id = control_id; command.new_value = new_value; - if (!DDCWrite(cdisplay, &command)){ - NSLog(@"E: Failed to send DDC command!"); + BOOL isCommandOk = DDCWrite(display_id, &command); + + if (! isCommandOk){ + NSLog(@"E: Failed to send DDCWrite command to display %u!", display_id); } + + return isCommandOk; } +static BOOL get_control(CGDirectDisplayID display_id, uint control_id, uint* current_value, uint* max_value) +{ + struct DDCReadCommand command = {.control_id = control_id, .max_value = 0, .current_value = 0 }; + BOOL isCommandOk = DDCRead(display_id, &command); + + if (isCommandOk) { + if (current_value != nil) { + *current_value = command.current_value; + } + + if (max_value != nil) { + *max_value = command.max_value; + } + } + else { + NSLog(@"E: Failed to send DDCRead command to display %u!", display_id); + } + return isCommandOk; +} -CGEventRef keyboardCGEventCallback(CGEventTapProxy proxy, - CGEventType type, - CGEventRef event, - void *refcon) +static CGEventRef keyboardCGEventCallback(CGEventTapProxy proxy, + CGEventType type, + CGEventRef event, + void *refcon) { //Surpress the F1/F2 key events to prevent other applications from catching it or playing beep sound if (type == NX_KEYDOWN || type == NX_KEYUP || type == NX_FLAGSCHANGED) { int64_t keyCode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); - if (keyCode == kVK_F2 || keyCode == kVK_F1) + if (keyCode == [[APP_DELEGATE.keys valueForKey:APP_DELEGATE.increaseBrightnessKey] unsignedShortValue] || + keyCode == [[APP_DELEGATE.keys valueForKey:APP_DELEGATE.decreaseBrightnessKey] unsignedShortValue] || + keyCode == [[APP_DELEGATE.keys valueForKey:APP_DELEGATE.colorTemperatureLessWarmKey] unsignedShortValue] || + keyCode == [[APP_DELEGATE.keys valueForKey:APP_DELEGATE.colorTemperatureMoreWarmKey] unsignedShortValue]) { return NULL; } @@ -53,17 +76,35 @@ CGEventRef keyboardCGEventCallback(CGEventTapProxy proxy, return event; } +static void showBrightnessLevelPaneOnDisplay (uint brightnessLevelInSubsteps, CGDirectDisplayID displayId) +{ + if (_BSDoGraphicWithMeterAndTimeout != NULL) + { + // El Capitan and probably older systems + _BSDoGraphicWithMeterAndTimeout(displayId, BSGraphicBacklightMeter, 0x0, (float)brightnessLevelInSubsteps/(float)brightnessSubstepsCount, 1); + } + else { + // Sierra+ + [[NSClassFromString(@"OSDManager") sharedManager] showImage:OSDGraphicBacklight + onDisplayID:displayId priority:OSDPriorityDefault + msecUntilFade:1000 + filledChiclets:(float)brightnessLevelInSubsteps + totalChiclets:brightnessSubstepsCount + locked:NO]; + } + +} + + #pragma mark - AppDelegate @interface AppDelegate () @property (weak) IBOutlet NSWindow *window; -@property (nonatomic) float brightness; @property (strong, nonatomic) dispatch_source_t signalHandlerSource; @end @implementation AppDelegate -@synthesize brightness=_brightness; - (BOOL)_loadBezelServices { @@ -92,32 +133,33 @@ - (void)_configureLoginItem LSSharedFileListInsertItemURL(loginItemsListRef, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)bundleURL, (__bridge CFDictionaryRef)properties,NULL); } -- (void)_checkTrusted -{ - BOOL isTrusted = AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{(__bridge NSString *)kAXTrustedCheckOptionPrompt: @true}); - NSLog(@"istrusted: %i",isTrusted); -} - - (void)_registerGlobalKeyboardEvents { - [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyDown | NSEventMaskKeyUp handler:^(NSEvent *_Nonnull event) { - //NSLog(@"event!!"); - if (event.keyCode == kVK_F1) + [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyDown handler:^(NSEvent *_Nonnull event) { + if (event.type == NSEventTypeKeyDown) { - if (event.type == NSEventTypeKeyDown) + BOOL isOptionModifierPressed = (event.modifierFlags & NSAlternateKeyMask) != 0 || self.smoothStep; + + if ((event.keyCode == [[self.keys valueForKey:self.decreaseBrightnessKey] unsignedShortValue]) || + (event.keyCode == [[self.keys valueForKey:self.increaseBrightnessKey] unsignedShortValue])) { + // Screen brightness adjustment + int brightnessDelta = isOptionModifierPressed ? 1 : brightnessSubstepsPerStep; + if (event.keyCode == [[self.keys valueForKey:self.decreaseBrightnessKey] unsignedShortValue]) { + // default F1 = decrease brightness + brightnessDelta = -brightnessDelta; + } + dispatch_async(dispatch_get_main_queue(), ^{ - [self decreaseBrightness]; + [AppDelegate changeMainScreenBrightnessWithStep: brightnessDelta]; }); } - } - else if (event.keyCode == kVK_F2) - { - if (event.type == NSEventTypeKeyDown) - { - dispatch_async(dispatch_get_main_queue(), ^{ - [self increaseBrightness]; - }); + + if (event.keyCode == [[self.keys valueForKey:self.colorTemperatureLessWarmKey] unsignedShortValue] || + event.keyCode == [[self.keys valueForKey:self.colorTemperatureMoreWarmKey] unsignedShortValue]) { + float valueStep = COLOR_TEMPERATURE_STEP; + valueStep = (event.keyCode == [[self.keys valueForKey:self.colorTemperatureLessWarmKey] unsignedShortValue]) ? -valueStep : valueStep; + [AppDelegate changeScreenColorTemperatureStep:valueStep]; } } }]; @@ -135,32 +177,157 @@ - (void)_registerGlobalKeyboardEvents CGEventTapEnable(eventTap, true); } -- (void)_saveBrightness +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - [[NSUserDefaults standardUserDefaults] setFloat:self.brightness forKey:brightnessValuePreferenceKey]; + +//DEBUG: clean user settings if needed +// NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; +// [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain]; +// [NSApp terminate:0]; + + self.keys = @{ + @"F1" : @0x7A, + @"F2" : @0x78, + @"F3" : @0x63, + @"F4" : @0x76, + @"F5" : @0x60, + @"F6" : @0x61, + @"F7" : @0x62, + @"F8" : @0x64, + @"F9" : @0x65, + @"F10" : @0x6D, + @"F11" : @0x67, + @"F12" : @0x6F, + @"F13" : @0x69, + @"F14" : @0x6B, + @"F15" : @0x71, + @"F16" : @0x6A, + @"F17" : @0x40, + @"F18" : @0x4F, + @"F19" : @0x50, + @"F20" : @0x5A + }; + + if (![self _loadBezelServices]) + { + [self _loadOSDFramework]; + } + [self _configureLoginItem]; + [self _registerSignalHandling]; + + //Color Temperature + self.blueLight = [[CBBlueLightClient alloc] init]; + self.supportsBlueLightReduction = [CBBlueLightClient supportsBlueLightReduction]; + + //Status Bar Icon + NSStatusBar *bar = [NSStatusBar systemStatusBar]; + self.statusBarIcon = [bar statusItemWithLength:self.showBrightness ? STATUS_ICON_WIDTH_TEXT : STATUS_ICON_WIDTH]; + + NSImage *icon = [NSImage imageNamed:@"icon"]; + icon.template = YES; + self.statusBarIcon.image = icon; + self.statusBarIcon.highlightMode = YES; + + CGDirectDisplayID currentDisplayId = [NSScreen.mainScreen.deviceDescription [@"NSScreenNumber"] unsignedIntValue]; + if (! CGDisplayIsBuiltin(currentDisplayId)) { + uint loadedBrightness = 50; + uint maxBrightness = 100; + bool loadedFromSettings = [AppDelegate loadSavedBrightness:&loadedBrightness forDisplayID:currentDisplayId]; + if (!loadedFromSettings) { + NSLog(@"Settings not loaded, use monitor value: %i",loadedBrightness); + get_control(currentDisplayId, BRIGHTNESS, &loadedBrightness, &maxBrightness); + [AppDelegate saveBrightness:loadedBrightness forDisplayID:currentDisplayId]; + } + self.statusBarIcon.title = self.showBrightness ? [NSString stringWithFormat:@"%d%%",loadedBrightness] : @""; + } + + //Status Bar Menu + self.statusBarMenu = [[NSMenu alloc] init]; + + //Brightness + NSMenuItem *brightness = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + self.brightnessView = [[BrightnessViewController alloc] initWithNibName:@"BrightnessViewController" bundle:nil]; + [brightness setView:self.brightnessView.view]; + [self.statusBarMenu addItem:brightness]; + + //Separator + [self.statusBarMenu addItem: [NSMenuItem separatorItem]]; + + //Color Temperature + self.colorTemperatureMenu = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + self.colorTemperatureView = [[ColorTemperatureViewController alloc] initWithNibName:@"ColorTemperatureViewController" bundle:nil]; + [self.colorTemperatureMenu setView:self.colorTemperatureView.view]; + [self.statusBarMenu addItem:self.colorTemperatureMenu]; + + //this for some reason not work..will just remove + //self.colorTemperatureMenu.hidden = !APP_DELEGATE.adjustColorTemperature; + + if (!APP_DELEGATE.adjustColorTemperature) { + [self.statusBarMenu removeItem:self.colorTemperatureMenu]; + } else { + float curStrength; + [self.blueLight getStrength:&curStrength]; + self.colorTemperatureView.sliderColorTemperature.floatValue = curStrength; + } + + //Separator + [self.statusBarMenu addItem: [NSMenuItem separatorItem]]; + + //Settings + NSMenuItem *settings = [[NSMenuItem alloc] initWithTitle:@"Settings..." + action:@selector(showSettings) + keyEquivalent:@""]; + settings.target = self; + [self.statusBarMenu addItem:settings]; + + //Quit + NSMenuItem *quit = [[NSMenuItem alloc] initWithTitle:@"Quit" + action:@selector(quitApp) + keyEquivalent:@""]; + quit.target = self; + [self.statusBarMenu addItem:quit]; + + + + self.statusBarIcon.menu = self.statusBarMenu; + + + // If the process is trusted, register for keyboard events; otherwise wait for the user to declare the process trusted + if (AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{(__bridge NSString *)kAXTrustedCheckOptionPrompt: @true})) { + [self _registerGlobalKeyboardEvents]; + } + else { + [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(startMonitoringKeysIfProcessTrusted:) userInfo:nil repeats:YES]; + } } -- (void)_loadBrightness +-(void)showSettings { + SettingsWindowController *settings = [[SettingsWindowController alloc] initWithWindowNibName:@"SettingsWindowController"]; + [NSApp activateIgnoringOtherApps:YES]; + [NSApp runModalForWindow:settings.window]; +} + +-(void)quitApp { + [NSApp terminate:self]; +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { - [[NSUserDefaults standardUserDefaults] registerDefaults:@{ - brightnessValuePreferenceKey: @(8*brightnessStep) - }]; - _brightness = [[NSUserDefaults standardUserDefaults] floatForKey:brightnessValuePreferenceKey]; - NSLog(@"Loaded value: %f",_brightness); } -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +- (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*) sender { - if (![self _loadBezelServices]) - { - [self _loadOSDFramework]; + return NO; +} + +- (void) startMonitoringKeysIfProcessTrusted:(NSTimer*)timer +{ + // Check if the process is trusted without prompting the user again + if (AXIsProcessTrustedWithOptions(nil)) { + [self _registerGlobalKeyboardEvents]; + [timer invalidate]; } - [self _configureLoginItem]; - [self _checkTrusted]; - [self _registerGlobalKeyboardEvents]; - [self _loadBrightness]; - [self _registerSignalHandling]; } void shutdownSignalHandler(int signal) @@ -181,63 +348,240 @@ - (void)_registerSignalHandling signal(SIGTERM, shutdownSignalHandler); } +//Settings +- (void)setMultiMonitor:(BOOL ) multiMonitor { + [NSUserDefaults.standardUserDefaults setObject:[NSNumber numberWithBool:multiMonitor] forKey:@"multiMonitor"]; +} -- (void)applicationWillTerminate:(NSNotification *)aNotification -{ - [self _willTerminate]; +- (BOOL)multiMonitor { + id multiMonitor = [NSUserDefaults.standardUserDefaults valueForKey:@"multiMonitor"]; + if (!multiMonitor) { + return YES; + } + return [multiMonitor boolValue]; } -- (void)_willTerminate -{ - NSLog(@"willTerminate"); - [self _saveBrightness]; +- (void)setSmoothStep:(BOOL ) smoothStep { + [NSUserDefaults.standardUserDefaults setObject:[NSNumber numberWithBool:smoothStep] forKey:@"smoothStep"]; } -- (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*) sender -{ - return NO; +- (BOOL)smoothStep { + id smoothStep = [NSUserDefaults.standardUserDefaults valueForKey:@"smoothStep"]; + if (!smoothStep) { + return NO; + } + return [smoothStep boolValue]; } -- (void)setBrightness:(float)value -{ - _brightness = value; - - CGDirectDisplayID display = CGSMainDisplayID(); - - if (_BSDoGraphicWithMeterAndTimeout != NULL) - { - // El Capitan and probably older systems - _BSDoGraphicWithMeterAndTimeout(display, BSGraphicBacklightMeter, 0x0, value/100.f, 1); +- (void)setShowBrightness:(BOOL)showBrightness { + [NSUserDefaults.standardUserDefaults setObject:[NSNumber numberWithBool:showBrightness] forKey:@"showBrightness"]; +} + +- (BOOL)showBrightness { + id showBrightness = [NSUserDefaults.standardUserDefaults valueForKey:@"showBrightness"]; + if (!showBrightness) { + return YES; } - else { - // Sierra+ - [[NSClassFromString(@"OSDManager") sharedManager] showImage:OSDGraphicBacklight onDisplayID:CGSMainDisplayID() priority:OSDPriorityDefault msecUntilFade:1000 filledChiclets:value/brightnessStep totalChiclets:100.f/brightnessStep locked:NO]; + return [showBrightness boolValue]; +} + +- (void)setMaxBrightness:(int)maxBrightness { + self.brightnessView.sliderBrightness.maxValue = maxBrightness; + [NSUserDefaults.standardUserDefaults setObject:[NSNumber numberWithInt:maxBrightness] forKey:@"maxBrightness"]; +} + +- (int)maxBrightness { + id maxBrightness = [NSUserDefaults.standardUserDefaults valueForKey:@"maxBrightness"]; + if (!maxBrightness) { + return 100; + } + return [maxBrightness intValue]; +} + +- (NSString *)decreaseBrightnessKey { + id decreaseBrightnessKeyCode = [NSUserDefaults.standardUserDefaults valueForKey:@"decreaseBrightnessKey"]; + if (!decreaseBrightnessKeyCode) { + return @"F1"; + } + return decreaseBrightnessKeyCode; +} + +- (void)setDecreaseBrightnessKey:(NSString *)decreaseBrightnessKeyCode { + [NSUserDefaults.standardUserDefaults setObject:decreaseBrightnessKeyCode forKey:@"decreaseBrightnessKey"]; +} + +- (NSString *)increaseBrightnessKey { + id increaseBrightnessKeyCode = [NSUserDefaults.standardUserDefaults valueForKey:@"increaseBrightnessKey"]; + if (!increaseBrightnessKeyCode) { + return @"F2"; + } + return increaseBrightnessKeyCode; +} + +- (void)setIncreaseBrightnessKey:(NSString *)increaseBrightnessKeyCode { + [NSUserDefaults.standardUserDefaults setObject:increaseBrightnessKeyCode forKey:@"increaseBrightnessKey"]; +} + +- (void)setAdjustColorTemperature:(BOOL)adjustColorTemperature { + [NSUserDefaults.standardUserDefaults setObject:[NSNumber numberWithBool:adjustColorTemperature] forKey:@"adjustColorTemperature"]; +} + +- (BOOL)adjustColorTemperature { + id adjustColorTemperature = [NSUserDefaults.standardUserDefaults valueForKey:@"adjustColorTemperature"]; + if (!adjustColorTemperature) { + return NO; + } + return [adjustColorTemperature boolValue]; +} + +- (float)colorTemperature { + id colorTemperature = [NSUserDefaults.standardUserDefaults valueForKey:@"colorTemperature"]; + if (!colorTemperature) { + return 0.0; + } + return [colorTemperature floatValue]; +} + +- (void)setColorTemperature:(float)colorTemperature { + [NSUserDefaults.standardUserDefaults setObject:[NSNumber numberWithFloat:self.colorTemperature] forKey:@"colorTemperature"]; +} + +- (void)setColorTemperatureLimit:(float)colorTemperatureLimit { + self.colorTemperatureView.sliderColorTemperature.maxValue = colorTemperatureLimit; + [NSUserDefaults.standardUserDefaults setObject:[NSNumber numberWithFloat:colorTemperatureLimit] forKey:@"colorTemperatureLimit"]; +} + +- (float)colorTemperatureLimit { + id colorTemperatureLimit = [NSUserDefaults.standardUserDefaults valueForKey:@"colorTemperatureLimit"]; + if (!colorTemperatureLimit) { + return 0.5; + } + return [colorTemperatureLimit floatValue]; +} + +- (NSString *)colorTemperatureLessWarmKey { + id colorTemperatureLessWarmKeyCode = [NSUserDefaults.standardUserDefaults valueForKey:@"colorTemperatureLessWarmKey"]; + if (!colorTemperatureLessWarmKeyCode) { + return @"F3"; + } + return colorTemperatureLessWarmKeyCode; +} + +- (void)setColorTemperatureLessWarmKey:(NSString *)colorTemperatureLessWarmKeyCode { + [NSUserDefaults.standardUserDefaults setObject:colorTemperatureLessWarmKeyCode forKey:@"colorTemperatureLessWarmKey"]; +} + +- (NSString *)colorTemperatureMoreWarmKey { + id colorTemperatureMoreWarmKeyCode = [NSUserDefaults.standardUserDefaults valueForKey:@"colorTemperatureMoreWarmKey"]; + if (!colorTemperatureMoreWarmKeyCode) { + return @"F4"; + } + return colorTemperatureMoreWarmKeyCode; +} + +- (void)setColorTemperatureMoreWarmKey:(NSString *)colorTemperatureMoreWarmKeyCode { + [NSUserDefaults.standardUserDefaults setObject:colorTemperatureMoreWarmKeyCode forKey:@"colorTemperatureMoreWarmKey"]; +} + +//------- + ++ (void)saveBrightness:(int) newBrightness forDisplayID:(CGDirectDisplayID) displayID { + NSMutableDictionary* newDisplayBrighnesses; + NSDictionary* savedDisplayBrighnesses = [NSUserDefaults.standardUserDefaults objectForKey:kDisplaysBrightnessDefaultsKey]; + + if ([savedDisplayBrighnesses isKindOfClass:[NSDictionary class]]) { + newDisplayBrighnesses = [NSMutableDictionary dictionaryWithDictionary:savedDisplayBrighnesses]; + } else { + newDisplayBrighnesses = [NSMutableDictionary new]; } + NSString* currentDisplayIdKey = [NSString stringWithFormat:@"%u", displayID]; + newDisplayBrighnesses [currentDisplayIdKey] = @(newBrightness); - for (NSScreen *screen in NSScreen.screens) { - NSDictionary *description = [screen deviceDescription]; - if ([description objectForKey:@"NSDeviceIsScreen"]) { - CGDirectDisplayID screenNumber = [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]; - - set_control(screenNumber, BRIGHTNESS, value); + [NSUserDefaults.standardUserDefaults setObject:newDisplayBrighnesses forKey:kDisplaysBrightnessDefaultsKey]; + [NSUserDefaults.standardUserDefaults synchronize]; + APP_DELEGATE.currentBrightness = newBrightness; + APP_DELEGATE.statusBarIcon.length = APP_DELEGATE.showBrightness ? APP_DELEGATE.currentBrightness == 100 + ? STATUS_ICON_WIDTH_TEXT_100 : STATUS_ICON_WIDTH_TEXT : STATUS_ICON_WIDTH; +} + ++ (BOOL)loadSavedBrightness:(uint*) savedBrightness forDisplayID:(CGDirectDisplayID) displayID { + NSString* currentDisplayIdKey = [NSString stringWithFormat:@"%u", displayID]; + NSDictionary* savedDisplayBrighnesses = [NSUserDefaults.standardUserDefaults objectForKey:kDisplaysBrightnessDefaultsKey]; + if ([savedDisplayBrighnesses isKindOfClass:[NSDictionary class]]) { + NSNumber* savedCurrentBrightness = savedDisplayBrighnesses [currentDisplayIdKey]; + if ([savedCurrentBrightness isKindOfClass:[NSNumber class]]) { + uint currentBrightness = (uint)savedCurrentBrightness.unsignedIntegerValue; + *savedBrightness = currentBrightness; + APP_DELEGATE.currentBrightness = currentBrightness; + return YES; } } + return NO; } -- (float)brightness -{ - return _brightness; ++(void)changeMainScreenBrightnessWithStep:(int) deltaInSubsteps { + CGDirectDisplayID currentDisplayId = [NSScreen.mainScreen.deviceDescription [@"NSScreenNumber"] unsignedIntValue]; + if (! CGDisplayIsBuiltin(currentDisplayId)) { + + uint currentBrightness = 50; + uint maxBrightness = 100; + + // Get the current display brightness + // Fist, try user defaults to avoid waiting for a timeout if the display is known not to support DDCRead; + // If user defaults are not set, read the brightness value from the display + + BOOL isCurrentBrighnessReadFromDefaults = [AppDelegate loadSavedBrightness:¤tBrightness forDisplayID:currentDisplayId]; + if (! isCurrentBrighnessReadFromDefaults) { + get_control(currentDisplayId, BRIGHTNESS, ¤tBrightness, &maxBrightness); + } + + int currentBrightnessInSubsteps = round((double)currentBrightness / (double)maxBrightness * (double)brightnessSubstepsCount); + int newBrightnessInSubsteps = MIN(MAX(0, currentBrightnessInSubsteps + deltaInSubsteps), brightnessSubstepsCount); + if (abs(deltaInSubsteps) != 1) { + // newBrightnessInSubsteps must be a multiple of deltaInSubsteps + newBrightnessInSubsteps = (newBrightnessInSubsteps / deltaInSubsteps) * deltaInSubsteps; + } + + uint newBrightness = (uint) round((double)newBrightnessInSubsteps / (double)brightnessSubstepsCount * (double)maxBrightness); + newBrightness = MIN(APP_DELEGATE.maxBrightness, newBrightness); + + if (newBrightness != currentBrightness) { + if (set_control(currentDisplayId, BRIGHTNESS, newBrightness)) { + NSLog(@"New brightness: %d", newBrightness); + APP_DELEGATE.statusBarIcon.title = APP_DELEGATE.showBrightness ? [NSString stringWithFormat:@"%i%%",newBrightness] : @""; + // Display the brighness level OSD + showBrightnessLevelPaneOnDisplay(newBrightnessInSubsteps, currentDisplayId); + APP_DELEGATE.brightnessView.sliderBrightness.integerValue = newBrightness; + [self saveBrightness:newBrightness forDisplayID:currentDisplayId]; + } + } + else { + // Min or max brightness level: present the OSD to provide a feedback to the user, but don't send a command + showBrightnessLevelPaneOnDisplay(newBrightness, currentDisplayId); + } + } } -- (void)increaseBrightness -{ - self.brightness = MIN(self.brightness+brightnessStep,100); ++(void)changeMainScreenBrightness:(int) newBrightness { + CGDirectDisplayID currentDisplayId = [NSScreen.mainScreen.deviceDescription [@"NSScreenNumber"] unsignedIntValue]; + if (! CGDisplayIsBuiltin(currentDisplayId)) { + if (set_control(currentDisplayId, BRIGHTNESS, newBrightness)) { + APP_DELEGATE.statusBarIcon.title = APP_DELEGATE.showBrightness ? [NSString stringWithFormat:@"%i%%",newBrightness] : @""; + [AppDelegate saveBrightness:newBrightness forDisplayID:currentDisplayId]; + } + } } -- (void)decreaseBrightness -{ - self.brightness = MAX(self.brightness-brightnessStep,0); -} ++(void)changeScreenColorTemperature:(float) colorTemperature { + [APP_DELEGATE.blueLight setStrength:colorTemperature commit:YES]; + APP_DELEGATE.colorTemperatureView.sliderColorTemperature.floatValue = colorTemperature; +} ++(void)changeScreenColorTemperatureStep:(float) colorTemperatureStep { + APP_DELEGATE.colorTemperatureView.sliderColorTemperature.floatValue += colorTemperatureStep; + [APP_DELEGATE.blueLight setStrength: APP_DELEGATE.colorTemperatureView.sliderColorTemperature.floatValue + commit: YES]; + NSLog(@"Color Temperature Strength: %f",APP_DELEGATE.colorTemperatureView.sliderColorTemperature.floatValue); +} @end diff --git a/NativeDisplayBrightness/Assets.xcassets/AppIcon.appiconset/Brightness.png b/NativeDisplayBrightness/Assets.xcassets/AppIcon.appiconset/Brightness.png new file mode 100644 index 0000000..56e662b Binary files /dev/null and b/NativeDisplayBrightness/Assets.xcassets/AppIcon.appiconset/Brightness.png differ diff --git a/NativeDisplayBrightness/Assets.xcassets/AppIcon.appiconset/Contents.json b/NativeDisplayBrightness/Assets.xcassets/AppIcon.appiconset/Contents.json index 2db2b1c..68bba48 100644 --- a/NativeDisplayBrightness/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/NativeDisplayBrightness/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -31,8 +31,9 @@ "scale" : "2x" }, { - "idiom" : "mac", "size" : "256x256", + "idiom" : "mac", + "filename" : "Brightness.png", "scale" : "1x" }, { diff --git a/NativeDisplayBrightness/Assets.xcassets/Contents.json b/NativeDisplayBrightness/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/NativeDisplayBrightness/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/NativeDisplayBrightness/Assets.xcassets/icon.imageset/Contents.json b/NativeDisplayBrightness/Assets.xcassets/icon.imageset/Contents.json new file mode 100644 index 0000000..495aad5 --- /dev/null +++ b/NativeDisplayBrightness/Assets.xcassets/icon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/NativeDisplayBrightness/Assets.xcassets/icon.imageset/icon.png b/NativeDisplayBrightness/Assets.xcassets/icon.imageset/icon.png new file mode 100644 index 0000000..adf8c20 Binary files /dev/null and b/NativeDisplayBrightness/Assets.xcassets/icon.imageset/icon.png differ diff --git a/NativeDisplayBrightness/Assets.xcassets/icon.imageset/icon@2x.png b/NativeDisplayBrightness/Assets.xcassets/icon.imageset/icon@2x.png new file mode 100644 index 0000000..1659429 Binary files /dev/null and b/NativeDisplayBrightness/Assets.xcassets/icon.imageset/icon@2x.png differ diff --git a/NativeDisplayBrightness/Base.lproj/MainMenu.xib b/NativeDisplayBrightness/Base.lproj/MainMenu.xib deleted file mode 100644 index 4168ee5..0000000 --- a/NativeDisplayBrightness/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,679 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/NativeDisplayBrightness/BezelServices.h b/NativeDisplayBrightness/BezelServices.h index 118ad26..2aac3be 100644 --- a/NativeDisplayBrightness/BezelServices.h +++ b/NativeDisplayBrightness/BezelServices.h @@ -9,8 +9,6 @@ #ifndef BezelServices_h #define BezelServices_h -#include "CoreGraphicsPriv.h" - typedef enum { BSGraphicBacklightMeter = 0xfffffff7, BSGraphicBacklightFailure = 0xfffffff6, diff --git a/NativeDisplayBrightness/BrightnessViewController.h b/NativeDisplayBrightness/BrightnessViewController.h new file mode 100644 index 0000000..3ee567a --- /dev/null +++ b/NativeDisplayBrightness/BrightnessViewController.h @@ -0,0 +1,18 @@ +// +// BrightnessViewController.h +// NativeDisplayBrightness +// +// Created by Volodymyr Klymenko on 1/28/18. +// Copyright © 2018 Volodymyr Klymenko. All rights reserved. +// + +#import + +@interface BrightnessViewController : NSViewController { + int lastBrightnessValue; +} + +@property (weak) IBOutlet NSSlider *sliderBrightness; +- (IBAction)changeBrightness:(NSSlider *)sender; + +@end diff --git a/NativeDisplayBrightness/BrightnessViewController.m b/NativeDisplayBrightness/BrightnessViewController.m new file mode 100644 index 0000000..b616688 --- /dev/null +++ b/NativeDisplayBrightness/BrightnessViewController.m @@ -0,0 +1,39 @@ +// +// BrightnessViewController.m +// NativeDisplayBrightness +// +// Created by Volodymyr Klymenko on 1/28/18. +// Copyright © 2018 Volodymyr Klymenko. All rights reserved. +// + +#import "BrightnessViewController.h" +#import "AppDelegate.h" + +@interface BrightnessViewController () + +@end + +@implementation BrightnessViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + CGDirectDisplayID currentDisplayId = [NSScreen.mainScreen.deviceDescription [@"NSScreenNumber"] unsignedIntValue]; + if (! CGDisplayIsBuiltin(currentDisplayId)) { + uint loadedBrightness = 50; + [AppDelegate loadSavedBrightness:&loadedBrightness forDisplayID:currentDisplayId]; + self.sliderBrightness.integerValue = loadedBrightness; + self.sliderBrightness.maxValue = APP_DELEGATE.maxBrightness; + lastBrightnessValue = loadedBrightness; + } +} + +- (IBAction)changeBrightness:(NSSlider *)sender { + if (lastBrightnessValue != self.sliderBrightness.intValue) { + lastBrightnessValue = (int)self.sliderBrightness.intValue; + [AppDelegate changeMainScreenBrightness:lastBrightnessValue]; + } +} + + +@end diff --git a/NativeDisplayBrightness/BrightnessViewController.xib b/NativeDisplayBrightness/BrightnessViewController.xib new file mode 100644 index 0000000..5e15bc0 --- /dev/null +++ b/NativeDisplayBrightness/BrightnessViewController.xib @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NativeDisplayBrightness/CBBlueLightClient.h b/NativeDisplayBrightness/CBBlueLightClient.h new file mode 100644 index 0000000..16a8d7c --- /dev/null +++ b/NativeDisplayBrightness/CBBlueLightClient.h @@ -0,0 +1,39 @@ +// +// CBBlueLightClient.h +// NightShifter +// +// Created by Eric Lanini on 6/11/17. +// Copyright © 2017 Eric Lanini. All rights reserved. +// + +#ifndef CBBlueLightClient_h +#define CBBlueLightClient_h + +typedef struct { + int hour; + int minute; +} Time; + +typedef struct { + Time fromTime; + Time toTime; +} Schedule; + +typedef struct { + char active; + char enabled; + char sunSchedulePermitted; + int mode; + Schedule schedule; + unsigned long long disableFlags; +} StatusData; + +@interface CBBlueLightClient : NSObject +- (BOOL)setStrength:(float)arg1 commit: (BOOL)arg2; +- (BOOL)getStrength:(float *)arg1; +- (BOOL)setEnabled:(BOOL)arg1; ++ (BOOL)supportsBlueLightReduction; +- (BOOL)getBlueLightStatus:(StatusData *)arg1; +@end + +#endif /* CBBlueLightClient_h */ diff --git a/NativeDisplayBrightness/ColorTemperatureViewController.h b/NativeDisplayBrightness/ColorTemperatureViewController.h new file mode 100644 index 0000000..b4d0a62 --- /dev/null +++ b/NativeDisplayBrightness/ColorTemperatureViewController.h @@ -0,0 +1,16 @@ +// +// ColorTemperatureViewController.h +// NativeDisplayBrightness +// +// Created by Volodymyr Klymenko on 1/29/18. +// Copyright © 2018 Volodymyr Klymenko. All rights reserved. +// + +#import + +@interface ColorTemperatureViewController : NSViewController + +@property (weak) IBOutlet NSButton *adjustColorTemperature; +@property (weak) IBOutlet NSSlider *sliderColorTemperature; + +@end diff --git a/NativeDisplayBrightness/ColorTemperatureViewController.m b/NativeDisplayBrightness/ColorTemperatureViewController.m new file mode 100644 index 0000000..6d0464e --- /dev/null +++ b/NativeDisplayBrightness/ColorTemperatureViewController.m @@ -0,0 +1,41 @@ +// +// ColorTemperatureViewController.m +// NativeDisplayBrightness +// +// Created by Volodymyr Klymenko on 1/29/18. +// Copyright © 2018 Volodymyr Klymenko. All rights reserved. +// + +#import "ColorTemperatureViewController.h" +#import "Config.h" + +@interface ColorTemperatureViewController () + +@end + +@implementation ColorTemperatureViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + StatusData status; + [APP_DELEGATE.blueLight getBlueLightStatus:&status]; + if (status.enabled) { + self.adjustColorTemperature.state = NSOnState; + } else { + self.adjustColorTemperature.state = NSOffState; + self.sliderColorTemperature.enabled = NO; + } + self.sliderColorTemperature.maxValue = APP_DELEGATE.colorTemperatureLimit; +} + +- (IBAction)enableColorTemperature:(NSButton *)sender { + [APP_DELEGATE.blueLight setEnabled:sender.state]; + self.sliderColorTemperature.enabled = sender.state; +} + +- (IBAction)changeColorTemperature:(NSSlider *)sender { + [AppDelegate changeScreenColorTemperature:sender.floatValue]; +} + + +@end diff --git a/NativeDisplayBrightness/ColorTemperatureViewController.xib b/NativeDisplayBrightness/ColorTemperatureViewController.xib new file mode 100644 index 0000000..5876338 --- /dev/null +++ b/NativeDisplayBrightness/ColorTemperatureViewController.xib @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NativeDisplayBrightness/Config.h b/NativeDisplayBrightness/Config.h new file mode 100644 index 0000000..3aac067 --- /dev/null +++ b/NativeDisplayBrightness/Config.h @@ -0,0 +1,71 @@ +// +// Config.h +// NativeDisplayBrightness +// +// Created by Volodymyr Klymenko on 1/29/18. +// Copyright © 2018 Volodymyr Klymenko. All rights reserved. +// + +#ifndef Config_h +#define Config_h + +#include "AppDelegate.h" + +#pragma mark - Key codes of special keys + +#define COLOR_TEMPERATURE_STEP 0.01 + +#define APP_DELEGATE ((AppDelegate *)[[NSApplication sharedApplication] delegate]) + +#define STATUS_ICON_WIDTH 24 +#define STATUS_ICON_WIDTH_TEXT 52 +#define STATUS_ICON_WIDTH_TEXT_100 58 + +// Extract from Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h +/* keycodes for keys that are independent of keyboard layout*/ +enum { + kVK_F17 = 0x40, + kVK_VolumeUp = 0x48, + kVK_VolumeDown = 0x49, + kVK_Mute = 0x4A, + kVK_F18 = 0x4F, + kVK_F19 = 0x50, + kVK_F20 = 0x5A, + kVK_F5 = 0x60, + kVK_F6 = 0x61, + kVK_F7 = 0x62, + kVK_F3 = 0x63, + kVK_F8 = 0x64, + kVK_F9 = 0x65, + kVK_F11 = 0x67, + kVK_F13 = 0x69, + kVK_F16 = 0x6A, + kVK_F14 = 0x6B, + kVK_F10 = 0x6D, + kVK_F12 = 0x6F, + kVK_F15 = 0x71, + kVK_Help = 0x72, + kVK_Home = 0x73, + kVK_PageUp = 0x74, + kVK_ForwardDelete = 0x75, + kVK_F4 = 0x76, + kVK_End = 0x77, + kVK_F2 = 0x78, + kVK_PageDown = 0x79, + kVK_F1 = 0x7A, + kVK_LeftArrow = 0x7B, + kVK_RightArrow = 0x7C, + kVK_DownArrow = 0x7D, + kVK_UpArrow = 0x7E +}; + + +#pragma mark - constants + +static NSString *const kDisplaysBrightnessDefaultsKey = @"displays-brightness"; +static const int brightnessStepsCount = 16; +static const int brightnessSubstepsPerStep = 6; +static const int brightnessSubstepsCount = brightnessStepsCount * brightnessSubstepsPerStep; + + +#endif /* Config_h */ diff --git a/NativeDisplayBrightness/CoreGraphicsPriv.h b/NativeDisplayBrightness/CoreGraphicsPriv.h deleted file mode 100644 index 37284fe..0000000 --- a/NativeDisplayBrightness/CoreGraphicsPriv.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// CoreGraphicsPriv.h -// NativeDisplayBrightness -// -// Created by Benno Krauss on 23.10.16. -// Copyright © 2016 Benno Krauss. All rights reserved. -// - -#ifndef CoreGraphicsPriv_h -#define CoreGraphicsPriv_h - -CG_EXTERN CGDirectDisplayID CGSMainDisplayID(void); - -#endif /* CoreGraphicsPriv_h */ diff --git a/NativeDisplayBrightness/DDC.c b/NativeDisplayBrightness/DDC.c new file mode 100644 index 0000000..1e5434e --- /dev/null +++ b/NativeDisplayBrightness/DDC.c @@ -0,0 +1,414 @@ +// +// DDC.c +// DDC Panel +// +// Created by Jonathan Taylor on 7/10/09. +// See http://github.com/jontaylor/DDC-CI-Tools-for-OS-X +// + +#include +#include +#include +#include "DDC.h" + +#define kMaxRequests 10 + +/* + + Iterate IOreg's device tree to find the IOFramebuffer mach service port that corresponds to a given CGDisplayID + replaces CGDisplayIOServicePort: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/index.html#//apple_ref/c/func/CGDisplayIOServicePort + based on: https://github.com/glfw/glfw/pull/192/files + */ +static io_service_t IOFramebufferPortFromCGDisplayID(CGDirectDisplayID displayID) +{ + io_iterator_t iter; + io_service_t serv, servicePort = 0; + + kern_return_t err = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(IOFRAMEBUFFER_CONFORMSTO), &iter); + + if (err != KERN_SUCCESS) + return 0; + + // now recurse the IOReg tree + while ((serv = IOIteratorNext(iter)) != MACH_PORT_NULL) + { + CFDictionaryRef info; + io_name_t name; + CFIndex vendorID = 0, productID = 0, serialNumber = 0; + CFNumberRef vendorIDRef, productIDRef, serialNumberRef; +#ifdef DEBUG + CFStringRef location = CFSTR(""); + CFStringRef serial = CFSTR(""); +#endif + Boolean success = 0; + + // get metadata from IOreg node + IORegistryEntryGetName(serv, name); + info = IODisplayCreateInfoDictionary(serv, kIODisplayOnlyPreferredName); + +#ifdef DEBUG + /* When assigning a display ID, Quartz considers the following parameters:Vendor, Model, Serial Number and Position in the I/O Kit registry */ + // http://opensource.apple.com//source/IOGraphics/IOGraphics-179.2/IOGraphicsFamily/IOKit/graphics/IOGraphicsTypes.h + CFStringRef locationRef = CFDictionaryGetValue(info, CFSTR(kIODisplayLocationKey)); + if (locationRef) location = CFStringCreateCopy(NULL, locationRef); + CFStringRef serialRef = CFDictionaryGetValue(info, CFSTR(kDisplaySerialString)); + if (serialRef) serial = CFStringCreateCopy(NULL, serialRef); +#endif + if (CFDictionaryGetValueIfPresent(info, CFSTR(kDisplayVendorID), (const void**)&vendorIDRef)) + success = CFNumberGetValue(vendorIDRef, kCFNumberCFIndexType, &vendorID); + + if (CFDictionaryGetValueIfPresent(info, CFSTR(kDisplayProductID), (const void**)&productIDRef)) + success &= CFNumberGetValue(productIDRef, kCFNumberCFIndexType, &productID); + + IOItemCount busCount; + IOFBGetI2CInterfaceCount(serv, &busCount); + + if (!success || busCount < 1 || CGDisplayIsBuiltin(displayID)) { + // this does not seem to be a DDC-enabled display, skip it + CFRelease(info); + continue; + } + // if (framebuffer.hasDDCConnect(0)) // https://developer.apple.com/reference/kernel/ioframebuffer/1813510-hasddcconnect?language=objc + // kAppleDisplayTypeKey -- if this is an Apple display, can use IODisplay func to change brightness: http://stackoverflow.com/a/32691700/3878712 + + if (CFDictionaryGetValueIfPresent(info, CFSTR(kDisplaySerialNumber), (const void**)&serialNumberRef)) + CFNumberGetValue(serialNumberRef, kCFNumberCFIndexType, &serialNumber); + + // compare IOreg's metadata to CGDisplay's metadata to infer if the IOReg's I2C monitor is the display for the given NSScreen.displayID + if (CGDisplayVendorNumber(displayID) != vendorID || + CGDisplayModelNumber(displayID) != productID || + CGDisplaySerialNumber(displayID) != serialNumber) // SN is zero in lots of cases, so duplicate-monitors can confuse us :-/ + { + CFRelease(info); + continue; + } + +#ifdef DEBUG + // considering this IOFramebuffer as the match for the CGDisplay, dump out its information + // compare with `make displaylist` + printf("\nFramebuffer: %s\n", name); + printf("%s\n", CFStringGetCStringPtr(location, kCFStringEncodingUTF8)); + printf("VN:%ld PN:%ld SN:%ld", vendorID, productID, serialNumber); + printf(" UN:%d", CGDisplayUnitNumber(displayID)); + printf(" IN:%d", iter); + printf(" Serial:%s\n\n", CFStringGetCStringPtr(serial, kCFStringEncodingUTF8)); +#endif + servicePort = serv; + CFRelease(info); + break; + } + + IOObjectRelease(iter); + return servicePort; +} + +dispatch_semaphore_t DisplayQueue(CGDirectDisplayID displayID) { + static UInt64 queueCount = 0; + static struct DDCQueue {CGDirectDisplayID id; dispatch_semaphore_t queue;} *queues = NULL; + dispatch_semaphore_t queue = NULL; + if (!queues) + queues = calloc(50, sizeof(*queues)); //FIXME: specify + UInt64 i = 0; + while (i < queueCount) + if (queues[i].id == displayID) + break; + else + i++; + if (queues[i].id == displayID) + queue = queues[i].queue; + else + queues[queueCount++] = (struct DDCQueue){displayID, (queue = dispatch_semaphore_create(1))}; + return queue; +} + +bool DisplayRequest(CGDirectDisplayID displayID, IOI2CRequest *request) { + dispatch_semaphore_t queue = DisplayQueue(displayID); + dispatch_semaphore_wait(queue, DISPATCH_TIME_FOREVER); + bool result = false; + io_service_t framebuffer; // https://developer.apple.com/reference/kernel/ioframebuffer + //if ((framebuffer = CGDisplayIOServicePort(displayID))) { // Deprecated in OSX 10.9 + if ((framebuffer = IOFramebufferPortFromCGDisplayID(displayID))) { + IOItemCount busCount; + if (IOFBGetI2CInterfaceCount(framebuffer, &busCount) == KERN_SUCCESS) { + IOOptionBits bus = 0; + while (bus < busCount) { + io_service_t interface; + if (IOFBCopyI2CInterfaceForBus(framebuffer, bus++, &interface) != KERN_SUCCESS) + continue; + + IOI2CConnectRef connect; + if (IOI2CInterfaceOpen(interface, kNilOptions, &connect) == KERN_SUCCESS) { + result = (IOI2CSendRequest(connect, kNilOptions, request) == KERN_SUCCESS); + IOI2CInterfaceClose(connect, kNilOptions); + } + IOObjectRelease(interface); + if (result) break; + } + } + IOObjectRelease(framebuffer); + } + if (request->replyTransactionType == kIOI2CNoTransactionType) + usleep(20000); + dispatch_semaphore_signal(queue); + return result && request->result == KERN_SUCCESS; +} + +bool DDCWrite(CGDirectDisplayID displayID, struct DDCWriteCommand *write) { + IOI2CRequest request; + UInt8 data[128]; + + bzero( &request, sizeof(request)); + + request.commFlags = 0; + + request.sendAddress = 0x6E; + request.sendTransactionType = kIOI2CSimpleTransactionType; + request.sendBuffer = (vm_address_t) &data[0]; + request.sendBytes = 7; + + data[0] = 0x51; + data[1] = 0x84; + data[2] = 0x03; + data[3] = write->control_id; + data[4] = (write->new_value) >> 8; + data[5] = write->new_value & 255; + data[6] = 0x6E ^ data[0] ^ data[1] ^ data[2] ^ data[3]^ data[4] ^ data[5]; + + request.replyTransactionType = kIOI2CNoTransactionType; + request.replyBytes = 0; + + bool result = DisplayRequest(displayID, &request); + return result; +} + +bool DDCRead(CGDirectDisplayID displayID, struct DDCReadCommand *read) { + IOI2CRequest request; + UInt8 reply_data[11] = {}; + bool result = false; + UInt8 data[128]; + + for (int i=1; i<=kMaxRequests; i++) { + bzero(&request, sizeof(request)); + + request.commFlags = 0; + request.sendAddress = 0x6E; + request.sendTransactionType = kIOI2CSimpleTransactionType; + request.sendBuffer = (vm_address_t) &data[0]; + request.sendBytes = 5; + request.minReplyDelay = 10; // too short can freeze kernel + + data[0] = 0x51; + data[1] = 0x82; + data[2] = 0x01; + data[3] = read->control_id; + data[4] = 0x6E ^ data[0] ^ data[1] ^ data[2] ^ data[3]; +#ifdef TT_SIMPLE + request.replyTransactionType = kIOI2CSimpleTransactionType; +#elif defined TT_DDC + request.replyTransactionType = kIOI2CDDCciReplyTransactionType; +#else + request.replyTransactionType = SupportedTransactionType(); +#endif + request.replyAddress = 0x6F; + request.replySubAddress = 0x51; + + request.replyBuffer = (vm_address_t) reply_data; + request.replyBytes = sizeof(reply_data); + + result = DisplayRequest(displayID, &request); + result = (result && reply_data[0] == request.sendAddress && reply_data[2] == 0x2 && reply_data[4] == read->control_id && reply_data[10] == (request.replyAddress ^ request.replySubAddress ^ reply_data[1] ^ reply_data[2] ^ reply_data[3] ^ reply_data[4] ^ reply_data[5] ^ reply_data[6] ^ reply_data[7] ^ reply_data[8] ^ reply_data[9])); + + if (result) { // checksum is ok + if (i > 1) { + printf("D: Tries required to get data: %d \n", i); + } + break; + } + + if (request.result == kIOReturnUnsupportedMode) + printf("E: Unsupported Transaction Type! \n"); + + // reset values and return 0, if data reading fails + if (i >= kMaxRequests) { + read->success = false; + read->max_value = 0; + read->current_value = 0; + printf("E: No data after %d tries! \n", i); + return 0; + } + + usleep(40000); // 40msec -> See DDC/CI Vesa Standard - 4.4.1 Communication Error Recovery + } + read->success = true; + read->max_value = reply_data[7]; + read->current_value = reply_data[9]; + return result; +} + +UInt32 SupportedTransactionType() { + /* + With my setup (Intel HD4600 via displaylink to 'DELL U2515H') the original app failed to read ddc and freezes my system. + This happens because AppleIntelFramebuffer do not support kIOI2CDDCciReplyTransactionType. + So this version comes with a reworked ddc read function to detect the correct TransactionType. + --SamanVDR 2016 + */ + + kern_return_t kr; + io_iterator_t io_objects; + io_service_t io_service; + + kr = IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceNameMatching("IOFramebufferI2CInterface"), &io_objects); + + if (kr != KERN_SUCCESS) { + printf("E: Fatal - No matching service! \n"); + return 0; + } + + UInt32 supportedType = 0; + + while((io_service = IOIteratorNext(io_objects)) != MACH_PORT_NULL) + { + CFMutableDictionaryRef service_properties; + CFIndex types = 0; + CFNumberRef typesRef; + + kr = IORegistryEntryCreateCFProperties(io_service, &service_properties, kCFAllocatorDefault, kNilOptions); + if (kr == KERN_SUCCESS) + { + if (CFDictionaryGetValueIfPresent(service_properties, CFSTR(kIOI2CTransactionTypesKey), (const void**)&typesRef)) + CFNumberGetValue(typesRef, kCFNumberCFIndexType, &types); + + /* + We want DDCciReply but Simple is better than No-thing. + Combined and DisplayPortNative are not useful in our case. + */ + if (types) { +#ifdef DEBUG + printf("\nD: IOI2CTransactionTypes: 0x%02lx (%ld)\n", types, types); + + // kIOI2CNoTransactionType = 0 + if ( 0 == ((1 << kIOI2CNoTransactionType) & (UInt64)types)) { + printf("E: IOI2CNoTransactionType unsupported \n"); + } else { + printf("D: IOI2CNoTransactionType supported \n"); + supportedType = kIOI2CNoTransactionType; + } + + // kIOI2CSimpleTransactionType = 1 + if ( 0 == ((1 << kIOI2CSimpleTransactionType) & (UInt64)types)) { + printf("E: IOI2CSimpleTransactionType unsupported \n"); + } else { + printf("D: IOI2CSimpleTransactionType supported \n"); + supportedType = kIOI2CSimpleTransactionType; + } + + // kIOI2CDDCciReplyTransactionType = 2 + if ( 0 == ((1 << kIOI2CDDCciReplyTransactionType) & (UInt64)types)) { + printf("E: IOI2CDDCciReplyTransactionType unsupported \n"); + } else { + printf("D: IOI2CDDCciReplyTransactionType supported \n"); + supportedType = kIOI2CDDCciReplyTransactionType; + } + + // kIOI2CCombinedTransactionType = 3 + if ( 0 == ((1 << kIOI2CCombinedTransactionType) & (UInt64)types)) { + printf("E: IOI2CCombinedTransactionType unsupported \n"); + } else { + printf("D: IOI2CCombinedTransactionType supported \n"); + //supportedType = kIOI2CCombinedTransactionType; + } + + // kIOI2CDisplayPortNativeTransactionType = 4 + if ( 0 == ((1 << kIOI2CDisplayPortNativeTransactionType) & (UInt64)types)) { + printf("E: IOI2CDisplayPortNativeTransactionType unsupported\n"); + } else { + printf("D: IOI2CDisplayPortNativeTransactionType supported \n"); + //supportedType = kIOI2CDisplayPortNativeTransactionType; + // http://hackipedia.org/Hardware/video/connectors/DisplayPort/VESA%20DisplayPort%20Standard%20v1.1a.pdf + // http://www.electronic-products-design.com/geek-area/displays/display-port + } +#else + // kIOI2CSimpleTransactionType = 1 + if ( 0 != ((1 << kIOI2CSimpleTransactionType) & (UInt64)types)) { + supportedType = kIOI2CSimpleTransactionType; + } + + // kIOI2CDDCciReplyTransactionType = 2 + if ( 0 != ((1 << kIOI2CDDCciReplyTransactionType) & (UInt64)types)) { + supportedType = kIOI2CDDCciReplyTransactionType; + } +#endif + } else printf("E: Fatal - No supported Transaction Types! \n"); + + CFRelease(service_properties); + } + + IOObjectRelease(io_service); + + // Mac OS offers three framebuffer devices, but we can leave here + if (supportedType > 0) return supportedType; + } + + return supportedType; +} + + +bool EDIDTest(CGDirectDisplayID displayID, struct EDID *edid) { + IOI2CRequest request = {}; +/*! from https://opensource.apple.com/source/IOGraphics/IOGraphics-513.1/IOGraphicsFamily/IOKit/i2c/IOI2CInterface.h.auto.html + * not in https://developer.apple.com/reference/kernel/1659924-ioi2cinterface.h/ioi2crequest?changes=latest_beta&language=objc + * struct IOI2CRequest + * @abstract A structure defining an I2C bus transaction. + * @discussion This structure is used to request an I2C transaction consisting of a send (write) to and reply (read) from a device, either of which is optional, to be carried out atomically on an I2C bus. + * @field __reservedA Set to zero. + * @field result The result of the transaction. Common errors are kIOReturnNoDevice if there is no device responding at the given address, kIOReturnUnsupportedMode if the type of transaction is unsupported on the requested bus. + * @field completion A completion routine to be executed when the request completes. If NULL is passed, the request is synchronous, otherwise it may execute asynchronously. + * @field commFlags Flags that modify the I2C transaction type. The following flags are defined:
+ * kIOI2CUseSubAddressCommFlag Transaction includes a subaddress.
+ * @field minReplyDelay Minimum delay as absolute time between send and reply transactions. + * @field sendAddress I2C address to write. + * @field sendSubAddress I2C subaddress to write. + * @field __reservedB Set to zero. + * @field sendTransactionType The following types of transaction are defined for the send part of the request:
+ * kIOI2CNoTransactionType No send transaction to perform.
+ * kIOI2CSimpleTransactionType Simple I2C message.
+ * kIOI2CCombinedTransactionType Combined format I2C R/~W transaction.
+ * @field sendBuffer Pointer to the send buffer. + * @field sendBytes Number of bytes to send. Set to actual bytes sent on completion of the request. + * @field replyAddress I2C Address from which to read. + * @field replySubAddress I2C Address from which to read. + * @field __reservedC Set to zero. + * @field replyTransactionType The following types of transaction are defined for the reply part of the request:
+ * kIOI2CNoTransactionType No reply transaction to perform.
+ * kIOI2CSimpleTransactionType Simple I2C message.
+ * kIOI2CDDCciReplyTransactionType DDC/ci message (with embedded length). See VESA DDC/ci specification.
+ * kIOI2CCombinedTransactionType Combined format I2C R/~W transaction.
+ * @field replyBuffer Pointer to the reply buffer. + * @field replyBytes Max bytes to reply (size of replyBuffer). Set to actual bytes received on completion of the request. + * @field __reservedD Set to zero. + */ + + UInt8 data[128] = {}; + request.sendAddress = 0xA0; + request.sendTransactionType = kIOI2CSimpleTransactionType; + request.sendBuffer = (vm_address_t) data; + request.sendBytes = 0x01; + data[0] = 0x00; + request.replyAddress = 0xA1; + request.replyTransactionType = kIOI2CSimpleTransactionType; + request.replyBuffer = (vm_address_t) data; + request.replyBytes = sizeof(data); + if (!DisplayRequest(displayID, &request)) return false; + if (edid) memcpy(edid, &data, 128); + UInt32 i = 0; + UInt8 sum = 0; + while (i < request.replyBytes) { + if (i % 128 == 0) { + if (sum) break; + sum = 0; + } + sum += data[i++]; + } + return !sum; +} diff --git a/NativeDisplayBrightness/DDC.h b/NativeDisplayBrightness/DDC.h index e693a43..d2ef94d 100644 --- a/NativeDisplayBrightness/DDC.h +++ b/NativeDisplayBrightness/DDC.h @@ -19,6 +19,7 @@ #define RESET_COLOR 0x08 #define BRIGHTNESS 0x10 //OK #define CONTRAST 0x12 //OK +#define COLOR_PRESET_A 0x14 // dell u2515h -> Presets: 4 = 5000K, 5 = 6500K, 6 = 7500K, 8 = 9300K, 9 = 10000K, 11 = 5700K, 12 = Custom Color #define RED_GAIN 0x16 #define GREEN_GAIN 0x18 #define BLUE_GAIN 0x1A @@ -48,12 +49,12 @@ #define ORIENTATION 0xAA #define AUDIO_MUTE 0x8D #define SETTINGS 0xB0 //unsure on this one -#define ON_SCREEN_DISPLAY 0xCA +#define ON_SCREEN_DISPLAY 0xCA // read only -> returns '1' (OSD closed) or '2' (OSD active) #define OSD_LANGUAGE 0xCC #define DPMS 0xD6 -#define MAGIC_BRIGHT 0xDC //unsure +#define COLOR_PRESET_B 0xDC // dell u2515h -> Presets: 0 = Standard, 2 = Multimedia, 3 = Movie, 5 = Game #define VCP_VERSION 0xDF -#define COLOR_PRESET 0xE0 +#define COLOR_PRESET_C 0xE0 // dell u2515h -> Brightness on/off (0 or 1) #define POWER_CONTROL 0xE1 #define TOP_LEFT_SCREEN_PURITY 0xE8 #define TOP_RIGHT_SCREEN_PURITY 0xE9 @@ -70,6 +71,7 @@ struct DDCWriteCommand struct DDCReadCommand { UInt8 control_id; + bool success; UInt8 max_value; UInt8 current_value; }; @@ -84,12 +86,14 @@ struct EDID { UInt8 year : 8; UInt8 versionmajor : 8; UInt8 versionminor : 8; - UInt8 digitalinput : 1; + union videoinput { struct digitalinput { + UInt8 type : 1; UInt8 : 6; UInt8 dfp : 1; } digital; struct analoginput { + UInt8 type : 1; UInt8 synclevels : 2; UInt8 pedestal : 1; UInt8 separate : 1; @@ -97,6 +101,7 @@ struct EDID { UInt8 green : 1; UInt8 serrated : 1; } analog; + } videoinput; UInt8 maxh : 8; UInt8 maxv : 8; UInt8 gamma : 8; @@ -181,14 +186,8 @@ struct EDID { UInt8 interlaced : 1; UInt8 stereo : 2; UInt8 synctype : 2; - struct analogsync { - UInt8 serrated : 1; - UInt8 syncall : 1; - } analog; - struct digitalsync { - UInt8 vsync : 1; - UInt8 hsync : 1; - } digital; + UInt8 vsyncpol_serrated: 1; + UInt8 hsyncpol_syncall: 1; UInt8 twowaystereo : 1; } timing; struct text { @@ -230,10 +229,7 @@ struct EDID { UInt8 gamma2 : 8; UInt32 : 24; } whitepoint; - } descriptor1; - union descriptor descriptor2; - union descriptor descriptor3; - union descriptor descriptor4; + } descriptors[4]; UInt8 extensions : 8; UInt8 checksum : 8; }; @@ -241,5 +237,5 @@ struct EDID { bool DDCWrite(CGDirectDisplayID displayID, struct DDCWriteCommand *write); bool DDCRead(CGDirectDisplayID displayID, struct DDCReadCommand *read); bool EDIDTest(CGDirectDisplayID displayID, struct EDID *edid); - +UInt32 SupportedTransactionType(); #endif diff --git a/NativeDisplayBrightness/DDC.m b/NativeDisplayBrightness/DDC.m deleted file mode 100644 index 1408db3..0000000 --- a/NativeDisplayBrightness/DDC.m +++ /dev/null @@ -1,249 +0,0 @@ -// -// DDC.c -// DDC Panel -// -// Created by Jonathan Taylor on 7/10/09. -// See http://github.com/jontaylor/DDC-CI-Tools-for-OS-X -// - -#include -#include -#include -#include "DDC.h" -#define kDelayBase 100 - -static io_service_t IOFramebufferPortFromCGDisplayID(CGDirectDisplayID displayID) -// iterate IOreg's device tree to find the IOFramebuffer mach service port that corresponds to a given CGDisplayID -// replaces CGDisplayIOServicePort: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/index.html#//apple_ref/c/func/CGDisplayIOServicePort -// based on: https://github.com/glfw/glfw/pull/192/files -{ - io_iterator_t iter; - io_service_t serv, servicePort = 0; - - kern_return_t err = IOServiceGetMatchingServices( kIOMasterPortDefault, - IOServiceMatching(IOFRAMEBUFFER_CONFORMSTO), // IOFramebufferI2CInterface - &iter); - - if (err != KERN_SUCCESS) - return 0; - - // now recurse the IOReg tree - while ((serv = IOIteratorNext(iter)) != MACH_PORT_NULL) - { - CFDictionaryRef info; - io_name_t name; - CFIndex vendorID = 0, productID = 0, serialNumber = 0; - CFNumberRef vendorIDRef, productIDRef, serialNumberRef; - CFStringRef location = CFSTR(""); - //CFStringRef serial = CFSTR(""); - Boolean success = 0; - - // get metadata from IOreg node - IORegistryEntryGetName(serv, name); - info = IODisplayCreateInfoDictionary(serv, kIODisplayOnlyPreferredName); - - /* When assigning a display ID, Quartz considers the following parameters:Vendor, Model, Serial Number and Position in the I/O Kit registry */ - // http://opensource.apple.com//source/IOGraphics/IOGraphics-179.2/IOGraphicsFamily/IOKit/graphics/IOGraphicsTypes.h - CFStringRef locationRef = CFDictionaryGetValue(info, CFSTR(kIODisplayLocationKey)); - location = CFStringCreateCopy(NULL, locationRef); - //CFStringRef serialRef = CFDictionaryGetValue(info, CFSTR(kDisplaySerialString)); - //serial = CFStringCreateCopy(NULL, serialRef); - - if(CFDictionaryGetValueIfPresent(info, CFSTR(kDisplayVendorID), (const void**)&vendorIDRef)) - success = CFNumberGetValue(vendorIDRef, kCFNumberCFIndexType, &vendorID); - - if(CFDictionaryGetValueIfPresent(info, CFSTR(kDisplayProductID), (const void**)&productIDRef)) - success &= CFNumberGetValue(productIDRef, kCFNumberCFIndexType, &productID); - - IOItemCount busCount; - IOFBGetI2CInterfaceCount(serv, &busCount); - - if (!success || busCount < 1) { - // this does not seem to be a DDC-enabled display, skip it - CFRelease(info); - continue; - } else { - // MacBook built-in screens have IOFBI2CInterfaceIDs=(0) but do not respond to DDC comms - // they also do not have a BusType: IOFBI2CInterfaceInfo = ({"IOI2CBusType"=1 .. }) - // if (framebuffer.hasDDCConnect(0)) // https://developer.apple.com/reference/kernel/ioframebuffer/1813510-hasddcconnect?language=objc - // kDisplayBundleKey - // kAppleDisplayTypeKey -- if this is an Apple display, can use IODisplay func to change brightness: http://stackoverflow.com/a/32691700/3878712 - } - - if(CFDictionaryGetValueIfPresent(info, CFSTR(kDisplaySerialNumber), (const void**)&serialNumberRef)) - CFNumberGetValue(serialNumberRef, kCFNumberCFIndexType, &serialNumber); - - // compare IOreg's metadata to CGDisplay's metadata to infer if the IOReg's I2C monitor is the display for the given NSScreen.displayID - if (CGDisplayVendorNumber(displayID) != vendorID || - CGDisplayModelNumber(displayID) != productID || - CGDisplaySerialNumber(displayID) != serialNumber ) // SN is zero in lots of cases, so duplicate-monitors can confuse us :-/ - { - CFRelease(info); - continue; - } - - // considering this IOFramebuffer as the match for the CGDisplay, dump out its information -// printf("VN:%ld PN:%ld SN:%ld", vendorID, productID, serialNumber); -// printf(" UN:%d", CGDisplayUnitNumber(displayID)); -// printf(" IN:%d", iter); - //printf(" Serial:%s\n", CFStringGetCStringPtr(serial, kCFStringEncodingUTF8)); -// printf(" %s %s\n", name, CFStringGetCStringPtr(location, kCFStringEncodingUTF8)); - servicePort = serv; - CFRelease(info); - break; - } - - IOObjectRelease(iter); - return servicePort; -} - -dispatch_semaphore_t DisplayQueue(CGDirectDisplayID displayID) { - static UInt64 queueCount = 0; - static struct DDCQueue {CGDirectDisplayID id; dispatch_semaphore_t queue;} *queues = NULL; - dispatch_semaphore_t queue = NULL; - if (!queues) - queues = calloc(50, sizeof(*queues)); //FIXME: specify - UInt64 i = 0; - while (i < queueCount) - if (queues[i].id == displayID) - break; - else - i++; - if (queues[i].id == displayID) - queue = queues[i].queue; - else - queues[queueCount++] = (struct DDCQueue){displayID, (queue = dispatch_semaphore_create(1))}; - return queue; -} - -bool DisplayRequest(CGDirectDisplayID displayID, IOI2CRequest *request) { - dispatch_semaphore_t queue = DisplayQueue(displayID); - dispatch_semaphore_wait(queue, DISPATCH_TIME_FOREVER); - bool result = false; - io_service_t framebuffer; // https://developer.apple.com/reference/kernel/ioframebuffer - //if ((framebuffer = CGDisplayIOServicePort(displayID))) { // Deprecated in OSX 10.9 - if ((framebuffer = IOFramebufferPortFromCGDisplayID(displayID))) { - IOItemCount busCount; - if (IOFBGetI2CInterfaceCount(framebuffer, &busCount) == KERN_SUCCESS) { - IOOptionBits bus = 0; - while (bus < busCount) { - io_service_t interface; - if (IOFBCopyI2CInterfaceForBus(framebuffer, bus++, &interface) != KERN_SUCCESS) - continue; - CFNumberRef flags = NULL; - CFIndex flag; - if (request->minReplyDelay - && (flags = IORegistryEntryCreateCFProperty(interface, CFSTR(kIOI2CSupportedCommFlagsKey), kCFAllocatorDefault, 0)) - && CFNumberGetValue(flags, kCFNumberCFIndexType, &flag) - && flag == kIOI2CUseSubAddressCommFlag) - request->minReplyDelay *= kMillisecondScale; - if (flags) - CFRelease(flags); - IOI2CConnectRef connect; - if (IOI2CInterfaceOpen(interface, kNilOptions, &connect) == KERN_SUCCESS) { - result = (IOI2CSendRequest(connect, kNilOptions, request) == KERN_SUCCESS); - IOI2CInterfaceClose(connect, kNilOptions); - } - IOObjectRelease(interface); - if (result) break; - } - } - } - if (request->replyTransactionType == kIOI2CNoTransactionType) - usleep(kDelayBase * kMicrosecondScale); - dispatch_semaphore_signal(queue); - return result && request->result == KERN_SUCCESS; -} - -bool DDCWrite(CGDirectDisplayID displayID, struct DDCWriteCommand *write) { - IOI2CRequest request; - UInt8 data[128]; - - bzero( &request, sizeof(request)); - - request.commFlags = 0; - - request.sendAddress = 0x6E; - request.sendTransactionType = kIOI2CSimpleTransactionType; - request.sendBuffer = (vm_address_t) &data[0]; - request.sendBytes = 7; - - data[0] = 0x51; - data[1] = 0x84; - data[2] = 0x03; - data[3] = write->control_id; - data[4] = (write->new_value) >> 8; - data[5] = write->new_value & 255; - data[6] = 0x6E ^ data[0] ^ data[1] ^ data[2] ^ data[3]^ data[4] ^ data[5]; - - - request.replyTransactionType = kIOI2CNoTransactionType; - request.replyBytes = 0; - - - bool result = DisplayRequest(displayID, &request); - return result; -} - -bool DDCRead(CGDirectDisplayID displayID, struct DDCReadCommand *read) { - IOI2CRequest request; - UInt8 reply_data[11] = {}; - bool result = false; - UInt8 data[128]; - - - bzero( &request, sizeof(request)); - - request.commFlags = 0; - - request.sendAddress = 0x6E; - request.sendTransactionType = kIOI2CSimpleTransactionType; - request.sendBuffer = (vm_address_t) &data[0]; - request.sendBytes = 5; - request.minReplyDelay = kDelayBase; - - data[0] = 0x51; - data[1] = 0x82; - data[2] = 0x01; - data[3] = read->control_id; - data[4] = 0x6E ^ data[0] ^ data[1] ^ data[2] ^ data[3]; - - request.replyTransactionType = kIOI2CDDCciReplyTransactionType; - request.replyAddress = 0x6F; - request.replySubAddress = 0x51; - - request.replyBuffer = (vm_address_t) reply_data; - request.replyBytes = sizeof(reply_data); - - result = DisplayRequest(displayID, &request); - result = (result && reply_data[0] == request.sendAddress && reply_data[2] == 0x2 && reply_data[4] == read->control_id && reply_data[10] == (request.replyAddress ^ request.replySubAddress ^ reply_data[1] ^ reply_data[2] ^ reply_data[3] ^ reply_data[4] ^ reply_data[5] ^ reply_data[6] ^ reply_data[7] ^ reply_data[8] ^ reply_data[9])); - read->max_value = reply_data[7]; - read->current_value = reply_data[9]; - return result; -} - -bool EDIDTest(CGDirectDisplayID displayID, struct EDID *edid) { - IOI2CRequest request = {}; - UInt8 data[128] = {}; - request.sendAddress = 0xA0; - request.sendTransactionType = kIOI2CSimpleTransactionType; - request.sendBuffer = (vm_address_t) data; - request.sendBytes = 0x01; - data[0] = 0x00; - request.replyAddress = 0xA1; - request.replyTransactionType = kIOI2CSimpleTransactionType; - request.replyBuffer = (vm_address_t) data; - request.replyBytes = sizeof(data); - if (!DisplayRequest(displayID, &request)) return false; - if (edid) memcpy(edid, &data, 128); - UInt32 i = 0; - UInt8 sum = 0; - while (i < request.replyBytes) { - if (i % 128 == 0) { - if (sum) break; - sum = 0; - } - sum += data[i++]; - } - return !sum; -} diff --git a/NativeDisplayBrightness/Info.plist b/NativeDisplayBrightness/Info.plist index 2dab834..bf78247 100644 --- a/NativeDisplayBrightness/Info.plist +++ b/NativeDisplayBrightness/Info.plist @@ -2,8 +2,6 @@ - LSBackgroundOnly - CFBundleDevelopmentRegion en CFBundleExecutable @@ -19,15 +17,15 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) + LSUIElement + NSHumanReadableCopyright - Copyright © 2016 Benno Krauss. All rights reserved. - NSMainNibFile - MainMenu + Copyright © 2017 JL Jumpertz + Benno Krauss. All rights reserved. NSPrincipalClass NSApplication diff --git a/License.txt b/NativeDisplayBrightness/License.txt similarity index 100% rename from License.txt rename to NativeDisplayBrightness/License.txt diff --git a/NativeDisplayBrightness/OSD.h b/NativeDisplayBrightness/OSD.h index 64f8ae6..7ecc198 100644 --- a/NativeDisplayBrightness/OSD.h +++ b/NativeDisplayBrightness/OSD.h @@ -9,8 +9,6 @@ #ifndef OSD_h #define OSD_h -#include "CoreGraphicsPriv.h" - typedef enum { OSDGraphicBacklight = 1,//0xfffffff7, OSDGraphicEject = 6, diff --git a/NativeDisplayBrightness/SettingsWindowController.h b/NativeDisplayBrightness/SettingsWindowController.h new file mode 100644 index 0000000..7ba39ed --- /dev/null +++ b/NativeDisplayBrightness/SettingsWindowController.h @@ -0,0 +1,35 @@ +// +// SettingsWindowController.h +// NativeDisplayBrightness +// +// Created by Volodymyr Klymenko on 1/28/18. +// Copyright © 2018 Volodymyr Klymenko. All rights reserved. +// + +#import + +@interface SettingsWindowController : NSWindowController + +@property (weak) IBOutlet NSButton *multiMonitor; +@property (weak) IBOutlet NSButton *smoothStep; +@property (weak) IBOutlet NSButton *showBrightness; + +@property (weak) IBOutlet NSPopUpButton *increaseBrightnessKey; +@property (weak) IBOutlet NSPopUpButton *decreaseBrightnessKey; +@property (weak) IBOutlet NSPopUpButton *maxBrightness; + +@property (weak) IBOutlet NSButton *adjustColorTemperature; +@property (weak) IBOutlet NSTextField *colorTemperatureLimitLabel; +@property (weak) IBOutlet NSSlider *colorTemperatureLimit; + +@property (weak) IBOutlet NSTextField *colorTemperatureLessWarmLabel; +@property (weak) IBOutlet NSTextField *colorTemperatureMoreWarmLabel; + +@property (weak) IBOutlet NSTextField *colorTemperatureLessWarmKeyLabel; +@property (weak) IBOutlet NSPopUpButton *colorTemperatureLessWarmKey; + +@property (weak) IBOutlet NSTextField *colorTemperatureMoreWarmKeyLabel; +@property (weak) IBOutlet NSPopUpButton *colorTemperatureMoreWarmKey; + + +@end diff --git a/NativeDisplayBrightness/SettingsWindowController.m b/NativeDisplayBrightness/SettingsWindowController.m new file mode 100644 index 0000000..6bf1941 --- /dev/null +++ b/NativeDisplayBrightness/SettingsWindowController.m @@ -0,0 +1,169 @@ +// +// SettingsWindowController.m +// NativeDisplayBrightness +// +// Created by Volodymyr Klymenko on 1/28/18. +// Copyright © 2018 Volodymyr Klymenko. All rights reserved. +// + +#import "SettingsWindowController.h" +#import "Config.h" + +@interface SettingsWindowController () + +@end + +@implementation SettingsWindowController + +-(void)windowDidLoad { + [super windowDidLoad]; + + [self.decreaseBrightnessKey removeAllItems]; + [self.increaseBrightnessKey removeAllItems]; + [self.colorTemperatureLessWarmKey removeAllItems]; + [self.colorTemperatureMoreWarmKey removeAllItems]; + + for (int i = 1; i <= 20; i++) { + NSString *item = [NSString stringWithFormat:@"F%d",i]; + [self.decreaseBrightnessKey addItemWithTitle:item]; + [self.increaseBrightnessKey addItemWithTitle:item]; + [self.colorTemperatureLessWarmKey addItemWithTitle:item]; + [self.colorTemperatureMoreWarmKey addItemWithTitle:item]; + } + [self.increaseBrightnessKey selectItemWithTitle:APP_DELEGATE.increaseBrightnessKey]; + [self.decreaseBrightnessKey selectItemWithTitle:APP_DELEGATE.decreaseBrightnessKey]; + [self.colorTemperatureLessWarmKey selectItemWithTitle:APP_DELEGATE.colorTemperatureLessWarmKey]; + [self.colorTemperatureMoreWarmKey selectItemWithTitle:APP_DELEGATE.colorTemperatureMoreWarmKey]; + + [self updateKeys]; + + self.multiMonitor.state = APP_DELEGATE.multiMonitor; + self.smoothStep.state = APP_DELEGATE.smoothStep; + self.showBrightness.state = APP_DELEGATE.showBrightness; + [self.maxBrightness selectItemWithTag: APP_DELEGATE.maxBrightness]; + + self.adjustColorTemperature.state = APP_DELEGATE.adjustColorTemperature; + self.colorTemperatureLimit.floatValue = APP_DELEGATE.colorTemperatureLimit; + + [self updateColorTemperatureStates:self.adjustColorTemperature.state]; + + //system not supports it, disable all controls + if (!APP_DELEGATE.supportsBlueLightReduction) { + self.adjustColorTemperature.enabled = NO; + self.colorTemperatureLimit.enabled = NO; + } +} + +- (void)windowWillClose:(NSNotification *)notification { + [NSApp stopModal]; +} + +- (IBAction)multiMonitor:(NSButton *)sender { + APP_DELEGATE.multiMonitor = sender.state; +} + +- (IBAction)smoothStep:(NSButton *)sender { + APP_DELEGATE.smoothStep = sender.state; +} + +- (IBAction)showBrightness:(NSButton *)sender { + APP_DELEGATE.showBrightness = sender.state; + if (sender.state == NSControlStateValueOn) { + APP_DELEGATE.statusBarIcon.title = [NSString stringWithFormat:@"%i%%",APP_DELEGATE.currentBrightness]; + APP_DELEGATE.statusBarIcon.length = APP_DELEGATE.currentBrightness == 100 ? STATUS_ICON_WIDTH_TEXT_100 : STATUS_ICON_WIDTH_TEXT; + } else { + APP_DELEGATE.statusBarIcon.title = @""; + APP_DELEGATE.statusBarIcon.length = STATUS_ICON_WIDTH; + } +} + +- (IBAction)changeDecreaseBrightnessKey:(NSPopUpButton *)sender { + APP_DELEGATE.decreaseBrightnessKey = sender.selectedItem.title; + [self updateKeys]; +} + +- (IBAction)changeIncreaseBrightnessKey:(NSPopUpButton *)sender { + APP_DELEGATE.increaseBrightnessKey = sender.selectedItem.title; + [self updateKeys]; +} + +- (IBAction)maxBrightness:(NSPopUpButton *)sender { + APP_DELEGATE.maxBrightness = (int)sender.selectedTag; +} + +- (IBAction)adjustColorTemperature:(NSButton *)sender { + APP_DELEGATE.adjustColorTemperature = sender.state; + [self updateColorTemperatureStates:sender.state]; + + if (!sender.state) { + [APP_DELEGATE.statusBarMenu removeItem:APP_DELEGATE.colorTemperatureMenu]; + } else { + [APP_DELEGATE.statusBarMenu insertItem:APP_DELEGATE.colorTemperatureMenu atIndex:1]; + } +} + +- (IBAction)colorTemperatureLimit:(NSSlider *)sender { + APP_DELEGATE.colorTemperatureLimit = sender.floatValue; +} + +- (IBAction)changeColorTemperatureLessWarmKey:(NSPopUpButton *)sender { + APP_DELEGATE.colorTemperatureLessWarmKey = sender.selectedItem.title; + [self updateKeys]; +} + +- (IBAction)changeColorTemperatureMoreWarmKey:(NSPopUpButton *)sender { + APP_DELEGATE.colorTemperatureMoreWarmKey = sender.selectedItem.title; + [self updateKeys]; +} + +- (void)updateKeys { + + //needed!? +// [self enableAllItems:self.increaseBrightnessKey.itemArray]; +// [self enableAllItems:self.decreaseBrightnessKey.itemArray]; +// [self enableAllItems:self.colorTemperatureLessWarmKey.itemArray]; +// [self enableAllItems:self.colorTemperatureMoreWarmKey.itemArray]; +// +// [self.increaseBrightnessKey itemWithTitle:APP_DELEGATE.decreaseBrightnessKey].enabled = NO; +// [self.increaseBrightnessKey itemWithTitle:APP_DELEGATE.colorTemperatureLessWarmKey].enabled = NO; +// [self.increaseBrightnessKey itemWithTitle:APP_DELEGATE.colorTemperatureMoreWarmKey].enabled = NO; +// +// [self.decreaseBrightnessKey itemWithTitle:APP_DELEGATE.increaseBrightnessKey].enabled = NO; +// [self.decreaseBrightnessKey itemWithTitle:APP_DELEGATE.colorTemperatureLessWarmKey].enabled = NO; +// [self.decreaseBrightnessKey itemWithTitle:APP_DELEGATE.colorTemperatureMoreWarmKey].enabled = NO; +// +// [self.colorTemperatureLessWarmKey itemWithTitle:APP_DELEGATE.increaseBrightnessKey].enabled = NO; +// [self.colorTemperatureLessWarmKey itemWithTitle:APP_DELEGATE.decreaseBrightnessKey].enabled = NO; +// [self.colorTemperatureLessWarmKey itemWithTitle:APP_DELEGATE.colorTemperatureMoreWarmKey].enabled = NO; +// +// [self.colorTemperatureMoreWarmKey itemWithTitle:APP_DELEGATE.increaseBrightnessKey].enabled = NO; +// [self.colorTemperatureMoreWarmKey itemWithTitle:APP_DELEGATE.decreaseBrightnessKey].enabled = NO; +// [self.colorTemperatureMoreWarmKey itemWithTitle:APP_DELEGATE.colorTemperatureLessWarmKey].enabled = NO; +} + +- (void) enableAllItems:(NSArray *) items { + for (NSMenuItem *item in items) { + item.enabled = YES; + } +} + +- (void)updateColorTemperatureStates:(bool) state { + + NSColor *color = state ? [NSColor textColor] : [NSColor disabledControlTextColor]; + + self.colorTemperatureLimit.enabled = state; + + self.colorTemperatureLimitLabel.textColor = color; + self.colorTemperatureLimit.enabled = state; + + self.colorTemperatureLessWarmLabel.textColor = color; + self.colorTemperatureMoreWarmLabel.textColor = color; + + self.colorTemperatureLessWarmKeyLabel.textColor = color; + self.colorTemperatureLessWarmKey.enabled = state; + + self.colorTemperatureMoreWarmKeyLabel.textColor = color; + self.colorTemperatureMoreWarmKey.enabled = state; +} + +@end diff --git a/NativeDisplayBrightness/SettingsWindowController.xib b/NativeDisplayBrightness/SettingsWindowController.xib new file mode 100644 index 0000000..0e05f56 --- /dev/null +++ b/NativeDisplayBrightness/SettingsWindowController.xib @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NativeDisplayBrightness/main.m b/NativeDisplayBrightness/main.m index 6aec762..c314c8d 100644 --- a/NativeDisplayBrightness/main.m +++ b/NativeDisplayBrightness/main.m @@ -7,7 +7,14 @@ // #import +#import "AppDelegate.h" int main(int argc, const char * argv[]) { - return NSApplicationMain(argc, argv); + @autoreleasepool { + NSApplication *application = [NSApplication sharedApplication]; + AppDelegate *appDelegate = [[AppDelegate alloc] init]; + [application setDelegate:appDelegate]; + [application run]; + } + return EXIT_SUCCESS; } diff --git a/Readme.md b/Readme.md index 05c461b..3549a7a 100644 --- a/Readme.md +++ b/Readme.md @@ -1,14 +1,43 @@ +# Project is not actively developed. + +This application was originally light and simple, for one monitor. + +# As a whole new level, I highly recommend this incredibly cool project: +# https://github.com/MonitorControl/MonitorControl + # NativeDisplayBrightness +https://www.tonymacx86.com/threads/nativedisplaybrightness-with-gui-fork.256152/ + *Control your desktop monitor brightness just like on a MacBook!* -![native brightness UI](https://raw.githubusercontent.com/Bensge/NativeDisplayBrightness/master/nativeUI.png) +![](Info/nativeUI.png) + +![](Info/Settings.png) + +This a utility application to control the brightness and color temperature of an external monitor directly from your keyboard. + +Use the `F1` / `F2` key to decrease / increase the brightness of the external monitor. If you have an Apple or similar keyboard, you probably need to also press the `fn` key. + +Also you can set any key from `F1` to `F20` in the settings for changing of brightness or color temperature. + +For a finer brightness level adjustment, you add the option key, i.e use `Alt` + `F1` to decrease the brightness, or `Alt` + `F2` to increase it. Also you can enable smooth step in the settings. + +This app shows the **native** system UI when changing brightness! It uses the private `BezelServices` framework for this. + +## Monitors compatibility + +Your monitor needs to support DDC/CI for this app to work. If you don't see the brightness system UI displayed on your monitor when pressing the F1 / F2 keys, this means that your monitor is not supported. + +If your monitor supports reading the current brightness value from DDC/CI, the app increments / decrements the brightness starting from the monitor current brightness value. This allows you to set the brightness using the monitor's OSD and to adjust it later with the app + +## Multiple monitors support -This a utility application to control monitor brightness with the F1, F2 keys. It utilizes DDC/CI, but this app doesn't have the freezing issues that similar aplications tend to suffer from. +If you have multiple external monitors connected to your Mac, the brightness adjustment is done on the monitor with the currently active window, and the brightness system UI is displayed on the adjusted monitor. -This app also shows the **native** system UI when changing brightness! It uses the private `BezelServices` framework for this. +## Requirements -Needless to say, your monitor needs to support DDC/CI for this app to work. +macOS version: 10.12.4 or later ## License diff --git a/nativeUI.png b/nativeUI.png deleted file mode 100644 index 56cfaa5..0000000 Binary files a/nativeUI.png and /dev/null differ