diff options
Diffstat (limited to 'src/detection/displayserver/displayserver_windows.c')
| -rw-r--r-- | src/detection/displayserver/displayserver_windows.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/src/detection/displayserver/displayserver_windows.c b/src/detection/displayserver/displayserver_windows.c new file mode 100644 index 0000000..f92c144 --- /dev/null +++ b/src/detection/displayserver/displayserver_windows.c @@ -0,0 +1,219 @@ +#include "displayserver.h" +#include "common/edidHelper.h" +#include "common/windows/registry.h" +#include "common/windows/unicode.h" + +#include <windows.h> +#include <shellscalingapi.h> + +static inline void freeArgBuffer(FFArgBuffer* buffer) { + if (buffer->data) { + free(buffer->data); + } + buffer->data = NULL; + buffer->length = 0; +} +#define FF_AUTO_FREE_ARG_BUFFER FF_A_CLEANUP(freeArgBuffer) + +// http://undoc.airesoft.co.uk/user32.dll/IsThreadDesktopComposited.php +BOOL WINAPI IsThreadDesktopComposited(); +BOOL WINAPI GetDpiForMonitorInternal(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY); + +static void detectDisplays(FFDisplayServerResult* ds) { + DISPLAYCONFIG_PATH_INFO paths[128]; + uint32_t pathCount = ARRAY_SIZE(paths); + DISPLAYCONFIG_MODE_INFO modes[256]; + uint32_t modeCount = ARRAY_SIZE(modes); + + if (QueryDisplayConfig( + QDC_ONLY_ACTIVE_PATHS, + &pathCount, + paths, + &modeCount, + modes, + NULL) == ERROR_SUCCESS) { + for (uint32_t i = 0; i < pathCount; ++i) { + const DISPLAYCONFIG_PATH_INFO* path = &paths[i]; + const DISPLAYCONFIG_SOURCE_MODE* sourceMode = &modes[path->sourceInfo.modeInfoIdx].sourceMode; + + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate(); + uint32_t physicalWidth = 0, physicalHeight = 0; + + DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = { + .header = { + .type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME, + .size = sizeof(targetName), + .adapterId = path->targetInfo.adapterId, + .id = path->targetInfo.id, + }, + }; + FF_AUTO_FREE_ARG_BUFFER FFArgBuffer edid = {}; + if (DisplayConfigGetDeviceInfo(&targetName.header) == ERROR_SUCCESS) { + 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 + ARRAY_SIZE(regPath) + strlen("Device Parameters")); + } + wcscpy(pRegPath, L"Device Parameters"); + + FF_AUTO_CLOSE_FD HANDLE hKey = NULL; + if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regPath, &hKey, NULL) && + ffRegReadData(hKey, L"EDID", &edid, NULL) && + ffEdidIsValid(edid.data, edid.length)) { + ffEdidGetName(edid.data, &name); + ffEdidGetPhysicalSize(edid.data, &physicalWidth, &physicalHeight); + } else { + edid.length = 0; + if (targetName.flags.friendlyNameFromEdid) { + ffStrbufSetWS(&name, targetName.monitorFriendlyDeviceName); + } else { + ffStrbufSetWS(&name, targetName.monitorDevicePath); + ffStrbufSubstrAfterFirstC(&name, '#'); + ffStrbufSubstrBeforeFirstC(&name, '#'); + } + } + } + + uint32_t width = sourceMode->width; + uint32_t height = sourceMode->height; + uint32_t rotation; + switch (path->targetInfo.rotation) { + case DISPLAYCONFIG_ROTATION_ROTATE90: + rotation = 90; + break; + case DISPLAYCONFIG_ROTATION_ROTATE180: + rotation = 180; + break; + case DISPLAYCONFIG_ROTATION_ROTATE270: + rotation = 270; + break; + default: + rotation = 0; + break; + } + + DISPLAYCONFIG_TARGET_PREFERRED_MODE preferredMode = { + .header = { + .type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE, + .size = sizeof(preferredMode), + .adapterId = path->targetInfo.adapterId, + .id = path->targetInfo.id, + } + }; + double preferredRefreshRate = 0; + if (DisplayConfigGetDeviceInfo(&preferredMode.header) == ERROR_SUCCESS) { + DISPLAYCONFIG_RATIONAL freq = preferredMode.targetMode.targetVideoSignalInfo.vSyncFreq; + preferredRefreshRate = freq.Numerator / (double) freq.Denominator; + } + + uint32_t systemDpi = 0; + HMONITOR hMonitor = MonitorFromPoint(*(POINT*) &sourceMode->position, MONITOR_DEFAULTTONULL); + if (hMonitor) { + UINT ignored; + GetDpiForMonitorInternal(hMonitor, MDT_EFFECTIVE_DPI, &systemDpi, &ignored); + } + + if (systemDpi == 0) { + HDC hdc = GetDC(NULL); + systemDpi = (uint32_t) GetDeviceCaps(hdc, LOGPIXELSX); + if (systemDpi == 0) { + systemDpi = 96; + } + ReleaseDC(NULL, hdc); + } + + if (path->targetInfo.rotation == DISPLAYCONFIG_ROTATION_ROTATE90 || + path->targetInfo.rotation == DISPLAYCONFIG_ROTATION_ROTATE270) { + uint32_t temp = width; + width = height; + height = temp; + } + + FFDisplayResult* display = ffdsAppendDisplay(ds, + width, + height, + path->targetInfo.refreshRate.Numerator / (double) path->targetInfo.refreshRate.Denominator, + systemDpi, + preferredMode.width, + preferredMode.height, + preferredRefreshRate, + rotation, + &name, + path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER ? FF_DISPLAY_TYPE_UNKNOWN : path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL || path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED || path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED ? FF_DISPLAY_TYPE_BUILTIN + : FF_DISPLAY_TYPE_EXTERNAL, + sourceMode->position.x == 0 && sourceMode->position.y == 0, + (uintptr_t) hMonitor, + physicalWidth, + physicalHeight, + "GDI"); + + if (display) { + DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO_2 advColorInfo2 = { + .header = { + .type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2, + .size = sizeof(advColorInfo2), + .adapterId = path->targetInfo.adapterId, + .id = path->targetInfo.id, + } + }; + if (DisplayConfigGetDeviceInfo(&advColorInfo2.header) == ERROR_SUCCESS) { + if (advColorInfo2.highDynamicRangeUserEnabled) { + display->hdrStatus = FF_DISPLAY_HDR_STATUS_ENABLED; + } else if (advColorInfo2.highDynamicRangeSupported) { + display->hdrStatus = FF_DISPLAY_HDR_STATUS_SUPPORTED; + } else { + display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNSUPPORTED; + } + display->bitDepth = (uint8_t) advColorInfo2.bitsPerColorChannel; + } else { + DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO advColorInfo = { + .header = { + .type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO, + .size = sizeof(advColorInfo), + .adapterId = path->targetInfo.adapterId, + .id = path->targetInfo.id, + } + }; + if (DisplayConfigGetDeviceInfo(&advColorInfo.header) == ERROR_SUCCESS) { + if (advColorInfo.advancedColorEnabled) { + display->hdrStatus = FF_DISPLAY_HDR_STATUS_ENABLED; + } else if (advColorInfo.advancedColorSupported) { + display->hdrStatus = FF_DISPLAY_HDR_STATUS_SUPPORTED; + } else { + display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNSUPPORTED; + } + display->bitDepth = (uint8_t) advColorInfo.bitsPerColorChannel; + } else { + display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNKNOWN; + } + } + if (edid.length > 0) { + ffEdidGetSerialAndManufactureDate(edid.data, &display->serial, &display->manufactureYear, &display->manufactureWeek); + } + display->drrStatus = path->flags & DISPLAYCONFIG_PATH_BOOST_REFRESH_RATE ? FF_DISPLAY_DRR_STATUS_ENABLED : FF_DISPLAY_DRR_STATUS_DISABLED; + } + } + } +} + +void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) { + if (IsThreadDesktopComposited()) { + ffStrbufSetStatic(&ds->wmProcessName, "dwm.exe"); + ffStrbufSetStatic(&ds->wmPrettyName, "Desktop Window Manager"); + } else { + // `explorer.exe` only provides a subset of WM functions, as well as the taskbar and desktop icons. + // While a window itself is drawn by kernel (GDI). Killing `explorer.exe` won't affect how windows are displayed generally. + ffStrbufSetStatic(&ds->wmProcessName, "explorer.exe"); + ffStrbufSetStatic(&ds->wmPrettyName, "Internal"); + } + + detectDisplays(ds); +} |