reg: Fail if the source and destination keys are the same when copying.
[wine.git] / dlls / winex11.drv / display.c
blobb647455a01fa96e3d314791d85501b6481a56a36
1 /*
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
21 #include "config.h"
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "rpc.h"
29 #include "winreg.h"
30 #include "initguid.h"
31 #include "devguid.h"
32 #include "devpkey.h"
33 #include "setupapi.h"
34 #define WIN32_NO_STATUS
35 #include "winternl.h"
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
38 #include "x11drv.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[] = {
68 'P','C','I','\\',
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','\\',
73 '%','0','8','X',0};
74 static const WCHAR gpu_hardware_id_fmtW[] = {
75 'P','C','I','\\',
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','\\',
89 '%','s','\\',
90 '%','0','4','x',0};
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);
136 return mutex;
139 void release_display_device_init_mutex(HANDLE mutex)
141 ReleaseMutex(mutex);
142 CloseHandle(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};
152 HANDLE mutex = NULL;
153 DWORD i = 0;
154 INT result;
155 DWORD type;
156 BOOL ret = FALSE;
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);
163 return FALSE;
165 result = CompareFileTime(&filetime, &last_query_screen_time);
166 LeaveCriticalSection(&screen_section);
167 if (result < 1)
168 return TRUE;
170 mutex = get_display_device_init_mutex();
172 devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_MONITOR, displayW, NULL, DIGCF_PRESENT);
173 if (devinfo == INVALID_HANDLE_VALUE)
174 goto fail;
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))
180 goto fail;
182 UnionRect(&virtual_rect, &virtual_rect, &monitor_rect);
183 if (i == 1)
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);
192 ret = TRUE;
193 fail:
194 SetupDiDestroyDeviceInfoList(devinfo);
195 release_display_device_init_mutex(mutex);
196 if (!ret)
197 WARN("Update screen cache failed!\n");
198 return ret;
201 POINT virtual_screen_to_root(INT x, INT y)
203 RECT virtual = get_virtual_screen_rect();
204 POINT pt;
206 pt.x = x - virtual.left;
207 pt.y = y - virtual.top;
208 return pt;
211 POINT root_to_virtual_screen(INT x, INT y)
213 RECT virtual = get_virtual_screen_rect();
214 POINT pt;
216 pt.x = x + virtual.left;
217 pt.y = y + virtual.top;
218 return pt;
221 RECT get_virtual_screen_rect(void)
223 RECT virtual;
225 update_screen_cache();
226 EnterCriticalSection(&screen_section);
227 virtual = virtual_screen_rect;
228 LeaveCriticalSection(&screen_section);
229 return virtual;
232 RECT get_primary_monitor_rect(void)
234 RECT primary;
236 update_screen_cache();
237 EnterCriticalSection(&screen_section);
238 primary = primary_monitor_rect;
239 LeaveCriticalSection(&screen_section);
240 return primary;
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;
250 RECT rect = {0};
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);
261 return rect;
264 BOOL get_host_primary_gpu(struct x11drv_gpu *gpu)
266 struct x11drv_gpu *gpus;
267 INT gpu_count;
269 if (host_handler.get_gpus(&gpus, &gpu_count) && gpu_count)
271 *gpu = gpus[0];
272 host_handler.free_gpus(gpus);
273 return TRUE;
276 return FALSE;
279 RECT get_work_area(const RECT *monitor_rect)
281 Atom type;
282 int format;
283 unsigned long count, remaining, i;
284 long *work_area;
285 RECT work_rect;
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)
294 for (i = 0; i < count / 4; ++i)
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));
304 XFree(work_area);
305 return work_rect;
309 XFree(work_area);
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));
326 XFree(work_area);
327 return work_rect;
330 XFree(work_area);
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)))
361 return TRUE;
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;
370 changes.x = pos.x;
371 changes.y = pos.y;
372 XReconfigureWMWindow(data->display, data->whole_window, data->vis.screen, mask, &changes);
374 release_win_data(data);
375 return TRUE;
378 void X11DRV_DisplayDevices_Update(BOOL send_display_change)
380 RECT old_virtual_rect, new_virtual_rect;
381 DWORD tid, pid;
382 HWND foreground;
383 UINT mask = 0;
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)
391 mask |= CWX;
392 if (old_virtual_rect.top != new_virtual_rect.top)
393 mask |= CWY;
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;
416 WCHAR bufferW[1024];
417 FILETIME filetime;
418 HKEY hkey = NULL;
419 GUID guid;
420 LUID luid;
421 INT written;
422 DWORD size;
423 BOOL ret = FALSE;
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))
432 goto done;
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)))
440 goto done;
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))
445 goto done;
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))
452 goto done;
454 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_GPU_LUID,
455 DEVPROP_TYPE_UINT64, (const BYTE *)&luid, sizeof(luid), 0))
456 goto done;
458 *gpu_luid = luid;
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))
465 goto done;
466 TRACE("Vulkan UUID:%s.\n", wine_dbgstr_guid(&gpu->vulkan_uuid));
468 /* Open driver key.
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)))
475 goto done;
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)))
479 goto done;
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)))
484 goto done;
486 RegCloseKey(hkey);
488 /* Retrieve driver value for adapters */
489 if (!SetupDiGetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, sizeof(bufferW),
490 NULL))
491 goto done;
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))
501 UuidCreate(&guid);
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)))
505 goto done;
507 lstrcpyW(guid_string, bufferW);
509 ret = TRUE;
510 done:
511 RegCloseKey(hkey);
512 if (!ret)
513 ERR("Failed to initialize GPU\n");
514 return ret;
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];
523 WCHAR bufferW[1024];
524 HKEY hkey = NULL;
525 BOOL ret = FALSE;
526 LSTATUS ls;
527 INT i;
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)))
536 goto done;
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)))
546 goto done;
547 RegCloseKey(hkey);
548 hkey = NULL;
550 /* FIXME:
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)))
560 goto done;
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)))
568 goto done;
571 /* Write StateFlags */
572 if (RegSetValueExW(hkey, state_flagsW, 0, REG_DWORD, (const BYTE *)&adapter->state_flags,
573 sizeof(adapter->state_flags)))
574 goto done;
576 ret = TRUE;
577 done:
578 RegCloseKey(hkey);
579 if (!ret)
580 ERR("Failed to initialize adapter\n");
581 return ret;
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];
589 HKEY hkey;
590 BOOL ret = FALSE;
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))
596 goto done;
598 /* Write HardwareID registry property */
599 if (!SetupDiSetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_HARDWAREID,
600 (const BYTE *)monitor_hardware_idW, sizeof(monitor_hardware_idW)))
601 goto done;
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))
606 goto done;
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))
611 goto done;
613 /* Create driver key */
614 hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
615 RegCloseKey(hkey);
617 /* FIXME:
618 * Following properties are Wine specific, see comments in X11DRV_InitAdapter for details */
619 /* StateFlags */
620 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_STATEFLAGS, DEVPROP_TYPE_UINT32,
621 (const BYTE *)&monitor->state_flags, sizeof(monitor->state_flags), 0))
622 goto done;
623 /* RcMonitor */
624 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_RCMONITOR, DEVPROP_TYPE_BINARY,
625 (const BYTE *)&monitor->rc_monitor, sizeof(monitor->rc_monitor), 0))
626 goto done;
627 /* RcWork */
628 if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_RCWORK, DEVPROP_TYPE_BINARY,
629 (const BYTE *)&monitor->rc_work, sizeof(monitor->rc_work), 0))
630 goto done;
631 /* Adapter name */
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))
635 goto done;
637 ret = TRUE;
638 done:
639 if (!ret)
640 ERR("Failed to initialize monitor\n");
641 return ret;
644 static void prepare_devices(HKEY video_hkey)
646 static const BOOL not_present = FALSE;
647 SP_DEVINFO_DATA device_data = {sizeof(device_data)};
648 HDEVINFO devinfo;
649 DWORD i = 0;
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);
663 /* FIXME:
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. */
668 i = 0;
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 *)&not_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)};
682 HDEVINFO devinfo;
683 DWORD type;
684 DWORD i = 0;
685 BOOL present;
687 devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, pciW, NULL, 0);
688 while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
690 present = FALSE;
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)
701 HANDLE mutex;
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;
710 INT video_index = 0;
711 DWORD disposition = 0;
712 WCHAR guidW[40];
713 WCHAR driverW[1024];
714 LUID gpu_luid;
715 UINT output_id = 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,
720 &disposition))
722 ERR("Failed to create video device key\n");
723 goto done;
726 /* Avoid unnecessary reinit */
727 if (!force && disposition != REG_CREATED_NEW_KEY)
728 goto done;
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))
739 goto done;
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))
745 goto done;
747 /* Initialize adapters */
748 if (!handler->get_adapters(gpus[gpu].id, &adapters, &adapter_count))
749 goto done;
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))
755 goto done;
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]))
760 goto done;
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++))
767 goto done;
770 handler->free_monitors(monitors);
771 monitors = NULL;
772 video_index++;
775 handler->free_adapters(adapters);
776 adapters = NULL;
779 done:
780 cleanup_devices();
781 SetupDiDestroyDeviceInfoList(monitor_devinfo);
782 SetupDiDestroyDeviceInfoList(gpu_devinfo);
783 RegCloseKey(video_hkey);
784 release_display_device_init_mutex(mutex);
785 if (gpus)
786 handler->free_gpus(gpus);
787 if (adapters)
788 handler->free_adapters(adapters);
789 if (monitors)
790 handler->free_monitors(monitors);