wine/dlls/winex11.drv/display.c
Zhiyi Zhang acf03ed9da winex11.drv: Get virtual desktop size limit from the host primary display.
After 25167fb286, get_primary_monitor_rect()
maybe invalid before display device initialization or returns the desktop
rectangle after desktop initialization in virtual desktop mode, which is
wrong. The desktop size limit should come from host system.

This fixes a regression from efbbe66669.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47815
Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-05 17:30:53 +01:00

616 lines
24 KiB
C

/*
* X11DRV display device functions
*
* Copyright 2019 Zhiyi Zhang for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "rpc.h"
#include "winreg.h"
#include "initguid.h"
#include "devguid.h"
#include "devpkey.h"
#include "setupapi.h"
#define WIN32_NO_STATUS
#include "winternl.h"
#include "wine/debug.h"
#include "wine/unicode.h"
#include "x11drv.h"
WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
/* 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);
DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCWORK, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 4);
DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_ADAPTERNAME, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 5);
static const WCHAR driver_date_dataW[] = {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0};
static const WCHAR displayW[] = {'D','I','S','P','L','A','Y',0};
static const WCHAR pciW[] = {'P','C','I',0};
static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0};
static const WCHAR symbolic_link_valueW[]= {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
static const WCHAR gpu_idW[] = {'G','P','U','I','D',0};
static const WCHAR mointor_id_fmtW[] = {'M','o','n','i','t','o','r','I','D','%','d',0};
static const WCHAR adapter_name_fmtW[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y','%','d',0};
static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0};
static const WCHAR guid_fmtW[] = {
'{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-','%','0','2','x','%','0','2','x','-',
'%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0};
static const WCHAR gpu_instance_fmtW[] = {
'P','C','I','\\',
'V','E','N','_','%','0','4','X','&',
'D','E','V','_','%','0','4','X','&',
'S','U','B','S','Y','S','_','%','0','8','X','&',
'R','E','V','_','%','0','2','X','\\',
'%','0','8','X',0};
static const WCHAR gpu_hardware_id_fmtW[] = {
'P','C','I','\\',
'V','E','N','_','%','0','4','X','&',
'D','E','V','_','%','0','4','X','&',
'S','U','B','S','Y','S','_','0','0','0','0','0','0','0','0','&',
'R','E','V','_','0','0',0};
static const WCHAR video_keyW[] = {
'H','A','R','D','W','A','R','E','\\',
'D','E','V','I','C','E','M','A','P','\\',
'V','I','D','E','O',0};
static const WCHAR adapter_key_fmtW[] = {
'S','y','s','t','e','m','\\',
'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
'C','o','n','t','r','o','l','\\',
'V','i','d','e','o','\\',
'%','s','\\',
'%','0','4','x',0};
static const WCHAR device_video_fmtW[] = {
'\\','D','e','v','i','c','e','\\',
'V','i','d','e','o','%','d',0};
static const WCHAR machine_prefixW[] = {
'\\','R','e','g','i','s','t','r','y','\\',
'M','a','c','h','i','n','e','\\',0};
static const WCHAR nt_classW[] = {
'\\','R','e','g','i','s','t','r','y','\\',
'M','a','c','h','i','n','e','\\',
'S','y','s','t','e','m','\\',
'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
'C','o','n','t','r','o','l','\\',
'C','l','a','s','s','\\',0};
static const WCHAR monitor_instance_fmtW[] = {
'D','I','S','P','L','A','Y','\\',
'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r','\\',
'%','0','4','X','&','%','0','4','X',0};
static const WCHAR monitor_hardware_idW[] = {
'M','O','N','I','T','O','R','\\',
'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r',0,0};
static struct x11drv_display_device_handler host_handler;
struct x11drv_display_device_handler desktop_handler;
/* Cached screen information, protected by screen_section */
static HKEY video_key;
static RECT virtual_screen_rect;
static RECT primary_monitor_rect;
static FILETIME last_query_screen_time;
static CRITICAL_SECTION screen_section;
static CRITICAL_SECTION_DEBUG screen_critsect_debug =
{
0, 0, &screen_section,
{&screen_critsect_debug.ProcessLocksList, &screen_critsect_debug.ProcessLocksList},
0, 0, {(DWORD_PTR)(__FILE__ ": screen_section")}
};
static CRITICAL_SECTION screen_section = {&screen_critsect_debug, -1, 0, 0, 0, 0};
static HANDLE get_display_device_init_mutex(void)
{
static const WCHAR init_mutexW[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0};
HANDLE mutex = CreateMutexW(NULL, FALSE, init_mutexW);
WaitForSingleObject(mutex, INFINITE);
return mutex;
}
static void release_display_device_init_mutex(HANDLE mutex)
{
ReleaseMutex(mutex);
CloseHandle(mutex);
}
/* Update screen rectangle cache from SetupAPI if it's outdated, return FALSE on failure and TRUE on success */
static BOOL update_screen_cache(void)
{
RECT virtual_rect = {0}, primary_rect = {0}, monitor_rect;
SP_DEVINFO_DATA device_data = {sizeof(device_data)};
HDEVINFO devinfo = INVALID_HANDLE_VALUE;
FILETIME filetime = {0};
HANDLE mutex = NULL;
DWORD i = 0;
INT result;
DWORD type;
BOOL ret = FALSE;
EnterCriticalSection(&screen_section);
if ((!video_key && RegOpenKeyW(HKEY_LOCAL_MACHINE, video_keyW, &video_key))
|| RegQueryInfoKeyW(video_key, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &filetime))
{
LeaveCriticalSection(&screen_section);
return FALSE;
}
result = CompareFileTime(&filetime, &last_query_screen_time);
LeaveCriticalSection(&screen_section);
if (result < 1)
return TRUE;
mutex = get_display_device_init_mutex();
devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR, displayW, NULL, DIGCF_PRESENT);
if (devinfo == INVALID_HANDLE_VALUE)
goto fail;
while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
{
if (!SetupDiGetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_RCMONITOR, &type,
(BYTE *)&monitor_rect, sizeof(monitor_rect), NULL, 0))
goto fail;
UnionRect(&virtual_rect, &virtual_rect, &monitor_rect);
if (i == 1)
primary_rect = monitor_rect;
}
EnterCriticalSection(&screen_section);
virtual_screen_rect = virtual_rect;
primary_monitor_rect = primary_rect;
last_query_screen_time = filetime;
LeaveCriticalSection(&screen_section);
ret = TRUE;
fail:
SetupDiDestroyDeviceInfoList(devinfo);
release_display_device_init_mutex(mutex);
if (!ret)
WARN("Update screen cache failed!\n");
return ret;
}
POINT virtual_screen_to_root(INT x, INT y)
{
RECT virtual = get_virtual_screen_rect();
POINT pt;
pt.x = x - virtual.left;
pt.y = y - virtual.top;
return pt;
}
POINT root_to_virtual_screen(INT x, INT y)
{
RECT virtual = get_virtual_screen_rect();
POINT pt;
pt.x = x + virtual.left;
pt.y = y + virtual.top;
return pt;
}
RECT get_virtual_screen_rect(void)
{
RECT virtual;
update_screen_cache();
EnterCriticalSection(&screen_section);
virtual = virtual_screen_rect;
LeaveCriticalSection(&screen_section);
return virtual;
}
RECT get_primary_monitor_rect(void)
{
RECT primary;
update_screen_cache();
EnterCriticalSection(&screen_section);
primary = primary_monitor_rect;
LeaveCriticalSection(&screen_section);
return primary;
}
/* Get the primary monitor rect from the host system */
RECT get_host_primary_monitor_rect(void)
{
INT gpu_count, adapter_count, monitor_count;
struct x11drv_gpu *gpus = NULL;
struct x11drv_adapter *adapters = NULL;
struct x11drv_monitor *monitors = NULL;
RECT rect = {0};
/* The first monitor is always primary */
if (host_handler.get_gpus(&gpus, &gpu_count) && gpu_count &&
host_handler.get_adapters(gpus[0].id, &adapters, &adapter_count) && adapter_count &&
host_handler.get_monitors(adapters[0].id, &monitors, &monitor_count) && monitor_count)
rect = monitors[0].rc_monitor;
if (gpus) host_handler.free_gpus(gpus);
if (adapters) host_handler.free_adapters(adapters);
if (monitors) host_handler.free_monitors(monitors);
return rect;
}
void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler *new_handler)
{
if (new_handler->priority > host_handler.priority)
{
host_handler = *new_handler;
TRACE("Display device functions are now handled by: %s\n", host_handler.name);
}
}
void X11DRV_DisplayDevices_RegisterEventHandlers(void)
{
struct x11drv_display_device_handler *handler = is_virtual_desktop() ? &desktop_handler : &host_handler;
if (handler->register_event_handlers)
handler->register_event_handlers();
}
/* Initialize a GPU instance and return its GUID string in guid_string and driver value in driver parameter */
static BOOL X11DRV_InitGpu(HDEVINFO devinfo, const struct x11drv_gpu *gpu, INT gpu_index, WCHAR *guid_string,
WCHAR *driver)
{
static const BOOL present = TRUE;
SP_DEVINFO_DATA device_data = {sizeof(device_data)};
WCHAR instanceW[MAX_PATH];
WCHAR bufferW[1024];
HKEY hkey = NULL;
GUID guid;
INT written;
DWORD size;
BOOL ret = FALSE;
FILETIME filetime;
sprintfW(instanceW, gpu_instance_fmtW, gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index);
if (!SetupDiOpenDeviceInfoW(devinfo, instanceW, NULL, 0, &device_data))
{
SetupDiCreateDeviceInfoW(devinfo, instanceW, &GUID_DEVCLASS_DISPLAY, gpu->name, NULL, 0, &device_data);
if (!SetupDiRegisterDeviceInfo(devinfo, &device_data, 0, NULL, NULL, NULL))
goto done;
}
/* Write HardwareID registry property, REG_MULTI_SZ */
written = sprintfW(bufferW, gpu_hardware_id_fmtW, gpu->vendor_id, gpu->device_id);
bufferW[written + 1] = 0;
if (!SetupDiSetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_HARDWAREID, (const BYTE *)bufferW,
(written + 2) * sizeof(WCHAR)))
goto done;
/* Write DEVPKEY_Device_IsPresent property */
if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN,
(const BYTE *)&present, sizeof(present), 0))
goto done;
/* Open driver key.
* This is where HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} links to */
hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
/* Write DriverDesc value */
if (RegSetValueExW(hkey, driver_descW, 0, REG_SZ, (const BYTE *)gpu->name,
(strlenW(gpu->name) + 1) * sizeof(WCHAR)))
goto done;
/* Write DriverDateData value, using current time as driver date, needed by Evoland */
GetSystemTimeAsFileTime(&filetime);
if (RegSetValueExW(hkey, driver_date_dataW, 0, REG_BINARY, (BYTE *)&filetime, sizeof(filetime)))
goto done;
RegCloseKey(hkey);
/* Retrieve driver value for adapters */
if (!SetupDiGetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, sizeof(bufferW),
NULL))
goto done;
lstrcpyW(driver, nt_classW);
lstrcatW(driver, bufferW);
/* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */
hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, NULL, NULL);
size = sizeof(bufferW);
if (RegQueryValueExW(hkey, video_idW, 0, NULL, (BYTE *)bufferW, &size))
{
UuidCreate(&guid);
sprintfW(bufferW, guid_fmtW, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
if (RegSetValueExW(hkey, video_idW, 0, REG_SZ, (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR)))
goto done;
}
lstrcpyW(guid_string, bufferW);
ret = TRUE;
done:
RegCloseKey(hkey);
if (!ret)
ERR("Failed to initialize GPU\n");
return ret;
}
static BOOL X11DRV_InitAdapter(HKEY video_hkey, INT video_index, INT gpu_index, INT adapter_index, INT monitor_count,
const struct x11drv_gpu *gpu, const WCHAR *guid_string,
const WCHAR *gpu_driver, const struct x11drv_adapter *adapter)
{
WCHAR adapter_keyW[MAX_PATH];
WCHAR key_nameW[MAX_PATH];
WCHAR bufferW[1024];
HKEY hkey = NULL;
BOOL ret = FALSE;
LSTATUS ls;
INT i;
sprintfW(key_nameW, device_video_fmtW, video_index);
lstrcpyW(bufferW, machine_prefixW);
sprintfW(adapter_keyW, adapter_key_fmtW, guid_string, adapter_index);
lstrcatW(bufferW, adapter_keyW);
/* Write value of \Device\Video? (adapter key) in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
if (RegSetValueExW(video_hkey, key_nameW, 0, REG_SZ, (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR)))
goto done;
/* Create HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} link to GPU driver */
ls = RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
KEY_ALL_ACCESS, NULL, &hkey, NULL);
if (ls == ERROR_ALREADY_EXISTS)
RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK,
KEY_ALL_ACCESS, NULL, &hkey, NULL);
if (RegSetValueExW(hkey, symbolic_link_valueW, 0, REG_LINK, (const BYTE *)gpu_driver,
strlenW(gpu_driver) * sizeof(WCHAR)))
goto done;
RegCloseKey(hkey);
hkey = NULL;
/* FIXME:
* Following information is Wine specific, it doesn't really exist on Windows. It is used so that we can
* implement EnumDisplayDevices etc by querying registry only. This information is most likely reported by the
* device driver on Windows */
RegCreateKeyExW(HKEY_CURRENT_CONFIG, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL);
/* Write GPU instance path so that we can find the GPU instance via adapters quickly. Another way is trying to match
* them via the GUID in Device Parameters/VideoID, but it would require enumerating all GPU instances */
sprintfW(bufferW, gpu_instance_fmtW, gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index);
if (RegSetValueExW(hkey, gpu_idW, 0, REG_SZ, (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR)))
goto done;
/* Write all monitor instances paths under this adapter */
for (i = 0; i < monitor_count; i++)
{
sprintfW(key_nameW, mointor_id_fmtW, i);
sprintfW(bufferW, monitor_instance_fmtW, video_index, i);
if (RegSetValueExW(hkey, key_nameW, 0, REG_SZ, (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR)))
goto done;
}
/* Write StateFlags */
if (RegSetValueExW(hkey, state_flagsW, 0, REG_DWORD, (const BYTE *)&adapter->state_flags,
sizeof(adapter->state_flags)))
goto done;
ret = TRUE;
done:
RegCloseKey(hkey);
if (!ret)
ERR("Failed to initialize adapter\n");
return ret;
}
static BOOL X11DRV_InitMonitor(HDEVINFO devinfo, const struct x11drv_monitor *monitor, int monitor_index,
int video_index)
{
SP_DEVINFO_DATA device_data = {sizeof(SP_DEVINFO_DATA)};
WCHAR bufferW[MAX_PATH];
HKEY hkey;
BOOL ret = FALSE;
/* Create GUID_DEVCLASS_MONITOR instance */
sprintfW(bufferW, monitor_instance_fmtW, video_index, monitor_index);
SetupDiCreateDeviceInfoW(devinfo, bufferW, &GUID_DEVCLASS_MONITOR, monitor->name, NULL, 0, &device_data);
if (!SetupDiRegisterDeviceInfo(devinfo, &device_data, 0, NULL, NULL, NULL))
goto done;
/* Write HardwareID registry property */
if (!SetupDiSetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_HARDWAREID,
(const BYTE *)monitor_hardware_idW, sizeof(monitor_hardware_idW)))
goto done;
/* Create driver key */
hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
RegCloseKey(hkey);
/* FIXME:
* Following properties are Wine specific, see comments in X11DRV_InitAdapter for details */
/* StateFlags */
if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_STATEFLAGS, DEVPROP_TYPE_UINT32,
(const BYTE *)&monitor->state_flags, sizeof(monitor->state_flags), 0))
goto done;
/* RcMonitor */
if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_RCMONITOR, DEVPROP_TYPE_BINARY,
(const BYTE *)&monitor->rc_monitor, sizeof(monitor->rc_monitor), 0))
goto done;
/* RcWork */
if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_RCWORK, DEVPROP_TYPE_BINARY,
(const BYTE *)&monitor->rc_work, sizeof(monitor->rc_work), 0))
goto done;
/* Adapter name */
sprintfW(bufferW, adapter_name_fmtW, video_index + 1);
if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_ADAPTERNAME, DEVPROP_TYPE_STRING,
(const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR), 0))
goto done;
ret = TRUE;
done:
if (!ret)
ERR("Failed to initialize monitor\n");
return ret;
}
static void prepare_devices(HKEY video_hkey)
{
static const BOOL not_present = FALSE;
SP_DEVINFO_DATA device_data = {sizeof(device_data)};
HDEVINFO devinfo;
DWORD i = 0;
/* Remove all monitors */
devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR, displayW, NULL, 0);
while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
{
if (!SetupDiRemoveDevice(devinfo, &device_data))
ERR("Failed to remove monitor\n");
}
SetupDiDestroyDeviceInfoList(devinfo);
/* Clean up old adapter keys for reinitialization */
RegDeleteTreeW(video_hkey, NULL);
/* FIXME:
* Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, So we need to clean up not present devices in
* case application uses SetupDiGetClassDevsW to enumerate devices. Wrong devices could exist in registry as a result
* of prefix copying or having devices unplugged. But then we couldn't simply delete GPUs because we need to retain
* the same GUID for the same GPU. */
i = 0;
devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, pciW, NULL, 0);
while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
{
if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN,
(const BYTE *)&not_present, sizeof(not_present), 0))
ERR("Failed to set GPU present property\n");
}
SetupDiDestroyDeviceInfoList(devinfo);
}
static void cleanup_devices(void)
{
SP_DEVINFO_DATA device_data = {sizeof(device_data)};
HDEVINFO devinfo;
DWORD type;
DWORD i = 0;
BOOL present;
devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, pciW, NULL, 0);
while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
{
present = FALSE;
SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, &type, (BYTE *)&present,
sizeof(present), NULL, 0);
if (!present && !SetupDiRemoveDevice(devinfo, &device_data))
ERR("Failed to remove GPU\n");
}
SetupDiDestroyDeviceInfoList(devinfo);
}
void X11DRV_DisplayDevices_Init(BOOL force)
{
HANDLE mutex;
struct x11drv_display_device_handler *handler = is_virtual_desktop() ? &desktop_handler : &host_handler;
struct x11drv_gpu *gpus = NULL;
struct x11drv_adapter *adapters = NULL;
struct x11drv_monitor *monitors = NULL;
INT gpu_count, adapter_count, monitor_count;
INT gpu, adapter, monitor;
HDEVINFO gpu_devinfo = NULL, monitor_devinfo = NULL;
HKEY video_hkey = NULL;
INT video_index = 0;
DWORD disposition = 0;
WCHAR guidW[40];
WCHAR driverW[1024];
mutex = get_display_device_init_mutex();
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, video_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &video_hkey,
&disposition))
{
ERR("Failed to create video device key\n");
goto done;
}
/* Avoid unnecessary reinit */
if (!force && disposition != REG_CREATED_NEW_KEY)
goto done;
TRACE("via %s\n", wine_dbgstr_a(handler->name));
prepare_devices(video_hkey);
gpu_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL);
monitor_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_MONITOR, NULL);
/* Initialize GPUs */
if (!handler->get_gpus(&gpus, &gpu_count))
goto done;
TRACE("GPU count: %d\n", gpu_count);
for (gpu = 0; gpu < gpu_count; gpu++)
{
if (!X11DRV_InitGpu(gpu_devinfo, &gpus[gpu], gpu, guidW, driverW))
goto done;
/* Initialize adapters */
if (!handler->get_adapters(gpus[gpu].id, &adapters, &adapter_count))
goto done;
TRACE("GPU: %#lx %s, adapter count: %d\n", gpus[gpu].id, wine_dbgstr_w(gpus[gpu].name), adapter_count);
for (adapter = 0; adapter < adapter_count; adapter++)
{
if (!handler->get_monitors(adapters[adapter].id, &monitors, &monitor_count))
goto done;
TRACE("adapter: %#lx, monitor count: %d\n", adapters[adapter].id, monitor_count);
if (!X11DRV_InitAdapter(video_hkey, video_index, gpu, adapter, monitor_count,
&gpus[gpu], guidW, driverW, &adapters[adapter]))
goto done;
/* Initialize monitors */
for (monitor = 0; monitor < monitor_count; monitor++)
{
TRACE("monitor: %#x %s\n", monitor, wine_dbgstr_w(monitors[monitor].name));
if (!X11DRV_InitMonitor(monitor_devinfo, &monitors[monitor], monitor, video_index))
goto done;
}
handler->free_monitors(monitors);
monitors = NULL;
video_index++;
}
handler->free_adapters(adapters);
adapters = NULL;
}
done:
cleanup_devices();
SetupDiDestroyDeviceInfoList(monitor_devinfo);
SetupDiDestroyDeviceInfoList(gpu_devinfo);
RegCloseKey(video_hkey);
release_display_device_init_mutex(mutex);
if (gpus)
handler->free_gpus(gpus);
if (adapters)
handler->free_adapters(adapters);
if (monitors)
handler->free_monitors(monitors);
}