user32: Implement QueryDisplayConfig().

Signed-off-by: Brendan Shanks <bshanks@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Brendan Shanks 2020-06-29 21:55:23 -07:00 committed by Alexandre Julliard
parent 2e1c48f351
commit 5fba152eea
2 changed files with 244 additions and 3 deletions

View File

@ -40,6 +40,7 @@
#include "winerror.h"
#include "initguid.h"
#include "d3dkmdt.h"
#include "devguid.h"
#include "setupapi.h"
#include "controls.h"
@ -249,6 +250,10 @@ static const WCHAR CSu[] = {'%','u',0};
static const WCHAR CSd[] = {'%','d',0};
static const WCHAR CSrgb[] = {'%','u',' ','%','u',' ','%','u',0};
DEFINE_DEVPROPKEY(DEVPROPKEY_GPU_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
DEFINE_DEVPROPKEY(DEVPROPKEY_MONITOR_GPU_LUID, 0xca085853, 0x16ce, 0x48aa, 0xb1, 0x14, 0xde, 0x9c, 0x72, 0x33, 0x42, 0x23, 1);
DEFINE_DEVPROPKEY(DEVPROPKEY_MONITOR_OUTPUT_ID, 0xca085853, 0x16ce, 0x48aa, 0xb1, 0x14, 0xde, 0x9c, 0x72, 0x33, 0x42, 0x23, 2);
/* Wine specific monitor properties */
DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_STATEFLAGS, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 2);
DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCMONITOR, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 3);
@ -4556,6 +4561,129 @@ done:
return ret;
}
static DISPLAYCONFIG_ROTATION get_dc_rotation(const DEVMODEW *devmode)
{
if (devmode->dmFields & DM_DISPLAYORIENTATION)
return devmode->u1.s2.dmDisplayOrientation + 1;
else
return DISPLAYCONFIG_ROTATION_IDENTITY;
}
static DISPLAYCONFIG_SCANLINE_ORDERING get_dc_scanline_ordering(const DEVMODEW *devmode)
{
if (!(devmode->dmFields & DM_DISPLAYFLAGS))
return DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
else if (devmode->u2.dmDisplayFlags & DM_INTERLACED)
return DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED;
else
return DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
}
static DISPLAYCONFIG_PIXELFORMAT get_dc_pixelformat(DWORD dmBitsPerPel)
{
if ((dmBitsPerPel == 8) || (dmBitsPerPel == 16) ||
(dmBitsPerPel == 24) || (dmBitsPerPel == 32))
return dmBitsPerPel / 8;
else
return DISPLAYCONFIG_PIXELFORMAT_NONGDI;
}
static void set_mode_target_info(DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid, UINT32 target_id,
UINT32 flags, const DEVMODEW *devmode)
{
DISPLAYCONFIG_TARGET_MODE *mode = &(info->u.targetMode);
info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET;
info->adapterId = *gpu_luid;
info->id = target_id;
/* FIXME: Populate pixelRate/hSyncFreq/totalSize with real data */
mode->targetVideoSignalInfo.pixelRate = devmode->dmDisplayFrequency * devmode->dmPelsWidth * devmode->dmPelsHeight;
mode->targetVideoSignalInfo.hSyncFreq.Numerator = devmode->dmDisplayFrequency * devmode->dmPelsWidth;
mode->targetVideoSignalInfo.hSyncFreq.Denominator = 1;
mode->targetVideoSignalInfo.vSyncFreq.Numerator = devmode->dmDisplayFrequency;
mode->targetVideoSignalInfo.vSyncFreq.Denominator = 1;
mode->targetVideoSignalInfo.activeSize.cx = devmode->dmPelsWidth;
mode->targetVideoSignalInfo.activeSize.cy = devmode->dmPelsHeight;
if (flags & QDC_DATABASE_CURRENT)
{
mode->targetVideoSignalInfo.totalSize.cx = 0;
mode->targetVideoSignalInfo.totalSize.cy = 0;
}
else
{
mode->targetVideoSignalInfo.totalSize.cx = devmode->dmPelsWidth;
mode->targetVideoSignalInfo.totalSize.cy = devmode->dmPelsHeight;
}
mode->targetVideoSignalInfo.u.videoStandard = D3DKMDT_VSS_OTHER;
mode->targetVideoSignalInfo.scanLineOrdering = get_dc_scanline_ordering(devmode);
}
static void set_path_target_info(DISPLAYCONFIG_PATH_TARGET_INFO *info, const LUID *gpu_luid,
UINT32 target_id, UINT32 mode_index, const DEVMODEW *devmode)
{
info->adapterId = *gpu_luid;
info->id = target_id;
info->u.modeInfoIdx = mode_index;
info->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL;
info->rotation = get_dc_rotation(devmode);
info->scaling = DISPLAYCONFIG_SCALING_IDENTITY;
info->refreshRate.Numerator = devmode->dmDisplayFrequency;
info->refreshRate.Denominator = 1;
info->scanLineOrdering = get_dc_scanline_ordering(devmode);
info->targetAvailable = TRUE;
info->statusFlags = DISPLAYCONFIG_TARGET_IN_USE;
}
static void set_mode_source_info(DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid,
UINT32 source_id, const DEVMODEW *devmode)
{
DISPLAYCONFIG_SOURCE_MODE *mode = &(info->u.sourceMode);
info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE;
info->adapterId = *gpu_luid;
info->id = source_id;
mode->width = devmode->dmPelsWidth;
mode->height = devmode->dmPelsHeight;
mode->pixelFormat = get_dc_pixelformat(devmode->dmBitsPerPel);
if (devmode->dmFields & DM_POSITION)
{
mode->position = devmode->u1.s2.dmPosition;
}
else
{
mode->position.x = 0;
mode->position.y = 0;
}
}
static void set_path_source_info(DISPLAYCONFIG_PATH_SOURCE_INFO *info, const LUID *gpu_luid,
UINT32 source_id, UINT32 mode_index)
{
info->adapterId = *gpu_luid;
info->id = source_id;
info->u.modeInfoIdx = mode_index;
info->statusFlags = DISPLAYCONFIG_SOURCE_IN_USE;
}
static BOOL source_mode_exists(const DISPLAYCONFIG_MODE_INFO *modeinfo, UINT32 num_modes,
UINT32 source_id, UINT32 *found_mode_index)
{
UINT32 i;
for (i = 0; i < num_modes; i++)
{
if (modeinfo[i].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE &&
modeinfo[i].id == source_id)
{
*found_mode_index = i;
return TRUE;
}
}
return FALSE;
}
/***********************************************************************
* QueryDisplayConfig (USER32.@)
*/
@ -4563,7 +4691,17 @@ LONG WINAPI QueryDisplayConfig(UINT32 flags, UINT32 *numpathelements, DISPLAYCON
UINT32 *numinfoelements, DISPLAYCONFIG_MODE_INFO *modeinfo,
DISPLAYCONFIG_TOPOLOGY_ID *topologyid)
{
FIXME("(%08x %p %p %p %p %p)\n", flags, numpathelements, pathinfo, numinfoelements, modeinfo, topologyid);
LONG adapter_index, ret;
HANDLE mutex;
HDEVINFO devinfo;
SP_DEVINFO_DATA device_data = {sizeof(device_data)};
DWORD monitor_index = 0, state_flags, type;
UINT32 output_id, source_mode_index, path_index = 0, mode_index = 0;
LUID gpu_luid;
WCHAR device_name[CCHDEVICENAME];
DEVMODEW devmode;
FIXME("(%08x %p %p %p %p %p): semi-stub\n", flags, numpathelements, pathinfo, numinfoelements, modeinfo, topologyid);
if (!numpathelements || !numinfoelements)
return ERROR_INVALID_PARAMETER;
@ -4571,10 +4709,105 @@ LONG WINAPI QueryDisplayConfig(UINT32 flags, UINT32 *numpathelements, DISPLAYCON
if (!*numpathelements || !*numinfoelements)
return ERROR_INVALID_PARAMETER;
if (!flags)
if (flags != QDC_ALL_PATHS &&
flags != QDC_ONLY_ACTIVE_PATHS &&
flags != QDC_DATABASE_CURRENT)
return ERROR_INVALID_PARAMETER;
return ERROR_NOT_SUPPORTED;
if (((flags == QDC_DATABASE_CURRENT) && !topologyid) ||
((flags != QDC_DATABASE_CURRENT) && topologyid))
return ERROR_INVALID_PARAMETER;
if (flags != QDC_ONLY_ACTIVE_PATHS)
FIXME("only returning active paths\n");
if (topologyid)
{
FIXME("setting toplogyid to DISPLAYCONFIG_TOPOLOGY_INTERNAL\n");
*topologyid = DISPLAYCONFIG_TOPOLOGY_INTERNAL;
}
wait_graphics_driver_ready();
mutex = get_display_device_init_mutex();
/* Iterate through "targets"/monitors.
* Each target corresponds to a path, and each path corresponds to one or two unique modes.
*/
devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR, DISPLAY, NULL, DIGCF_PRESENT);
if (devinfo == INVALID_HANDLE_VALUE)
{
ret = ERROR_GEN_FAILURE;
goto done;
}
ret = ERROR_GEN_FAILURE;
while (SetupDiEnumDeviceInfo(devinfo, monitor_index++, &device_data))
{
/* Only count active monitors */
if (!SetupDiGetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_STATEFLAGS,
&type, (BYTE *)&state_flags, sizeof(state_flags), NULL, 0))
goto done;
if (!(state_flags & DISPLAY_DEVICE_ACTIVE))
continue;
if (!SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_MONITOR_GPU_LUID,
&type, (BYTE *)&gpu_luid, sizeof(gpu_luid), NULL, 0))
goto done;
if (!SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_MONITOR_OUTPUT_ID,
&type, (BYTE *)&output_id, sizeof(output_id), NULL, 0))
goto done;
if (!SetupDiGetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_ADAPTERNAME,
&type, (BYTE *)device_name, sizeof(device_name), NULL, 0))
goto done;
devmode.dmSize = sizeof(devmode);
if (!EnumDisplaySettingsW(device_name, ENUM_CURRENT_SETTINGS, &devmode))
goto done;
/* Extract the adapter index from device_name to use as the source ID */
adapter_index = strtolW(device_name + ARRAY_SIZE(ADAPTER_PREFIX), NULL, 10);
adapter_index--;
if (path_index == *numpathelements || mode_index == *numinfoelements)
{
ret = ERROR_INSUFFICIENT_BUFFER;
goto done;
}
pathinfo[path_index].flags = DISPLAYCONFIG_PATH_ACTIVE;
set_mode_target_info(&modeinfo[mode_index], &gpu_luid, output_id, flags, &devmode);
set_path_target_info(&(pathinfo[path_index].targetInfo), &gpu_luid, output_id, mode_index, &devmode);
mode_index++;
if (mode_index == *numinfoelements)
{
ret = ERROR_INSUFFICIENT_BUFFER;
goto done;
}
/* Multiple targets can be driven by the same source, ensure a mode
* hasn't already been added for this source.
*/
if (!source_mode_exists(modeinfo, mode_index, adapter_index, &source_mode_index))
{
set_mode_source_info(&modeinfo[mode_index], &gpu_luid, adapter_index, &devmode);
source_mode_index = mode_index;
mode_index++;
}
set_path_source_info(&(pathinfo[path_index].sourceInfo), &gpu_luid, adapter_index, source_mode_index);
path_index++;
}
*numpathelements = path_index;
*numinfoelements = mode_index;
ret = ERROR_SUCCESS;
done:
SetupDiDestroyDeviceInfoList(devinfo);
release_display_device_init_mutex(mutex);
return ret;
}
/***********************************************************************

View File

@ -1299,6 +1299,7 @@ static void test_QueryDisplayConfig_result(UINT32 paths, const DISPLAYCONFIG_PAT
for (i = 0; i < paths; i++)
{
todo_wine {
source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
source_name.header.size = sizeof(source_name);
source_name.header.adapterId = pi[i].sourceInfo.adapterId;
@ -1307,7 +1308,9 @@ static void test_QueryDisplayConfig_result(UINT32 paths, const DISPLAYCONFIG_PAT
ret = pDisplayConfigGetDeviceInfo(&source_name.header);
ok(!ret, "Expected 0, got %d\n", ret);
ok(source_name.viewGdiDeviceName[0] != '\0', "Expected GDI device name, got empty string\n");
}
todo_wine {
target_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
target_name.header.size = sizeof(target_name);
target_name.header.adapterId = pi[i].targetInfo.adapterId;
@ -1316,7 +1319,9 @@ static void test_QueryDisplayConfig_result(UINT32 paths, const DISPLAYCONFIG_PAT
ret = pDisplayConfigGetDeviceInfo(&target_name.header);
ok(!ret, "Expected 0, got %d\n", ret);
ok(target_name.monitorDevicePath[0] != '\0', "Expected monitor device path, got empty string\n");
}
todo_wine {
preferred_mode.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE;
preferred_mode.header.size = sizeof(preferred_mode);
preferred_mode.header.adapterId = pi[i].targetInfo.adapterId;
@ -1326,7 +1331,9 @@ static void test_QueryDisplayConfig_result(UINT32 paths, const DISPLAYCONFIG_PAT
ok(!ret, "Expected 0, got %d\n", ret);
ok(preferred_mode.width > 0 && preferred_mode.height > 0, "Expected non-zero height/width, got %ux%u\n",
preferred_mode.width, preferred_mode.height);
}
todo_wine {
adapter_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME;
adapter_name.header.size = sizeof(adapter_name);
adapter_name.header.adapterId = pi[i].sourceInfo.adapterId;
@ -1334,6 +1341,7 @@ static void test_QueryDisplayConfig_result(UINT32 paths, const DISPLAYCONFIG_PAT
ret = pDisplayConfigGetDeviceInfo(&adapter_name.header);
ok(!ret, "Expected 0, got %d\n", ret);
ok(adapter_name.adapterDevicePath[0] != '\0', "Expected adapter device path, got empty string\n");
}
/* Check corresponding modes */
if (pi[i].sourceInfo.modeInfoIdx == DISPLAYCONFIG_PATH_MODE_IDX_INVALID)