Signed-off-by: Zhiyi Zhang zzhang@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..df067f361c 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 unplugged. 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; }