2023-08-09 16:31:24 +08:00
|
|
|
#include "monitor.h"
|
2023-08-09 10:11:24 +08:00
|
|
|
#include "detection/displayserver/displayserver.h"
|
|
|
|
|
#include "util/apple/cf_helpers.h"
|
|
|
|
|
#include "util/edidHelper.h"
|
|
|
|
|
|
2023-08-11 16:45:16 +08:00
|
|
|
#import <AppKit/NSScreen.h>
|
|
|
|
|
#import <Foundation/Foundation.h>
|
2023-10-04 14:41:06 +08:00
|
|
|
#import <CoreGraphics/CoreGraphics.h>
|
2023-08-11 16:45:16 +08:00
|
|
|
|
2024-08-12 16:23:01 +08:00
|
|
|
#ifdef MAC_OS_X_VERSION_10_15
|
2023-08-09 10:11:24 +08:00
|
|
|
extern CFDictionaryRef CoreDisplay_DisplayCreateInfoDictionary(CGDirectDisplayID display) __attribute__((weak_import));
|
2024-08-12 16:23:01 +08:00
|
|
|
#else
|
|
|
|
|
#include <IOKit/graphics/IOGraphicsLib.h>
|
2024-05-19 09:31:08 +08:00
|
|
|
#endif
|
|
|
|
|
|
2023-10-04 00:28:49 +08:00
|
|
|
static bool detectHdrSupportWithNSScreen(FFDisplayResult* display)
|
2023-08-09 10:11:24 +08:00
|
|
|
{
|
2023-10-04 00:28:49 +08:00
|
|
|
NSScreen* mainScreen = NSScreen.mainScreen;
|
|
|
|
|
if (display->primary)
|
2024-05-19 09:31:08 +08:00
|
|
|
{
|
2024-07-11 14:05:34 +08:00
|
|
|
#ifdef MAC_OS_X_VERSION_10_15
|
2023-10-04 00:28:49 +08:00
|
|
|
return mainScreen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1;
|
2024-05-19 09:31:08 +08:00
|
|
|
#else
|
|
|
|
|
return mainScreen.maximumExtendedDynamicRangeColorComponentValue > 1;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2023-10-04 00:28:49 +08:00
|
|
|
else
|
2023-08-09 10:11:24 +08:00
|
|
|
{
|
2023-10-04 00:28:49 +08:00
|
|
|
for (NSScreen* screen in NSScreen.screens)
|
2023-08-09 10:11:24 +08:00
|
|
|
{
|
2023-10-04 00:28:49 +08:00
|
|
|
if (screen == mainScreen) continue;
|
2024-09-27 09:16:54 +08:00
|
|
|
NSNumber* screenNumber = screen.deviceDescription[@"NSScreenNumber"];
|
2023-10-04 00:28:49 +08:00
|
|
|
if (screenNumber && screenNumber.longValue == (long) display->id)
|
2023-08-09 10:11:24 +08:00
|
|
|
{
|
2024-05-19 09:31:08 +08:00
|
|
|
#ifdef MAC_OS_X_VERSION_10_15
|
2023-10-04 00:28:49 +08:00
|
|
|
return screen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1;
|
2024-05-19 09:31:08 +08:00
|
|
|
#else
|
|
|
|
|
return screen.maximumExtendedDynamicRangeColorComponentValue > 1;
|
|
|
|
|
#endif
|
2023-08-09 10:11:24 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-04 00:28:49 +08:00
|
|
|
return false;
|
2023-08-09 10:11:24 +08:00
|
|
|
}
|
|
|
|
|
|
2023-10-04 00:28:49 +08:00
|
|
|
const char* ffDetectMonitor(FFlist* results)
|
2023-08-09 10:11:24 +08:00
|
|
|
{
|
2024-05-19 09:31:08 +08:00
|
|
|
#ifdef MAC_OS_X_VERSION_10_15
|
2023-10-04 00:28:49 +08:00
|
|
|
if(!CoreDisplay_DisplayCreateInfoDictionary) return "CoreDisplay_DisplayCreateInfoDictionary is not available";
|
2024-05-19 09:31:08 +08:00
|
|
|
#endif
|
2023-10-04 00:28:49 +08:00
|
|
|
const FFDisplayServerResult* displayServer = ffConnectDisplayServer();
|
2023-08-09 10:11:24 +08:00
|
|
|
|
2023-10-04 00:28:49 +08:00
|
|
|
FF_LIST_FOR_EACH(FFDisplayResult, display, displayServer->displays)
|
2023-08-09 10:11:24 +08:00
|
|
|
{
|
2024-05-19 09:31:08 +08:00
|
|
|
#ifdef MAC_OS_X_VERSION_10_15
|
2023-10-04 00:28:49 +08:00
|
|
|
CFDictionaryRef FF_CFTYPE_AUTO_RELEASE displayInfo = CoreDisplay_DisplayCreateInfoDictionary((CGDirectDisplayID) display->id);
|
2024-05-19 09:31:08 +08:00
|
|
|
#else
|
|
|
|
|
io_service_t servicePort = CGDisplayIOServicePort((CGDirectDisplayID) display->id);
|
2024-08-12 16:23:01 +08:00
|
|
|
CFDictionaryRef FF_CFTYPE_AUTO_RELEASE displayInfo = IODisplayCreateInfoDictionary(servicePort, kIODisplayOnlyPreferredName);
|
2024-05-19 09:31:08 +08:00
|
|
|
#endif
|
2023-10-04 00:28:49 +08:00
|
|
|
if(!displayInfo) continue;
|
2023-08-09 10:11:24 +08:00
|
|
|
|
2024-01-10 16:34:14 +08:00
|
|
|
bool isVirtual = false;
|
|
|
|
|
if (ffCfDictGetBool(displayInfo, CFSTR("kCGDisplayIsVirtualDevice"), &isVirtual) == NULL && isVirtual)
|
|
|
|
|
continue;
|
|
|
|
|
|
2023-10-04 00:28:49 +08:00
|
|
|
uint8_t edidData[128] = {};
|
|
|
|
|
uint32_t edidLength = 0;
|
2024-08-12 16:23:01 +08:00
|
|
|
if (ffCfDictGetData(displayInfo, CFSTR(kIODisplayEDIDKey), 0, sizeof(edidData), edidData, &edidLength) == NULL)
|
2023-08-09 10:11:24 +08:00
|
|
|
{
|
2023-10-04 00:28:49 +08:00
|
|
|
uint32_t width, height;
|
|
|
|
|
ffEdidGetPhysicalResolution(edidData, &width, &height);
|
|
|
|
|
if (width > 0 && height > 0)
|
|
|
|
|
{
|
|
|
|
|
FFMonitorResult* monitor = (FFMonitorResult*) ffListAdd(results);
|
|
|
|
|
ffStrbufInitCopy(&monitor->name, &display->name);
|
|
|
|
|
monitor->width = width;
|
|
|
|
|
monitor->height = height;
|
|
|
|
|
ffEdidGetPhysicalSize(edidData, &monitor->physicalWidth, &monitor->physicalHeight);
|
|
|
|
|
monitor->hdrCompatible = CFDictionaryContainsKey(displayInfo, CFSTR("ReferencePeakHDRLuminance")) ||
|
|
|
|
|
detectHdrSupportWithNSScreen(display);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-08-09 10:11:24 +08:00
|
|
|
}
|
|
|
|
|
|
2023-10-04 00:28:49 +08:00
|
|
|
int width, height;
|
|
|
|
|
if (ffCfDictGetInt(displayInfo, CFSTR("kCGDisplayPixelWidth"), &width) || // Default resolution (limited by connectors, GPUs, etc.)
|
|
|
|
|
ffCfDictGetInt(displayInfo, CFSTR("kCGDisplayPixelHeight"), &height) ||
|
|
|
|
|
width <= 0 || height <= 0)
|
2023-08-09 10:11:24 +08:00
|
|
|
continue;
|
|
|
|
|
|
2023-10-04 00:28:49 +08:00
|
|
|
FFMonitorResult* monitor = (FFMonitorResult*) ffListAdd(results);
|
|
|
|
|
monitor->width = (uint32_t) width;
|
|
|
|
|
monitor->height = (uint32_t) height;
|
|
|
|
|
ffStrbufInitCopy(&monitor->name, &display->name);
|
2023-08-11 22:04:26 +08:00
|
|
|
|
2023-10-04 00:28:49 +08:00
|
|
|
CGSize size = CGDisplayScreenSize((CGDirectDisplayID) display->id);
|
|
|
|
|
monitor->physicalWidth = (uint32_t) (size.width + 0.5);
|
|
|
|
|
monitor->physicalHeight = (uint32_t) (size.height + 0.5);
|
|
|
|
|
monitor->hdrCompatible = CFDictionaryContainsKey(displayInfo, CFSTR("ReferencePeakHDRLuminance")) ||
|
|
|
|
|
detectHdrSupportWithNSScreen(display);
|
2024-07-11 14:05:34 +08:00
|
|
|
monitor->serial = CGDisplaySerialNumber((CGDirectDisplayID) display->id);
|
2024-01-10 16:34:14 +08:00
|
|
|
|
2024-07-18 13:46:26 +08:00
|
|
|
FF_CFTYPE_AUTO_RELEASE CFArrayRef modes = CGDisplayCopyAllDisplayModes((CGDirectDisplayID) display->id, NULL);
|
|
|
|
|
double maxRefreshRate = 0;
|
|
|
|
|
for (uint32_t j = 0; j < CFArrayGetCount(modes); ++j)
|
|
|
|
|
{
|
|
|
|
|
CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, j);
|
|
|
|
|
if (CGDisplayModeGetWidth(mode) == (uint32_t) width && CGDisplayModeGetHeight(mode) == (uint32_t) height)
|
|
|
|
|
{
|
|
|
|
|
double refreshRate = CGDisplayModeGetRefreshRate(mode);
|
|
|
|
|
if (refreshRate > maxRefreshRate) maxRefreshRate = refreshRate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
monitor->refreshRate = maxRefreshRate;
|
|
|
|
|
|
2024-07-11 14:05:34 +08:00
|
|
|
int64_t year, week;
|
2024-01-10 16:34:14 +08:00
|
|
|
if (ffCfDictGetInt64(displayInfo, CFSTR("DisplayYearManufacture"), &year) == NULL)
|
|
|
|
|
monitor->manufactureYear = (uint16_t) year;
|
|
|
|
|
if (ffCfDictGetInt64(displayInfo, CFSTR("DisplayWeekManufacture"), &week) == NULL)
|
|
|
|
|
monitor->manufactureWeek = (uint16_t) week;
|
2023-08-09 10:11:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|