diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index 0a097cc85ca..bee1085a14b 100644 --- a/dlls/user32/tests/monitor.c +++ b/dlls/user32/tests/monitor.c @@ -113,9 +113,9 @@ static void test_enumdisplaydevices_adapter(int index, const DISPLAY_DEVICEA *de memset(video_value, 0, sizeof(video_value)); size = sizeof(video_value); ls = RegQueryValueExA(hkey, video_name, NULL, NULL, (unsigned char *)video_value, &size); - todo_wine ok(!ls, "#%d: failed to get registry value, error: %#x\n", index, ls); + ok(!ls, "#%d: failed to get registry value, error: %#x\n", index, ls); RegCloseKey(hkey); - ok(!strcmp(video_value, device->DeviceKey), "#%d: wrong DeviceKey: %s\n", index, device->DeviceKey); + todo_wine ok(!strcmp(video_value, device->DeviceKey), "#%d: wrong DeviceKey: %s\n", index, device->DeviceKey); } } else diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index 5b51f6d9ca9..737621d909b 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -41,6 +41,9 @@ 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 symbolic_link_valueW[]= {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0}; +static const WCHAR gpu_idW[] = {'G','P','U','I','D',0}; +static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',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}; @@ -61,6 +64,26 @@ 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 const WCHAR adapter_key_fmtW[] = { + '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','\\', + 'V','i','d','e','o','\\', + '%','s','\\', + '%','0','4','x',0}; +static const WCHAR device_video_fmtW[] = { + '\\','D','e','v','i','c','e','\\', + 'V','i','d','e','o','%','d',0}; +static const WCHAR machine_prefixW[] = { + '\\','R','e','g','i','s','t','r','y','\\', + 'M','a','c','h','i','n','e','\\',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 struct x11drv_display_device_handler handler; @@ -73,8 +96,9 @@ 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) +/* 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)}; @@ -116,6 +140,13 @@ static BOOL X11DRV_InitGpu(HDEVINFO devinfo, const struct x11drv_gpu *gpu, INT g goto done; RegCloseKey(hkey); + /* Retrieve driver value for adapters */ + if (!SetupDiGetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, sizeof(bufferW), + NULL)) + goto done; + lstrcpyW(driver, nt_classW); + lstrcatW(driver, bufferW); + /* 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); @@ -128,6 +159,7 @@ static BOOL X11DRV_InitGpu(HDEVINFO devinfo, const struct x11drv_gpu *gpu, INT g if (RegSetValueExW(hkey, video_idW, 0, REG_SZ, (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR))) goto done; } + lstrcpyW(guid_string, bufferW); ret = TRUE; done: @@ -137,13 +169,73 @@ done: return ret; } -static void prepare_devices(void) +static BOOL X11DRV_InitAdapter(HKEY video_hkey, INT video_index, INT gpu_index, INT adapter_index, + const struct x11drv_gpu *gpu, const WCHAR *guid_string, + const WCHAR *gpu_driver, const struct x11drv_adapter *adapter) +{ + WCHAR adapter_keyW[MAX_PATH]; + WCHAR key_nameW[MAX_PATH]; + WCHAR bufferW[1024]; + HKEY hkey = NULL; + BOOL ret = FALSE; + LSTATUS ls; + + sprintfW(key_nameW, device_video_fmtW, video_index); + lstrcpyW(bufferW, machine_prefixW); + sprintfW(adapter_keyW, adapter_key_fmtW, guid_string, adapter_index); + lstrcatW(bufferW, adapter_keyW); + + /* Write value of \Device\Video? (adapter key) in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */ + if (RegSetValueExW(video_hkey, key_nameW, 0, REG_SZ, (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR))) + goto done; + + /* Create HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} link to GPU driver */ + ls = RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, + KEY_ALL_ACCESS, NULL, &hkey, NULL); + if (ls == ERROR_ALREADY_EXISTS) + RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK, + KEY_ALL_ACCESS, NULL, &hkey, NULL); + if (RegSetValueExW(hkey, symbolic_link_valueW, 0, REG_LINK, (const BYTE *)gpu_driver, + strlenW(gpu_driver) * sizeof(WCHAR))) + goto done; + RegCloseKey(hkey); + hkey = NULL; + + /* FIXME: + * Following information is Wine specific, it doesn't really exist on Windows. It is used so that we can + * implement EnumDisplayDevices etc by querying registry only. This information is most likely reported by the + * device driver on Windows */ + RegCreateKeyExW(HKEY_CURRENT_CONFIG, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL); + + /* Write GPU instance path so that we can find the GPU instance via adapters quickly. Another way is trying to match + * them via the GUID in Device Paramters/VideoID, but it would required enumrating all GPU instances */ + sprintfW(bufferW, gpu_instance_fmtW, gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index); + if (RegSetValueExW(hkey, gpu_idW, 0, REG_SZ, (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR))) + goto done; + + /* Write StateFlags */ + if (RegSetValueExW(hkey, state_flagsW, 0, REG_DWORD, (const BYTE *)&adapter->state_flags, + sizeof(adapter->state_flags))) + goto done; + + ret = TRUE; +done: + RegCloseKey(hkey); + if (!ret) + ERR("Failed to initialize adapter\n"); + return ret; +} + +static void prepare_devices(HKEY video_hkey) { static const BOOL not_present = FALSE; SP_DEVINFO_DATA device_data = {sizeof(device_data)}; HDEVINFO devinfo; DWORD i = 0; + /* Clean up old adapter keys for reinitialization */ + RegDeleteTreeW(video_hkey, NULL); + /* FIXME: * Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, So we need to clean up not present devices in * case application uses SetupDiGetClassDevsW to enumerate devices. Wrong devices could exist in registry as a result @@ -184,11 +276,15 @@ 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; + struct x11drv_adapter *adapters = NULL; + INT gpu_count, adapter_count; + INT gpu, adapter; HDEVINFO gpu_devinfo = NULL; HKEY video_hkey = NULL; + INT video_index = 0; DWORD disposition = 0; + WCHAR guidW[40]; + WCHAR driverW[1024]; mutex = CreateMutexW(NULL, FALSE, init_mutexW); WaitForSingleObject(mutex, INFINITE); @@ -206,7 +302,7 @@ void X11DRV_DisplayDevices_Init(void) TRACE("via %s\n", wine_dbgstr_a(handler.name)); - prepare_devices(); + prepare_devices(video_hkey); gpu_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL); @@ -216,8 +312,24 @@ void X11DRV_DisplayDevices_Init(void) for (gpu = 0; gpu < gpu_count; gpu++) { - if (!X11DRV_InitGpu(gpu_devinfo, &gpus[gpu], gpu)) + if (!X11DRV_InitGpu(gpu_devinfo, &gpus[gpu], gpu, guidW, driverW)) goto done; + + /* Initialize adapters */ + if (!handler.pGetAdapters(gpus[gpu].id, &adapters, &adapter_count)) + goto done; + + for (adapter = 0; adapter < adapter_count; adapter++) + { + if (!X11DRV_InitAdapter(video_hkey, video_index, gpu, adapter, + &gpus[gpu], guidW, driverW, &adapters[adapter])) + goto done; + + video_index++; + } + + handler.pFreeAdapters(adapters); + adapters = NULL; } done: @@ -228,4 +340,6 @@ done: CloseHandle(mutex); if (gpus) handler.pFreeGpus(gpus); + if (adapters) + handler.pFreeAdapters(adapters); } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 1bc7ed2f3de..5114d8feabb 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -681,6 +681,15 @@ struct x11drv_gpu UINT revision_id; }; +/* Represent an adapter in EnumDisplayDevices context */ +struct x11drv_adapter +{ + /* ID to uniquely identify an adapter in handler */ + ULONG_PTR id; + /* as StateFlags in DISPLAY_DEVICE struct */ + DWORD state_flags; +}; + /* Required functions for display device registry initialization */ struct x11drv_display_device_handler { @@ -695,8 +704,17 @@ struct x11drv_display_device_handler * Return FALSE on failure with parameters unchanged */ BOOL (*pGetGpus)(struct x11drv_gpu **gpus, int *count); + /* pGetAdapters will be called to get a list of adapters in EnumDisplayDevices context under a GPU. + * The first adapter has to be primary if GPU is primary. + * + * Return FALSE on failure with parameters unchanged */ + BOOL (*pGetAdapters)(ULONG_PTR gpu_id, struct x11drv_adapter **adapters, int *count); + /* pFreeGpus will be called to free a GPU list from pGetGpus */ void (*pFreeGpus)(struct x11drv_gpu *gpus); + + /* pFreeAdapters will be called to free an adapter list from pGetAdapters */ + void (*pFreeAdapters)(struct x11drv_adapter *adapters); }; 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 2cd875b29c1..df8dbcd9f98 100644 --- a/dlls/winex11.drv/xinerama.c +++ b/dlls/winex11.drv/xinerama.c @@ -224,6 +224,73 @@ static void xinerama_free_gpus( struct x11drv_gpu *gpus ) heap_free( gpus ); } +static BOOL xinerama_get_adapters( ULONG_PTR gpu_id, struct x11drv_adapter **new_adapters, int *count ) +{ + struct x11drv_adapter *adapters = NULL; + INT index = 0; + INT i, j; + INT primary_index; + BOOL mirrored; + + if (gpu_id) + return FALSE; + + /* Being lazy, actual adapter count may be less */ + adapters = heap_calloc( nb_monitors, sizeof(*adapters) ); + if (!adapters) + return FALSE; + + primary_index = primary_monitor; + if (primary_index >= nb_monitors) + primary_index = 0; + + for (i = 0; i < nb_monitors; i++) + { + mirrored = FALSE; + for (j = 0; j < i; j++) + { + if (EqualRect( &monitors[i].rcMonitor, &monitors[j].rcMonitor) && !IsRectEmpty( &monitors[j].rcMonitor )) + { + mirrored = TRUE; + break; + } + } + + /* Mirrored monitors share the same adapter */ + if (mirrored) + continue; + + /* Use monitor index as id */ + adapters[index].id = (ULONG_PTR)i; + + if (i == primary_index) + adapters[index].state_flags |= DISPLAY_DEVICE_PRIMARY_DEVICE; + + if (!IsRectEmpty( &monitors[i].rcMonitor )) + adapters[index].state_flags |= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP; + + index++; + } + + /* Primary adapter has to be first */ + if (primary_index) + { + struct x11drv_adapter tmp; + tmp = adapters[primary_index]; + adapters[primary_index] = adapters[0]; + adapters[0] = tmp; + } + + *new_adapters = adapters; + *count = index; + return TRUE; +} + +static void xinerama_free_adapters( struct x11drv_adapter *adapters ) +{ + heap_free( adapters ); +} + void xinerama_init( unsigned int width, unsigned int height ) { struct x11drv_display_device_handler handler; @@ -263,7 +330,9 @@ void xinerama_init( unsigned int width, unsigned int height ) handler.name = "Xinerama"; handler.priority = 100; handler.pGetGpus = xinerama_get_gpus; + handler.pGetAdapters = xinerama_get_adapters; handler.pFreeGpus = xinerama_free_gpus; + handler.pFreeAdapters = xinerama_free_adapters; X11DRV_DisplayDevices_SetHandler( &handler ); TRACE( "virtual size: %s primary: %s\n",