From: Zhiyi Zhang Subject: [PATCH 5/6] user32: Add a cache for EnumDisplayDevicesW(). Message-Id: <0e70d733-e37f-bfa7-4785-a5713f10d28d@codeweavers.com> Date: Tue, 18 May 2021 17:14:18 +0800 Signed-off-by: Zhiyi Zhang --- You might notice that there are duplications with update_display_cache() and update_monitor_cache(). EnumDisplayMonitors() is planned to use wineserver calls directly and subsequently update_monitor_cache() will be removed. So I don't try to deduplicate the code. dlls/user32/sysparams.c | 313 ++++++++++++++++++++++++++++------------ 1 file changed, 218 insertions(+), 95 deletions(-) diff --git a/dlls/user32/sysparams.c b/dlls/user32/sysparams.c index 015db0db50e..0336bf943c4 100644 --- a/dlls/user32/sysparams.c +++ b/dlls/user32/sysparams.c @@ -107,6 +107,32 @@ DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_ADAPTERNAME, 0x233a9ef3, 0xafc4, 0x4ab #define NULLDRV_DEFAULT_HMONITOR ((HMONITOR)(UINT_PTR)(0x10000 + 1)) +/* Cached display device information */ +struct display_device +{ + struct list entry; /* Device list entry */ + struct list childs; /* Child device list entry. For adapters, this is monitor list. For monitors, this is unused. */ + WCHAR device_name[32]; /* as DeviceName in DISPLAY_DEVICEW */ + WCHAR device_string[128]; /* as DeviceString in DISPLAY_DEVICEW */ + DWORD state_flags; /* as StateFlags in DISPLAY_DEVICEW */ + WCHAR device_id[128]; /* as DeviceID in DISPLAY_DEVICEW when EDD_GET_DEVICE_INTERFACE_NAME is not set */ + WCHAR interface_name[128]; /* as DeviceID in DISPLAY_DEVICEW when EDD_GET_DEVICE_INTERFACE_NAME is set */ + WCHAR device_key[128]; /* as DeviceKey in DISPLAY_DEVICEW */ +}; + +static struct list adapters = LIST_INIT(adapters); +static FILETIME last_query_display_time; +static CRITICAL_SECTION display_section; +static CRITICAL_SECTION_DEBUG display_critsect_debug = +{ + 0, 0, &display_section, + { &display_critsect_debug.ProcessLocksList, &display_critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": display_section") } +}; +static CRITICAL_SECTION display_section = { &display_critsect_debug, -1, 0, 0, 0, 0 }; + +static BOOL enum_display_device( WCHAR *device, DWORD index, struct display_device *info ); + /* Cached monitor information */ static MONITORINFOEXW *monitors; static UINT monitor_count; @@ -3791,6 +3817,69 @@ HMONITOR WINAPI MonitorFromWindow(HWND hWnd, DWORD dwFlags) return MonitorFromRect( &rect, dwFlags ); } +/* Return FALSE on failure and TRUE on success */ +static BOOL update_display_cache(void) +{ + struct display_device device, *adapter, *adapter2, *monitor, *monitor2; + DWORD adapter_idx, monitor_idx; + struct list *monitor_list; + FILETIME filetime = {0}; + HANDLE mutex = NULL; + BOOL ret = FALSE; + + /* Update display cache from SetupAPI if it's outdated */ + wait_graphics_driver_ready(); + + if (!video_key && RegOpenKeyW( HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\VIDEO", &video_key )) + return FALSE; + if (RegQueryInfoKeyW( video_key, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &filetime )) + return FALSE; + if (CompareFileTime( &filetime, &last_query_display_time ) < 1) + return TRUE; + + mutex = get_display_device_init_mutex(); + EnterCriticalSection( &display_section ); + + LIST_FOR_EACH_ENTRY_SAFE(adapter, adapter2, &adapters, struct display_device, entry) + { + LIST_FOR_EACH_ENTRY_SAFE(monitor, monitor2, &adapter->childs, struct display_device, entry) + { + list_remove( &monitor->entry ); + heap_free( monitor ); + } + list_remove( &adapter->entry ); + heap_free( adapter ); + } + + for (adapter_idx = 0; enum_display_device( NULL, adapter_idx, &device ); ++adapter_idx) + { + adapter = heap_alloc( sizeof(*adapter) ); + if (!adapter) + goto fail; + + memcpy( adapter, &device, sizeof(device) ); + monitor_list = &adapter->childs; + list_init( monitor_list ); + list_add_tail( &adapters, &adapter->entry ); + for (monitor_idx = 0; enum_display_device( adapter->device_name, monitor_idx, &device ); ++monitor_idx) + { + monitor = heap_alloc( sizeof(*monitor) ); + if (!monitor) + goto fail; + + memcpy( monitor, &device, sizeof(device) ); + list_add_tail( monitor_list, &monitor->entry ); + } + } + + last_query_display_time = filetime; + ret = TRUE; +fail: + LeaveCriticalSection( &display_section ); + release_display_device_init_mutex( mutex ); + return ret; +} + /* Return FALSE on failure and TRUE on success */ static BOOL update_monitor_cache(void) { @@ -4117,6 +4206,76 @@ BOOL WINAPI EnumDisplayDevicesA( LPCSTR device, DWORD index, DISPLAY_DEVICEA *in * EnumDisplayDevicesW (USER32.@) */ BOOL WINAPI EnumDisplayDevicesW( LPCWSTR device, DWORD index, DISPLAY_DEVICEW *info, DWORD flags ) +{ + struct display_device *adapter, *monitor, *found = NULL; + DWORD device_idx = 0; + + TRACE("%s %u %p %#x\n", debugstr_w( device ), index, info, flags); + + if (!update_display_cache()) + return FALSE; + + EnterCriticalSection( &display_section ); + /* Enumerate adapters */ + if (!device) + { + LIST_FOR_EACH_ENTRY(adapter, &adapters, struct display_device, entry) + { + if (index == device_idx++) + { + found = adapter; + break; + } + } + } + /* Enumerate monitors */ + else + { + LIST_FOR_EACH_ENTRY(adapter, &adapters, struct display_device, entry) + { + if (!lstrcmpiW( device, adapter->device_name )) + { + found = adapter; + break; + } + } + + if (found) + { + found = NULL; + LIST_FOR_EACH_ENTRY(monitor, &adapter->childs, struct display_device, entry) + { + if (index == device_idx++) + { + found = monitor; + break; + } + } + } + } + + if (!found) + { + LeaveCriticalSection( &display_section ); + return FALSE; + } + + if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceName) + sizeof(info->DeviceName)) + lstrcpyW( info->DeviceName, found->device_name ); + if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceString) + sizeof(info->DeviceString)) + lstrcpyW( info->DeviceString, found->device_string ); + if (info->cb >= offsetof(DISPLAY_DEVICEW, StateFlags) + sizeof(info->StateFlags)) + info->StateFlags = found->state_flags; + if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID)) + lstrcpyW( info->DeviceID, (flags & EDD_GET_DEVICE_INTERFACE_NAME) ? found->interface_name : found->device_id ); + if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey)) + lstrcpyW( info->DeviceKey, found->device_key ); + LeaveCriticalSection( &display_section ); + return TRUE; +} + +/* Call this function with the display_device_init mutex held */ +static BOOL enum_display_device( WCHAR *device, DWORD index, struct display_device *info ) { SP_DEVINFO_DATA device_data = {sizeof(device_data)}; HDEVINFO set = INVALID_HANDLE_VALUE; @@ -4125,17 +4284,11 @@ BOOL WINAPI EnumDisplayDevicesW( LPCWSTR device, DWORD index, DISPLAY_DEVICEW *i WCHAR bufferW[1024]; LONG adapter_index; WCHAR *next_charW; - HANDLE mutex; DWORD size; DWORD type; HKEY hkey; BOOL ret = FALSE; - TRACE("%s %d %p %#x\n", debugstr_w( device ), index, info, flags); - - wait_graphics_driver_ready(); - mutex = get_display_device_init_mutex(); - /* Find adapter */ if (!device) { @@ -4145,46 +4298,40 @@ BOOL WINAPI EnumDisplayDevicesW( LPCWSTR device, DWORD index, DISPLAY_DEVICEW *i goto done; /* DeviceKey */ - if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey)) - lstrcpyW( info->DeviceKey, bufferW ); + lstrcpyW( info->device_key, bufferW ); /* DeviceName */ - swprintf( info->DeviceName, ARRAY_SIZE(info->DeviceName), L"\\\\.\\DISPLAY%d", index + 1 ); + swprintf( info->device_name, ARRAY_SIZE(info->device_name), L"\\\\.\\DISPLAY%d", index + 1 ); /* Strip \Registry\Machine\ */ lstrcpyW( key_nameW, bufferW + 18 ); /* DeviceString */ - size = sizeof(info->DeviceString); + size = sizeof(info->device_string); if (RegGetValueW( HKEY_LOCAL_MACHINE, key_nameW, L"DriverDesc", RRF_RT_REG_SZ, NULL, - info->DeviceString, &size )) + info->device_string, &size )) goto done; /* StateFlags */ - size = sizeof(info->StateFlags); + size = sizeof(info->state_flags); if (RegGetValueW( HKEY_CURRENT_CONFIG, key_nameW, L"StateFlags", RRF_RT_REG_DWORD, NULL, - &info->StateFlags, &size )) + &info->state_flags, &size )) goto done; + /* Interface name */ + info->interface_name[0] = 0; + /* DeviceID */ - if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID)) - { - if (flags & EDD_GET_DEVICE_INTERFACE_NAME) - info->DeviceID[0] = 0; - else - { - size = sizeof(bufferW); - if (RegGetValueW( HKEY_CURRENT_CONFIG, key_nameW, L"GPUID", RRF_RT_REG_SZ | RRF_ZEROONFAILURE, NULL, - bufferW, &size )) - goto done; - set = SetupDiCreateDeviceInfoList( &GUID_DEVCLASS_DISPLAY, NULL ); - if (!SetupDiOpenDeviceInfoW( set, bufferW, NULL, 0, &device_data ) - || !SetupDiGetDeviceRegistryPropertyW( set, &device_data, SPDRP_HARDWAREID, NULL, (BYTE *)bufferW, - sizeof(bufferW), NULL )) - goto done; - lstrcpyW( info->DeviceID, bufferW ); - } - } + size = sizeof(bufferW); + if (RegGetValueW( HKEY_CURRENT_CONFIG, key_nameW, L"GPUID", RRF_RT_REG_SZ | RRF_ZEROONFAILURE, NULL, + bufferW, &size )) + goto done; + set = SetupDiCreateDeviceInfoList( &GUID_DEVCLASS_DISPLAY, NULL ); + if (!SetupDiOpenDeviceInfoW( set, bufferW, NULL, 0, &device_data ) + || !SetupDiGetDeviceRegistryPropertyW( set, &device_data, SPDRP_HARDWAREID, NULL, (BYTE *)bufferW, + sizeof(bufferW), NULL )) + goto done; + lstrcpyW( info->device_id, bufferW ); } /* Find monitor */ else @@ -4201,7 +4348,7 @@ BOOL WINAPI EnumDisplayDevicesW( LPCWSTR device, DWORD index, DISPLAY_DEVICEW *i goto done; /* DeviceName */ - swprintf( info->DeviceName, ARRAY_SIZE(info->DeviceName), L"\\\\.\\DISPLAY%d\\Monitor%d", adapter_index, index ); + swprintf( info->device_name, ARRAY_SIZE(info->device_name), L"\\\\.\\DISPLAY%d\\Monitor%d", adapter_index, index ); /* Get monitor instance */ /* Strip \Registry\Machine\ first */ @@ -4218,63 +4365,51 @@ BOOL WINAPI EnumDisplayDevicesW( LPCWSTR device, DWORD index, DISPLAY_DEVICEW *i /* StateFlags */ if (!SetupDiGetDevicePropertyW( set, &device_data, &WINE_DEVPROPKEY_MONITOR_STATEFLAGS, &type, - (BYTE *)&info->StateFlags, sizeof(info->StateFlags), NULL, 0 )) + (BYTE *)&info->state_flags, sizeof(info->state_flags), NULL, 0 )) goto done; /* DeviceString */ if (!SetupDiGetDeviceRegistryPropertyW( set, &device_data, SPDRP_DEVICEDESC, NULL, - (BYTE *)info->DeviceString, - sizeof(info->DeviceString), NULL )) + (BYTE *)info->device_string, + sizeof(info->device_string), NULL )) goto done; /* DeviceKey */ - if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey)) - { - if (!SetupDiGetDeviceRegistryPropertyW( set, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, - sizeof(bufferW), NULL )) - goto done; + if (!SetupDiGetDeviceRegistryPropertyW( set, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, + sizeof(bufferW), NULL )) + goto done; - lstrcpyW( info->DeviceKey, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\" ); - lstrcatW( info->DeviceKey, bufferW ); + lstrcpyW( info->device_key, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\" ); + lstrcatW( info->device_key, bufferW ); + + /* Interface name */ + lstrcpyW( info->interface_name, L"\\\\\?\\" ); + lstrcatW( info->interface_name, instanceW ); + lstrcatW( info->interface_name, L"#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}" ); + /* Replace '\\' with '#' after prefix */ + for (next_charW = info->interface_name + lstrlenW( L"\\\\\?\\" ); *next_charW; next_charW++) + { + if (*next_charW == '\\') + *next_charW = '#'; } /* DeviceID */ - if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID)) - { - if (flags & EDD_GET_DEVICE_INTERFACE_NAME) - { - lstrcpyW( info->DeviceID, L"\\\\\?\\" ); - lstrcatW( info->DeviceID, instanceW ); - lstrcatW( info->DeviceID, L"#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}" ); - /* Replace '\\' with '#' after prefix */ - for (next_charW = info->DeviceID + lstrlenW( L"\\\\\?\\" ); *next_charW; - next_charW++) - { - if (*next_charW == '\\') - *next_charW = '#'; - } - } - else - { - if (!SetupDiGetDeviceRegistryPropertyW( set, &device_data, SPDRP_HARDWAREID, NULL, (BYTE *)bufferW, - sizeof(bufferW), NULL )) - goto done; + if (!SetupDiGetDeviceRegistryPropertyW( set, &device_data, SPDRP_HARDWAREID, NULL, (BYTE *)bufferW, + sizeof(bufferW), NULL )) + goto done; - lstrcpyW( info->DeviceID, bufferW ); - lstrcatW( info->DeviceID, L"\\" ); + lstrcpyW( info->device_id, bufferW ); + lstrcatW( info->device_id, L"\\" ); - if (!SetupDiGetDeviceRegistryPropertyW( set, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, - sizeof(bufferW), NULL )) - goto done; + if (!SetupDiGetDeviceRegistryPropertyW( set, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, + sizeof(bufferW), NULL )) + goto done; - lstrcatW( info->DeviceID, bufferW ); - } - } + lstrcatW( info->device_id, bufferW ); } ret = TRUE; done: - release_display_device_init_mutex( mutex ); SetupDiDestroyDeviceInfoList( set ); if (ret) return ret; @@ -4294,17 +4429,12 @@ done: /* Adapter */ if (!device) { - lstrcpyW( info->DeviceName, L"\\\\.\\DISPLAY1" ); - lstrcpyW( info->DeviceString, L"Wine Adapter" ); - info->StateFlags = - DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE; - if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID)) - { - if (flags & EDD_GET_DEVICE_INTERFACE_NAME) - info->DeviceID[0] = 0; - else - lstrcpyW( info->DeviceID, L"PCI\\VEN_0000&DEV_0000&SUBSYS_00000000&REV_00" ); - } + lstrcpyW( info->device_name, L"\\\\.\\DISPLAY1" ); + lstrcpyW( info->device_string, L"Wine Adapter" ); + info->state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE; + info->interface_name[0] = 0; + lstrcpyW( info->device_id, L"PCI\\VEN_0000&DEV_0000&SUBSYS_00000000&REV_00" ); + info->device_key[0] = 0; } /* Monitor */ else @@ -4312,21 +4442,14 @@ done: if (lstrcmpiW( L"\\\\.\\DISPLAY1", device )) return FALSE; - lstrcpyW( info->DeviceName, L"\\\\.\\DISPLAY1\\Monitor0" ); - lstrcpyW( info->DeviceString, L"Generic Non-PnP Monitor" ); - info->StateFlags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED; - if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID)) - { - if (flags & EDD_GET_DEVICE_INTERFACE_NAME) - lstrcpyW( info->DeviceID, L"\\\\\?\\DISPLAY#Default_Monitor#4&17f0ff54&0&UID0#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}" ); - else - lstrcpyW( info->DeviceID, L"MONITOR\\Default_Monitor\\{4d36e96e-e325-11ce-bfc1-08002be10318}\\0000" ); - } + lstrcpyW( info->device_name, L"\\\\.\\DISPLAY1\\Monitor0" ); + lstrcpyW( info->device_string, L"Generic Non-PnP Monitor" ); + info->state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED; + lstrcpyW( info->interface_name, L"\\\\\?\\DISPLAY#Default_Monitor#4&17f0ff54&0&UID0#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}" ); + lstrcpyW( info->device_id, L"MONITOR\\Default_Monitor\\{4d36e96e-e325-11ce-bfc1-08002be10318}\\0000" ); + info->device_key[0] = 0; } - if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey)) - info->DeviceKey[0] = 0; - return TRUE; } -- 2.30.2