I did find this small "c" program somewhere, that switches the monitors as primary and secondary display. In the original version, I figured out that it returned the same serial number on both displays, even the displays itself had different serial numbers. I modified it to just display a list of monitors, and switched display 0 and display 1. Then I could run this only when the arrangement was wrong. This is far easier than reorganising the displays using the "display" system settings panel.
Unfortunately, I could not find the original author for it, but here is my modified version.
#include <stdio.h>
#include <ApplicationServices/ApplicationServices.h>
#include <IOKit/graphics/IOGraphicsLib.h>
#include <IOKit/IOKitLib.h>
#define MAX_DISPLAYS 32
void printDisplayInfo(io_service_t display) {
CFDictionaryRef info = IODisplayCreateInfoDictionary(display, kIODisplayOnlyPreferredName);
if (info) {
// Display product name
CFDictionaryRef names = CFDictionaryGetValue(info, CFSTR(kDisplayProductName));
if (names) {
CFStringRef productName;
if (CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), (const void**)&productName)) {
char buffer[256];
CFStringGetCString(productName, buffer, sizeof(buffer), kCFStringEncodingUTF8);
printf("Product Name: %s\n", buffer);
}
}
CFRelease(info);
} else {
printf("Failed to create display info dictionary.\n");
}
}
void getDisplaySerialNumber(io_service_t display) {
CFDataRef edidData = IORegistryEntryCreateCFProperty(display, CFSTR("IODisplayEDID"), kCFAllocatorDefault, 0);
if (edidData) {
const uint8_t *edidBytes = CFDataGetBytePtr(edidData);
if (edidBytes) {
char serialNumber[14] = {0};
// EDID block contains ASCII serial number in descriptor 1 (bytes 54-71)
if (edidBytes[54] == 0x00 && edidBytes[55] == 0x00 && edidBytes[56] == 0x00 && edidBytes[57] == 0xFF) {
for (int i = 0; i < 13; i++) {
serialNumber[i] = (char)edidBytes[58 + i];
}
printf("Human-readable Serial Number: %s\n", serialNumber);
} else {
printf("No human-readable serial number found in EDID descriptor.\n");
}
}
CFRelease(edidData);
} else {
printf("Failed to get EDID from display.\n");
}
}
int main (int argc, const char ** argv) {
CGDirectDisplayID activeDisplays[MAX_DISPLAYS], mainID = 99, primaryID = 99;
uint32_t mainSerial=0, serial, primaryserial=0;
CGDisplayErr err;
CGDisplayCount displayCount;
CGDisplayConfigRef config;
//DEBUG argc=2; argv[1]= "151d050f";
if( argc > 1 && strcmp(argv[1],"-h")==0 ) {
printf("Usage: %s [<primary-serial>]\n\nNo arguments will just list your displays. Give <primary-serial> to set corresponding monitor as the main display.\n", argv[0]);
exit(0);
} else if( argc > 1 ) {
sscanf(argv[1], "%x", &primaryserial);
} else {
// Trick to include serial in app name so you dont need args and an extra script ;-)
char *p=strchr(argv[0],(int)'_');
if (p != NULL) {
sscanf(p+1, "%x", &primaryserial);
}
}
err = CGGetActiveDisplayList(MAX_DISPLAYS, activeDisplays, &displayCount);
if ( err != kCGErrorSuccess )
{
printf("Cannot get displays (%d)\n", err);
exit(1);
}
printf("%d displays detected\n", displayCount);
for (int i = 0; i < displayCount; i++) {/*
serial = CGDisplaySerialNumber(activeDisplays[i]);
uint32_t vendor = CGDisplayVendorNumber(activeDisplays[i]);
uint32_t model = CGDisplayModelNumber(activeDisplays[i]);
if (serial == primaryserial) {
primaryID = i;
}
printf("\tdisplay %d = unit %d vendor %x model %x serial %x", i, CGDisplayUnitNumber(activeDisplays[i]), vendor, model, serial);
if (CGDisplayIsMain(activeDisplays[i])) {
printf(" (main)");
mainID = i;
mainSerial = serial;
}
printf("\n");*/
io_service_t display = CGDisplayIOServicePort(activeDisplays[i]);
printf("Display %d:\n", i);
printDisplayInfo(display);
getDisplaySerialNumber(display);
}
if( /*primaryserial != 0
&& primaryID != 99
&& mainSerial != primaryserial*/ true
) {
mainID = 0;
primaryID = 1;
printf("swaping %d and %d!\n", mainID, primaryID);
CGBeginDisplayConfiguration(&config);
CGConfigureDisplayOrigin(config,activeDisplays[primaryID], 0, 0); //Set the second display as the new main display by positionning at 0,0
CGConfigureDisplayOrigin(config,activeDisplays[mainID], (int)CGDisplayPixelsWide(activeDisplays[mainID])*-1, 0); //Arrangement of the old main display to the left of the new main display
CGCompleteDisplayConfiguration(config,kCGConfigureForSession);
}
return 0;
}
Just open Xcode, start a new "C" command line utility, paste in the source and compile. The compiled product can be moved into you app folder, and make a shortcut on your desktop. Each time the displays has switched arrangement, just run it and it should be all good.