I recently encountered the same issue and finally found a solution myself
- Enumerate all displays with QueryDisplayConfig
if (QueryDisplayConfig(
QDC_ONLY_ACTIVE_PATHS,
&pathCount,
paths,
&modeCount,
modes,
NULL) == ERROR_SUCCESS)
{
// Use paths and modes
}
paths
and modes
contain all active display paths, which contain width / height (in pixel), refresh rate, etc.
- Query EDID registry path of the display path with DisplayConfigGetDeviceInfo
DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {
.header = {
.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME,
.size = sizeof(targetName),
.adapterId = path->targetInfo.adapterId,
.id = path->targetInfo.id,
},
};
if(DisplayConfigGetDeviceInfo(&targetName.header) == ERROR_SUCCESS)
{
// Use targetName.monitorDevicePath
}
In my laptop, targetName.monitorDevicePath
is \\?\DISPLAY#PRL5000#3&21de92e8&0&UID0#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
. Notibly DISPLAY#PRL5000#3&21de92e8&0&UID0
- Replace all
#
with \
; pretend SYSTEM\CurrentControlSet\Enum
; append Device Parameters
wchar_t regPath[256] = L"SYSTEM\\CurrentControlSet\\Enum";
wchar_t* pRegPath = regPath + strlen("SYSTEM\\CurrentControlSet\\Enum");
wchar_t* pDevPath = targetName.monitorDevicePath + strlen("\\\\?");
while (*pDevPath && *pDevPath != L'{')
{
if (*pDevPath == L'#')
*pRegPath = L'\\';
else
*pRegPath = *pDevPath;
++pRegPath;
++pDevPath;
assert(pRegPath < regPath + sizeof(regPath) / sizeof(wchar_t) + strlen("Device Parameters"));
}
wcscpy(pRegPath, L"Device Parameters");
You get L"SYSTEM\CurrentControlSet\Enum\DISPLAY\PRL5000\3&21de92e8&0&UID0\Device Parameters"
- Read the registry value
uint8_t edidData[1024];
DWORD edidLength = sizeof(edidData);
if (RegGetValueW(HKEY_LOCAL_MACHINE, regPath, L"EDID", RRF_RT_REG_BINARY, NULL, edidData, &edidLength) == ERROR_SUCCESS &&
edidLength > 0 && edidLength % 128 == 0)
{
// Read physical size from EDID data
}
I use this function to read physical size from EDID
void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height)
{
*width = (((uint32_t) edid[68] & 0xF0) << 4) + edid[66];
*height = (((uint32_t) edid[68] & 0x0F) << 8) + edid[67];
}
For multi-monitor machine, you may match an HMONITOR
to an DISPLAYCONFIG_PATH_INFO
:
- Query source name of the display path with DisplayConfigGetDeviceInfo
DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {
.header = {
.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME,
.size = sizeof(sourceName),
.adapterId = path->sourceInfo.adapterId,
.id = path->sourceInfo.id,
},
};
if (DisplayConfigGetDeviceInfo(&sourceName.header) == ERROR_SUCCESS)
{
// Use sourceName.viewGdiDeviceName
}
- Get
szDevice
from a HMONITOR
MONITORINFOEXW monitorInfo;
GetMonitorInfoW(hMonitor, (MONITORINFO*) &monitorInfo);
// Use monitorInfo.szDevice
sourceName.viewGdiDeviceName
should contain the same string as monitorInfo.szDevice
, which is \\.\DISPLAY1
in my laptop
Full code, which detects current resolution in pixel
, refresh rate
, DPI scaled resolution in pixel
, rotation in degree
, monitor brand name
, physical size in mm
, etc: https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/displayserver/displayserver_windows.c