diff --git a/BrightnessMenulet/.DS_Store b/BrightnessMenulet/.DS_Store deleted file mode 100644 index 7b6d0b5..0000000 Binary files a/BrightnessMenulet/.DS_Store and /dev/null differ diff --git a/BrightnessMenulet/AppDelegate.m b/BrightnessMenulet/AppDelegate.m index 2762588..b2452e8 100644 --- a/BrightnessMenulet/AppDelegate.m +++ b/BrightnessMenulet/AppDelegate.m @@ -16,8 +16,25 @@ @interface AppDelegate () @end + @implementation AppDelegate +- (void)awakeFromNib +{ + NSLog(@"%@ %@", + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"], + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]); + + if ([[NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]] count] > 1) { + NSLog(@"... is already running!"); + [NSApp terminate:nil]; + } + + // just to be shure, that we didn't miss something super fancy + [super awakeFromNib]; +} + + - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Set Menulet Icon NSBundle *bundle = [NSBundle mainBundle]; @@ -36,8 +53,10 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // init _mainMenu [_mainMenu refreshMenuScreens]; + // LMU [LMUController singleton]; lmuCon.delegate = _mainMenu; + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; @@ -46,14 +65,25 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { if([defaults boolForKey:@"autoBrightOnStartup"]) [lmuCon startMonitoring]; + + // Unregister hotkeys + [_mainMenu registerHotKeys]; } - (void)applicationDidChangeScreenParameters:(NSNotification *)notification { NSLog(@"AppDelegate: DidChangeScreenParameters"); // BUG: May crash if displays are connected/disconnected quickly so lets try waiting - [NSThread sleepForTimeInterval:2.0f]; - [_mainMenu refreshMenuScreens]; + [NSTimer scheduledTimerWithTimeInterval:2.0f + target:_mainMenu + selector:@selector(refreshMenuScreens) + userInfo:nil + repeats:NO]; +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Unregister hotkeys + [_mainMenu unregisterHotKeys]; } @end diff --git a/BrightnessMenulet/BrightnessMenulet.xcodeproj/project.pbxproj b/BrightnessMenulet/BrightnessMenulet.xcodeproj/project.pbxproj index 32c91ba..b1fae05 100755 --- a/BrightnessMenulet/BrightnessMenulet.xcodeproj/project.pbxproj +++ b/BrightnessMenulet/BrightnessMenulet.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 0B4E1FEC19E5FD4B0044BAFE /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0B4E1FEB19E5FD4B0044BAFE /* Preferences.xib */; }; 0B4E1FEF19E5FEBB0044BAFE /* PreferencesController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B4E1FEE19E5FEBB0044BAFE /* PreferencesController.m */; }; 0BCFE67319C9F1B40094B561 /* DDCControls.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCFE67219C9F1B30094B561 /* DDCControls.m */; }; + 498A53C91E00AA190036FC5A /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 498A53C81E00AA190036FC5A /* Carbon.framework */; }; 7D2FF98311406B0100707C79 /* MainMenuController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D2FF98211406B0100707C79 /* MainMenuController.m */; }; 7D2FF98611406B2C00707C79 /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D2FF98411406B2C00707C79 /* icon.png */; }; 7D2FF98711406B2C00707C79 /* icon-alt.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D2FF98511406B2C00707C79 /* icon-alt.png */; }; @@ -37,6 +38,8 @@ 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 32CA4F630368D1EE00C91783 /* BrightnessMenulet_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BrightnessMenulet_Prefix.pch; sourceTree = ""; }; + 498493181E01EFFA00FA89FD /* VirtualKeyCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VirtualKeyCodes.h; sourceTree = ""; }; + 498A53C81E00AA190036FC5A /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; 7D2FF98111406B0100707C79 /* MainMenuController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainMenuController.h; sourceTree = ""; }; 7D2FF98211406B0100707C79 /* MainMenuController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainMenuController.m; sourceTree = ""; }; 7D2FF98411406B2C00707C79 /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = ""; }; @@ -60,6 +63,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 498A53C91E00AA190036FC5A /* Carbon.framework in Frameworks */, 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 7D2FF9FC1140B94600707C79 /* IOKit.framework in Frameworks */, ); @@ -138,6 +142,7 @@ 29B97314FDCFA39411CA2CEA /* MyStatusItem */ = { isa = PBXGroup; children = ( + 498493181E01EFFA00FA89FD /* VirtualKeyCodes.h */, 0B38B5E419E8EF9200D72911 /* AppDelegate.h */, 0B38B5E519E8EF9200D72911 /* AppDelegate.m */, 0B8CF6CC19E71BA400F3FD6F /* Menu */, @@ -165,6 +170,7 @@ 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( + 498A53C81E00AA190036FC5A /* Carbon.framework */, 79897D331664F808008C7D5C /* ddc */, 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, @@ -222,7 +228,7 @@ LastUpgradeCheck = 0700; TargetAttributes = { 8D1107260486CEB800E47090 = { - DevelopmentTeam = N6A2S4TCDQ; + ProvisioningStyle = Manual; }; }; }; @@ -281,9 +287,10 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_OBJC_ARC = YES; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = ""; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; @@ -306,9 +313,10 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_OBJC_ARC = YES; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = BrightnessMenulet_Prefix.pch; diff --git a/BrightnessMenulet/Brightness_Menulet.zip b/BrightnessMenulet/Brightness_Menulet.zip deleted file mode 100644 index ec25e84..0000000 Binary files a/BrightnessMenulet/Brightness_Menulet.zip and /dev/null differ diff --git a/BrightnessMenulet/DDCControls.h b/BrightnessMenulet/DDCControls.h index 6b67b6b..830bedb 100644 --- a/BrightnessMenulet/DDCControls.h +++ b/BrightnessMenulet/DDCControls.h @@ -19,7 +19,7 @@ + (DDCControls*)singleton; - (NSString*)EDIDString:(char*) string; -- (struct DDCReadResponse)readDisplay:(CGDirectDisplayID)display_id controlValue:(int)control; +- (struct DDCReadCommand)readDisplay:(CGDirectDisplayID)display_id controlValue:(int)control; - (void)changeDisplay:(CGDirectDisplayID)display_id control:(int)control withValue:(int)value; - (void)refreshScreens; diff --git a/BrightnessMenulet/DDCControls.m b/BrightnessMenulet/DDCControls.m index 8048e6f..24e99ba 100644 --- a/BrightnessMenulet/DDCControls.m +++ b/BrightnessMenulet/DDCControls.m @@ -29,19 +29,19 @@ - (NSString*)EDIDString:(char*) string { : temp; } -- (struct DDCReadResponse)readDisplay:(CGDirectDisplayID)display_id controlValue:(int)control{ +- (struct DDCReadCommand)readDisplay:(CGDirectDisplayID)display_id controlValue:(int)control{ struct DDCReadCommand read_command = (struct DDCReadCommand){.control_id = control}; - if(ddc_read(display_id, &read_command) != 1) + if(DDCRead(display_id, &read_command) != 1) NSLog(@"readDisplay:%u controlValue: failed need to retry...", display_id); - return read_command.response; + return read_command; } - (void)changeDisplay:(CGDirectDisplayID)display_id control:(int)control withValue:(int)value{ struct DDCWriteCommand write_command = (struct DDCWriteCommand){.control_id = control, .new_value = value}; - if(ddc_write(display_id, &write_command) != 1) + if(DDCWrite(display_id, &write_command) != 1) NSLog(@"writeDisplay:%u withValue: failed need to retry...", display_id); } @@ -55,31 +55,42 @@ - (void)refreshScreens { // Fetch Monitor info via EDID struct EDID edid = {}; - EDIDRead([screenNumber unsignedIntegerValue], &edid); + if (EDIDTest([screenNumber unsignedIntegerValue], &edid)) { - NSString* name; - NSString* serial; - for (NSValue *value in @[[NSValue valueWithPointer:&edid.descriptor1], [NSValue valueWithPointer:&edid.descriptor2], [NSValue valueWithPointer:&edid.descriptor3], [NSValue valueWithPointer:&edid.descriptor4]]) { - union descriptor *des = value.pointerValue; - switch (des->text.type) { - case 0xFF: - serial = [self EDIDString:des->text.data]; - break; - case 0xFC: - name = [self EDIDString:des->text.data]; - break; + NSString* name; + NSString* serial; + for (NSValue *value in @[[NSValue valueWithPointer:&edid.descriptor1], [NSValue valueWithPointer:&edid.descriptor2], [NSValue valueWithPointer:&edid.descriptor3], [NSValue valueWithPointer:&edid.descriptor4]]) { + union descriptor *des = value.pointerValue; + switch (des->text.type) { + case 0xFF: + serial = [self EDIDString:des->text.data]; + break; + case 0xFC: + name = [self EDIDString:des->text.data]; + break; + } } - } - // don't want to manage invalid screen or integrated LCD - if(!name || [name isEqualToString:@"Color LCD"] || [name isEqualToString:@"iMac"]) continue; + // don't want to manage invalid screen or integrated LCD + if(!name || [name isEqualToString:@"Color LCD"] || [name isEqualToString:@"iMac"]) continue; + + // skipping screen's, that don't support data reading + struct DDCReadCommand read_command = (struct DDCReadCommand){.control_id = BRIGHTNESS}; + if(DDCRead([screenNumber unsignedIntegerValue] , &read_command) != 1) { + NSLog(@"Reading data from display:%@ failed ", screenNumber); + NSLog(@"... skipping %@ ", name); + continue; + } - // Build screen instance - NSLog(@"DDCControls: Found %@ - %@", name, screenNumber); - Screen* screen = [[Screen alloc] initWithModel:name screenID:[screenNumber unsignedIntegerValue] serial:serial]; - [screen refreshValues]; + // Build screen instance + NSLog(@"DDCControls: Found %@ - %@", name, screenNumber); + Screen* screen = [[Screen alloc] initWithModel:name screenID:[screenNumber unsignedIntegerValue] serial:serial]; + [screen refreshValues]; - [newScreens addObject:screen]; + [newScreens addObject:screen]; + } else { + NSLog(@"Failed to poll display: %@", screenNumber); + } } _screens = [newScreens copy]; diff --git a/BrightnessMenulet/Info.plist b/BrightnessMenulet/Info.plist index 4b1c0ef..2a01afa 100755 --- a/BrightnessMenulet/Info.plist +++ b/BrightnessMenulet/Info.plist @@ -4,6 +4,8 @@ CFBundleDevelopmentRegion English + CFBundleDisplayName + ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile diff --git a/BrightnessMenulet/LMUController.h b/BrightnessMenulet/LMUController.h index 9854aef..a68a3fe 100644 --- a/BrightnessMenulet/LMUController.h +++ b/BrightnessMenulet/LMUController.h @@ -16,6 +16,7 @@ @property (weak) id delegate; +@property BOOL available; @property BOOL monitoring; + (LMUController*)singleton; diff --git a/BrightnessMenulet/LMUController.m b/BrightnessMenulet/LMUController.m index 72e11f8..39d1470 100644 --- a/BrightnessMenulet/LMUController.m +++ b/BrightnessMenulet/LMUController.m @@ -50,11 +50,13 @@ - (io_connect_t)getLMUDataPort { io_service_t serviceObject; if(_lmuDataPort) return _lmuDataPort; + self.available = YES; serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleLMUController")); if(!serviceObject){ NSLog(@"LMUController: Failed to find LMU\n"); + self.available = NO; return 0; } @@ -64,6 +66,7 @@ - (io_connect_t)getLMUDataPort { if(kr != KERN_SUCCESS){ NSLog(@"LMUController: Failed to open LMU IOService object"); + self.available = NO; return 0; } diff --git a/BrightnessMenulet/MainMenuController.h b/BrightnessMenulet/MainMenuController.h index 69ad10f..bdc1374 100755 --- a/BrightnessMenulet/MainMenuController.h +++ b/BrightnessMenulet/MainMenuController.h @@ -14,4 +14,7 @@ - (void)refreshMenuScreens; +- (void)registerHotKeys; +- (void)unregisterHotKeys; + @end diff --git a/BrightnessMenulet/MainMenuController.m b/BrightnessMenulet/MainMenuController.m index b4f71b3..6403ce0 100755 --- a/BrightnessMenulet/MainMenuController.m +++ b/BrightnessMenulet/MainMenuController.m @@ -11,14 +11,21 @@ #import "MainMenuController.h" #import "PreferencesController.h" -@interface MainMenuController () +#import "VirtualKeyCodes.h" +#import + +@interface MainMenuController () { + EventHotKeyRef hotKeyRef; +} @property PreferencesController* preferencesController; @property (weak) IBOutlet NSMenuItem *autoBrightnessItem; +@property (assign, nonatomic) BOOL darkModeOn; @end + @implementation MainMenuController - (void)refreshMenuScreens { @@ -38,6 +45,14 @@ - (void)refreshMenuScreens { [lmuCon stopMonitoring]; return; } + + // No LMU available + if(!lmuCon.available) { + if(self.autoBrightnessItem) { + [self removeItem:self.autoBrightnessItem]; + NSLog(@"Remove 'Auto-Brightness' menu item"); + } + } // add new outlets for screens for(Screen* screen in controls.screens){ @@ -98,7 +113,8 @@ - (void)sliderUpdate:(NSSlider*)slider { } - (IBAction)quit:(id)sender { - exit(1); + //exit(1); + [[NSApplication sharedApplication] terminate:self]; } #pragma mark - LMUDelegate @@ -111,4 +127,93 @@ - (void)LMUControllerDidStopMonitoring { [_autoBrightnessItem setState:NSOffState]; } +#pragma mark - Global HotKeys +-(void)registerHotKeys +{ + //EventHotKeyRef hotKeyRef; + if (!hotKeyRef) { + NSLog(@"Register HotKeys"); + + EventHotKeyID hotKeyID; + EventTypeSpec eventType; + eventType.eventClass=kEventClassKeyboard; + eventType.eventKind=kEventHotKeyPressed; + + InstallApplicationEventHandler(&OnHotKeyEvent, 1, &eventType, (void *)CFBridgingRetain(self), NULL); + + hotKeyID.signature='htk1'; + hotKeyID.id=1; + RegisterEventHotKey(kVK_ANSI_T, cmdKey+optionKey, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef); + + hotKeyID.signature='htk2'; + hotKeyID.id=2; + RegisterEventHotKey(kVK_BrightnessUp, 0, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef); + + hotKeyID.signature='htk3'; + hotKeyID.id=3; + RegisterEventHotKey(kVK_BrightnessDown, 0, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef); + + hotKeyID.signature='htk4'; + hotKeyID.id=4; + RegisterEventHotKey(kVK_BrightnessUp, cmdKey+optionKey, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef); + + hotKeyID.signature='htk5'; + hotKeyID.id=5; + RegisterEventHotKey(kVK_BrightnessDown, cmdKey+optionKey, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef); + } +} + +- (void)unregisterHotKeys { + if (hotKeyRef) { + NSLog(@"Unregister HotKeys"); + UnregisterEventHotKey(hotKeyRef); + hotKeyRef = 0; + } +} + +OSStatus OnHotKeyEvent(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) +{ + EventHotKeyID hkCom; + + GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hkCom), NULL, &hkCom); + MainMenuController *app = (__bridge MainMenuController *)userData; + + int l = hkCom.id; + + switch (l) { + case 1: + NSLog(@"Capture COMMAND + OPTION + T"); + [app helloWorld]; + break; + case 2: + NSLog(@"Capture BRIGHTNESS_UP"); + if ([controls.screens count] < 1) return 0; + [controls.screens[0] setBrightnessRelativeToValue:@"5+"]; + break; + case 3: + NSLog(@"Capture BRIGHTNESS_DOWN"); + if ([controls.screens count] < 1) return 0; + [controls.screens[0] setBrightnessRelativeToValue:@"5-"]; + break; + case 4: + NSLog(@"Capture COMMAND + OPTION + BRIGHTNESS_UP"); + for(Screen* screen in controls.screens) { + [[controls screenForDisplayID:screen.screenNumber] setBrightnessRelativeToValue:@"5+"]; + } + break; + case 5: + NSLog(@"Capture COMMAND + OPTION + BRIGHTNESS_DOWN"); + for(Screen* screen in controls.screens) { + [[controls screenForDisplayID:screen.screenNumber] setBrightnessRelativeToValue:@"5-"]; + } + break; + } + + return noErr; +} + +- (void)helloWorld { + NSLog(@"Hello World"); +} + @end diff --git a/BrightnessMenulet/Preferences.xib b/BrightnessMenulet/Preferences.xib index d725301..07312db 100644 --- a/BrightnessMenulet/Preferences.xib +++ b/BrightnessMenulet/Preferences.xib @@ -1,14 +1,18 @@ - - + + - + + + + + @@ -16,7 +20,13 @@ + + + + + + @@ -27,20 +37,22 @@ - - + + - + - - + + + + @@ -52,6 +64,7 @@ + @@ -62,14 +75,16 @@ - + + - + + @@ -77,6 +92,7 @@ + @@ -84,6 +100,7 @@ + @@ -91,6 +108,7 @@ + @@ -99,6 +117,7 @@ + @@ -107,11 +126,10 @@ - - - + + @@ -128,7 +146,8 @@ - + + - + + @@ -162,6 +184,7 @@ + @@ -173,6 +196,7 @@ + @@ -180,6 +204,7 @@ + @@ -188,11 +213,10 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/BrightnessMenulet/PreferencesController.m b/BrightnessMenulet/PreferencesController.m index 8baec03..95357e5 100644 --- a/BrightnessMenulet/PreferencesController.m +++ b/BrightnessMenulet/PreferencesController.m @@ -24,9 +24,23 @@ @interface PreferencesController () @property (weak) IBOutlet NSTextField* contCTextField; @property (weak) IBOutlet NSStepper* contCStepper; +// RGB Colors +@property (weak) IBOutlet NSSlider* redCSlider; +@property (weak) IBOutlet NSTextField* redCTextField; +@property (weak) IBOutlet NSStepper* redCStepper; +@property (weak) IBOutlet NSSlider* greenCSlider; +@property (weak) IBOutlet NSTextField* greenCTextField; +@property (weak) IBOutlet NSStepper* greenCStepper; +@property (weak) IBOutlet NSSlider* blueCSlider; +@property (weak) IBOutlet NSTextField* blueCTextField; +@property (weak) IBOutlet NSStepper* blueCStepper; + // If only OSX supported IBOutlet​Collection... @property (strong) NSArray* brightnessOutlets; @property (strong) NSArray* contrastOutlets; +@property (strong) NSArray* redOutlets; +@property (strong) NSArray* greenOutlets; +@property (strong) NSArray* blueOutlets; // Auto-Brightness IBOutlets @property (weak) IBOutlet NSButton *autoBrightOnStartupButton; @@ -46,7 +60,10 @@ - (void)showWindow { if(!_preferenceWindow){ NSLog(@"PreferencesController: Pref Window alloc"); [[NSBundle mainBundle] loadNibNamed:@"Preferences" owner:self topLevelObjects:nil]; - + + // Save the last location (to fix a xcode bug, we have to set this here) + _preferenceWindow.frameAutosaveName = @"PreferencesWindowLocation"; + _preferenceWindow.delegate = self; NSNumberFormatter* decFormater = [[NSNumberFormatter alloc] init]; @@ -54,9 +71,17 @@ - (void)showWindow { [_brightCTextField setFormatter:decFormater]; [_contCTextField setFormatter:decFormater]; + + [_redCTextField setFormatter:decFormater]; + [_greenCTextField setFormatter:decFormater]; + [_blueCTextField setFormatter:decFormater]; _brightnessOutlets = @[_brightCSlider, _brightCTextField, _brightCStepper]; _contrastOutlets = @[_contCSlider, _contCTextField, _contCStepper]; + + _redOutlets = @[_redCSlider, _redCTextField, _redCStepper]; + _greenOutlets = @[_greenCSlider, _greenCTextField, _greenCStepper]; + _blueOutlets = @[_blueCSlider, _blueCTextField, _blueCStepper]; _updateIntervalOutlets = @[_updateIntervalSlider, _updateIntTextField, _updateIntStepper]; @@ -83,6 +108,10 @@ - (void)showWindow { [self updateBrightnessControls]; [self updateContrastControls]; + + [self updateRedControls]; + [self updateGreenControls]; + [self updateBlueControls]; [[self preferenceWindow] makeKeyAndOrderFront:self]; // does not order front? @@ -112,12 +141,50 @@ - (void)updateContrastControls { } } +- (void)updateRedControls { + NSInteger currentRed = _currentScreen.currentRed; + + for(id redOutlet in _redOutlets){ + if(![redOutlet isKindOfClass:[NSTextField class]]) + [redOutlet setMaxValue:_currentScreen.maxRed]; + + [redOutlet setIntValue:currentRed]; + } +} +- (void)updateGreenControls { + NSInteger currentGreen = _currentScreen.currentGreen; + + for(id greenOutlet in _greenOutlets){ + if(![greenOutlet isKindOfClass:[NSTextField class]]) + [greenOutlet setMaxValue:_currentScreen.maxGreen]; + + [greenOutlet setIntValue:currentGreen]; + } +} + +- (void)updateBlueControls { + NSInteger currentBlue = _currentScreen.currentBlue; + + for(id blueOutlet in _blueOutlets){ + if(![blueOutlet isKindOfClass:[NSTextField class]]) + [blueOutlet setMaxValue:_currentScreen.maxBlue]; + + [blueOutlet setIntValue:currentBlue]; + } +} + + + - (void)refreshScreenPopUpList { // Reset Variables [_displayPopUpButton removeAllItems]; [_currentScreen.brightnessOutlets removeObjectsInArray:_brightnessOutlets]; [_currentScreen.contrastOutlets removeObjectsInArray:_contrastOutlets]; + [_currentScreen.redOutlets removeObjectsInArray:_redOutlets]; + [_currentScreen.greenOutlets removeObjectsInArray:_greenOutlets]; + [_currentScreen.blueOutlets removeObjectsInArray:_blueOutlets]; + if([controls.screens count] == 0){ // no screens so disable outlets [_displayPopUpButton setEnabled:NO]; @@ -125,7 +192,9 @@ - (void)refreshScreenPopUpList { // makeObjectsPerformSelector:withObject: only allows NO because it is same as nil lol... [_brightnessOutlets makeObjectsPerformSelector:@selector(setEnabled:) withObject:NO]; [_contrastOutlets makeObjectsPerformSelector:@selector(setEnabled:) withObject:NO]; - + [_redOutlets makeObjectsPerformSelector:@selector(setEnabled:) withObject:NO]; + [_greenOutlets makeObjectsPerformSelector:@selector(setEnabled:) withObject:NO]; + [_blueOutlets makeObjectsPerformSelector:@selector(setEnabled:) withObject:NO]; return; } @@ -145,8 +214,15 @@ - (void)refreshScreenPopUpList { [_currentScreen.brightnessOutlets addObjectsFromArray:_brightnessOutlets]; [_currentScreen.contrastOutlets addObjectsFromArray:_contrastOutlets]; + [_currentScreen.redOutlets addObjectsFromArray:_redOutlets]; + [_currentScreen.greenOutlets addObjectsFromArray:_greenOutlets]; + [_currentScreen.blueOutlets addObjectsFromArray:_blueOutlets]; + [self updateBrightnessControls]; [self updateContrastControls]; + [self updateRedControls]; + [self updateGreenControls]; + [self updateBlueControls]; } #pragma mark - Brightness and Contrast IBActions @@ -157,15 +233,26 @@ - (IBAction)didChangeDisplayMenu:(id)sender { // remove outlets from old screen [_currentScreen.brightnessOutlets removeObjectsInArray:_brightnessOutlets]; [_currentScreen.contrastOutlets removeObjectsInArray:_contrastOutlets]; + + [_currentScreen.redOutlets removeObjectsInArray:_redOutlets]; + [_currentScreen.greenOutlets removeObjectsInArray:_greenOutlets]; + [_currentScreen.blueOutlets removeObjectsInArray:_blueOutlets]; _currentScreen = [controls screenForDisplayName:selectedItem]; // Add outlets to new _currentScreen [_currentScreen.brightnessOutlets addObjectsFromArray:_brightnessOutlets]; [_currentScreen.contrastOutlets addObjectsFromArray:_contrastOutlets]; + + [_currentScreen.redOutlets addObjectsFromArray:_redOutlets]; + [_currentScreen.greenOutlets addObjectsFromArray:_greenOutlets]; + [_currentScreen.blueOutlets addObjectsFromArray:_blueOutlets]; [self updateBrightnessControls]; [self updateContrastControls]; + [self updateRedControls]; + [self updateGreenControls]; + [self updateBlueControls]; } - (IBAction)pressedDebug:(NSButton *)sender { @@ -196,6 +283,36 @@ - (IBAction)contrastOutletValueChanged:(id)sender{ [outlet setIntegerValue:[sender integerValue]]; } +- (IBAction)redOutletValueChanged:(id)sender{ + [_currentScreen setRed:[sender integerValue] byOutlet:sender]; + + NSMutableArray* dirtyOutlets = [_redOutlets mutableCopy]; + [dirtyOutlets removeObject:sender]; + + for(id outlet in dirtyOutlets) + [outlet setIntegerValue:[sender integerValue]]; +} + +- (IBAction)greenOutletValueChanged:(id)sender{ + [_currentScreen setGreen:[sender integerValue] byOutlet:sender]; + + NSMutableArray* dirtyOutlets = [_greenOutlets mutableCopy]; + [dirtyOutlets removeObject:sender]; + + for(id outlet in dirtyOutlets) + [outlet setIntegerValue:[sender integerValue]]; +} + +- (IBAction)blueOutletValueChanged:(id)sender{ + [_currentScreen setBlue:[sender integerValue] byOutlet:sender]; + + NSMutableArray* dirtyOutlets = [_blueOutlets mutableCopy]; + [dirtyOutlets removeObject:sender]; + + for(id outlet in dirtyOutlets) + [outlet setIntegerValue:[sender integerValue]]; +} + #pragma mark - Auto-Brightness IBActions - (IBAction)didToggleAutoBrightOnStartupButton:(NSButton*)sender { @@ -228,6 +345,9 @@ - (IBAction)updateIntOutletValueChanged:(id)sender { - (void)windowWillClose:(NSNotification *)notification { _brightnessOutlets = nil; _contrastOutlets = nil; + _redOutlets = nil; + _greenOutlets = nil; + _blueOutlets = nil; _updateIntervalOutlets = nil; _preferenceWindow = nil; diff --git a/BrightnessMenulet/Screen.h b/BrightnessMenulet/Screen.h index 47f43f3..d6567e9 100644 --- a/BrightnessMenulet/Screen.h +++ b/BrightnessMenulet/Screen.h @@ -21,9 +21,22 @@ @property (readonly) NSInteger currentContrast; @property (readonly) NSInteger maxContrast; +@property (readonly) NSInteger currentRed; +@property (readonly) NSInteger maxRed; + +@property (readonly) NSInteger currentGreen; +@property (readonly) NSInteger maxGreen; + +@property (readonly) NSInteger currentBlue; +@property (readonly) NSInteger maxBlue; + @property (strong) NSMutableArray* brightnessOutlets; @property (strong) NSMutableArray* contrastOutlets; +@property (strong) NSMutableArray* redOutlets; +@property (strong) NSMutableArray* greenOutlets; +@property (strong) NSMutableArray* blueOutlets; + - (instancetype)initWithModel:(NSString*)model screenID:(CGDirectDisplayID)screenID serial:(NSString*)serial; - (void)refreshValues; @@ -31,8 +44,17 @@ - (void)setBrightnessWithPercentage:(NSInteger)percentage byOutlet:(NSView*)outlet; - (void)setBrightness:(NSInteger)brightness byOutlet:(NSView*)outlet; +- (void)setBrightnessRelativeToValue:(NSString *)value; - (void)setContrastWithPercentage:(NSInteger)percentage byOutlet:(NSView*)outlet; - (void)setContrast:(NSInteger)contrast byOutlet:(NSView*)outlet; +- (void)setContrastRelativeToValue:(NSString *)value; + +- (void)setRedWithPercentage:(NSInteger)percentage byOutlet:(NSView*)outlet; +- (void)setRed:(NSInteger)red byOutlet:(NSView*)outlet; +- (void)setGreenWithPercentage:(NSInteger)percentage byOutlet:(NSView*)outlet; +- (void)setGreen:(NSInteger)green byOutlet:(NSView*)outlet; +- (void)setBlueWithPercentage:(NSInteger)percentage byOutlet:(NSView*)outlet; +- (void)setBlue:(NSInteger)blue byOutlet:(NSView*)outlet; @end diff --git a/BrightnessMenulet/Screen.m b/BrightnessMenulet/Screen.m index 7e7248c..7fbb187 100644 --- a/BrightnessMenulet/Screen.m +++ b/BrightnessMenulet/Screen.m @@ -20,6 +20,13 @@ @interface Screen () @property (readwrite) NSInteger currentContrast; @property (readwrite) NSInteger maxContrast; +@property (readwrite) NSInteger currentRed; +@property (readwrite) NSInteger maxRed; +@property (readwrite) NSInteger currentGreen; +@property (readwrite) NSInteger maxGreen; +@property (readwrite) NSInteger currentBlue; +@property (readwrite) NSInteger maxBlue; + @end @implementation Screen @@ -32,27 +39,43 @@ - (instancetype)initWithModel:(NSString*)model screenID:(CGDirectDisplayID)scree _brightnessOutlets = [NSMutableArray array]; _contrastOutlets = [NSMutableArray array]; + _redOutlets = [NSMutableArray array]; + _greenOutlets = [NSMutableArray array]; + _blueOutlets = [NSMutableArray array]; } return self; } - (void)refreshValues { - struct DDCReadResponse cBrightness = [controls readDisplay:self.screenNumber controlValue:BRIGHTNESS]; - struct DDCReadResponse cContrast = [controls readDisplay:self.screenNumber controlValue:CONTRAST]; + struct DDCReadCommand cBrightness = [controls readDisplay:self.screenNumber controlValue:BRIGHTNESS]; + struct DDCReadCommand cContrast = [controls readDisplay:self.screenNumber controlValue:CONTRAST]; + + struct DDCReadCommand cRed = [controls readDisplay:self.screenNumber controlValue:RED_GAIN]; + struct DDCReadCommand cGreen = [controls readDisplay:self.screenNumber controlValue:GREEN_GAIN]; + struct DDCReadCommand cBlue = [controls readDisplay:self.screenNumber controlValue:BLUE_GAIN]; self.currentBrightness = cBrightness.current_value; self.maxBrightness = cBrightness.max_value; self.currentContrast = cContrast.current_value; self.maxContrast = cContrast.max_value; + + self.currentRed = cRed.current_value; + self.maxRed = cRed.max_value; + + self.currentGreen = cGreen.current_value; + self.maxGreen = cGreen.max_value; + + self.currentBlue = cBlue.current_value; + self.maxBlue = cBlue.max_value; NSLog(@"Screen: %@ set BR %ld CON %ld", _model , (long)self.currentBrightness, (long)self.currentContrast); } - (void)ddcReadOut { for(int i=0x00; i<=255; i++){ - struct DDCReadResponse response = [controls readDisplay:self.screenNumber controlValue:i]; + struct DDCReadCommand response = [controls readDisplay:self.screenNumber controlValue:i]; NSLog(@"VCP: %x - %d / %d \n", i, response.current_value, response.max_value); } @@ -86,6 +109,24 @@ - (void)setBrightness:(NSInteger)brightness byOutlet:(NSView*)outlet { [dirtyOutlet setIntegerValue:self.currentBrightness]; } +- (void)setBrightnessRelativeToValue:(NSString *)value { + // relative setting: read, calculate, then write + NSString *value_num = [value stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"-+"]]; + NSString *formula = [NSString stringWithFormat:@"%ld %@ %@", (long)self.currentBrightness, [value substringFromIndex:value.length - 1], value_num]; + NSExpression *exp = [NSExpression expressionWithFormat:formula]; + NSNumber *value_set = [exp expressionValueWithObject:nil context:nil]; + + if ((value_set.intValue > self.maxContrast) || (value_set.intValue < 0)) return; + + [controls changeDisplay:self.screenNumber control:BRIGHTNESS withValue: value_set.intValue]; + self.currentBrightness = value_set.intValue; + + // update the sliders, this fails with multiple screens + for(id outlet in _brightnessOutlets) [outlet setIntegerValue:self.currentBrightness]; + + NSLog(@"Screen: %@ - %ud Brightness changed to %ld", _model, self.screenNumber, (long)self.currentBrightness); +} + - (void)setContrast:(NSInteger)contrast { if(contrast > self.maxContrast) contrast = self.maxContrast; @@ -114,4 +155,111 @@ - (void)setContrast:(NSInteger)contrast byOutlet:(NSView*)outlet { [dirtyOutlet setIntegerValue:self.currentContrast]; } +- (void)setContrastRelativeToValue:(NSString *)value { + // relative setting: read, calculate, then write + NSString *value_num = [value stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"-+"]]; + NSString *formula = [NSString stringWithFormat:@"%ld %@ %@", (long)self.currentContrast, [value substringFromIndex:value.length - 1], value_num]; + NSExpression *exp = [NSExpression expressionWithFormat:formula]; + NSNumber *value_set = [exp expressionValueWithObject:nil context:nil]; + + if ((value_set.intValue > self.maxContrast) || (value_set.intValue < 0)) return; + + [controls changeDisplay:self.screenNumber control:CONTRAST withValue: value_set.intValue]; + self.currentContrast = value_set.intValue; + + + // update the sliders, this fails with multiple screens + for(id outlet in _contrastOutlets) [outlet setIntegerValue:self.currentContrast]; + + NSLog(@"Screen: %@ - %ud Contrast changed to %ld", _model, self.screenNumber, (long)self.currentContrast); +} + + + +- (void)setRed:(NSInteger)red { + if(red > self.maxRed) + red = self.maxRed; + + [controls changeDisplay:self.screenNumber control:RED_GAIN withValue: red]; + self.currentRed = red; + + NSLog(@"Screen: %@ - %ud Red changed to %ld", _model, self.screenNumber, (long)self.currentRed); +} + +- (void)setRedWithPercentage:(NSInteger)percentage byOutlet:(NSView*)outlet { + [self setRed:(self.maxRed * ((double)(percentage)/100)) byOutlet:outlet]; +} + +- (void)setRed:(NSInteger)red byOutlet:(NSView*)outlet { + if(red == self.currentRed) + return; + else + [self setRed:red]; + + NSMutableArray* dirtyOutlets = [_redOutlets mutableCopy]; + if(outlet) + [dirtyOutlets removeObject:outlet]; + + for(id dirtyOutlet in dirtyOutlets) + [dirtyOutlet setIntegerValue:self.currentRed]; +} + + +- (void)setGreen:(NSInteger)green { + if(green > self.maxGreen) + green = self.maxGreen; + + [controls changeDisplay:self.screenNumber control:GREEN_GAIN withValue: green]; + self.currentGreen = green; + + NSLog(@"Screen: %@ - %ud Green changed to %ld", _model, self.screenNumber, (long)self.currentGreen); +} + +- (void)setGreenWithPercentage:(NSInteger)percentage byOutlet:(NSView*)outlet { + [self setGreen:(self.maxGreen * ((double)(percentage)/100)) byOutlet:outlet]; +} + +- (void)setGreen:(NSInteger)green byOutlet:(NSView*)outlet { + if(green == self.currentGreen) + return; + else + [self setGreen:green]; + + NSMutableArray* dirtyOutlets = [_greenOutlets mutableCopy]; + if(outlet) + [dirtyOutlets removeObject:outlet]; + + for(id dirtyOutlet in dirtyOutlets) + [dirtyOutlet setIntegerValue:self.currentGreen]; +} + + +- (void)setBlue:(NSInteger)blue { + if(blue > self.maxBlue) + blue = self.maxBlue; + + [controls changeDisplay:self.screenNumber control:BLUE_GAIN withValue: blue]; + self.currentBlue = blue; + + NSLog(@"Screen: %@ - %ud Blue changed to %ld", _model, self.screenNumber, (long)self.currentBlue); +} + +- (void)setBlueWithPercentage:(NSInteger)percentage byOutlet:(NSView*)outlet { + [self setBlue:(self.maxBlue * ((double)(percentage)/100)) byOutlet:outlet]; +} + +- (void)setBlue:(NSInteger)blue byOutlet:(NSView*)outlet { + if(blue == self.currentBlue) + return; + else + [self setBlue:blue]; + + NSMutableArray* dirtyOutlets = [_blueOutlets mutableCopy]; + if(outlet) + [dirtyOutlets removeObject:outlet]; + + for(id dirtyOutlet in dirtyOutlets) + [dirtyOutlet setIntegerValue:self.currentBlue]; +} + @end diff --git a/BrightnessMenulet/VirtualKeyCodes.h b/BrightnessMenulet/VirtualKeyCodes.h new file mode 100644 index 0000000..e8142cb --- /dev/null +++ b/BrightnessMenulet/VirtualKeyCodes.h @@ -0,0 +1,160 @@ +// +// VirtualKeyCodes.h +// BrightnessMenulet +// +// Created by Marc on 14.12.16. +// +// + +#ifndef VirtualKeyCodes_h +#define VirtualKeyCodes_h + +// carbon modifier flags +// cmdKey optionKey controlKey shiftKey; + + +/* additional keycodes */ +enum { + kVK_BrightnessUp = 0x90, + kVK_BrightnessDown = 0x91 +}; + +// >>> Source: HIToolbox/Events.h + +/* + * Summary: + * Virtual keycodes + * + * Discussion: + * These constants are the virtual keycodes defined originally in + * Inside Mac Volume V, pg. V-191. They identify physical keys on a + * keyboard. Those constants with "ANSI" in the name are labeled + * according to the key position on an ANSI-standard US keyboard. + * For example, kVK_ANSI_A indicates the virtual keycode for the key + * with the letter 'A' in the US keyboard layout. Other keyboard + * layouts may have the 'A' key label on a different physical key; + * in this case, pressing 'A' will generate a different virtual + * keycode. + * / +enum { + kVK_ANSI_A = 0x00, + kVK_ANSI_S = 0x01, + kVK_ANSI_D = 0x02, + kVK_ANSI_F = 0x03, + kVK_ANSI_H = 0x04, + kVK_ANSI_G = 0x05, + kVK_ANSI_Z = 0x06, + kVK_ANSI_X = 0x07, + kVK_ANSI_C = 0x08, + kVK_ANSI_V = 0x09, + kVK_ANSI_B = 0x0B, + kVK_ANSI_Q = 0x0C, + kVK_ANSI_W = 0x0D, + kVK_ANSI_E = 0x0E, + kVK_ANSI_R = 0x0F, + kVK_ANSI_Y = 0x10, + kVK_ANSI_T = 0x11, + kVK_ANSI_1 = 0x12, + kVK_ANSI_2 = 0x13, + kVK_ANSI_3 = 0x14, + kVK_ANSI_4 = 0x15, + kVK_ANSI_6 = 0x16, + kVK_ANSI_5 = 0x17, + kVK_ANSI_Equal = 0x18, + kVK_ANSI_9 = 0x19, + kVK_ANSI_7 = 0x1A, + kVK_ANSI_Minus = 0x1B, + kVK_ANSI_8 = 0x1C, + kVK_ANSI_0 = 0x1D, + kVK_ANSI_RightBracket = 0x1E, + kVK_ANSI_O = 0x1F, + kVK_ANSI_U = 0x20, + kVK_ANSI_LeftBracket = 0x21, + kVK_ANSI_I = 0x22, + kVK_ANSI_P = 0x23, + kVK_ANSI_L = 0x25, + kVK_ANSI_J = 0x26, + kVK_ANSI_Quote = 0x27, + kVK_ANSI_K = 0x28, + kVK_ANSI_Semicolon = 0x29, + kVK_ANSI_Backslash = 0x2A, + kVK_ANSI_Comma = 0x2B, + kVK_ANSI_Slash = 0x2C, + kVK_ANSI_N = 0x2D, + kVK_ANSI_M = 0x2E, + kVK_ANSI_Period = 0x2F, + kVK_ANSI_Grave = 0x32, + kVK_ANSI_KeypadDecimal = 0x41, + kVK_ANSI_KeypadMultiply = 0x43, + kVK_ANSI_KeypadPlus = 0x45, + kVK_ANSI_KeypadClear = 0x47, + kVK_ANSI_KeypadDivide = 0x4B, + kVK_ANSI_KeypadEnter = 0x4C, + kVK_ANSI_KeypadMinus = 0x4E, + kVK_ANSI_KeypadEquals = 0x51, + kVK_ANSI_Keypad0 = 0x52, + kVK_ANSI_Keypad1 = 0x53, + kVK_ANSI_Keypad2 = 0x54, + kVK_ANSI_Keypad3 = 0x55, + kVK_ANSI_Keypad4 = 0x56, + kVK_ANSI_Keypad5 = 0x57, + kVK_ANSI_Keypad6 = 0x58, + kVK_ANSI_Keypad7 = 0x59, + kVK_ANSI_Keypad8 = 0x5B, + kVK_ANSI_Keypad9 = 0x5C +}; +*/ +/* keycodes for keys that are independent of keyboard layout * / +enum { + kVK_Return = 0x24, + kVK_Tab = 0x30, + kVK_Space = 0x31, + kVK_Delete = 0x33, + kVK_Escape = 0x35, + kVK_Command = 0x37, + kVK_Shift = 0x38, + kVK_CapsLock = 0x39, + kVK_Option = 0x3A, + kVK_Control = 0x3B, + kVK_RightCommand = 0x36, + kVK_RightShift = 0x3C, + kVK_RightOption = 0x3D, + kVK_RightControl = 0x3E, + kVK_Function = 0x3F, + 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 +}; +*/ + +#endif /* VirtualKeyCodes_h */ diff --git a/BrightnessMenulet/ddc.c b/BrightnessMenulet/ddc.c index 4bb9ebd..3b87141 100755 --- a/BrightnessMenulet/ddc.c +++ b/BrightnessMenulet/ddc.c @@ -1,198 +1,383 @@ -/* - * ddc.c - * ddc - * - * Created by Jonathan Taylor on 07/10/2009. - * Copyright 2009 __MyCompanyName__. All rights reserved. - * - */ - -#include -#include -#include "ddc.h" +// +// DDC.c +// DDC Panel +// +// Created by Jonathan Taylor on 7/10/09. +// See http://github.com/jontaylor/DDC-CI-Tools-for-OS-X +// -IOI2CConnectRef display_connection(CGDirectDisplayID display_id) { - kern_return_t kr; - io_service_t framebuffer, interface; - IOOptionBits bus; - IOItemCount busCount; +#include +#include +#include +#include "DDC.h" - //printf("Querying for displayid: %d\n", display_id); - framebuffer = CGDisplayIOServicePort(display_id); // fixme! - CGDisplayIOServicePort deprecated +#define kMaxRequests 10 - io_string_t path; - kr = IORegistryEntryGetPath(framebuffer, kIOServicePlane, path); - if(KERN_SUCCESS != kr) // display path find failed - return nil; - - kr = IOFBGetI2CInterfaceCount( framebuffer, &busCount ); - assert(kIOReturnSuccess == kr); +/* + + 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, productID, 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) { + // 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; + } + +#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; +} - for(bus = 0; bus < busCount; bus++){ - IOI2CConnectRef connect; +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; +} - kr = IOFBCopyI2CInterfaceForBus(framebuffer, bus, &interface); - if(kIOReturnSuccess != kr) - continue; +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; +} - kr = IOI2CInterfaceOpen(interface, kNilOptions, &connect); +bool DDCWrite(CGDirectDisplayID displayID, struct DDCWriteCommand *write) { + IOI2CRequest request; + UInt8 data[128]; - IOObjectRelease(interface); - assert(kIOReturnSuccess == kr); - if(kIOReturnSuccess != kr) - continue; + bzero( &request, sizeof(request)); - return connect; - } + request.commFlags = 0; - return nil; -} + request.sendAddress = 0x6E; + request.sendTransactionType = kIOI2CSimpleTransactionType; + request.sendBuffer = (vm_address_t) &data[0]; + request.sendBytes = 7; -int ddc_write(CGDirectDisplayID display_id, struct DDCWriteCommand* p_write) { - UInt8 data[128]; - IOI2CRequest request; - kern_return_t kr; - - IOI2CConnectRef connect = display_connection(display_id); - if(!connect) - return 0; - - 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] = (*p_write).control_id; - data[4] = 0x1; - data[5] = (*p_write).new_value; - data[6] = 0x6E ^ data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4] ^ data[5]; - - request.replyTransactionType = kIOI2CNoTransactionType; - request.replyBytes = 0; - - kr = IOI2CSendRequest(connect, kNilOptions, &request); - IOI2CInterfaceClose(connect, kNilOptions); + 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]; - assert(kIOReturnSuccess == kr); - if(kIOReturnSuccess != request.result) - return 0; - - return 1; + request.replyTransactionType = kIOI2CNoTransactionType; + request.replyBytes = 0; + + bool result = DisplayRequest(displayID, &request); + return result; } -int ddc_read(CGDirectDisplayID display_id, struct DDCReadCommand* p_read) { - UInt8 data[128]; +bool DDCRead(CGDirectDisplayID displayID, struct DDCReadCommand *read) { IOI2CRequest request; - kern_return_t kr; - UInt8 reply_data[11]; + 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])); - IOI2CConnectRef connect = display_connection(display_id); - if(!connect) - return 0; + if (result) { // checksum is ok + if (i > 1) { + printf("D: Tries required to get data: %d \n", i); + } + break; + } - int successful_reads = 0; - - for (int i=0; i<60; i++) { - bzero( &request, sizeof(request)); - - request.commFlags = 0; - request.sendAddress = 0x6E; - request.sendTransactionType = kIOI2CDDCciReplyTransactionType; - request.sendBuffer = (vm_address_t) &data[0]; - request.sendBytes = 5; - - data[0] = 0x51; - data[1] = 0x82; - data[2] = 0x01; // We want to read this time - data[3] = (*p_read).control_id; - - data[4] = 0x6E ^ data[0] ^ data[1] ^ data[2] ^ data[3]; - - request.replyAddress = 0x6f; - request.replyTransactionType = kIOI2CDDCciReplyTransactionType; - - request.replyBuffer = (vm_address_t) &reply_data[0] ; - request.replyBytes = sizeof(reply_data); - //request.minReplyDelay = 50 * 10000; // causes Kernal panic - - int calculated_checksum; - - bzero( &reply_data[0], request.replyBytes); - - kr = IOI2CSendRequest( connect, kNilOptions, &request ); - calculated_checksum = 0x6f ^ 0x51 ^ 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 ((reply_data[10] == calculated_checksum) && reply_data[4] == data[3] ) { - successful_reads++; - if (successful_reads > 1) - break; - - } - //fprintf(stderr, "READ ERROR\n"); - - } - - IOI2CInterfaceClose(connect, kNilOptions); - - (*p_read).response.max_value = reply_data[7]; - (*p_read).response.current_value = reply_data[9]; - - assert(kIOReturnSuccess == kr); - if(kIOReturnSuccess != request.result) { - printf("Error getting result\n"); - return 0; + 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 } - - return 1; + read->success = true; + read->max_value = reply_data[7]; + read->current_value = reply_data[9]; + return result; } -void EDIDRead(CGDirectDisplayID display_id, struct EDID* edid) { - kern_return_t kr; - IOI2CConnectRef connect; - IOI2CRequest request; - UInt8 data[128]; +int 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 + */ - if(!(connect = display_connection(display_id))) - return; + kern_return_t kr; + io_iterator_t io_objects; + io_service_t io_service; - bzero( &request, sizeof(request) ); + kr = IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceNameMatching("IOFramebufferI2CInterface"), &io_objects); - request.commFlags = 0; - request.sendAddress = 0xA0; - request.sendTransactionType = kIOI2CSimpleTransactionType; - request.sendBuffer = (vm_address_t)&data[0]; - request.sendBytes = 0x01; - data[0] = 0x00; - - request.replyAddress = 0xA1; - request.replyTransactionType = kIOI2CSimpleTransactionType; - request.replyBuffer = (vm_address_t)&data[0]; - request.replyBytes = sizeof(data); - bzero( &data[0], request.replyBytes ); + if (kr != KERN_SUCCESS) { + printf("E: Fatal - No matching service! \n"); + return 0; + } - kr = IOI2CSendRequest(connect, kNilOptions, &request); - assert(kIOReturnSuccess == kr); - if(kIOReturnSuccess != request.result) - return; + UInt64 supportedType = 0; - if(edid) memcpy(edid, &data, 128); + 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; + } +#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 = {}; + 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; + while (i < request.replyBytes) { + if (i % 128 == 0) { + if (sum) break; sum = 0; } sum += data[i++]; } - - IOI2CInterfaceClose(connect, kNilOptions); + return !sum; } - diff --git a/BrightnessMenulet/ddc.h b/BrightnessMenulet/ddc.h index 7c5fc6c..7f48cf1 100755 --- a/BrightnessMenulet/ddc.h +++ b/BrightnessMenulet/ddc.h @@ -1,85 +1,81 @@ -/* - * ddc.h - * ddc - * - * Created by Jonathan Taylor on 07/10/2009. - * Copyright 2009 __MyCompanyName__. All rights reserved. - * - */ +// +// DDC.h +// DDC Panel +// +// Created by Jonathan Taylor on 7/10/09. +// See ftp://ftp.cis.nctu.edu.tw/pub/csie/Software/X11/private/VeSaSpEcS/VESA_Document_Center_Monitor_Interface/mccsV3.pdf +// See http://read.pudn.com/downloads110/ebook/456020/E-EDID%20Standard.pdf +// See ftp://ftp.cis.nctu.edu.tw/pub/csie/Software/X11/private/VeSaSpEcS/VESA_Document_Center_Monitor_Interface/EEDIDrAr2.pdf +// -#ifdef __cplusplus -extern "C" { -#endif +#ifndef DDC_Panel_DDC_h +#define DDC_Panel_DDC_h -#include -#include #include -#define RESET 0x04 -#define RESET_BRIGHTNESS_AND_CONTRAST 0x05 -#define RESET_GEOMETRY 0x06 -#define RESET_COLOR 0x08 -#define BRIGHTNESS 0x10 //OK -#define CONTRAST 0x12 //OK -#define RED_GAIN 0x16 -#define GREEN_GAIN 0x18 -#define BLUE_GAIN 0x1A -#define AUTO_SIZE_CENTER 0x1E -#define WIDTH 0x22 -#define HEIGHT 0x32 -#define VERTICAL_POS 0x30 -#define HORIZONTAL_POS 0x20 -#define PINCUSHION_AMP 0x24 -#define PINCUSHION_PHASE 0x42 -#define KEYSTONE_BALANCE 0x40 -#define PINCUSHION_BALANCE 0x26 -#define TOP_PINCUSHION_AMP 0x46 -#define TOP_PINCUSHION_BALANCE 0x48 -#define BOTTOM_PINCUSHION_AMP 0x4A -#define BOTTOM_PINCUSHION_BALANCE 0x4C -#define VERTICAL_LINEARITY 0x3A -#define VERTICAL_LINEARITY_BALANCE 0x3C -#define HORIZONTAL_STATIC_CONVERGENCE 0x28 -#define VERTICAL_STATIC_CONVERGENCE 0x28 -#define MOIRE_CANCEL 0x56 -#define INPUT_SOURCE 0x60 -#define AUDIO_SPEAKER_VOLUME 0x62 -#define RED_BLACK_LEVEL 0x6C -#define GREEN_BLACK_LEVEL 0x6E -#define BLUE_BLACK_LEVEL 0x70 +#define RESET 0x04 +#define RESET_BRIGHTNESS_AND_CONTRAST 0x05 +#define RESET_GEOMETRY 0x06 +#define RESET_COLOR 0x08 +#define BRIGHTNESS 0x10 +#define CONTRAST 0x12 +#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 +#define AUTO_SIZE_CENTER 0x1E +#define WIDTH 0x22 +#define HEIGHT 0x32 +#define VERTICAL_POS 0x30 +#define HORIZONTAL_POS 0x20 +#define PINCUSHION_AMP 0x24 +#define PINCUSHION_PHASE 0x42 +#define KEYSTONE_BALANCE 0x40 +#define PINCUSHION_BALANCE 0x26 +#define TOP_PINCUSHION_AMP 0x46 +#define TOP_PINCUSHION_BALANCE 0x48 +#define BOTTOM_PINCUSHION_AMP 0x4A +#define BOTTOM_PINCUSHION_BALANCE 0x4C +#define VERTICAL_LINEARITY 0x3A +#define VERTICAL_LINEARITY_BALANCE 0x3C +#define HORIZONTAL_STATIC_CONVERGENCE 0x28 +#define VERTICAL_STATIC_CONVERGENCE 0x38 +#define MOIRE_CANCEL 0x56 +#define INPUT_SOURCE 0x60 +#define AUDIO_SPEAKER_VOLUME 0x62 +#define RED_BLACK_LEVEL 0x6C +#define GREEN_BLACK_LEVEL 0x6E +#define BLUE_BLACK_LEVEL 0x70 +#define ORIENTATION 0xAA +#define AUDIO_MUTE 0x8D +#define SETTINGS 0xB0 // unsure +#define ON_SCREEN_DISPLAY 0xCA // read only -> returns '1' (OSD closed) or '2' (OSD active) +#define OSD_LANGUAGE 0xCC // read only +#define DPMS 0xD6 +#define COLOR_PRESET_B 0xDC // dell u2515h -> Presets: 0 = Standard, 2 = Multimedia, 3 = Movie, 5 = Game +#define VCP_VERSION 0xDF +#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 +#define BOTTOM_LEFT_SCREEN_PURITY 0xEA +#define BOTTOM_RIGHT_SCREEN_PURITY 0xEB -#define SETTINGS 0xB0 //unsure on this one -#define ON_SCREEN_DISPLAY 0xCA -#define OSD_LANGUAGE 0xCC -#define DPMS 0xD6 -#define MAGIC_BRIGHT 0xDC //unsure -#define VCP_VERSION 0xDF -#define COLOR_PRESET 0xE0 -#define POWER_CONTROL 0xE1 - -#define TOP_LEFT_SCREEN_PURITY 0xE8 -#define TOP_RIGHT_SCREEN_PURITY 0xE9 -#define BOTTOM_LEFT_SCREEN_PURITY 0xE8 -#define BOTTOM_RIGHT_SCREEN_PURITY 0xEB - -struct DDCWriteCommand { + +struct DDCWriteCommand +{ UInt8 control_id; UInt8 new_value; }; - -struct DDCReadResponse { + +struct DDCReadCommand +{ + UInt8 control_id; + bool success; UInt8 max_value; UInt8 current_value; }; -struct DDCReadCommand { - UInt8 control_id; - size_t reply_bytes; - unsigned char* reply_buffer; - struct DDCReadResponse response; -}; - -// EDID struct credits to https://github.com/kfix/ddcctl struct EDID { UInt64 header : 64; UInt8 : 1; @@ -90,13 +86,14 @@ struct EDID { UInt8 year : 8; UInt8 versionmajor : 8; UInt8 versionminor : 8; - UInt8 digitalinput : 1; - union inputbitmap { + 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; @@ -104,7 +101,7 @@ struct EDID { UInt8 green : 1; UInt8 serrated : 1; } analog; - }; + } videoinput; UInt8 maxh : 8; UInt8 maxv : 8; UInt8 gamma : 8; @@ -189,16 +186,8 @@ struct EDID { UInt8 interlaced : 1; UInt8 stereo : 2; UInt8 synctype : 2; - union sync { - 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 { @@ -248,10 +237,8 @@ struct EDID { UInt8 checksum : 8; }; -int ddc_write(CGDirectDisplayID display_id, struct DDCWriteCommand * p_write); -int ddc_read(CGDirectDisplayID display_id, struct DDCReadCommand * p_read); -void EDIDRead(CGDirectDisplayID display_id, struct EDID *edid); - -#ifdef __cplusplus -} -#endif \ No newline at end of file +bool DDCWrite(CGDirectDisplayID displayID, struct DDCWriteCommand *write); +bool DDCRead(CGDirectDisplayID displayID, struct DDCReadCommand *read); +bool EDIDTest(CGDirectDisplayID displayID, struct EDID *edid); +int SupportedTransactionType(); +#endif diff --git a/README.rst b/README.rst index db17b37..59ce86d 100644 --- a/README.rst +++ b/README.rst @@ -3,63 +3,30 @@ Brightness Menulet Allows you to control monitor brigthness via menu in status bar. -This tool works on OSX 10.8+. In 10.8+, I2C tools are not able to detect monitor buses for communication. -The main API calls for DDC communication is deprecated so this project could be obselete if Apple -completely removes CGDisplayIOServicePort. If you have tested your monitor(s) with this tool, please -let me know wether it worked or not so I can add monitor models here. Preference's Debug button logs to the +This tool works on OSX 10.8+. If you have tested your monitor(s) with this tool, please +let me know wether it worked or not. Preference's Debug button logs to the console VCP codes and their values on the selected monitor. -Download app build: `BrightnessMenulet.zip`_. +Modified version! +See commit messages for more information. -.. _BrightnessMenulet.zip: - https://raw.github.com/kalvin126/BrightnessMenulet/master/BrightnessMenulet/Brightness_Menulet.zip +No Binary download available. .. image:: https://raw.github.com/kalvin126/BrightnessMenulet/master/BrightnessMenulet/screenshot.png -Monitors: -....................... -+------------------+---------------+ -| Working | Non-Working | -+==================+===============+ -| Dell U2014h | Dell P2715Q | -+------------------+---------------+ -| Dell U2414h | Philips 4065UC| -+------------------+---------------+ -| Dell U2415h | Dell P2412H | -+------------------+---------------+ -| Dell U2515h | | -+------------------+---------------+ -| Dell U2715h | | -+------------------+---------------+ -| Dell U2713HM | | -+------------------+---------------+ -| Dell P2415Q | | -+------------------+---------------+ -| Dell S2216M | | -+------------------+---------------+ -| Samsung SA 350 | | -+------------------+---------------+ -| BenQ G2410HD | | -+------------------+---------------+ -| Viseo 230Ws | | -+------------------+---------------+ - -If you have tested your monitor(s) with this tool, please let me know whether or not it work and I will update this table. - - Features: ............ - Automatic Brightness using built in light sensor (Modify LMUController’s percentageForSensorValue: to your liking) - Multi-Monitor support (no limit to amount of monitors)! - Compatible with OSX 10.8+ +- Keyboard bindings (hardcoded) Roadmap: ........ - Support for other monitor makes (Currently only tested on Dell and certian HP displays) - Time based settings -- Add keyboard bindings Credits: ........