Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/winex11.drv/Makefile.in | 2 +- dlls/winex11.drv/display.c | 154 +++++++++++++++++++++++++++++++++++ dlls/winex11.drv/x11drv.h | 22 +++++ dlls/winex11.drv/xinerama.c | 25 ++++++ 4 files changed, 202 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 e3e4dce84d..6ff5828b3a 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -26,11 +26,37 @@ #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 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 video_keyW[] = { 'H','A','R','D','W','A','R','E','\', 'D','E','V','I','C','E','M','A','P','\', @@ -47,10 +73,120 @@ void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler } }
+/* Initialize a GPU instance */ +static BOOL X11DRV_InitGpu(HDEVINFO devinfo, const struct x11drv_gpu *gpu, INT gpu_index) +{ + 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 done; + } + + /* Write HaredwareID registry property, REG_MULTI_SZ */ + 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 done; + + /* Write DEVPKEY_Device_IsPresent property */ + if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN, + (const BYTE *)&present, sizeof(present), 0)) + goto done; + + /* Open driver key. + * This is where HKLM\System\CurrentControlSet\Control\Video{GPU GUID}{Adapter Index} links to */ + hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL); + + /* Write DriverDesc value */ + if (RegSetValueExW(hkey, driver_descW, 0, REG_SZ, (const BYTE *)gpu->name, + (strlenW(gpu->name) + 1) * sizeof(WCHAR))) + goto done; + RegCloseKey(hkey); + + /* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */ + hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, NULL, NULL); + + size = sizeof(bufferW); + 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 done; + } + + ret = TRUE; +done: + 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; + + /* FIXME: + * Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, 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. */ + 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); +} + void X11DRV_DisplayDevices_Init(void) { static const WCHAR init_mutexW[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0}; HANDLE mutex; + struct x11drv_gpu *gpus = NULL; + INT gpu_count; + INT gpu; + HDEVINFO gpu_devinfo = NULL; HKEY video_hkey = NULL; DWORD disposition = 0;
@@ -70,8 +206,26 @@ void X11DRV_DisplayDevices_Init(void)
TRACE("via %s\n", wine_dbgstr_a(handler.name));
+ prepare_devices(); + + gpu_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL); + + /* Initialize GPUs */ + if (!handler.pGetGpus(&gpus, &gpu_count)) + goto done; + + for (gpu = 0; gpu < gpu_count; gpu++) + { + if (!X11DRV_InitGpu(gpu_devinfo, &gpus[gpu], gpu)) + goto done; + } + done: + cleanup_devices(); + SetupDiDestroyDeviceInfoList(gpu_devinfo); RegCloseKey(video_hkey); ReleaseMutex(mutex); CloseHandle(mutex); + if (gpus) + handler.pFreeGpus(gpus); } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index fbc5a104f6..e50158d282 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -667,6 +667,20 @@ void X11DRV_XRandR_Init(void) DECLSPEC_HIDDEN;
/* X11 display device handler. Used to initialize display device registry data */
+/* Represent a physical GPU in the PCI slots */ +struct x11drv_gpu +{ + /* ID to uniquely identify a GPU in handler */ + ULONG_PTR id; + /* Name */ + WCHAR name[128]; + /* PCI ID */ + UINT vendor_id; + UINT device_id; + UINT subsys_id; + UINT revision_id; +}; + /* Required functions for display device registry initialization */ struct x11drv_display_device_handler { @@ -675,6 +689,14 @@ struct x11drv_display_device_handler
/* Higher priority can override handlers with lower proprity */ INT priority; + + /* pGetGpus will be called to get a list of GPUs. First GPU has to be where the primary adapter is. + * + * Return FALSE on failure with parameters unchanged */ + BOOL (*pGetGpus)(struct x11drv_gpu **gpus, int *count); + + /* pFreeGpus will be called to free a GPU list from pGetGpus */ + void (*pFreeGpus)(struct x11drv_gpu *gpus); };
extern void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler *handler) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/xinerama.c b/dlls/winex11.drv/xinerama.c index 35dd36049b..900cfd91db 100644 --- a/dlls/winex11.drv/xinerama.c +++ b/dlls/winex11.drv/xinerama.c @@ -201,6 +201,29 @@ RECT get_primary_monitor_rect(void) return get_primary()->rcMonitor; }
+static BOOL xinerama_get_gpus( struct x11drv_gpu **new_gpus, int *count ) +{ + static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0}; + struct x11drv_gpu *gpus; + + /* Xinerama has no support for GPU, faking one */ + gpus = heap_calloc( 1, sizeof(*gpus) ); + if (!gpus) + return FALSE; + + lstrcpyW( gpus[0].name, wine_adapterW ); + + *new_gpus = gpus; + *count = 1; + + return TRUE; +} + +static void xinerama_free_gpus( struct x11drv_gpu *gpus ) +{ + heap_free( gpus ); +} + void xinerama_init( unsigned int width, unsigned int height ) { struct x11drv_display_device_handler handler; @@ -239,6 +262,8 @@ void xinerama_init( unsigned int width, unsigned int height )
strcpy( handler.name, "Xinerama" ); handler.priority = 100; + handler.pGetGpus = xinerama_get_gpus; + handler.pFreeGpus = xinerama_free_gpus; X11DRV_DisplayDevices_SetHandler( &handler );
TRACE( "virtual size: %s primary: %s\n",