Screen Brightness & Display Hardware Management #13

Closed
opened 2026-09-08 10:28:54 +01:00 by gronod · 1 comment
Owner

Resolved in Milestone 4 via commit 6473a1a on develop.

  • Implemented MMDisplayManager.h/.m using CGGetOnlineDisplayList, CGDisplayMode, DisplayServicesGetBrightness/DisplayServicesSetBrightness with IODisplayGetFloatParameter/IODisplaySetFloatParameter fallback.
  • Added Display Card in ContentView.swift with dynamic resolution, refresh rate info, and interactive brightness slider.
  • Added unit tests in MMDisplayTests.swift.
Resolved in Milestone 4 via commit `6473a1a` on `develop`. - Implemented `MMDisplayManager.h/.m` using `CGGetOnlineDisplayList`, `CGDisplayMode`, `DisplayServicesGetBrightness`/`DisplayServicesSetBrightness` with `IODisplayGetFloatParameter`/`IODisplaySetFloatParameter` fallback. - Added Display Card in `ContentView.swift` with dynamic resolution, refresh rate info, and interactive brightness slider. - Added unit tests in `MMDisplayTests.swift`.
Author
Owner

Technical Implementation Plan & Code Solution

1. macOS Display & Brightness Architecture

  • Display Enumeration & Modes: CoreGraphics APIs (CGGetActiveDisplayList, CGDisplayCopyDisplayMode, CGDisplayModeGetRefreshRate).
  • Brightness Control: Implemented via dynamic lookup of DisplayServices private framework (DisplayServicesGetBrightness, DisplayServicesSetBrightness). This allows built-in MacBook screens and Apple Studio/Pro Displays to be monitored and adjusted seamlessly without private link errors.

2. Objective-C Provider (MMDisplayManager.m)

#import "MMDisplayManager.h"
#import <CoreGraphics/CoreGraphics.h>
#import <dlfcn.h>

typedef int (*DisplayServicesGetBrightnessFunc)(CGDirectDisplayID display, float *brightness);
typedef int (*DisplayServicesSetBrightnessFunc)(CGDirectDisplayID display, float brightness);

@interface MMDisplayManager () {
    void *_displayServicesHandle;
    DisplayServicesGetBrightnessFunc _getBrightness;
    DisplayServicesSetBrightnessFunc _setBrightness;
}
@end

@implementation MMDisplayManager

- (instancetype)init {
    self = [super init];
    if (self) {
        [self loadDisplayServices];
    }
    return self;
}

- (void)dealloc {
    if (_displayServicesHandle) {
        dlclose(_displayServicesHandle);
    }
}

- (void)loadDisplayServices {
    _displayServicesHandle = dlopen("/System/Library/PrivateFrameworks/DisplayServices.framework/DisplayServices", RTLD_LAZY);
    if (_displayServicesHandle) {
        _getBrightness = (DisplayServicesGetBrightnessFunc)dlsym(_displayServicesHandle, "DisplayServicesGetBrightness");
        _setBrightness = (DisplayServicesSetBrightnessFunc)dlsym(_displayServicesHandle, "DisplayServicesSetBrightness");
    }
}

- (NSArray<NSDictionary *> *)sampleDisplays {
    CGDirectDisplayID activeDisplays[16];
    uint32_t displayCount = 0;
    CGGetActiveDisplayList(16, activeDisplays, &displayCount);

    NSMutableArray *results = [NSMutableArray arrayWithCapacity:displayCount];

    for (uint32_t i = 0; i < displayCount; i++) {
        CGDirectDisplayID dID = activeDisplays[i];
        BOOL isBuiltIn = CGDisplayIsBuiltin(dID);

        float brightness = -1.0f;
        if (_getBrightness) {
            _getBrightness(dID, &brightness);
        }

        CGDisplayModeRef mode = CGDisplayCopyDisplayMode(dID);
        double refreshRate = 60.0;
        size_t width = CGDisplayPixelsWide(dID);
        size_t height = CGDisplayPixelsHigh(dID);

        if (mode) {
            refreshRate = CGDisplayModeGetRefreshRate(mode);
            if (refreshRate == 0) refreshRate = 60.0; // Fixed default
            CGDisplayModeRelease(mode);
        }

        [results addObject:@{
            @"displayID": @(dID),
            @"isBuiltIn": @(isBuiltIn),
            @"brightness": @(brightness >= 0 ? brightness : 1.0f),
            @"hasBrightnessControl": @(brightness >= 0),
            @"width": @(width),
            @"height": @(height),
            @"refreshRate": @(refreshRate)
        }];
    }

    return [results copy];
}

- (BOOL)setBrightness:(float)val forDisplayID:(CGDirectDisplayID)dID {
    if (_setBrightness) {
        return (_setBrightness(dID, val) == 0);
    }
    return NO;
}
@end

