feat(display): add display_control CoreGraphics binary, replace displayplacer as primary path

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>
This commit is contained in:
Scott Idem
2026-05-20 16:48:19 -04:00
parent 9df9d884e5
commit a14c7c7a3f
6 changed files with 267 additions and 51 deletions

View File

@@ -0,0 +1,40 @@
#!/bin/bash
# scripts/build-display-control.sh
# Compile the display_control binary for macOS.
#
# Requirements: Xcode Command Line Tools
# xcode-select --install
#
# Run this on a Mac. Commit the resulting binary to resources/bin/
# so it is bundled into the packaged Electron app without any Homebrew dependency.
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
SRC="$SCRIPT_DIR/display_control.m"
OUT_DIR="$REPO_ROOT/resources/bin"
OUT_BIN="$OUT_DIR/display_control"
if ! command -v clang &>/dev/null; then
echo "ERROR: clang not found."
echo "Install Xcode Command Line Tools: xcode-select --install"
exit 1
fi
mkdir -p "$OUT_DIR"
echo "Building display_control..."
clang -framework Cocoa -framework Carbon \
-o "$OUT_BIN" "$SRC"
chmod +x "$OUT_BIN"
echo "Built: $OUT_BIN"
echo ""
echo "Test it:"
echo " $OUT_BIN status"
echo " $OUT_BIN extend"
echo " $OUT_BIN mirror"
echo ""
echo "Once verified, commit resources/bin/display_control to the repo."

149
scripts/display_control.m Normal file
View File

@@ -0,0 +1,149 @@
/*
* 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;
}
}
}