Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/winex11.drv/Makefile.in | 2 +-
dlls/winex11.drv/display.c | 228 +++++++++++++++++++++++++++++++++
dlls/winex11.drv/x11drv.h | 1 +
dlls/winex11.drv/x11drv_main.c | 1 +
4 files changed, 231 insertions(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in
index 9ca2fc6efe..72ab1bb77b 100644
--- a/dlls/winex11.drv/Makefile.in
+++ b/dlls/winex11.drv/Makefile.in
@@ -1,5 +1,5 @@
MODULE = winex11.drv
-IMPORTS = uuid user32 gdi32 advapi32
+IMPORTS = uuid user32 gdi32 advapi32 ntoskrnl setupapi
DELAYIMPORTS = comctl32 ole32 shell32 imm32
EXTRAINCL = $(X_CFLAGS)
EXTRALIBS = $(X_LIBS) $(X_EXTRA_LIBS)
diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c
index 9b9009c255..adf30ee4d3 100644
--- a/dlls/winex11.drv/display.c
+++ b/dlls/winex11.drv/display.c
@@ -20,11 +20,61 @@
#include "config.h"
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "initguid.h"
+#include "devguid.h"
+#include "devpkey.h"
+#include "setupapi.h"
+#define WIN32_NO_STATUS
+#include "winternl.h"
+#include "ddk/ntddk.h"
#include "wine/debug.h"
+#include "wine/unicode.h"
#include "x11drv.h"
WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
+static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0};
+static const WCHAR graphics_driverW[] = {'G','r','a','p','h','i','c','s','D','r','i','v','e','r',0};
+static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',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 winex11_drvW[] = {
+ 'C',':','\\',
+ 'W','i','n','d','o','w','s','\\',
+ 'S','y','s','t','e','m','3','2','\\',
+ 'w','i','n','e','x','1','1','.','d','r','v',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 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 struct x11drv_display_device_handler handler;
void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler *new_handler)
@@ -34,4 +84,182 @@ void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler
handler = *new_handler;
TRACE("Display device functions are now handled by: %s\n", handler.name);
}
+}
+
+/* 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;
+
+ 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 fail;
+ }
+
+ /* Write HaredwareID registry property */
+ 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 fail;
+
+ /* Write DEVPKEY_Device_IsPresent property */
+ if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN,
+ (const BYTE *)&present, sizeof(present), 0))
+ goto fail;
+
+ /* Open driver key.
+ * This is where HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} links to */
+ hkey = SetupDiOpenDevRegKey(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS);
+ if (hkey == INVALID_HANDLE_VALUE)
+ hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
+
+ /* Write GraphicsDriver value */
+ if (RegSetValueExW(hkey, graphics_driverW, 0, REG_SZ, (const BYTE *)winex11_drvW, sizeof(winex11_drvW)))
+ goto fail;
+
+ /* Write DriverDesc value */
+ if (RegSetValueExW(hkey, driver_descW, 0, REG_SZ, (const BYTE *)gpu->name,
+ (strlenW(gpu->name) + 1) * sizeof(WCHAR)))
+ goto fail;
+ RegCloseKey(hkey);
+ hkey = NULL;
+
+ /* Retrieve driver value for adapters */
+ if (!SetupDiGetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, sizeof(bufferW),
+ NULL))
+ goto fail;
+ strcpyW(driver, nt_classW);
+ strcatW(driver, bufferW);
+
+ /* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */
+ hkey = SetupDiOpenDevRegKey(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_ALL_ACCESS);
+ if (hkey == INVALID_HANDLE_VALUE)
+ hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, NULL, NULL);
+
+ size = sizeof(bufferW);
+ bufferW[0] = 0;
+ if (RegQueryValueExW(hkey, video_idW, 0, NULL, (BYTE *)bufferW, &size))
+ {
+ ExUuidCreate(&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 fail;
+ }
+ strcpyW(guid_string, bufferW);
+
+ ret = TRUE;
+fail:
+ RegCloseKey(hkey);
+ if (!ret)
+ ERR("Failed to initialize GPU\n");
+ return ret;
+}
+
+static void prepare_devices(void)
+{
+ static const BOOL not_present = FALSE;
+ SP_DEVINFO_DATA device_data = {sizeof(device_data)};
+ HDEVINFO devinfo;
+ DWORD i = 0;
+
+ /* Set all GPUs as not present. We can't simply delete them because we need to keep the GUID consistent with
+ * each initialization run. We clean up non present GPUs at the end of initialization */
+ devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, 0);
+ while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data))
+ {
+ if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN,
+ (const BYTE *)¬_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, NULL, 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);
+}
+
+BOOL X11DRV_DisplayDevices_Init(void)
+{
+ struct x11drv_gpu *gpus = NULL;
+ INT gpu_count;
+ INT gpu;
+ HDEVINFO gpu_devinfo = NULL;
+ HKEY video_hkey = NULL;
+ DWORD disposition = 0;
+ WCHAR guidW[40];
+ WCHAR driverW[1024];
+ BOOL ret = FALSE;
+
+ TRACE("via %s\n", wine_dbgstr_a(handler.name));
+
+ if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, video_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &video_hkey,
+ &disposition))
+ goto fail;
+
+ /* Ensure only one thread is initializing the registry and avoid unnecessary reinit */
+ if (disposition != REG_CREATED_NEW_KEY)
+ {
+ ret = TRUE;
+ goto fail;
+ }
+
+ /* FIXME:
+ * Currently we have no idea how to implement SetupDiGetClassDevsW with DIGCF_PRESENT properly. So we need to clean
+ * up not present devices in case application use SetupDiGetClassDevsW to enumerate devices. Wrong devices could
+ * exist in registry as a result of prefix copying or having devices unpluged. But then we couldn't simply delete
+ * GPUs because we need to retain the same GUID for the same GPU. */
+ prepare_devices();
+
+ gpu_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL);
+
+ /* Initialize GPUs */
+ if (!handler.pGetGpus(&gpus, &gpu_count))
+ goto fail;
+
+ for (gpu = 0; gpu < gpu_count; gpu++)
+ {
+ if (!X11DRV_InitGpu(gpu_devinfo, &gpus[gpu], gpu, guidW, driverW))
+ goto fail;
+ }
+
+ ret = TRUE;
+fail:
+ cleanup_devices();
+ SetupDiDestroyDeviceInfoList(gpu_devinfo);
+ RegCloseKey(video_hkey);
+ if (gpus)
+ handler.pFreeGpus(gpus);
+ if (!ret)
+ ERR("Failed to initialize display devices\n");
+ return ret;
}
\ No newline at end of file
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 22c50667be..56fbf3f22d 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -740,6 +740,7 @@ struct x11drv_display_device_handler
};
extern void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler *handler) DECLSPEC_HIDDEN;
+extern BOOL X11DRV_DisplayDevices_Init(void) DECLSPEC_HIDDEN;
/* XIM support */
extern BOOL X11DRV_InitXIM( const char *input_style ) DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c
index e67a3c05a9..69478741f1 100644
--- a/dlls/winex11.drv/x11drv_main.c
+++ b/dlls/winex11.drv/x11drv_main.c
@@ -597,6 +597,7 @@ static BOOL process_attach(void)
X11DRV_InitKeyboard( gdi_display );
if (use_xim) use_xim = X11DRV_InitXIM( input_style );
+ X11DRV_DisplayDevices_Init();
return TRUE;
}
--
2.20.1