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_dateW
[] = {'D','r','i','v','e','r','D','a','t','e',0};
55 static const WCHAR driver_descW
[] = {'D','r','i','v','e','r','D','e','s','c',0};
56 static const WCHAR displayW
[] = {'D','I','S','P','L','A','Y',0};
57 static const WCHAR pciW
[] = {'P','C','I',0};
58 static const WCHAR video_idW
[] = {'V','i','d','e','o','I','D',0};
59 static const WCHAR symbolic_link_valueW
[]= {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
60 static const WCHAR gpu_idW
[] = {'G','P','U','I','D',0};
61 static const WCHAR monitor_id_fmtW
[] = {'M','o','n','i','t','o','r','I','D','%','d',0};
62 static const WCHAR adapter_name_fmtW
[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y','%','d',0};
63 static const WCHAR state_flagsW
[] = {'S','t','a','t','e','F','l','a','g','s',0};
64 static const WCHAR guid_fmtW
[] = {
65 '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-','%','0','2','x','%','0','2','x','-',
66 '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0};
67 static const WCHAR gpu_instance_fmtW
[] = {
69 'V','E','N','_','%','0','4','X','&',
70 'D','E','V','_','%','0','4','X','&',
71 'S','U','B','S','Y','S','_','%','0','8','X','&',
72 'R','E','V','_','%','0','2','X','\\',
74 static const WCHAR gpu_hardware_id_fmtW
[] = {
76 'V','E','N','_','%','0','4','X','&',
77 'D','E','V','_','%','0','4','X','&',
78 'S','U','B','S','Y','S','_','0','0','0','0','0','0','0','0','&',
79 'R','E','V','_','0','0',0};
80 static const WCHAR video_keyW
[] = {
81 'H','A','R','D','W','A','R','E','\\',
82 'D','E','V','I','C','E','M','A','P','\\',
83 'V','I','D','E','O',0};
84 static const WCHAR adapter_key_fmtW
[] = {
85 'S','y','s','t','e','m','\\',
86 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
87 'C','o','n','t','r','o','l','\\',
88 'V','i','d','e','o','\\',
91 static const WCHAR device_video_fmtW
[] = {
92 '\\','D','e','v','i','c','e','\\',
93 'V','i','d','e','o','%','d',0};
94 static const WCHAR machine_prefixW
[] = {
95 '\\','R','e','g','i','s','t','r','y','\\',
96 'M','a','c','h','i','n','e','\\',0};
97 static const WCHAR nt_classW
[] = {
98 '\\','R','e','g','i','s','t','r','y','\\',
99 'M','a','c','h','i','n','e','\\',
100 'S','y','s','t','e','m','\\',
101 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
102 'C','o','n','t','r','o','l','\\',
103 'C','l','a','s','s','\\',0};
104 static const WCHAR monitor_instance_fmtW
[] = {
105 'D','I','S','P','L','A','Y','\\',
106 'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r','\\',
107 '%','0','4','X','&','%','0','4','X',0};
108 static const WCHAR monitor_hardware_idW
[] = {
109 'M','O','N','I','T','O','R','\\',
110 'D','e','f','a','u','l','t','_','M','o','n','i','t','o','r',0,0};
111 static const WCHAR driver_date_fmtW
[] = {'%','u','-','%','u','-','%','u',0};
113 static struct x11drv_display_device_handler host_handler
;
114 struct x11drv_display_device_handler desktop_handler
;
116 /* Cached screen information, protected by screen_section */
117 static HKEY video_key
;
118 static RECT virtual_screen_rect
;
119 static RECT primary_monitor_rect
;
120 static FILETIME last_query_screen_time
;
121 static CRITICAL_SECTION screen_section
;
122 static CRITICAL_SECTION_DEBUG screen_critsect_debug
=
124 0, 0, &screen_section
,
125 {&screen_critsect_debug
.ProcessLocksList
, &screen_critsect_debug
.ProcessLocksList
},
126 0, 0, {(DWORD_PTR
)(__FILE__
": screen_section")}
128 static CRITICAL_SECTION screen_section
= {&screen_critsect_debug
, -1, 0, 0, 0, 0};
130 HANDLE
get_display_device_init_mutex(void)
132 static const WCHAR init_mutexW
[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0};
133 HANDLE mutex
= CreateMutexW(NULL
, FALSE
, init_mutexW
);
135 WaitForSingleObject(mutex
, INFINITE
);
139 void release_display_device_init_mutex(HANDLE mutex
)
145 /* Update screen rectangle cache from SetupAPI if it's outdated, return FALSE on failure and TRUE on success */
146 static BOOL
update_screen_cache(void)
148 RECT virtual_rect
= {0}, primary_rect
= {0}, monitor_rect
;
149 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
150 HDEVINFO devinfo
= INVALID_HANDLE_VALUE
;
151 FILETIME filetime
= {0};
158 EnterCriticalSection(&screen_section
);
159 if ((!video_key
&& RegOpenKeyW(HKEY_LOCAL_MACHINE
, video_keyW
, &video_key
))
160 || RegQueryInfoKeyW(video_key
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &filetime
))
162 LeaveCriticalSection(&screen_section
);
165 result
= CompareFileTime(&filetime
, &last_query_screen_time
);
166 LeaveCriticalSection(&screen_section
);
170 mutex
= get_display_device_init_mutex();
172 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR
, displayW
, NULL
, DIGCF_PRESENT
);
173 if (devinfo
== INVALID_HANDLE_VALUE
)
176 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
178 if (!SetupDiGetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_RCMONITOR
, &type
,
179 (BYTE
*)&monitor_rect
, sizeof(monitor_rect
), NULL
, 0))
182 UnionRect(&virtual_rect
, &virtual_rect
, &monitor_rect
);
184 primary_rect
= monitor_rect
;
187 EnterCriticalSection(&screen_section
);
188 virtual_screen_rect
= virtual_rect
;
189 primary_monitor_rect
= primary_rect
;
190 last_query_screen_time
= filetime
;
191 LeaveCriticalSection(&screen_section
);
194 SetupDiDestroyDeviceInfoList(devinfo
);
195 release_display_device_init_mutex(mutex
);
197 WARN("Update screen cache failed!\n");
201 POINT
virtual_screen_to_root(INT x
, INT y
)
203 RECT
virtual = get_virtual_screen_rect();
206 pt
.x
= x
- virtual.left
;
207 pt
.y
= y
- virtual.top
;
211 POINT
root_to_virtual_screen(INT x
, INT y
)
213 RECT
virtual = get_virtual_screen_rect();
216 pt
.x
= x
+ virtual.left
;
217 pt
.y
= y
+ virtual.top
;
221 RECT
get_virtual_screen_rect(void)
225 update_screen_cache();
226 EnterCriticalSection(&screen_section
);
227 virtual = virtual_screen_rect
;
228 LeaveCriticalSection(&screen_section
);
232 RECT
get_primary_monitor_rect(void)
236 update_screen_cache();
237 EnterCriticalSection(&screen_section
);
238 primary
= primary_monitor_rect
;
239 LeaveCriticalSection(&screen_section
);
243 /* Get the primary monitor rect from the host system */
244 RECT
get_host_primary_monitor_rect(void)
246 INT gpu_count
, adapter_count
, monitor_count
;
247 struct x11drv_gpu
*gpus
= NULL
;
248 struct x11drv_adapter
*adapters
= NULL
;
249 struct x11drv_monitor
*monitors
= NULL
;
252 /* The first monitor is always primary */
253 if (host_handler
.get_gpus(&gpus
, &gpu_count
) && gpu_count
&&
254 host_handler
.get_adapters(gpus
[0].id
, &adapters
, &adapter_count
) && adapter_count
&&
255 host_handler
.get_monitors(adapters
[0].id
, &monitors
, &monitor_count
) && monitor_count
)
256 rect
= monitors
[0].rc_monitor
;
258 if (gpus
) host_handler
.free_gpus(gpus
);
259 if (adapters
) host_handler
.free_adapters(adapters
);
260 if (monitors
) host_handler
.free_monitors(monitors
);
264 BOOL
get_host_primary_gpu(struct x11drv_gpu
*gpu
)
266 struct x11drv_gpu
*gpus
;
269 if (host_handler
.get_gpus(&gpus
, &gpu_count
) && gpu_count
)
272 host_handler
.free_gpus(gpus
);
279 RECT
get_work_area(const RECT
*monitor_rect
)
283 unsigned long count
, remaining
, i
;
287 /* Try _GTK_WORKAREAS first as _NET_WORKAREA may be incorrect on multi-monitor systems */
288 if (!XGetWindowProperty(gdi_display
, DefaultRootWindow(gdi_display
),
289 x11drv_atom(_GTK_WORKAREAS_D0
), 0, ~0, False
, XA_CARDINAL
, &type
,
290 &format
, &count
, &remaining
, (unsigned char **)&work_area
))
292 if (type
== XA_CARDINAL
&& format
== 32 && count
>= 4)
294 for (i
= 0; i
+ 3 < count
; i
+= 4)
296 work_rect
.left
= work_area
[i
* 4];
297 work_rect
.top
= work_area
[i
* 4 + 1];
298 work_rect
.right
= work_rect
.left
+ work_area
[i
* 4 + 2];
299 work_rect
.bottom
= work_rect
.top
+ work_area
[i
* 4 + 3];
301 if (IntersectRect(&work_rect
, &work_rect
, monitor_rect
))
303 TRACE("work_rect:%s.\n", wine_dbgstr_rect(&work_rect
));
312 WARN("_GTK_WORKAREAS is not supported, fallback to _NET_WORKAREA. "
313 "Work areas may be incorrect on multi-monitor systems.\n");
314 if (!XGetWindowProperty(gdi_display
, DefaultRootWindow(gdi_display
), x11drv_atom(_NET_WORKAREA
),
315 0, ~0, False
, XA_CARDINAL
, &type
, &format
, &count
, &remaining
,
316 (unsigned char **)&work_area
))
318 if (type
== XA_CARDINAL
&& format
== 32 && count
>= 4)
320 SetRect(&work_rect
, work_area
[0], work_area
[1], work_area
[0] + work_area
[2],
321 work_area
[1] + work_area
[3]);
323 if (IntersectRect(&work_rect
, &work_rect
, monitor_rect
))
325 TRACE("work_rect:%s.\n", wine_dbgstr_rect(&work_rect
));
333 WARN("_NET_WORKAREA is not supported, Work areas may be incorrect.\n");
334 TRACE("work_rect:%s.\n", wine_dbgstr_rect(monitor_rect
));
335 return *monitor_rect
;
338 void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler
*new_handler
)
340 if (new_handler
->priority
> host_handler
.priority
)
342 host_handler
= *new_handler
;
343 TRACE("Display device functions are now handled by: %s\n", host_handler
.name
);
347 void X11DRV_DisplayDevices_RegisterEventHandlers(void)
349 struct x11drv_display_device_handler
*handler
= is_virtual_desktop() ? &desktop_handler
: &host_handler
;
351 if (handler
->register_event_handlers
)
352 handler
->register_event_handlers();
355 static BOOL CALLBACK
update_windows_on_display_change(HWND hwnd
, LPARAM lparam
)
357 struct x11drv_win_data
*data
;
358 UINT mask
= (UINT
)lparam
;
360 if (!(data
= get_win_data(hwnd
)))
363 /* update the full screen state */
364 update_net_wm_states(data
);
366 if (mask
&& data
->whole_window
)
368 POINT pos
= virtual_screen_to_root(data
->whole_rect
.left
, data
->whole_rect
.top
);
369 XWindowChanges changes
;
372 XReconfigureWMWindow(data
->display
, data
->whole_window
, data
->vis
.screen
, mask
, &changes
);
374 release_win_data(data
);
378 void X11DRV_DisplayDevices_Update(BOOL send_display_change
)
380 RECT old_virtual_rect
, new_virtual_rect
;
385 old_virtual_rect
= get_virtual_screen_rect();
386 X11DRV_DisplayDevices_Init(TRUE
);
387 new_virtual_rect
= get_virtual_screen_rect();
389 /* Calculate XReconfigureWMWindow() mask */
390 if (old_virtual_rect
.left
!= new_virtual_rect
.left
)
392 if (old_virtual_rect
.top
!= new_virtual_rect
.top
)
395 X11DRV_resize_desktop(send_display_change
);
396 EnumWindows(update_windows_on_display_change
, (LPARAM
)mask
);
398 /* forward clip_fullscreen_window request to the foreground window */
399 if ((foreground
= GetForegroundWindow()) && (tid
= GetWindowThreadProcessId( foreground
, &pid
)) && pid
== GetCurrentProcessId())
401 if (tid
== GetCurrentThreadId()) clip_fullscreen_window( foreground
, TRUE
);
402 else SendNotifyMessageW( foreground
, WM_X11DRV_CLIP_CURSOR_REQUEST
, TRUE
, TRUE
);
406 /* Initialize a GPU instance.
407 * Return its GUID string in guid_string, driver value in driver parameter and LUID in gpu_luid */
408 static BOOL
X11DRV_InitGpu(HDEVINFO devinfo
, const struct x11drv_gpu
*gpu
, INT gpu_index
, WCHAR
*guid_string
,
409 WCHAR
*driver
, LUID
*gpu_luid
)
411 static const BOOL present
= TRUE
;
412 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
413 WCHAR instanceW
[MAX_PATH
];
414 DEVPROPTYPE property_type
;
415 SYSTEMTIME systemtime
;
425 TRACE("GPU id:0x%s name:%s.\n", wine_dbgstr_longlong(gpu
->id
), wine_dbgstr_w(gpu
->name
));
427 sprintfW(instanceW
, gpu_instance_fmtW
, gpu
->vendor_id
, gpu
->device_id
, gpu
->subsys_id
, gpu
->revision_id
, gpu_index
);
428 if (!SetupDiOpenDeviceInfoW(devinfo
, instanceW
, NULL
, 0, &device_data
))
430 SetupDiCreateDeviceInfoW(devinfo
, instanceW
, &GUID_DEVCLASS_DISPLAY
, gpu
->name
, NULL
, 0, &device_data
);
431 if (!SetupDiRegisterDeviceInfo(devinfo
, &device_data
, 0, NULL
, NULL
, NULL
))
435 /* Write HardwareID registry property, REG_MULTI_SZ */
436 written
= sprintfW(bufferW
, gpu_hardware_id_fmtW
, gpu
->vendor_id
, gpu
->device_id
);
437 bufferW
[written
+ 1] = 0;
438 if (!SetupDiSetDeviceRegistryPropertyW(devinfo
, &device_data
, SPDRP_HARDWAREID
, (const BYTE
*)bufferW
,
439 (written
+ 2) * sizeof(WCHAR
)))
442 /* Write DEVPKEY_Device_IsPresent property */
443 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPKEY_Device_IsPresent
, DEVPROP_TYPE_BOOLEAN
,
444 (const BYTE
*)&present
, sizeof(present
), 0))
447 /* Write DEVPROPKEY_GPU_LUID property */
448 if (!SetupDiGetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_GPU_LUID
, &property_type
,
449 (BYTE
*)&luid
, sizeof(luid
), NULL
, 0))
451 if (!AllocateLocallyUniqueId(&luid
))
454 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_GPU_LUID
,
455 DEVPROP_TYPE_UINT64
, (const BYTE
*)&luid
, sizeof(luid
), 0))
459 TRACE("LUID:%08x:%08x.\n", luid
.HighPart
, luid
.LowPart
);
461 /* Write WINE_DEVPROPKEY_GPU_VULKAN_UUID property */
462 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_GPU_VULKAN_UUID
,
463 DEVPROP_TYPE_GUID
, (const BYTE
*)&gpu
->vulkan_uuid
,
464 sizeof(gpu
->vulkan_uuid
), 0))
466 TRACE("Vulkan UUID:%s.\n", wine_dbgstr_guid(&gpu
->vulkan_uuid
));
469 * This is where HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} links to */
470 hkey
= SetupDiCreateDevRegKeyW(devinfo
, &device_data
, DICS_FLAG_GLOBAL
, 0, DIREG_DRV
, NULL
, NULL
);
472 /* Write DriverDesc value */
473 if (RegSetValueExW(hkey
, driver_descW
, 0, REG_SZ
, (const BYTE
*)gpu
->name
,
474 (strlenW(gpu
->name
) + 1) * sizeof(WCHAR
)))
476 /* Write DriverDateData value, using current time as driver date, needed by Evoland */
477 GetSystemTimeAsFileTime(&filetime
);
478 if (RegSetValueExW(hkey
, driver_date_dataW
, 0, REG_BINARY
, (BYTE
*)&filetime
, sizeof(filetime
)))
481 GetSystemTime(&systemtime
);
482 sprintfW(bufferW
, driver_date_fmtW
, systemtime
.wMonth
, systemtime
.wDay
, systemtime
.wYear
);
483 if (RegSetValueExW(hkey
, driver_dateW
, 0, REG_SZ
, (BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
)))
488 /* Retrieve driver value for adapters */
489 if (!SetupDiGetDeviceRegistryPropertyW(devinfo
, &device_data
, SPDRP_DRIVER
, NULL
, (BYTE
*)bufferW
, sizeof(bufferW
),
492 lstrcpyW(driver
, nt_classW
);
493 lstrcatW(driver
, bufferW
);
495 /* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */
496 hkey
= SetupDiCreateDevRegKeyW(devinfo
, &device_data
, DICS_FLAG_GLOBAL
, 0, DIREG_DEV
, NULL
, NULL
);
498 size
= sizeof(bufferW
);
499 if (RegQueryValueExW(hkey
, video_idW
, 0, NULL
, (BYTE
*)bufferW
, &size
))
502 sprintfW(bufferW
, guid_fmtW
, guid
.Data1
, guid
.Data2
, guid
.Data3
, guid
.Data4
[0], guid
.Data4
[1], guid
.Data4
[2],
503 guid
.Data4
[3], guid
.Data4
[4], guid
.Data4
[5], guid
.Data4
[6], guid
.Data4
[7]);
504 if (RegSetValueExW(hkey
, video_idW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
)))
507 lstrcpyW(guid_string
, bufferW
);
513 ERR("Failed to initialize GPU\n");
517 static BOOL
X11DRV_InitAdapter(HKEY video_hkey
, INT video_index
, INT gpu_index
, INT adapter_index
, INT monitor_count
,
518 const struct x11drv_gpu
*gpu
, const WCHAR
*guid_string
,
519 const WCHAR
*gpu_driver
, const struct x11drv_adapter
*adapter
)
521 WCHAR adapter_keyW
[MAX_PATH
];
522 WCHAR key_nameW
[MAX_PATH
];
529 sprintfW(key_nameW
, device_video_fmtW
, video_index
);
530 lstrcpyW(bufferW
, machine_prefixW
);
531 sprintfW(adapter_keyW
, adapter_key_fmtW
, guid_string
, adapter_index
);
532 lstrcatW(bufferW
, adapter_keyW
);
534 /* Write value of \Device\Video? (adapter key) in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
535 if (RegSetValueExW(video_hkey
, key_nameW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
)))
538 /* Create HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} link to GPU driver */
539 ls
= RegCreateKeyExW(HKEY_LOCAL_MACHINE
, adapter_keyW
, 0, NULL
, REG_OPTION_VOLATILE
| REG_OPTION_CREATE_LINK
,
540 KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
);
541 if (ls
== ERROR_ALREADY_EXISTS
)
542 RegCreateKeyExW(HKEY_LOCAL_MACHINE
, adapter_keyW
, 0, NULL
, REG_OPTION_VOLATILE
| REG_OPTION_OPEN_LINK
,
543 KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
);
544 if (RegSetValueExW(hkey
, symbolic_link_valueW
, 0, REG_LINK
, (const BYTE
*)gpu_driver
,
545 strlenW(gpu_driver
) * sizeof(WCHAR
)))
551 * Following information is Wine specific, it doesn't really exist on Windows. It is used so that we can
552 * implement EnumDisplayDevices etc by querying registry only. This information is most likely reported by the
553 * device driver on Windows */
554 RegCreateKeyExW(HKEY_CURRENT_CONFIG
, adapter_keyW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &hkey
, NULL
);
556 /* Write GPU instance path so that we can find the GPU instance via adapters quickly. Another way is trying to match
557 * them via the GUID in Device Parameters/VideoID, but it would require enumerating all GPU instances */
558 sprintfW(bufferW
, gpu_instance_fmtW
, gpu
->vendor_id
, gpu
->device_id
, gpu
->subsys_id
, gpu
->revision_id
, gpu_index
);
559 if (RegSetValueExW(hkey
, gpu_idW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
)))
562 /* Write all monitor instances paths under this adapter */
563 for (i
= 0; i
< monitor_count
; i
++)
565 sprintfW(key_nameW
, monitor_id_fmtW
, i
);
566 sprintfW(bufferW
, monitor_instance_fmtW
, video_index
, i
);
567 if (RegSetValueExW(hkey
, key_nameW
, 0, REG_SZ
, (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
)))
571 /* Write StateFlags */
572 if (RegSetValueExW(hkey
, state_flagsW
, 0, REG_DWORD
, (const BYTE
*)&adapter
->state_flags
,
573 sizeof(adapter
->state_flags
)))
580 ERR("Failed to initialize adapter\n");
584 static BOOL
X11DRV_InitMonitor(HDEVINFO devinfo
, const struct x11drv_monitor
*monitor
, int monitor_index
,
585 int video_index
, const LUID
*gpu_luid
, UINT output_id
)
587 SP_DEVINFO_DATA device_data
= {sizeof(SP_DEVINFO_DATA
)};
588 WCHAR bufferW
[MAX_PATH
];
592 /* Create GUID_DEVCLASS_MONITOR instance */
593 sprintfW(bufferW
, monitor_instance_fmtW
, video_index
, monitor_index
);
594 SetupDiCreateDeviceInfoW(devinfo
, bufferW
, &GUID_DEVCLASS_MONITOR
, monitor
->name
, NULL
, 0, &device_data
);
595 if (!SetupDiRegisterDeviceInfo(devinfo
, &device_data
, 0, NULL
, NULL
, NULL
))
598 /* Write HardwareID registry property */
599 if (!SetupDiSetDeviceRegistryPropertyW(devinfo
, &device_data
, SPDRP_HARDWAREID
,
600 (const BYTE
*)monitor_hardware_idW
, sizeof(monitor_hardware_idW
)))
603 /* Write DEVPROPKEY_MONITOR_GPU_LUID */
604 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_MONITOR_GPU_LUID
,
605 DEVPROP_TYPE_INT64
, (const BYTE
*)gpu_luid
, sizeof(*gpu_luid
), 0))
608 /* Write DEVPROPKEY_MONITOR_OUTPUT_ID */
609 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPROPKEY_MONITOR_OUTPUT_ID
,
610 DEVPROP_TYPE_UINT32
, (const BYTE
*)&output_id
, sizeof(output_id
), 0))
613 /* Create driver key */
614 hkey
= SetupDiCreateDevRegKeyW(devinfo
, &device_data
, DICS_FLAG_GLOBAL
, 0, DIREG_DRV
, NULL
, NULL
);
618 * Following properties are Wine specific, see comments in X11DRV_InitAdapter for details */
620 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_STATEFLAGS
, DEVPROP_TYPE_UINT32
,
621 (const BYTE
*)&monitor
->state_flags
, sizeof(monitor
->state_flags
), 0))
624 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_RCMONITOR
, DEVPROP_TYPE_BINARY
,
625 (const BYTE
*)&monitor
->rc_monitor
, sizeof(monitor
->rc_monitor
), 0))
628 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_RCWORK
, DEVPROP_TYPE_BINARY
,
629 (const BYTE
*)&monitor
->rc_work
, sizeof(monitor
->rc_work
), 0))
632 sprintfW(bufferW
, adapter_name_fmtW
, video_index
+ 1);
633 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &WINE_DEVPROPKEY_MONITOR_ADAPTERNAME
, DEVPROP_TYPE_STRING
,
634 (const BYTE
*)bufferW
, (strlenW(bufferW
) + 1) * sizeof(WCHAR
), 0))
640 ERR("Failed to initialize monitor\n");
644 static void prepare_devices(HKEY video_hkey
)
646 static const BOOL not_present
= FALSE
;
647 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
651 /* Remove all monitors */
652 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR
, displayW
, NULL
, 0);
653 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
655 if (!SetupDiRemoveDevice(devinfo
, &device_data
))
656 ERR("Failed to remove monitor\n");
658 SetupDiDestroyDeviceInfoList(devinfo
);
660 /* Clean up old adapter keys for reinitialization */
661 RegDeleteTreeW(video_hkey
, NULL
);
664 * Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, So we need to clean up not present devices in
665 * case application uses SetupDiGetClassDevsW to enumerate devices. Wrong devices could exist in registry as a result
666 * of prefix copying or having devices unplugged. But then we couldn't simply delete GPUs because we need to retain
667 * the same GUID for the same GPU. */
669 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY
, pciW
, NULL
, 0);
670 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
672 if (!SetupDiSetDevicePropertyW(devinfo
, &device_data
, &DEVPKEY_Device_IsPresent
, DEVPROP_TYPE_BOOLEAN
,
673 (const BYTE
*)¬_present
, sizeof(not_present
), 0))
674 ERR("Failed to set GPU present property\n");
676 SetupDiDestroyDeviceInfoList(devinfo
);
679 static void cleanup_devices(void)
681 SP_DEVINFO_DATA device_data
= {sizeof(device_data
)};
687 devinfo
= SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY
, pciW
, NULL
, 0);
688 while (SetupDiEnumDeviceInfo(devinfo
, i
++, &device_data
))
691 SetupDiGetDevicePropertyW(devinfo
, &device_data
, &DEVPKEY_Device_IsPresent
, &type
, (BYTE
*)&present
,
692 sizeof(present
), NULL
, 0);
693 if (!present
&& !SetupDiRemoveDevice(devinfo
, &device_data
))
694 ERR("Failed to remove GPU\n");
696 SetupDiDestroyDeviceInfoList(devinfo
);
699 void X11DRV_DisplayDevices_Init(BOOL force
)
702 struct x11drv_display_device_handler
*handler
= is_virtual_desktop() ? &desktop_handler
: &host_handler
;
703 struct x11drv_gpu
*gpus
= NULL
;
704 struct x11drv_adapter
*adapters
= NULL
;
705 struct x11drv_monitor
*monitors
= NULL
;
706 INT gpu_count
, adapter_count
, monitor_count
;
707 INT gpu
, adapter
, monitor
;
708 HDEVINFO gpu_devinfo
= NULL
, monitor_devinfo
= NULL
;
709 HKEY video_hkey
= NULL
;
711 DWORD disposition
= 0;
717 mutex
= get_display_device_init_mutex();
719 if (RegCreateKeyExW(HKEY_LOCAL_MACHINE
, video_keyW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &video_hkey
,
722 ERR("Failed to create video device key\n");
726 /* Avoid unnecessary reinit */
727 if (!force
&& disposition
!= REG_CREATED_NEW_KEY
)
730 TRACE("via %s\n", wine_dbgstr_a(handler
->name
));
732 prepare_devices(video_hkey
);
734 gpu_devinfo
= SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY
, NULL
);
735 monitor_devinfo
= SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_MONITOR
, NULL
);
737 /* Initialize GPUs */
738 if (!handler
->get_gpus(&gpus
, &gpu_count
))
740 TRACE("GPU count: %d\n", gpu_count
);
742 for (gpu
= 0; gpu
< gpu_count
; gpu
++)
744 if (!X11DRV_InitGpu(gpu_devinfo
, &gpus
[gpu
], gpu
, guidW
, driverW
, &gpu_luid
))
747 /* Initialize adapters */
748 if (!handler
->get_adapters(gpus
[gpu
].id
, &adapters
, &adapter_count
))
750 TRACE("GPU: %#lx %s, adapter count: %d\n", gpus
[gpu
].id
, wine_dbgstr_w(gpus
[gpu
].name
), adapter_count
);
752 for (adapter
= 0; adapter
< adapter_count
; adapter
++)
754 if (!handler
->get_monitors(adapters
[adapter
].id
, &monitors
, &monitor_count
))
756 TRACE("adapter: %#lx, monitor count: %d\n", adapters
[adapter
].id
, monitor_count
);
758 if (!X11DRV_InitAdapter(video_hkey
, video_index
, gpu
, adapter
, monitor_count
,
759 &gpus
[gpu
], guidW
, driverW
, &adapters
[adapter
]))
762 /* Initialize monitors */
763 for (monitor
= 0; monitor
< monitor_count
; monitor
++)
765 TRACE("monitor: %#x %s\n", monitor
, wine_dbgstr_w(monitors
[monitor
].name
));
766 if (!X11DRV_InitMonitor(monitor_devinfo
, &monitors
[monitor
], monitor
, video_index
, &gpu_luid
, output_id
++))
770 handler
->free_monitors(monitors
);
775 handler
->free_adapters(adapters
);
781 SetupDiDestroyDeviceInfoList(monitor_devinfo
);
782 SetupDiDestroyDeviceInfoList(gpu_devinfo
);
783 RegCloseKey(video_hkey
);
784 release_display_device_init_mutex(mutex
);
786 handler
->free_gpus(gpus
);
788 handler
->free_adapters(adapters
);
790 handler
->free_monitors(monitors
);