3. Swift UI Slider Integration

public struct DisplayInfo: Identifiable, Sendable {
    public var id: CGDirectDisplayID { displayID }
    public let displayID: CGDirectDisplayID
    public let isBuiltIn: Bool
    public var brightness: Float
    public let hasBrightnessControl: Bool
    public let width: Int
    public let height: Int
    public let refreshRate: Double
}

4. Dynamic Linking Safety

Using dlopen/dlsym ensures binary compilation never fails across different macOS SDK versions while maintaining runtime safety if running in restricted sandbox environments.

## Technical Implementation Plan & Code Solution ### 1. macOS Display & Brightness Architecture - **Display Enumeration & Modes**: CoreGraphics APIs (`CGGetActiveDisplayList`, `CGDisplayCopyDisplayMode`, `CGDisplayModeGetRefreshRate`). - **Brightness Control**: Implemented via dynamic lookup of `DisplayServices` private framework (`DisplayServicesGetBrightness`, `DisplayServicesSetBrightness`). This allows built-in MacBook screens and Apple Studio/Pro Displays to be monitored and adjusted seamlessly without private link errors. --- ### 2. Objective-C Provider (`MMDisplayManager.m`) ```objc #import "MMDisplayManager.h" #import <CoreGraphics/CoreGraphics.h> #import <dlfcn.h> typedef int (*DisplayServicesGetBrightnessFunc)(CGDirectDisplayID display, float *brightness); typedef int (*DisplayServicesSetBrightnessFunc)(CGDirectDisplayID display, float brightness); @interface MMDisplayManager () { void *_displayServicesHandle; DisplayServicesGetBrightnessFunc _getBrightness; DisplayServicesSetBrightnessFunc _setBrightness; } @end @implementation MMDisplayManager - (instancetype)init { self = [super init]; if (self) { [self loadDisplayServices]; } return self; } - (void)dealloc { if (_displayServicesHandle) { dlclose(_displayServicesHandle); } } - (void)loadDisplayServices { _displayServicesHandle = dlopen("/System/Library/PrivateFrameworks/DisplayServices.framework/DisplayServices", RTLD_LAZY); if (_displayServicesHandle) { _getBrightness = (DisplayServicesGetBrightnessFunc)dlsym(_displayServicesHandle, "DisplayServicesGetBrightness"); _setBrightness = (DisplayServicesSetBrightnessFunc)dlsym(_displayServicesHandle, "DisplayServicesSetBrightness"); } } - (NSArray<NSDictionary *> *)sampleDisplays { CGDirectDisplayID activeDisplays[16]; uint32_t displayCount = 0; CGGetActiveDisplayList(16, activeDisplays, &displayCount); NSMutableArray *results = [NSMutableArray arrayWithCapacity:displayCount]; for (uint32_t i = 0; i < displayCount; i++) { CGDirectDisplayID dID = activeDisplays[i]; BOOL isBuiltIn = CGDisplayIsBuiltin(dID); float brightness = -1.0f; if (_getBrightness) { _getBrightness(dID, &brightness); } CGDisplayModeRef mode = CGDisplayCopyDisplayMode(dID); double refreshRate = 60.0; size_t width = CGDisplayPixelsWide(dID); size_t height = CGDisplayPixelsHigh(dID); if (mode) { refreshRate = CGDisplayModeGetRefreshRate(mode); if (refreshRate == 0) refreshRate = 60.0; // Fixed default CGDisplayModeRelease(mode); } [results addObject:@{ @"displayID": @(dID), @"isBuiltIn": @(isBuiltIn), @"brightness": @(brightness >= 0 ? brightness : 1.0f), @"hasBrightnessControl": @(brightness >= 0), @"width": @(width), @"height": @(height), @"refreshRate": @(refreshRate) }]; } return [results copy]; } - (BOOL)setBrightness:(float)val forDisplayID:(CGDirectDisplayID)dID { if (_setBrightness) { return (_setBrightness(dID, val) == 0); } return NO; } @end ``` --- ### 3. Swift UI Slider Integration ```swift public struct DisplayInfo: Identifiable, Sendable { public var id: CGDirectDisplayID { displayID } public let displayID: CGDirectDisplayID public let isBuiltIn: Bool public var brightness: Float public let hasBrightnessControl: Bool public let width: Int public let height: Int public let refreshRate: Double } ``` --- ### 4. Dynamic Linking Safety Using `dlopen`/`dlsym` ensures binary compilation never fails across different macOS SDK versions while maintaining runtime safety if running in restricted sandbox environments.
gronod added this to the M4: Presentation, Visuals & Menu Bar Integration milestone 2026-09-08 10:42:45 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: gronod/MacMonitor#13