mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 10:52:08 +02:00
245 lines
10 KiB
C
245 lines
10 KiB
C
#include "brightness.h"
|
|
#include "detection/displayserver/displayserver.h"
|
|
#include "common/apple/cf_helpers.h"
|
|
#include "common/edidHelper.h"
|
|
#include "common/time.h"
|
|
|
|
#include <CoreGraphics/CoreGraphics.h>
|
|
|
|
// DDC/CI
|
|
#ifdef __aarch64__
|
|
typedef CFTypeRef IOAVServiceRef;
|
|
extern IOAVServiceRef IOAVServiceCreate(CFAllocatorRef allocator) FF_A_WEAK_IMPORT;
|
|
extern IOAVServiceRef IOAVServiceCreateWithService(CFAllocatorRef allocator, io_service_t service) FF_A_WEAK_IMPORT;
|
|
extern IOReturn IOAVServiceCopyEDID(IOAVServiceRef service, CFDataRef* x2) FF_A_WEAK_IMPORT;
|
|
extern IOReturn IOAVServiceReadI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t offset, void* outputBuffer, uint32_t outputBufferSize) FF_A_WEAK_IMPORT;
|
|
extern IOReturn IOAVServiceWriteI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t dataAddress, void* inputBuffer, uint32_t inputBufferSize) FF_A_WEAK_IMPORT;
|
|
#else
|
|
// DDC/CI (Intel)
|
|
#include <IOKit/IOKitLib.h>
|
|
#include <IOKit/graphics/IOGraphicsLib.h>
|
|
#include <IOKit/i2c/IOI2CInterface.h>
|
|
extern void CGSServiceForDisplayNumber(CGDirectDisplayID display, io_service_t* service) FF_A_WEAK_IMPORT;
|
|
#endif
|
|
|
|
// ACPI
|
|
extern int DisplayServicesGetBrightness(CGDirectDisplayID display, float* brightness) FF_A_WEAK_IMPORT;
|
|
|
|
// Works for internal display
|
|
static const char* detectWithDisplayServices(const FFDisplayServerResult* displayServer, FFlist* result) {
|
|
if (DisplayServicesGetBrightness == NULL) {
|
|
return "DisplayServices function DisplayServicesGetBrightness is not available";
|
|
}
|
|
|
|
FF_LIST_FOR_EACH (FFDisplayResult, display, displayServer->displays) {
|
|
if (display->type == FF_DISPLAY_TYPE_BUILTIN || display->type == FF_DISPLAY_TYPE_UNKNOWN) {
|
|
float value;
|
|
if (DisplayServicesGetBrightness((CGDirectDisplayID) display->id, &value) == kCGErrorSuccess) {
|
|
FFBrightnessResult* brightness = FF_LIST_ADD(FFBrightnessResult, *result);
|
|
brightness->current = value;
|
|
brightness->max = 1;
|
|
brightness->min = 0;
|
|
ffStrbufInitCopy(&brightness->name, &display->name);
|
|
brightness->builtin = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef __aarch64__
|
|
// https://github.com/waydabber/m1ddc
|
|
// Works for Apple Silicon and USB-C adapter connection ( but not HTMI )
|
|
static const char* detectWithDdcci(FF_A_UNUSED const FFDisplayServerResult* displayServer, FFBrightnessOptions* options, FFlist* result) {
|
|
if (!IOAVServiceCreate || !IOAVServiceReadI2C || !IOAVServiceWriteI2C) {
|
|
return "IOAVService is not available";
|
|
}
|
|
|
|
FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL;
|
|
if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("DCPAVServiceProxy"), &iterator) != kIOReturnSuccess) {
|
|
return "IOServiceGetMatchingServices() failed";
|
|
}
|
|
|
|
io_registry_entry_t registryEntry;
|
|
while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) {
|
|
FF_CFTYPE_AUTO_RELEASE IOAVServiceRef service = NULL;
|
|
{
|
|
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryAv = registryEntry;
|
|
|
|
FF_CFTYPE_AUTO_RELEASE CFBooleanRef IOAVServiceUserInterfaceSupported = IORegistryEntryCreateCFProperty(entryAv, CFSTR("IOAVServiceUserInterfaceSupported"), kCFAllocatorDefault, kNilOptions);
|
|
if (IOAVServiceUserInterfaceSupported && !CFBooleanGetValue(IOAVServiceUserInterfaceSupported)) {
|
|
// IOAVServiceCreateWithService won't work
|
|
continue;
|
|
}
|
|
|
|
FF_CFTYPE_AUTO_RELEASE CFStringRef location = IORegistryEntryCreateCFProperty(entryAv, CFSTR("Location"), kCFAllocatorDefault, kNilOptions);
|
|
if (location && CFStringCompare(location, CFSTR("Embedded"), 0) == 0) {
|
|
// Builtin display should be handled by DisplayServices
|
|
continue;
|
|
}
|
|
|
|
service = IOAVServiceCreateWithService(kCFAllocatorDefault, (io_service_t) registryEntry);
|
|
if (!service) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
{
|
|
uint8_t i2cIn[4] = { FF_DDC_CI_MAKE_HEADER(2), FF_DDC_CI_GET_VCP, FF_DDC_CI_LUMINANCE_OPCODE };
|
|
i2cIn[3] = FF_DDC_CI_WRITE_ADDR ^ i2cIn[0] ^ i2cIn[1] ^ i2cIn[2];
|
|
|
|
for (uint32_t i = 0; i < 2; ++i) {
|
|
IOAVServiceWriteI2C(service, FF_DDC_CI_ADDR, FF_DDC_CI_VCP_COMMAND, i2cIn, ARRAY_SIZE(i2cIn));
|
|
ffTimeSleep(options->ddcciSleep);
|
|
}
|
|
}
|
|
|
|
uint8_t i2cOut[12] = {};
|
|
if (IOAVServiceReadI2C(service, FF_DDC_CI_ADDR, FF_DDC_CI_VCP_COMMAND, i2cOut, ARRAY_SIZE(i2cOut)) == KERN_SUCCESS) {
|
|
if (i2cOut[2] != 0x02 || i2cOut[3] != 0x00) {
|
|
continue;
|
|
}
|
|
|
|
uint32_t current = ((uint32_t) i2cOut[8] << 8u) + (uint32_t) i2cOut[9];
|
|
uint32_t max = ((uint32_t) i2cOut[6] << 8u) + (uint32_t) i2cOut[7];
|
|
|
|
FFBrightnessResult* brightness = FF_LIST_ADD(FFBrightnessResult, *result);
|
|
brightness->max = max;
|
|
brightness->min = 0;
|
|
brightness->current = current;
|
|
ffStrbufInit(&brightness->name);
|
|
brightness->builtin = false;
|
|
|
|
uint8_t edid[128] = {};
|
|
if (IOAVServiceReadI2C(service, 0x50, 0x00, edid, ARRAY_SIZE(edid)) == KERN_SUCCESS) {
|
|
ffEdidGetName(edid, &brightness->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#else
|
|
static IOOptionBits getSupportedTransactionType(void) {
|
|
FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL;
|
|
|
|
if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceNameMatching("IOFramebufferI2CInterface"), &iterator) != KERN_SUCCESS) {
|
|
return kIOI2CNoTransactionType;
|
|
}
|
|
|
|
io_registry_entry_t registryEntry;
|
|
while ((registryEntry = IOIteratorNext(iterator)) != MACH_PORT_NULL) {
|
|
FF_IOOBJECT_AUTO_RELEASE io_service_t io_service = registryEntry;
|
|
FF_CFTYPE_AUTO_RELEASE CFNumberRef IOI2CTransactionTypes = IORegistryEntryCreateCFProperty(io_service, CFSTR(kIOI2CTransactionTypesKey), kCFAllocatorDefault, kNilOptions);
|
|
|
|
if (IOI2CTransactionTypes) {
|
|
int64_t types = 0;
|
|
ffCfNumGetInt64(IOI2CTransactionTypes, &types);
|
|
|
|
if (types) {
|
|
if ((1 << kIOI2CDDCciReplyTransactionType) & (uint64_t) types) {
|
|
return kIOI2CDDCciReplyTransactionType;
|
|
}
|
|
if ((1 << kIOI2CSimpleTransactionType) & (uint64_t) types) {
|
|
return kIOI2CSimpleTransactionType;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return kIOI2CNoTransactionType;
|
|
}
|
|
|
|
static const char* detectWithDdcci(const FFDisplayServerResult* displayServer, FFBrightnessOptions* options, FFlist* result) {
|
|
if (!CGSServiceForDisplayNumber) {
|
|
return "CGSServiceForDisplayNumber is not available";
|
|
}
|
|
IOOptionBits transactionType = getSupportedTransactionType();
|
|
if (transactionType == kIOI2CNoTransactionType) {
|
|
return "No supported IOI2C transaction type found";
|
|
}
|
|
|
|
FF_LIST_FOR_EACH (FFDisplayResult, display, displayServer->displays) {
|
|
if (display->type == FF_DISPLAY_TYPE_EXTERNAL) {
|
|
FF_IOOBJECT_AUTO_RELEASE io_service_t framebuffer = IO_OBJECT_NULL;
|
|
CGSServiceForDisplayNumber((CGDirectDisplayID) display->id, &framebuffer);
|
|
if (framebuffer == IO_OBJECT_NULL) {
|
|
continue;
|
|
}
|
|
|
|
IOItemCount count;
|
|
if (IOFBGetI2CInterfaceCount(framebuffer, &count) != KERN_SUCCESS || count == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (IOItemCount bus = 0; bus < count; ++bus) {
|
|
FF_IOOBJECT_AUTO_RELEASE io_service_t interface = IO_OBJECT_NULL;
|
|
if (IOFBCopyI2CInterfaceForBus(framebuffer, bus, &interface) != KERN_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
uint8_t i2cOut[12] = {};
|
|
IOI2CConnectRef connect = NULL;
|
|
if (IOI2CInterfaceOpen(interface, kNilOptions, &connect) != KERN_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
uint8_t i2cIn[] = { FF_DDC_CI_VCP_COMMAND, FF_DDC_CI_MAKE_HEADER(2), FF_DDC_CI_GET_VCP, FF_DDC_CI_LUMINANCE_OPCODE, 0 };
|
|
i2cIn[4] = FF_DDC_CI_WRITE_ADDR ^ i2cIn[0] ^ i2cIn[1] ^ i2cIn[2] ^ i2cIn[3];
|
|
|
|
IOI2CRequest request = {
|
|
.commFlags = kNilOptions,
|
|
.sendAddress = FF_DDC_CI_WRITE_ADDR,
|
|
.sendTransactionType = kIOI2CSimpleTransactionType,
|
|
.sendBuffer = (vm_address_t) i2cIn,
|
|
.sendBytes = ARRAY_SIZE(i2cIn),
|
|
.minReplyDelay = options->ddcciSleep * 1000ULL,
|
|
.replyAddress = FF_DDC_CI_READ_ADDR,
|
|
.replySubAddress = FF_DDC_CI_VCP_COMMAND,
|
|
.replyTransactionType = transactionType,
|
|
.replyBytes = ARRAY_SIZE(i2cOut),
|
|
.replyBuffer = (vm_address_t) i2cOut,
|
|
};
|
|
IOReturn ret = IOI2CSendRequest(connect, kNilOptions, &request);
|
|
IOI2CInterfaceClose(connect, kNilOptions);
|
|
|
|
if (ret != KERN_SUCCESS || request.result != kIOReturnSuccess || request.replyBytes < 10) {
|
|
continue;
|
|
}
|
|
if (i2cOut[2] != 0x02 || i2cOut[3] != 0x00) {
|
|
continue;
|
|
}
|
|
|
|
uint32_t current = ((uint32_t) i2cOut[8] << 8u) + (uint32_t) i2cOut[9];
|
|
uint32_t max = ((uint32_t) i2cOut[6] << 8u) + (uint32_t) i2cOut[7];
|
|
|
|
FFBrightnessResult* brightness = FF_LIST_ADD(FFBrightnessResult, *result);
|
|
brightness->max = max;
|
|
brightness->min = 0;
|
|
brightness->current = current;
|
|
ffStrbufInitCopy(&brightness->name, &display->name);
|
|
brightness->builtin = false;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
const char* ffDetectBrightness(FFBrightnessOptions* options, FFlist* result) {
|
|
const FFDisplayServerResult* displayServer = ffConnectDisplayServer();
|
|
|
|
detectWithDisplayServices(displayServer, result);
|
|
|
|
if (options->ddcciSleep != FF_BRIGHTNESS_DDCCI_SLEEP_SKIP && displayServer->displays.length > result->length) {
|
|
detectWithDdcci(displayServer, options, result);
|
|
}
|
|
|
|
return NULL;
|
|
}
|