Skip to content

Instantly share code, notes, and snippets.

@ybbond
Last active April 17, 2024 22:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ybbond/f3bdcd8b7faa21b6e348c2126fc4eb07 to your computer and use it in GitHub Desktop.
Save ybbond/f3bdcd8b7faa21b6e348c2126fc4eb07 to your computer and use it in GitHub Desktop.
Change external monitor brightness and contrast programmatically via terminal
@import Darwin;
@import Foundation;
@import IOKit;
/*******
This should change the brightness level of a single external display connected to an M1 MBP or MBA relative to the current setting using DDC/CI. Reportedly does not work on M1 Mini.
Credits to @tao-j and @alin23
Gists Sources:
- tao-j/brt.m (idea) https://gist.github.com/tao-j/c7a3c1ec7c2f59ebe45d161d620d9169
- waydabber/chbrt.m (original code of this) https://gist.github.com/waydabber/ca5c4f52183598fe95cd8045adfc7623
Compile:
clang -fmodules -o chbrt chbrt.m
To increase brightness by 10% enter:
./chbrt 10
To decrease brightness by 10% enter:
./chbrt -10
*******/
typedef CFTypeRef IOAVServiceRef;
extern IOAVServiceRef IOAVServiceCreate(CFAllocatorRef allocator);
extern IOReturn IOAVServiceCopyEDID(IOAVServiceRef service, CFDataRef* x2);
extern IOReturn IOAVServiceReadI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t offset, void* outputBuffer,
uint32_t outputBufferSize);
extern IOReturn IOAVServiceWriteI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t dataAddress, void* inputBuffer,
uint32_t inputBufferSize);
#define BRIGHTNESS 0x10
#define CONTRAST 0x12
#define AUDIO_SPEAKER_VOLUME 0x62
#define AUDIO_MUTE 0x8D
#define SLEEPTIME 5000
struct DDCWriteCommand {
UInt8 control_id;
UInt8 new_value;
};
int main(int argc, char** argv) {
IOAVServiceRef avService = IOAVServiceCreate(kCFAllocatorDefault);
if (!avService) {
NSLog(@"This is a tool to change the brightness of a single external display connected to an M1 Mac via DDC/CI. Do you have an external monitor? Are you on M1? Are you root?");
return 1;
}
IOReturn err;
UInt8 data[256];
memset(data, 0, sizeof(data));
data[0] = 0x82;
data[1] = 0x01;
data[2] = BRIGHTNESS;
data[3] = 0x6e ^ data[0] ^ data[1] ^ data[2] ^ data[3];
char i2cBytes[12];
memset(i2cBytes, 0, sizeof(i2cBytes));
NSData *sendingData = [NSData dataWithBytes:(const void *)data length:(NSUInteger)4];
usleep(SLEEPTIME);
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 4);
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
return 1;
}
usleep(SLEEPTIME);
err = IOAVServiceReadI2C(avService, 0x37, 0x51, i2cBytes, 12);
NSRange maxValueRange = {7, 1};
NSRange currentValueRange = {9, 1};
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
} else {
NSData *readData = [NSData dataWithBytes:(const void *)i2cBytes length:(NSUInteger)11];
char maxValue, currentValue;
[[readData subdataWithRange:maxValueRange] getBytes:&maxValue length:sizeof(1)];
[[readData subdataWithRange:currentValueRange] getBytes:&currentValue length:sizeof(1)];
NSLog(@"Current brightness is %i", currentValue);
if (argc == 2) {
int nextValue;
nextValue = currentValue+atoi(argv[1]);
if (nextValue<0) nextValue=0;
if (nextValue>maxValue) nextValue=maxValue;
struct DDCWriteCommand command;
command.control_id = BRIGHTNESS;
NSLog(@"Setting brightness to %i", nextValue);
command.new_value = nextValue;
data[0] = 0x84;
data[1] = 0x03;
data[2] = command.control_id;
data[3] = (command.new_value) >> 8;
data[4] = command.new_value & 255;
data[5] = 0x6E ^ 0x51 ^ data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4];
for (int i = 0; i < 3; ++i)
{
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 6);
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
}
usleep(SLEEPTIME);
}
NSData *nsdata = [NSData dataWithBytes:(const void *)data length:(NSUInteger)6];
} else {
NSLog(@"Please specify a value between -100 and 100 as an argument to increase/decrease brightness by the desired level.");
}
}
return 0;
}
@import Darwin;
@import Foundation;
@import IOKit;
/*******
This should change the brightness level of a single external display connected to an M1 MBP or MBA relative to the current setting using DDC/CI. Reportedly does not work on M1 Mini.
Credits to @tao-j and @alin23
Gists Sources:
- tao-j/brt.m (idea) https://gist.github.com/tao-j/c7a3c1ec7c2f59ebe45d161d620d9169
- waydabber/chbrt.m (original code of this) https://gist.github.com/waydabber/ca5c4f52183598fe95cd8045adfc7623
Compile:
clang -fmodules -o chbrtlog chbrtlog.m
To increase brightness by 10% enter:
./chbrtlog 10
To decrease brightness by 10% enter:
./chbrtlog -10
*******/
typedef CFTypeRef IOAVServiceRef;
extern IOAVServiceRef IOAVServiceCreate(CFAllocatorRef allocator);
extern IOReturn IOAVServiceCopyEDID(IOAVServiceRef service, CFDataRef* x2);
extern IOReturn IOAVServiceReadI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t offset, void* outputBuffer,
uint32_t outputBufferSize);
extern IOReturn IOAVServiceWriteI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t dataAddress, void* inputBuffer,
uint32_t inputBufferSize);
#define BRIGHTNESS 0x10
#define CONTRAST 0x12
#define AUDIO_SPEAKER_VOLUME 0x62
#define AUDIO_MUTE 0x8D
#define SLEEPTIME 5000
struct DDCWriteCommand {
UInt8 control_id;
UInt8 new_value;
};
int main(int argc, char** argv) {
NSString *returnValue = @"";
IOAVServiceRef avService = IOAVServiceCreate(kCFAllocatorDefault);
if (!avService) {
returnValue = @"This is a tool to change the brightness of a single external display connected to an M1 Mac via DDC/CI. Do you have an external monitor? Are you on M1? Are you root?";
[returnValue writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
return 1;
}
IOReturn err;
UInt8 data[256];
memset(data, 0, sizeof(data));
data[0] = 0x82;
data[1] = 0x01;
data[2] = BRIGHTNESS;
data[3] = 0x6e ^ data[0] ^ data[1] ^ data[2] ^ data[3];
char i2cBytes[12];
memset(i2cBytes, 0, sizeof(i2cBytes));
usleep(SLEEPTIME);
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 4);
if (err) {
returnValue = [NSString stringWithFormat:@"i2c error: %s", mach_error_string(err)];
[returnValue writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
return 1;
}
usleep(SLEEPTIME);
err = IOAVServiceReadI2C(avService, 0x37, 0x51, i2cBytes, 12);
NSRange maxValueRange = {7, 1};
NSRange currentValueRange = {9, 1};
if (err) {
returnValue = [NSString stringWithFormat:@"i2c error: %s", mach_error_string(err)];
} else {
NSData *readData = [NSData dataWithBytes:(const void *)i2cBytes length:(NSUInteger)11];
char maxValue, currentValue;
[[readData subdataWithRange:maxValueRange] getBytes:&maxValue length:sizeof(1)];
[[readData subdataWithRange:currentValueRange] getBytes:&currentValue length:sizeof(1)];
if (argc == 2) {
int nextValue;
nextValue = currentValue+atoi(argv[1]);
if (nextValue<0) nextValue=0;
if (nextValue>maxValue) nextValue=maxValue;
struct DDCWriteCommand command;
command.control_id = BRIGHTNESS;
returnValue = [NSString stringWithFormat:@"Current brightness is %i\nSetting brightness to %i", currentValue, nextValue];
command.new_value = nextValue;
data[0] = 0x84;
data[1] = 0x03;
data[2] = command.control_id;
data[3] = (command.new_value) >> 8;
data[4] = command.new_value & 255;
data[5] = 0x6E ^ 0x51 ^ data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4];
for (int i = 0; i < 3; ++i)
{
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 6);
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
}
usleep(SLEEPTIME);
}
} else {
returnValue = @"Please specify a value between -100 and 100 as an argument to increase/decrease brightness by the desired level.";
}
}
[returnValue writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
return 0;
}
@import Darwin;
@import Foundation;
@import IOKit;
/*******
This should change the contrast level of a single external display connected to an M1 MBP or MBA relative to the current setting using DDC/CI. Reportedly does not work on M1 Mini.
Credits to @tao-j and @alin23
Gists Sources:
- tao-j/brt.m (idea) https://gist.github.com/tao-j/c7a3c1ec7c2f59ebe45d161d620d9169
- waydabber/chbrt.m (original code of this) https://gist.github.com/waydabber/ca5c4f52183598fe95cd8045adfc7623
Compile:
clang -fmodules -o chcnt chcnt.m
To increase contrast by 10% enter:
./chcnt 10
To decrease contrast by 10% enter:
./chcnt -10
*******/
typedef CFTypeRef IOAVServiceRef;
extern IOAVServiceRef IOAVServiceCreate(CFAllocatorRef allocator);
extern IOReturn IOAVServiceCopyEDID(IOAVServiceRef service, CFDataRef* x2);
extern IOReturn IOAVServiceReadI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t offset, void* outputBuffer,
uint32_t outputBufferSize);
extern IOReturn IOAVServiceWriteI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t dataAddress, void* inputBuffer,
uint32_t inputBufferSize);
#define BRIGHTNESS 0x10
#define CONTRAST 0x12
#define AUDIO_SPEAKER_VOLUME 0x62
#define AUDIO_MUTE 0x8D
#define SLEEPTIME 5000
struct DDCWriteCommand {
UInt8 control_id;
UInt8 new_value;
};
int main(int argc, char** argv) {
IOAVServiceRef avService = IOAVServiceCreate(kCFAllocatorDefault);
if (!avService) {
NSLog(@"This is a tool to change the contrast of a single external display connected to an M1 Mac via DDC/CI. Do you have an external monitor? Are you on M1? Are you root?");
return 1;
}
IOReturn err;
UInt8 data[256];
memset(data, 0, sizeof(data));
data[0] = 0x82;
data[1] = 0x01;
data[2] = CONTRAST;
data[3] = 0x6e ^ data[0] ^ data[1] ^ data[2] ^ data[3];
char i2cBytes[12];
memset(i2cBytes, 0, sizeof(i2cBytes));
NSData *sendingData = [NSData dataWithBytes:(const void *)data length:(NSUInteger)4];
usleep(SLEEPTIME);
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 4);
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
return 1;
}
usleep(SLEEPTIME);
err = IOAVServiceReadI2C(avService, 0x37, 0x51, i2cBytes, 12);
NSRange maxValueRange = {7, 1};
NSRange currentValueRange = {9, 1};
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
} else {
NSData *readData = [NSData dataWithBytes:(const void *)i2cBytes length:(NSUInteger)11];
char maxValue, currentValue;
[[readData subdataWithRange:maxValueRange] getBytes:&maxValue length:sizeof(1)];
[[readData subdataWithRange:currentValueRange] getBytes:&currentValue length:sizeof(1)];
NSLog(@"Current contrast is %i", currentValue);
if (argc == 2) {
int nextValue;
nextValue = currentValue+atoi(argv[1]);
if (nextValue<0) nextValue=0;
if (nextValue>maxValue) nextValue=maxValue;
struct DDCWriteCommand command;
command.control_id = CONTRAST;
NSLog(@"Setting contrast to %i", nextValue);
command.new_value = nextValue;
data[0] = 0x84;
data[1] = 0x03;
data[2] = command.control_id;
data[3] = (command.new_value) >> 8;
data[4] = command.new_value & 255;
data[5] = 0x6E ^ 0x51 ^ data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4];
for (int i = 0; i < 3; ++i)
{
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 6);
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
}
usleep(SLEEPTIME);
}
NSData *nsdata = [NSData dataWithBytes:(const void *)data length:(NSUInteger)6];
} else {
NSLog(@"Please specify a value between -100 and 100 as an argument to increase/decrease contrast by the desired level.");
}
}
return 0;
}
@import Darwin;
@import Foundation;
@import IOKit;
/*******
This should change the contrast level of a single external display connected to an M1 MBP or MBA relative to the current setting using DDC/CI. Reportedly does not work on M1 Mini.
Credits to @tao-j and @alin23
Gists Sources:
- tao-j/brt.m (idea) https://gist.github.com/tao-j/c7a3c1ec7c2f59ebe45d161d620d9169
- waydabber/chbrt.m (original code of this) https://gist.github.com/waydabber/ca5c4f52183598fe95cd8045adfc7623
Compile:
clang -fmodules -o chcntlog chcntlog.m
To increase contrast by 10% enter:
./chcntlog 10
To decrease contrast by 10% enter:
./chcntlog -10
*******/
typedef CFTypeRef IOAVServiceRef;
extern IOAVServiceRef IOAVServiceCreate(CFAllocatorRef allocator);
extern IOReturn IOAVServiceCopyEDID(IOAVServiceRef service, CFDataRef* x2);
extern IOReturn IOAVServiceReadI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t offset, void* outputBuffer,
uint32_t outputBufferSize);
extern IOReturn IOAVServiceWriteI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t dataAddress, void* inputBuffer,
uint32_t inputBufferSize);
#define BRIGHTNESS 0x10
#define CONTRAST 0x12
#define AUDIO_SPEAKER_VOLUME 0x62
#define AUDIO_MUTE 0x8D
#define SLEEPTIME 5000
struct DDCWriteCommand {
UInt8 control_id;
UInt8 new_value;
};
int main(int argc, char** argv) {
NSString *returnValue = @"";
IOAVServiceRef avService = IOAVServiceCreate(kCFAllocatorDefault);
if (!avService) {
returnValue = @"This is a tool to change the contrast of a single external display connected to an M1 Mac via DDC/CI. Do you have an external monitor? Are you on M1? Are you root?";
[returnValue writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
return 1;
}
IOReturn err;
UInt8 data[256];
memset(data, 0, sizeof(data));
data[0] = 0x82;
data[1] = 0x01;
data[2] = CONTRAST;
data[3] = 0x6e ^ data[0] ^ data[1] ^ data[2] ^ data[3];
char i2cBytes[12];
memset(i2cBytes, 0, sizeof(i2cBytes));
usleep(SLEEPTIME);
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 4);
if (err) {
returnValue = [NSString stringWithFormat:@"i2c error: %s", mach_error_string(err)];
[returnValue writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
return 1;
}
usleep(SLEEPTIME);
err = IOAVServiceReadI2C(avService, 0x37, 0x51, i2cBytes, 12);
NSRange maxValueRange = {7, 1};
NSRange currentValueRange = {9, 1};
if (err) {
returnValue = [NSString stringWithFormat:@"i2c error: %s", mach_error_string(err)];
} else {
NSData *readData = [NSData dataWithBytes:(const void *)i2cBytes length:(NSUInteger)11];
char maxValue, currentValue;
[[readData subdataWithRange:maxValueRange] getBytes:&maxValue length:sizeof(1)];
[[readData subdataWithRange:currentValueRange] getBytes:&currentValue length:sizeof(1)];
if (argc == 2) {
int nextValue;
nextValue = currentValue+atoi(argv[1]);
if (nextValue<0) nextValue=0;
if (nextValue>maxValue) nextValue=maxValue;
struct DDCWriteCommand command;
command.control_id = CONTRAST;
returnValue = [NSString stringWithFormat:@"Current contrast is %i\nSetting contrast to %i", currentValue, nextValue];
command.new_value = nextValue;
data[0] = 0x84;
data[1] = 0x03;
data[2] = command.control_id;
data[3] = (command.new_value) >> 8;
data[4] = command.new_value & 255;
data[5] = 0x6E ^ 0x51 ^ data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4];
for (int i = 0; i < 3; ++i)
{
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 6);
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
}
usleep(SLEEPTIME);
}
} else {
returnValue = @"Please specify a value between -100 and 100 as an argument to increase/decrease contrast by the desired level.";
}
}
[returnValue writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment