Derived from OSIT MasterKey app (LegacyUtilities.m, Ian Kohl 2019). Uses CGConfigureDisplayMirrorOfDisplay — native macOS API, no Homebrew dependency. Handler now tries display_control first; falls back to displayplacer for missing binary or per-device configStr overrides. Build the binary via scripts/build-display-control.sh on a Mac (requires Xcode CLT only), then commit resources/bin/display_control. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
150 lines
4.7 KiB
Objective-C
150 lines
4.7 KiB
Objective-C
/*
|
|
* display_control.m
|
|
* Native macOS CLI for programmatic display mirror/extend control.
|
|
*
|
|
* Derived from the OSIT MasterKey app (LegacyUtilities.m, Ian Kohl 2019).
|
|
* Uses CoreGraphics APIs — no external dependencies (no displayplacer/Homebrew required).
|
|
*
|
|
* Build: run scripts/build-display-control.sh on a Mac (requires Xcode Command Line Tools)
|
|
* Usage: display_control <mirror|extend|status>
|
|
*/
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
#include <Carbon/Carbon.h>
|
|
|
|
#define MAX_DISPLAYS 8
|
|
|
|
static int mirror_displays(void) {
|
|
CGDirectDisplayID onlineDspys[MAX_DISPLAYS] = {0};
|
|
CGDisplayCount numOnline = 0;
|
|
|
|
CGGetOnlineDisplayList(MAX_DISPLAYS, onlineDspys, &numOnline);
|
|
|
|
if (numOnline < 2) {
|
|
fprintf(stderr, "No secondary display detected (%u online).\n", numOnline);
|
|
return 1;
|
|
}
|
|
|
|
CGDirectDisplayID mainID = CGMainDisplayID();
|
|
|
|
CGDisplayConfigRef config;
|
|
CGError err = CGBeginDisplayConfiguration(&config);
|
|
if (err != kCGErrorSuccess) {
|
|
fprintf(stderr, "CGBeginDisplayConfiguration failed: %d\n", err);
|
|
return 1;
|
|
}
|
|
|
|
BOOL any_configured = NO;
|
|
for (CGDisplayCount i = 0; i < numOnline; i++) {
|
|
if (onlineDspys[i] != mainID) {
|
|
err = CGConfigureDisplayMirrorOfDisplay(config, onlineDspys[i], mainID);
|
|
if (err != kCGErrorSuccess) {
|
|
fprintf(stderr, "CGConfigureDisplayMirrorOfDisplay failed: %d\n", err);
|
|
CGCancelDisplayConfiguration(config);
|
|
return 1;
|
|
}
|
|
any_configured = YES;
|
|
}
|
|
}
|
|
|
|
if (!any_configured) {
|
|
CGCancelDisplayConfiguration(config);
|
|
fprintf(stderr, "No secondary displays to mirror.\n");
|
|
return 1;
|
|
}
|
|
|
|
err = CGCompleteDisplayConfiguration(config, kCGConfigurePermanently);
|
|
if (err != kCGErrorSuccess) {
|
|
fprintf(stderr, "CGCompleteDisplayConfiguration failed: %d\n", err);
|
|
return 1;
|
|
}
|
|
|
|
printf("Mirrored %u display(s).\n", numOnline - 1);
|
|
return 0;
|
|
}
|
|
|
|
static int extend_displays(void) {
|
|
CGDirectDisplayID onlineDspys[MAX_DISPLAYS] = {0};
|
|
CGDirectDisplayID activeDspys[MAX_DISPLAYS] = {0};
|
|
CGDisplayCount numOnline = 0, numActive = 0;
|
|
|
|
CGGetOnlineDisplayList(MAX_DISPLAYS, onlineDspys, &numOnline);
|
|
CGGetActiveDisplayList(MAX_DISPLAYS, activeDspys, &numActive);
|
|
|
|
if (numOnline < 2) {
|
|
fprintf(stderr, "No secondary display detected (%u online).\n", numOnline);
|
|
return 1;
|
|
}
|
|
|
|
if (numActive >= numOnline) {
|
|
printf("Displays already extended.\n");
|
|
return 0;
|
|
}
|
|
|
|
CGDirectDisplayID mainID = CGMainDisplayID();
|
|
|
|
CGDisplayConfigRef config;
|
|
CGError err = CGBeginDisplayConfiguration(&config);
|
|
if (err != kCGErrorSuccess) {
|
|
fprintf(stderr, "CGBeginDisplayConfiguration failed: %d\n", err);
|
|
return 1;
|
|
}
|
|
|
|
for (CGDisplayCount i = 0; i < numOnline; i++) {
|
|
if (onlineDspys[i] != mainID) {
|
|
// kCGNullDirectDisplay as master = un-mirror (extend)
|
|
err = CGConfigureDisplayMirrorOfDisplay(config, onlineDspys[i], kCGNullDirectDisplay);
|
|
if (err != kCGErrorSuccess) {
|
|
fprintf(stderr, "CGConfigureDisplayMirrorOfDisplay(null) failed: %d\n", err);
|
|
CGCancelDisplayConfiguration(config);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
err = CGCompleteDisplayConfiguration(config, kCGConfigurePermanently);
|
|
if (err != kCGErrorSuccess) {
|
|
fprintf(stderr, "CGCompleteDisplayConfiguration failed: %d\n", err);
|
|
return 1;
|
|
}
|
|
|
|
printf("Displays extended.\n");
|
|
return 0;
|
|
}
|
|
|
|
static void print_status(void) {
|
|
CGDirectDisplayID onlineDspys[MAX_DISPLAYS] = {0};
|
|
CGDirectDisplayID activeDspys[MAX_DISPLAYS] = {0};
|
|
CGDisplayCount numOnline = 0, numActive = 0;
|
|
|
|
CGGetOnlineDisplayList(MAX_DISPLAYS, onlineDspys, &numOnline);
|
|
CGGetActiveDisplayList(MAX_DISPLAYS, activeDspys, &numActive);
|
|
|
|
printf("online=%u active=%u %s\n",
|
|
numOnline, numActive,
|
|
(numOnline > 1 && numActive < numOnline) ? "mirrored" : "extended");
|
|
}
|
|
|
|
int main(int argc, const char * argv[]) {
|
|
@autoreleasepool {
|
|
if (argc < 2) {
|
|
fprintf(stderr, "Usage: display_control <mirror|extend|status>\n");
|
|
return 1;
|
|
}
|
|
|
|
const char *cmd = argv[1];
|
|
|
|
if (strcmp(cmd, "mirror") == 0) {
|
|
return mirror_displays();
|
|
} else if (strcmp(cmd, "extend") == 0) {
|
|
return extend_displays();
|
|
} else if (strcmp(cmd, "status") == 0) {
|
|
print_status();
|
|
return 0;
|
|
} else {
|
|
fprintf(stderr, "Unknown command: %s\nUsage: display_control <mirror|extend|status>\n", cmd);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|