2 * X11DRV display device functions
4 * Copyright 2019 Zhiyi Zhang for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
34 #define WIN32_NO_STATUS
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(x11drv
);
42 DEFINE_DEVPROPKEY(DEVPROPKEY_GPU_LUID
, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
43 DEFINE_DEVPROPKEY(DEVPROPKEY_MONITOR_GPU_LUID
, 0xca085853, 0x16ce, 0x48aa, 0xb1, 0x14, 0xde, 0x9c, 0x72, 0x33, 0x42, 0x23, 1);
44 DEFINE_DEVPROPKEY(DEVPROPKEY_MONITOR_OUTPUT_ID
, 0xca085853, 0x16ce, 0x48aa, 0xb1, 0x14, 0xde, 0x9c, 0x72, 0x33, 0x42, 0x23, 2);
46 /* Wine specific properties */
47 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_GPU_VULKAN_UUID
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5c, 2);
48 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_STATEFLAGS
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 2);
49 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCMONITOR
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 3);
50 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCWORK
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 4);
51 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_ADAPTERNAME
, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 5);
53 static const WCHAR driver_date_dataW
[] = {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
54 static const WCHAR driver_descW
[] = {'D','r','i','v','e','r','D','e','s','c',0};
55 static const WCHAR displayW
[] = {'D','I','S','P','L','A','Y',0};
56 static const WCHAR pciW
[] = {'P','C','I',0};
57 static const WCHAR video_idW
[] = {'V','i','d','e','o','I','D',0};
58 static const WCHAR symbolic_link_valueW
[]= {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
59 static const WCHAR gpu_idW
[] = {'G','P','U','I','D',0};
60 static const WCHAR mointor_id_fmtW
[] = {'M','o','n','i','t','o','r','I','D','%','d',0};
61 static const WCHAR adapter_name_fmtW
[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y','%','d',0};
62 static const WCHAR state_flagsW
[] = {'S','t','a','t','e','F','l','a','g','s',0};
63 static const WCHAR guid_fmtW
[] = {
64 '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-','%','0','2','x','%','0','2','x','-',
65 '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0};
66 static const WCHAR gpu_instance_fmtW
[] = {
68 'V','E','N','_','%','0','4','X','&',
69 'D','E','V','_','%','0','4','X','&',
70 'S','U','B','S','Y','S','_','%','0','8','X','&',
71 'R','E','V','_','%','0','2','X','\\',
73 static const WCHAR gpu_hardware_id_fmtW
[] = {
75 'V','E','N','_','%','0','4','X','&',
76 'D','E','V','_','%','0','4','X','&',
77 'S','U','B','S','Y','S','_','0','0','0','0','0','0','0','0','&',
78 'R','E','V','_','0','0',0};
79 static const WCHAR video_keyW
[] = {
80 'H','A','R','D','W','A','R','E','\\',
81 'D','E','V','I','C','E','M','A','P','\\',
82 'V','I','D','E','O',0};
83 static const WCHAR adapter_key_fmtW
[] = {
84 'S','y','s','t','e','m','\\',
85 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
86 'C','o','n','t','r','o','l','\\',
87 'V','i','d','e','o','\\',
90 static const WCHAR device_video_fmtW
[] = {
91 '\\','D','e','v','i','c','e','\\',
92 'V','i','d','e','o','%','d',0};
93 static const WCHAR machine_prefixW
[] = {
94 '\\','R','e','g','i','s','t','r','y','\\',
95 'M','a','c','h','i','n','e','\\',0};
96 static const WCHAR nt_classW
[] = {
97 '\\','R','e','g','i','s','t','r','y','\\',
98 'M','a','c','h','i','n','e','\\',
99 'S','y','s','t','e','m','\\',
100 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
101 'C','o','n','t','r','o','l','\\',
102 'C','l','a','s','s','\\',0};
103 static const WCHAR monitor_instance_fmtW
[] = {
104 'D','I','S','P','L','A','Y','\\',
105 'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r','\\',
106 '%','0','4','X','&','%','0','4','X',0};
107 static const WCHAR monitor_hardware_idW
[] = {
108 'M','O','N','I','T','O','R','\\',
109 'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r',0,0};
111 static struct x11drv_display_device_handler host_handler
;
112 struct x11drv_display_device_handler desktop_handler
;
114 /* Cached screen information, protected by screen_section */
115 static HKEY video_key
;
116 static RECT virtual_screen_rect
;
117 static RECT primary_monitor_rect
;
118 static FILETIME last_query_screen_time
;
119 static CRITICAL_SECTION screen_section
;
120 static CRITICAL_SECTION_DEBUG screen_critsect_debug
=
122 0, 0, &screen_section
,
123 {&screen_critsect_debug
.ProcessLocksList
, &screen_critsect_debug
.ProcessLocksList
},
124 0, 0, {(DWORD_PTR
)(__FILE__
": screen_section")}
126 static CRITICAL_SECTION screen_section
= {&screen_critsect_debug
, -1, 0, 0, 0, 0};
128 HANDLE
get_display_device_init_mutex(void)
130 static const WCHAR init_mutexW
[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0};
131 HANDLE mutex
= CreateMutexW(NULL
, FALSE
, init_mutexW
);
133 WaitForSingleObject(mutex
, INFINITE
);
137 void release_display_device_init_mutex(HANDLE mutex
)
143 /* Update screen rectangle cache from SetupAPI if it's outdated, return FALSE on failure and TRUE on success */
144 static BOOL
update_screen_cache(void)
146 RECT virtual_rect
= {0}, primary_rect
= {0}, monitor_rect
;
147 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
148 HDEVINFO devinfo
= INVALID_HANDLE_VALUE
;
149 FILETIME filetime
= {0};
156 EnterCriticalSection(&screen_section
);
157 if ((!video_key
&& RegOpenKeyW(HKEY_LOCAL_MACHINE
, video_keyW
, &video_key
))
158 || RegQueryInfoKeyW(video_key
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &filetime
))
160 LeaveCriticalSection(&screen_section
);
163 result
= CompareFileTime(&filetime
, &last_query_screen_time
);
164 LeaveCriticalSection(&screen_section
);
168 mutex
= get_display_device_init_mutex();
170 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR
, displayW
, NULL
, DIGCF_PRESENT
);
171 if (devinfo
== INVALID_HANDLE_VALUE
)
174 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
176 if (!SetupDiGetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_RCMONITOR
, &type
,
177 (BYTE
*)&monitor_rect
, sizeof(monitor_rect
), NULL
, 0))
180 UnionRect(&virtual_rect
, &virtual_rect
, &monitor_rect
);
182 primary_rect
= monitor_rect
;
185 EnterCriticalSection(&screen_section
);
186 virtual_screen_rect
= virtual_rect
;
187 primary_monitor_rect
= primary_rect
;
188 last_query_screen_time
= filetime
;
189 LeaveCriticalSection(&screen_section
);
192 SetupDiDestroyDeviceInfoList(devinfo
);
193 release_display_device_init_mutex(mutex
);
195 WARN("Update screen cache failed!\n");
199 POINT
virtual_screen_to_root(INT x
, INT y
)
201 RECT
virtual = get_virtual_screen_rect();
204 pt
.x
= x
- virtual.left
;
205 pt
.y
= y
- virtual.top
;
209 POINT
root_to_virtual_screen(INT x
, INT y
)
211 RECT
virtual = get_virtual_screen_rect();
214 pt
.x
= x
+ virtual.left
;
215 pt
.y
= y
+ virtual.top
;
219 RECT
get_virtual_screen_rect(void)
223 update_screen_cache();
224 EnterCriticalSection(&screen_section
);
225 virtual = virtual_screen_rect
;
226 LeaveCriticalSection(&screen_section
);
230 RECT
get_primary_monitor_rect(void)
234 update_screen_cache();
235 EnterCriticalSection(&screen_section
);
236 primary
= primary_monitor_rect
;
237 LeaveCriticalSection(&screen_section
);
241 /* Get the primary monitor rect from the host system */
242 RECT
get_host_primary_monitor_rect(void)
244 INT gpu_count
, adapter_count
, monitor_count
;
245 struct x11drv_gpu
*gpus
= NULL
;
246 struct x11drv_adapter
*adapters
= NULL
;
247 struct x11drv_monitor
*monitors
= NULL
;
250 /* The first monitor is always primary */
251 if (host_handler
.get_gpus(&gpus
, &gpu_count
) && gpu_count
&&
252 host_handler
.get_adapters(gpus
[0].id
, &adapters
, &adapter_count
) && adapter_count
&&
253 host_handler
.get_monitors(adapters
[0].id
, &monitors
, &monitor_count
) && monitor_count
)
254 rect
= monitors
[0].rc_monitor
;
256 if (gpus
) host_handler
.free_gpus(gpus
);
257 if (adapters
) host_handler
.free_adapters(adapters
);
258 if (monitors
) host_handler
.free_monitors(monitors
);
262 RECT
get_work_area(const RECT
*monitor_rect
)
266 unsigned long count
, remaining
, i
;
270 /* Try _GTK_WORKAREAS first as _NET_WORKAREA may be incorrect on multi-monitor systems */
271 if (!XGetWindowProperty(gdi_display
, DefaultRootWindow(gdi_display
),
272 x11drv_atom(_GTK_WORKAREAS_D0
), 0, ~0, False
, XA_CARDINAL
, &type
,
273 &format
, &count
, &remaining
, (unsigned char **)&work_area
))
275 if (type
== XA_CARDINAL
&& format
== 32 && count
>= 4)
277 for (i
= 0; i
+ 3 < count
; i
+= 4)
279 work_rect
.left
= work_area
[i
* 4];
280 work_rect
.top
= work_area
[i
* 4 + 1];
281 work_rect
.right
= work_rect
.left
+ work_area
[i
* 4 + 2];
282 work_rect
.bottom
= work_rect
.top
+ work_area
[i
* 4 + 3];
284 if (IntersectRect(&work_rect
, &work_rect
, monitor_rect
))
286 TRACE("work_rect:%s.\n", wine_dbgstr_rect(&work_rect
));
295 WARN("_GTK_WORKAREAS is not supported, fallback to _NET_WORKAREA. "
296 "Work areas may be incorrect on multi-monitor systems.\n");
297 if (!XGetWindowProperty(gdi_display
, DefaultRootWindow(gdi_display
), x11drv_atom(_NET_WORKAREA
),
298 0, ~0, False
, XA_CARDINAL
, &type
, &format
, &count
, &remaining
,
299 (unsigned char **)&work_area
))
301 if (type
== XA_CARDINAL
&& format
== 32 && count
>= 4)
303 SetRect(&work_rect
, work_area
[0], work_area
[1], work_area
[0] + work_area
[2],
304 work_area
[1] + work_area
[3]);
306 if (IntersectRect(&work_rect
, &work_rect
, monitor_rect
))
308 TRACE("work_rect:%s.\n", wine_dbgstr_rect(&work_rect
));
316 WARN("_NET_WORKAREA is not supported, Work areas may be incorrect.\n");
317 TRACE("work_rect:%s.\n", wine_dbgstr_rect(monitor_rect
));
318 return *monitor_rect
;
321 void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler
*new_handler
)
323 if (new_handler
->priority
> host_handler
.priority
)
325 host_handler
= *new_handler
;
326 TRACE("Display device functions are now handled by: %s\n", host_handler
.name
);
330 void X11DRV_DisplayDevices_RegisterEventHandlers(void)
332 struct x11drv_display_device_handler
*handler
= is_virtual_desktop() ? &desktop_handler
: &host_handler
;
334 if (handler
->register_event_handlers
)
335 handler
->register_event_handlers();
338 static BOOL CALLBACK
update_windows_on_display_change(HWND hwnd
, LPARAM lparam
)
340 struct x11drv_win_data
*data
;
341 UINT mask
= (UINT
)lparam
;
343 if (!(data
= get_win_data(hwnd
)))
346 /* update the full screen state */
347 update_net_wm_states(data
);
349 if (mask
&& data
->whole_window
)
351 POINT pos
= virtual_screen_to_root(data
->whole_rect
.left
, data
->whole_rect
.top
);
352 XWindowChanges changes
;
355 XReconfigureWMWindow(data
->display
, data
->whole_window
, data
->vis
.screen
, mask
, &changes
);
357 release_win_data(data
);
358 if (hwnd
== GetForegroundWindow())
359 clip_fullscreen_window(hwnd
, TRUE
);
363 void X11DRV_DisplayDevices_Update(BOOL send_display_change
)
365 RECT old_virtual_rect
, new_virtual_rect
;
368 old_virtual_rect
= get_virtual_screen_rect();
369 X11DRV_DisplayDevices_Init(TRUE
);
370 new_virtual_rect
= get_virtual_screen_rect();
372 /* Calculate XReconfigureWMWindow() mask */
373 if (old_virtual_rect
.left
!= new_virtual_rect
.left
)
375 if (old_virtual_rect
.top
!= new_virtual_rect
.top
)
378 X11DRV_resize_desktop(send_display_change
);
379 EnumWindows(update_windows_on_display_change
, (LPARAM
)mask
);
382 /* Initialize a GPU instance.
383 * Return its GUID string in guid_string, driver value in driver parameter and LUID in gpu_luid */
384 static BOOL
X11DRV_InitGpu(HDEVINFO devinfo
, const struct x11drv_gpu
*gpu
, INT gpu_index
, WCHAR
*guid_string
,
385 WCHAR
*driver
, LUID
*gpu_luid
)
387 static const BOOL present
= TRUE
;
388 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
389 WCHAR instanceW
[MAX_PATH
];
390 DEVPROPTYPE property_type
;
400 TRACE("GPU id:0x%s name:%s.\n", wine_dbgstr_longlong(gpu
->id
), wine_dbgstr_w(gpu
->name
));
402 sprintfW(instanceW
, gpu_instance_fmtW
, gpu
->vendor_id
, gpu
->device_id
, gpu
->subsys_id
, gpu
->revision_id
, gpu_index
);
403 if (!SetupDiOpenDeviceInfoW(devinfo
, instanceW
, NULL
, 0, &device_data
))
405 SetupDiCreateDeviceInfoW(devinfo
, instanceW
, &GUID_DEVCLASS_DISPLAY
, gpu
->name
, NULL
, 0, &device_data
);
406 if (!SetupDiRegisterDeviceInfo(devinfo
, &device_data
, 0, NULL
, NULL
, NULL
))
410 /* Write HardwareID registry property, REG_MULTI_SZ */
411 written
= sprintfW(bufferW
, gpu_hardware_id_fmtW
, gpu
->vendor_id
, gpu
->device_id
);
412 bufferW
[written
+ 1] = 0;
413 if (!SetupDiSetDeviceRegistryPropertyW(devinfo
, &device_data
, SPDRP_HARDWAREID
, (const BYTE
*)bufferW
,
414 (written
+ 2) * sizeof(WCHAR
)))
417 /* Write DEVPKEY_Device_IsPresent property */
418 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPKEY_Device_IsPresent
, DEVPROP_TYPE_BOOLEAN
,
419 (const BYTE
*)&present
, sizeof(present
), 0))
422 /* Write DEVPROPKEY_GPU_LUID property */
423 if (!SetupDiGetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_GPU_LUID
, &property_type
,
424 (BYTE
*)&luid
, sizeof(luid
), NULL
, 0))
426 if (!AllocateLocallyUniqueId(&luid
))
429 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_GPU_LUID
,
430 DEVPROP_TYPE_UINT64
, (const BYTE
*)&luid
, sizeof(luid
), 0))
434 TRACE("LUID:%08x:%08x.\n", luid
.HighPart
, luid
.LowPart
);
436 /* Write WINE_DEVPROPKEY_GPU_VULKAN_UUID property */
437 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_GPU_VULKAN_UUID
,
438 DEVPROP_TYPE_GUID
, (const BYTE
*)&gpu
->vulkan_uuid
,
439 sizeof(gpu
->vulkan_uuid
), 0))
441 TRACE("Vulkan UUID:%s.\n", wine_dbgstr_guid(&gpu
->vulkan_uuid
));
444 * This is where HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} links to */
445 hkey
= SetupDiCreateDevRegKeyW(devinfo
, &device_data
, DICS_FLAG_GLOBAL
, 0, DIREG_DRV
, NULL
, NULL
);
447 /* Write DriverDesc value */
448 if (RegSetValueExW(hkey
, driver_descW
, 0, REG_SZ
, (const BYTE
*)gpu
->name
,
449 (strlenW(gpu
->name
) + 1) * sizeof(WCHAR
)))
451 /* Write DriverDateData value, using current time as driver date, needed by Evoland */
452 GetSystemTimeAsFileTime(&filetime
);
453 if (RegSetValueExW(hkey
, driver_date_dataW
, 0, REG_BINARY
, (BYTE
*)&filetime
, sizeof(filetime
)))
458 /* Retrieve driver value for adapters */
459 if (!SetupDiGetDeviceRegistryPropertyW(devinfo
, &device_data
, SPDRP_DRIVER
, NULL
, (BYTE
*)bufferW
, sizeof(bufferW
),
462 lstrcpyW(driver
, nt_classW
);
463 lstrcatW(driver
, bufferW
);
465 /* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */
466 hkey
= SetupDiCreateDevRegKeyW(devinfo
, &device_data
, DICS_FLAG_GLOBAL
, 0, DIREG_DEV
, NULL
, NULL
);
468 size
= sizeof(bufferW
);
469 if (RegQueryValueExW(hkey
, video_idW
, 0, NULL
, (BYTE
*)bufferW
, &size
))
472 sprintfW(bufferW
, guid_fmtW
, guid
.Data1
, guid
.Data2
, guid
.Data3
, guid
.Data4
[0], guid
.Data4
[1], guid
.Data4
[2],
473 guid
.Data4
[3], guid
.Data4
[4], guid
.Data4
[5], guid
.Data4
[6], guid
.Data4
[7]);
474 if (RegSetValueExW(hkey
, video_idW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
)))
477 lstrcpyW(guid_string
, bufferW
);
483 ERR("Failed to initialize GPU\n");
487 static BOOL
X11DRV_InitAdapter(HKEY video_hkey
, INT video_index
, INT gpu_index
, INT adapter_index
, INT monitor_count
,
488 const struct x11drv_gpu
*gpu
, const WCHAR
*guid_string
,
489 const WCHAR
*gpu_driver
, const struct x11drv_adapter
*adapter
)
491 WCHAR adapter_keyW
[MAX_PATH
];
492 WCHAR key_nameW
[MAX_PATH
];
499 sprintfW(key_nameW
, device_video_fmtW
, video_index
);
500 lstrcpyW(bufferW
, machine_prefixW
);
501 sprintfW(adapter_keyW
, adapter_key_fmtW
, guid_string
, adapter_index
);
502 lstrcatW(bufferW
, adapter_keyW
);
504 /* Write value of \Device\Video? (adapter key) in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
505 if (RegSetValueExW(video_hkey
, key_nameW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
)))
508 /* Create HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} link to GPU driver */
509 ls
= RegCreateKeyExW(HKEY_LOCAL_MACHINE
, adapter_keyW
, 0, NULL
, REG_OPTION_VOLATILE
| REG_OPTION_CREATE_LINK
,
510 KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
);
511 if (ls
== ERROR_ALREADY_EXISTS
)
512 RegCreateKeyExW(HKEY_LOCAL_MACHINE
, adapter_keyW
, 0, NULL
, REG_OPTION_VOLATILE
| REG_OPTION_OPEN_LINK
,
513 KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
);
514 if (RegSetValueExW(hkey
, symbolic_link_valueW
, 0, REG_LINK
, (const BYTE
*)gpu_driver
,
515 strlenW(gpu_driver
) * sizeof(WCHAR
)))
521 * Following information is Wine specific, it doesn't really exist on Windows. It is used so that we can
522 * implement EnumDisplayDevices etc by querying registry only. This information is most likely reported by the
523 * device driver on Windows */
524 RegCreateKeyExW(HKEY_CURRENT_CONFIG
, adapter_keyW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &hkey
, NULL
);
526 /* Write GPU instance path so that we can find the GPU instance via adapters quickly. Another way is trying to match
527 * them via the GUID in Device Parameters/VideoID, but it would require enumerating all GPU instances */
528 sprintfW(bufferW
, gpu_instance_fmtW
, gpu
->vendor_id
, gpu
->device_id
, gpu
->subsys_id
, gpu
->revision_id
, gpu_index
);
529 if (RegSetValueExW(hkey
, gpu_idW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
)))
532 /* Write all monitor instances paths under this adapter */
533 for (i
= 0; i
< monitor_count
; i
++)
535 sprintfW(key_nameW
, mointor_id_fmtW
, i
);
536 sprintfW(bufferW
, monitor_instance_fmtW
, video_index
, i
);
537 if (RegSetValueExW(hkey
, key_nameW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
)))
541 /* Write StateFlags */
542 if (RegSetValueExW(hkey
, state_flagsW
, 0, REG_DWORD
, (const BYTE
*)&adapter
->state_flags
,
543 sizeof(adapter
->state_flags
)))
550 ERR("Failed to initialize adapter\n");
554 static BOOL
X11DRV_InitMonitor(HDEVINFO devinfo
, const struct x11drv_monitor
*monitor
, int monitor_index
,
555 int video_index
, const LUID
*gpu_luid
, UINT output_id
)
557 SP_DEVINFO_DATA device_data
= {sizeof(SP_DEVINFO_DATA
)};
558 WCHAR bufferW
[MAX_PATH
];
562 /* Create GUID_DEVCLASS_MONITOR instance */
563 sprintfW(bufferW
, monitor_instance_fmtW
, video_index
, monitor_index
);
564 SetupDiCreateDeviceInfoW(devinfo
, bufferW
, &GUID_DEVCLASS_MONITOR
, monitor
->name
, NULL
, 0, &device_data
);
565 if (!SetupDiRegisterDeviceInfo(devinfo
, &device_data
, 0, NULL
, NULL
, NULL
))
568 /* Write HardwareID registry property */
569 if (!SetupDiSetDeviceRegistryPropertyW(devinfo
, &device_data
, SPDRP_HARDWAREID
,
570 (const BYTE
*)monitor_hardware_idW
, sizeof(monitor_hardware_idW
)))
573 /* Write DEVPROPKEY_MONITOR_GPU_LUID */
574 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_MONITOR_GPU_LUID
,
575 DEVPROP_TYPE_INT64
, (const BYTE
*)gpu_luid
, sizeof(*gpu_luid
), 0))
578 /* Write DEVPROPKEY_MONITOR_OUTPUT_ID */
579 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_MONITOR_OUTPUT_ID
,
580 DEVPROP_TYPE_UINT32
, (const BYTE
*)&output_id
, sizeof(output_id
), 0))
583 /* Create driver key */
584 hkey
= SetupDiCreateDevRegKeyW(devinfo
, &device_data
, DICS_FLAG_GLOBAL
, 0, DIREG_DRV
, NULL
, NULL
);
588 * Following properties are Wine specific, see comments in X11DRV_InitAdapter for details */
590 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_STATEFLAGS
, DEVPROP_TYPE_UINT32
,
591 (const BYTE
*)&monitor
->state_flags
, sizeof(monitor
->state_flags
), 0))
594 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_RCMONITOR
, DEVPROP_TYPE_BINARY
,
595 (const BYTE
*)&monitor
->rc_monitor
, sizeof(monitor
->rc_monitor
), 0))
598 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_RCWORK
, DEVPROP_TYPE_BINARY
,
599 (const BYTE
*)&monitor
->rc_work
, sizeof(monitor
->rc_work
), 0))
602 sprintfW(bufferW
, adapter_name_fmtW
, video_index
+ 1);
603 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_ADAPTERNAME
, DEVPROP_TYPE_STRING
,
604 (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
), 0))
610 ERR("Failed to initialize monitor\n");
614 static void prepare_devices(HKEY video_hkey
)
616 static const BOOL not_present
= FALSE
;
617 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
621 /* Remove all monitors */
622 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR
, displayW
, NULL
, 0);
623 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
625 if (!SetupDiRemoveDevice(devinfo
, &device_data
))
626 ERR("Failed to remove monitor\n");
628 SetupDiDestroyDeviceInfoList(devinfo
);
630 /* Clean up old adapter keys for reinitialization */
631 RegDeleteTreeW(video_hkey
, NULL
);
634 * Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, So we need to clean up not present devices in
635 * case application uses SetupDiGetClassDevsW to enumerate devices. Wrong devices could exist in registry as a result
636 * of prefix copying or having devices unplugged. But then we couldn't simply delete GPUs because we need to retain
637 * the same GUID for the same GPU. */
639 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY
, pciW
, NULL
, 0);
640 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
642 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPKEY_Device_IsPresent
, DEVPROP_TYPE_BOOLEAN
,
643 (const BYTE
*)¬_present
, sizeof(not_present
), 0))
644 ERR("Failed to set GPU present property\n");
646 SetupDiDestroyDeviceInfoList(devinfo
);
649 static void cleanup_devices(void)
651 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
657 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY
, pciW
, NULL
, 0);
658 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
661 SetupDiGetDevicePropertyW(devinfo
, &device_data
, &DEVPKEY_Device_IsPresent
, &type
, (BYTE
*)&present
,
662 sizeof(present
), NULL
, 0);
663 if (!present
&& !SetupDiRemoveDevice(devinfo
, &device_data
))
664 ERR("Failed to remove GPU\n");
666 SetupDiDestroyDeviceInfoList(devinfo
);
669 void X11DRV_DisplayDevices_Init(BOOL force
)
672 struct x11drv_display_device_handler
*handler
= is_virtual_desktop() ? &desktop_handler
: &host_handler
;
673 struct x11drv_gpu
*gpus
= NULL
;
674 struct x11drv_adapter
*adapters
= NULL
;
675 struct x11drv_monitor
*monitors
= NULL
;
676 INT gpu_count
, adapter_count
, monitor_count
;
677 INT gpu
, adapter
, monitor
;
678 HDEVINFO gpu_devinfo
= NULL
, monitor_devinfo
= NULL
;
679 HKEY video_hkey
= NULL
;
681 DWORD disposition
= 0;
687 mutex
= get_display_device_init_mutex();
689 if (RegCreateKeyExW(HKEY_LOCAL_MACHINE
, video_keyW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &video_hkey
,
692 ERR("Failed to create video device key\n");
696 /* Avoid unnecessary reinit */
697 if (!force
&& disposition
!= REG_CREATED_NEW_KEY
)
700 TRACE("via %s\n", wine_dbgstr_a(handler
->name
));
702 prepare_devices(video_hkey
);
704 gpu_devinfo
= SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY
, NULL
);
705 monitor_devinfo
= SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_MONITOR
, NULL
);
707 /* Initialize GPUs */
708 if (!handler
->get_gpus(&gpus
, &gpu_count
))
710 TRACE("GPU count: %d\n", gpu_count
);
712 for (gpu
= 0; gpu
< gpu_count
; gpu
++)
714 if (!X11DRV_InitGpu(gpu_devinfo
, &gpus
[gpu
], gpu
, guidW
, driverW
, &gpu_luid
))
717 /* Initialize adapters */
718 if (!handler
->get_adapters(gpus
[gpu
].id
, &adapters
, &adapter_count
))
720 TRACE("GPU: %#lx %s, adapter count: %d\n", gpus
[gpu
].id
, wine_dbgstr_w(gpus
[gpu
].name
), adapter_count
);
722 for (adapter
= 0; adapter
< adapter_count
; adapter
++)
724 if (!handler
->get_monitors(adapters
[adapter
].id
, &monitors
, &monitor_count
))
726 TRACE("adapter: %#lx, monitor count: %d\n", adapters
[adapter
].id
, monitor_count
);
728 if (!X11DRV_InitAdapter(video_hkey
, video_index
, gpu
, adapter
, monitor_count
,
729 &gpus
[gpu
], guidW
, driverW
, &adapters
[adapter
]))
732 /* Initialize monitors */
733 for (monitor
= 0; monitor
< monitor_count
; monitor
++)
735 TRACE("monitor: %#x %s\n", monitor
, wine_dbgstr_w(monitors
[monitor
].name
));
736 if (!X11DRV_InitMonitor(monitor_devinfo
, &monitors
[monitor
], monitor
, video_index
, &gpu_luid
, output_id
++))
740 handler
->free_monitors(monitors
);
745 handler
->free_adapters(adapters
);
751 SetupDiDestroyDeviceInfoList(monitor_devinfo
);
752 SetupDiDestroyDeviceInfoList(gpu_devinfo
);
753 RegCloseKey(video_hkey
);
754 release_display_device_init_mutex(mutex
);
756 handler
->free_gpus(gpus
);
758 handler
->free_adapters(adapters
);
760 handler
->free_monitors(monitors
);