/* * System parameters functions * * Copyright 1994 Alexandre Julliard * Copyright 2019 Zhiyi Zhang for CodeWeavers * Copyright 2021 Jacek Caban for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #if 0 #pragma makedep unix #endif #include #include #include "ntstatus.h" #define WIN32_NO_STATUS #include "ntgdi_private.h" #include "ntuser_private.h" #include "winreg.h" #include "cfgmgr32.h" #include "d3dkmdt.h" #include "wine/wingdi16.h" #include "wine/server.h" WINE_DEFAULT_DEBUG_CHANNEL(system); static HKEY video_key, enum_key, control_key, config_key, volatile_base_key; static const char devicemap_video_keyA[] = "\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO"; static const char enum_keyA[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Enum"; static const char control_keyA[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Control"; static const char config_keyA[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\Current"; static const char devpropkey_gpu_vulkan_uuidA[] = "Properties\\{233A9EF3-AFC4-4ABD-B564-C32F21F1535C}\\0002"; static const char devpropkey_gpu_luidA[] = "Properties\\{60B193CB-5276-4D0F-96FC-F173ABAD3EC6}\\0002"; static const char devpkey_device_matching_device_id[] = "Properties\\{A8B865DD-2E3D-4094-AD97-E593A70C75D6}\\0008"; static const char devpkey_device_bus_number[] = "Properties\\{A45C254E-DF1C-4EFD-8020-67D146A850E0}\\0017"; static const char devpkey_device_removal_policy[] = "Properties\\{A45C254E-DF1C-4EFD-8020-67D146A850E0}\\0021"; static const char devpropkey_device_ispresentA[] = "Properties\\{540B947E-8B40-45BC-A8A2-6A0B894CBDA2}\\0005"; static const char devpropkey_monitor_gpu_luidA[] = "Properties\\{CA085853-16CE-48AA-B114-DE9C72334223}\\0001"; static const char devpropkey_monitor_output_idA[] = "Properties\\{CA085853-16CE-48AA-B114-DE9C72334223}\\0002"; static const char wine_devpropkey_monitor_rcmonitorA[] = "Properties\\{233a9ef3-afc4-4abd-b564-c32f21f1535b}\\0003"; static const char wine_devpropkey_monitor_rcworkA[] = "Properties\\{233a9ef3-afc4-4abd-b564-c32f21f1535b}\\0004"; static const WCHAR linkedW[] = {'L','i','n','k','e','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 state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0}; static const WCHAR hardware_idW[] = {'H','a','r','d','w','a','r','e','I','D',0}; static const WCHAR device_descW[] = {'D','e','v','i','c','e','D','e','s','c',0}; static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0}; static const WCHAR yesW[] = {'Y','e','s',0}; static const WCHAR noW[] = {'N','o',0}; static const WCHAR mode_countW[] = {'M','o','d','e','C','o','u','n','t',0}; static const char guid_devclass_displayA[] = "{4D36E968-E325-11CE-BFC1-08002BE10318}"; static const WCHAR guid_devclass_displayW[] = {'{','4','D','3','6','E','9','6','8','-','E','3','2','5','-','1','1','C','E','-', 'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0}; static const char guid_devclass_monitorA[] = "{4D36E96E-E325-11CE-BFC1-08002BE10318}"; static const WCHAR guid_devclass_monitorW[] = {'{','4','D','3','6','E','9','6','E','-','E','3','2','5','-','1','1','C','E','-', 'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0}; static const char guid_devinterface_display_adapterA[] = "{5B45201D-F2F2-4F3B-85BB-30FF1F953599}"; static const char guid_display_device_arrivalA[] = "{1CA05180-A699-450A-9A0C-DE4FBE3DDD89}"; static const char guid_devinterface_monitorA[] = "{E6F07B5F-EE97-4A90-B076-33F57BF4EAA7}"; #define NEXT_DEVMODEW(mode) ((DEVMODEW *)((char *)((mode) + 1) + (mode)->dmDriverExtra)) struct pci_id { UINT16 vendor; UINT16 device; UINT16 subsystem; UINT16 revision; }; struct gpu { LONG refcount; struct list entry; char path[MAX_PATH]; WCHAR name[128]; char guid[39]; LUID luid; UINT index; GUID vulkan_uuid; UINT adapter_count; }; struct adapter { LONG refcount; struct list entry; unsigned int id; struct gpu *gpu; UINT state_flags; WCHAR config_key[MAX_PATH]; UINT monitor_count; UINT mode_count; DEVMODEW *modes; }; #define MONITOR_INFO_HAS_MONITOR_ID 0x00000001 #define MONITOR_INFO_HAS_MONITOR_NAME 0x00000002 #define MONITOR_INFO_HAS_PREFERRED_MODE 0x00000004 struct edid_monitor_info { unsigned int flags; /* MONITOR_INFO_HAS_MONITOR_ID */ unsigned short manufacturer, product_code; char monitor_id_string[8]; /* MONITOR_INFO_HAS_MONITOR_NAME */ WCHAR monitor_name[14]; /* MONITOR_INFO_HAS_PREFERRED_MODE */ unsigned int preferred_width, preferred_height; }; struct monitor { struct list entry; char path[MAX_PATH]; struct adapter *adapter; HANDLE handle; unsigned int id; unsigned int output_id; RECT rc_monitor; RECT rc_work; BOOL is_clone; struct edid_monitor_info edid_info; }; static struct list gpus = LIST_INIT(gpus); static struct list adapters = LIST_INIT(adapters); static struct list monitors = LIST_INIT(monitors); static INT64 last_query_display_time; static pthread_mutex_t display_lock = PTHREAD_MUTEX_INITIALIZER; BOOL enable_thunk_lock = FALSE; #define VIRTUAL_HMONITOR ((HMONITOR)(UINT_PTR)(0x10000 + 1)) static struct monitor virtual_monitor = { .handle = VIRTUAL_HMONITOR, .rc_monitor.right = 1024, .rc_monitor.bottom = 768, .rc_work.right = 1024, .rc_work.bottom = 768, }; /* the various registry keys that are used to store parameters */ enum parameter_key { COLORS_KEY, DESKTOP_KEY, KEYBOARD_KEY, MOUSE_KEY, METRICS_KEY, SOUND_KEY, VERSION_KEY, SHOWSOUNDS_KEY, KEYBOARDPREF_KEY, SCREENREADER_KEY, AUDIODESC_KEY, NB_PARAM_KEYS }; static const char *parameter_key_names[NB_PARAM_KEYS] = { "Control Panel\\Colors", "Control Panel\\Desktop", "Control Panel\\Keyboard", "Control Panel\\Mouse", "Control Panel\\Desktop\\WindowMetrics", "Control Panel\\Sound", "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows", "Control Panel\\Accessibility\\ShowSounds", "Control Panel\\Accessibility\\Keyboard Preference", "Control Panel\\Accessibility\\Blind Access", "Control Panel\\Accessibility\\AudioDescription", }; /* System parameters storage */ union sysparam_all_entry; struct sysparam_entry { BOOL (*get)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ); BOOL (*set)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ); BOOL (*init)( union sysparam_all_entry *entry ); enum parameter_key base_key; const char *regval; enum parameter_key mirror_key; const char *mirror; BOOL loaded; }; struct sysparam_uint_entry { struct sysparam_entry hdr; UINT val; }; struct sysparam_bool_entry { struct sysparam_entry hdr; BOOL val; }; struct sysparam_dword_entry { struct sysparam_entry hdr; DWORD val; }; struct sysparam_rgb_entry { struct sysparam_entry hdr; COLORREF val; HBRUSH brush; HPEN pen; }; struct sysparam_binary_entry { struct sysparam_entry hdr; void *ptr; size_t size; }; struct sysparam_path_entry { struct sysparam_entry hdr; WCHAR *path; }; struct sysparam_font_entry { struct sysparam_entry hdr; UINT weight; LOGFONTW val; WCHAR fullname[LF_FACESIZE]; }; struct sysparam_pref_entry { struct sysparam_entry hdr; union sysparam_all_entry *parent; UINT offset; UINT mask; }; union sysparam_all_entry { struct sysparam_entry hdr; struct sysparam_uint_entry uint; struct sysparam_bool_entry bool; struct sysparam_dword_entry dword; struct sysparam_rgb_entry rgb; struct sysparam_binary_entry bin; struct sysparam_path_entry path; struct sysparam_font_entry font; struct sysparam_pref_entry pref; }; static UINT system_dpi; static RECT work_area; static DWORD process_layout = ~0u; static HDC display_dc; static pthread_mutex_t display_dc_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t user_mutex; static unsigned int user_lock_thread, user_lock_rec; void user_lock(void) { pthread_mutex_lock( &user_mutex ); if (!user_lock_rec++) user_lock_thread = GetCurrentThreadId(); } void user_unlock(void) { if (!--user_lock_rec) user_lock_thread = 0; pthread_mutex_unlock( &user_mutex ); } void user_check_not_lock(void) { if (user_lock_thread == GetCurrentThreadId()) { ERR( "BUG: holding USER lock\n" ); assert( 0 ); } } static HANDLE get_display_device_init_mutex( void ) { WCHAR bufferW[256]; UNICODE_STRING name = {.Buffer = bufferW}; OBJECT_ATTRIBUTES attr; char buffer[256]; HANDLE mutex; snprintf( buffer, ARRAY_SIZE(buffer), "\\Sessions\\%u\\BaseNamedObjects\\display_device_init", (int)NtCurrentTeb()->Peb->SessionId ); name.MaximumLength = asciiz_to_unicode( bufferW, buffer ); name.Length = name.MaximumLength - sizeof(WCHAR); InitializeObjectAttributes( &attr, &name, OBJ_OPENIF, NULL, NULL ); if (NtCreateMutant( &mutex, MUTEX_ALL_ACCESS, &attr, FALSE ) < 0) return 0; NtWaitForSingleObject( mutex, FALSE, NULL ); return mutex; } static void release_display_device_init_mutex( HANDLE mutex ) { NtReleaseMutant( mutex, NULL ); NtClose( mutex ); } static struct gpu *gpu_acquire( struct gpu *gpu ) { InterlockedIncrement( &gpu->refcount ); return gpu; } static void gpu_release( struct gpu *gpu ) { if (!InterlockedDecrement( &gpu->refcount )) free( gpu ); } static struct adapter *adapter_acquire( struct adapter *adapter ) { InterlockedIncrement( &adapter->refcount ); return adapter; } static void adapter_release( struct adapter *adapter ) { if (!InterlockedDecrement( &adapter->refcount )) { gpu_release( adapter->gpu ); free( adapter->modes ); free( adapter ); } } C_ASSERT(sizeof(DEVMODEW) - offsetof(DEVMODEW, dmFields) == 0x94); static void get_monitor_info_from_edid( struct edid_monitor_info *info, const unsigned char *edid, unsigned int edid_len ) { unsigned int i, j; unsigned short w; unsigned char d; const char *s; info->flags = 0; if (!edid || edid_len < 128) return; w = (edid[8] << 8) | edid[9]; /* Manufacturer ID, big endian. */ for (i = 0; i < 3; ++i) { d = w & 0x1f; if (!d || d - 1 > 'Z' - 'A') return; info->monitor_id_string[2 - i] = 'A' + d - 1; w >>= 5; } if (w) return; w = edid[10] | (edid[11] << 8); /* Product code, little endian. */ info->manufacturer = *(unsigned short *)(edid + 8); info->product_code = w; sprintf( info->monitor_id_string + 3, "%04X", w ); info->flags = MONITOR_INFO_HAS_MONITOR_ID; TRACE( "Monitor id %s.\n", info->monitor_id_string ); for (i = 0; i < 4; ++i) { if (edid[54 + i * 18] || edid[54 + i * 18 + 1]) { /* Detailed timing descriptor. */ if (info->flags & MONITOR_INFO_HAS_PREFERRED_MODE) continue; info->preferred_width = edid[54 + i * 18 + 2] | ((UINT32)(edid[54 + i * 18 + 4] & 0xf0) << 4); info->preferred_height = edid[54 + i * 18 + 5] | ((UINT32)(edid[54 + i * 18 + 7] & 0xf0) << 4); if (info->preferred_width && info->preferred_height) info->flags |= MONITOR_INFO_HAS_PREFERRED_MODE; continue; } if (edid[54 + i * 18 + 3] != 0xfc) continue; /* "Display name" ASCII descriptor. */ s = (const char *)&edid[54 + i * 18 + 5]; for (j = 0; s[j] && j < 13; ++j) info->monitor_name[j] = s[j]; while (j && isspace(s[j - 1])) --j; info->monitor_name[j] = 0; info->flags |= MONITOR_INFO_HAS_MONITOR_NAME; break; } } static BOOL write_adapter_mode( HKEY adapter_key, UINT index, const DEVMODEW *mode ) { WCHAR bufferW[MAX_PATH] = {0}; char buffer[MAX_PATH]; sprintf( buffer, "Modes\\%08X", index ); asciiz_to_unicode( bufferW, buffer ); return set_reg_value( adapter_key, bufferW, REG_BINARY, &mode->dmFields, sizeof(*mode) - offsetof(DEVMODEW, dmFields) ); } static BOOL read_adapter_mode( HKEY adapter_key, UINT index, DEVMODEW *mode ) { char value_buf[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(*mode)])]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)value_buf; char buffer[MAX_PATH]; sprintf( buffer, "Modes\\%08X", index ); if (!query_reg_ascii_value( adapter_key, buffer, value, sizeof(value_buf) )) return FALSE; memcpy( &mode->dmFields, value->Data, sizeof(*mode) - offsetof(DEVMODEW, dmFields) ); return TRUE; } static BOOL adapter_get_registry_settings( const struct adapter *adapter, DEVMODEW *mode ) { BOOL ret = FALSE; HANDLE mutex; HKEY hkey; mutex = get_display_device_init_mutex(); if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE; else { ret = read_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode ); NtClose( hkey ); } release_display_device_init_mutex( mutex ); return ret; } static BOOL adapter_set_registry_settings( const struct adapter *adapter, const DEVMODEW *mode ) { HANDLE mutex; HKEY hkey; BOOL ret; mutex = get_display_device_init_mutex(); if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE; else { ret = write_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode ); NtClose( hkey ); } release_display_device_init_mutex( mutex ); return ret; } static BOOL adapter_get_current_settings( const struct adapter *adapter, DEVMODEW *mode ) { BOOL is_primary = !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE); WCHAR device_nameW[CCHDEVICENAME]; char device_name[CCHDEVICENAME]; HANDLE mutex; HKEY hkey; BOOL ret; sprintf( device_name, "\\\\.\\DISPLAY%d", adapter->id + 1 ); asciiz_to_unicode( device_nameW, device_name ); /* use the default implementation in virtual desktop mode */ if (is_virtual_desktop()) ret = FALSE; else ret = user_driver->pGetCurrentDisplaySettings( device_nameW, is_primary, mode ); if (ret) return TRUE; /* default implementation: read current display settings from the registry. */ mutex = get_display_device_init_mutex(); if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE; else { ret = read_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode ); NtClose( hkey ); } release_display_device_init_mutex( mutex ); return ret; } static BOOL adapter_set_current_settings( const struct adapter *adapter, const DEVMODEW *mode ) { HANDLE mutex; HKEY hkey; BOOL ret; mutex = get_display_device_init_mutex(); if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE; else { ret = write_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode ); NtClose( hkey ); } release_display_device_init_mutex( mutex ); return ret; } static int mode_compare(const void *p1, const void *p2) { BOOL a_interlaced, b_interlaced, a_stretched, b_stretched; DWORD a_width, a_height, b_width, b_height; const DEVMODEW *a = p1, *b = p2; int ret; /* Depth in descending order */ if ((ret = b->dmBitsPerPel - a->dmBitsPerPel)) return ret; /* Use the width and height in landscape mode for comparison */ if (a->dmDisplayOrientation == DMDO_DEFAULT || a->dmDisplayOrientation == DMDO_180) { a_width = a->dmPelsWidth; a_height = a->dmPelsHeight; } else { a_width = a->dmPelsHeight; a_height = a->dmPelsWidth; } if (b->dmDisplayOrientation == DMDO_DEFAULT || b->dmDisplayOrientation == DMDO_180) { b_width = b->dmPelsWidth; b_height = b->dmPelsHeight; } else { b_width = b->dmPelsHeight; b_height = b->dmPelsWidth; } /* Width in ascending order */ if ((ret = a_width - b_width)) return ret; /* Height in ascending order */ if ((ret = a_height - b_height)) return ret; /* Frequency in descending order */ if ((ret = b->dmDisplayFrequency - a->dmDisplayFrequency)) return ret; /* Orientation in ascending order */ if ((ret = a->dmDisplayOrientation - b->dmDisplayOrientation)) return ret; if (!(a->dmFields & DM_DISPLAYFLAGS)) a_interlaced = FALSE; else a_interlaced = !!(a->dmDisplayFlags & DM_INTERLACED); if (!(b->dmFields & DM_DISPLAYFLAGS)) b_interlaced = FALSE; else b_interlaced = !!(b->dmDisplayFlags & DM_INTERLACED); /* Interlaced in ascending order */ if ((ret = a_interlaced - b_interlaced)) return ret; if (!(a->dmFields & DM_DISPLAYFIXEDOUTPUT)) a_stretched = FALSE; else a_stretched = a->dmDisplayFixedOutput == DMDFO_STRETCH; if (!(b->dmFields & DM_DISPLAYFIXEDOUTPUT)) b_stretched = FALSE; else b_stretched = b->dmDisplayFixedOutput == DMDFO_STRETCH; /* Stretched in ascending order */ if ((ret = a_stretched - b_stretched)) return ret; return 0; } static unsigned int query_reg_subkey_value( HKEY hkey, const char *name, KEY_VALUE_PARTIAL_INFORMATION *value, unsigned int size ) { HKEY subkey; if (!(subkey = reg_open_ascii_key( hkey, name ))) return 0; size = query_reg_value( subkey, NULL, value, size ); NtClose( subkey ); return size; } static BOOL read_display_adapter_settings( unsigned int index, struct adapter *info, char *gpu_path ) { char buffer[4096]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; WCHAR *value_str = (WCHAR *)value->Data; DEVMODEW *mode; DWORD i, size; HKEY hkey; if (!enum_key && !(enum_key = reg_open_ascii_key( NULL, enum_keyA ))) return FALSE; /* Find adapter */ sprintf( buffer, "\\Device\\Video%d", index ); size = query_reg_ascii_value( video_key, buffer, value, sizeof(buffer) ); if (!size || value->Type != REG_SZ || value->DataLength <= sizeof("\\Registry\\Machine\\") * sizeof(WCHAR)) return FALSE; /* DeviceKey */ size = sizeof("\\Registry\\Machine\\") - 1; memcpy( info->config_key, value_str + size, value->DataLength - size ); if (!(hkey = reg_open_key( config_key, info->config_key, lstrlenW( info->config_key ) * sizeof(WCHAR) ))) return FALSE; /* StateFlags */ if (query_reg_ascii_value( hkey, "StateFlags", value, sizeof(buffer) ) && value->Type == REG_DWORD) info->state_flags = *(const DWORD *)value->Data; /* ModeCount */ if (query_reg_ascii_value( hkey, "ModeCount", value, sizeof(buffer) ) && value->Type == REG_DWORD) info->mode_count = *(const DWORD *)value->Data; /* Modes, allocate an extra mode for easier iteration */ if ((info->modes = calloc( info->mode_count + 1, sizeof(DEVMODEW) ))) { for (i = 0, mode = info->modes; i < info->mode_count; i++) { mode->dmSize = offsetof(DEVMODEW, dmICMMethod); if (!read_adapter_mode( hkey, i, mode )) break; mode = NEXT_DEVMODEW(mode); } info->mode_count = i; qsort(info->modes, info->mode_count, sizeof(*info->modes) + info->modes->dmDriverExtra, mode_compare); } /* DeviceID */ size = query_reg_ascii_value( hkey, "GPUID", value, sizeof(buffer) ); NtClose( hkey ); if (!size || value->Type != REG_SZ || !info->mode_count || !info->modes) return FALSE; for (i = 0; i < value->DataLength / sizeof(WCHAR); i++) gpu_path[i] = value_str[i]; return TRUE; } static BOOL read_monitor_from_registry( struct monitor *monitor ) { char buffer[4096]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; HKEY hkey, subkey; DWORD size; if (!(hkey = reg_open_ascii_key( enum_key, monitor->path ))) return FALSE; /* Output ID */ size = query_reg_subkey_value( hkey, devpropkey_monitor_output_idA, value, sizeof(buffer) ); if (size != sizeof(monitor->output_id)) { NtClose( hkey ); return FALSE; } monitor->output_id = *(const unsigned int *)value->Data; /* rc_monitor, WINE_DEVPROPKEY_MONITOR_RCMONITOR */ size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcmonitorA, value, sizeof(buffer) ); if (size != sizeof(monitor->rc_monitor)) { NtClose( hkey ); return FALSE; } monitor->rc_monitor = *(const RECT *)value->Data; /* rc_work, WINE_DEVPROPKEY_MONITOR_RCWORK */ size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcworkA, value, sizeof(buffer) ); if (size != sizeof(monitor->rc_work)) { NtClose( hkey ); return FALSE; } monitor->rc_work = *(const RECT *)value->Data; /* EDID */ if ((subkey = reg_open_ascii_key( hkey, "Device Parameters" ))) { if (query_reg_ascii_value( subkey, "EDID", value, sizeof(buffer) )) get_monitor_info_from_edid( &monitor->edid_info, value->Data, value->DataLength ); NtClose( subkey ); } NtClose( hkey ); return TRUE; } static BOOL read_adapter_monitor_path( HKEY hkey, UINT index, char *path ) { char buffer[4096]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; WCHAR *value_str = (WCHAR *)value->Data; DWORD size; UINT i; sprintf( buffer, "MonitorID%u", index ); size = query_reg_ascii_value( hkey, buffer, value, sizeof(buffer) ); if (!size || value->Type != REG_SZ) return FALSE; for (i = 0; i < value->DataLength / sizeof(WCHAR); i++) path[i] = value_str[i]; return TRUE; } static void reg_empty_key( HKEY root, const char *key_name ) { char buffer[4096]; KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer; KEY_VALUE_FULL_INFORMATION *value = (KEY_VALUE_FULL_INFORMATION *)buffer; DWORD size; HKEY hkey = key_name ? reg_open_ascii_key( root, key_name ) : root; while (!NtEnumerateKey( hkey, 0, KeyNodeInformation, key, sizeof(buffer), &size )) reg_delete_tree( hkey, key->Name, key->NameLength ); while (!NtEnumerateValueKey( hkey, 0, KeyValueFullInformation, value, sizeof(buffer), &size )) { UNICODE_STRING name = { value->NameLength, value->NameLength, value->Name }; NtDeleteValueKey( hkey, &name ); } if (hkey != root) NtClose( hkey ); } static void prepare_devices(void) { char buffer[4096]; KEY_NODE_INFORMATION *key = (void *)buffer; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; WCHAR *value_str = (WCHAR *)value->Data; unsigned i = 0; DWORD size; HKEY hkey, subkey, device_key, prop_key; if (!enum_key) enum_key = reg_create_ascii_key( NULL, enum_keyA, 0, NULL ); if (!control_key) control_key = reg_create_ascii_key( NULL, control_keyA, 0, NULL ); if (!video_key) video_key = reg_create_ascii_key( NULL, devicemap_video_keyA, REG_OPTION_VOLATILE, NULL ); /* delete monitors */ reg_empty_key( enum_key, "DISPLAY" ); sprintf( buffer, "Class\\%s", guid_devclass_monitorA ); hkey = reg_create_ascii_key( control_key, buffer, 0, NULL ); reg_empty_key( hkey, NULL ); set_reg_ascii_value( hkey, "Class", "Monitor" ); NtClose( hkey ); /* delete adapters */ reg_empty_key( video_key, NULL ); /* clean GPUs */ sprintf( buffer, "Class\\%s", guid_devclass_displayA ); hkey = reg_create_ascii_key( control_key, buffer, 0, NULL ); reg_empty_key( hkey, NULL ); set_reg_ascii_value( hkey, "Class", "Display" ); NtClose( hkey ); hkey = reg_open_ascii_key( enum_key, "PCI" ); /* To preserve GPU GUIDs, mark them as not present and delete them in cleanup_devices if needed. */ while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size )) { unsigned int j = 0; if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue; while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size )) { if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue; size = query_reg_ascii_value( device_key, "ClassGUID", value, sizeof(buffer) ); if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW )) { NtClose( device_key ); continue; } if ((prop_key = reg_create_ascii_key( device_key, devpropkey_device_ispresentA, 0, NULL ))) { BOOL present = FALSE; set_reg_value( prop_key, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN, &present, sizeof(present) ); NtClose( prop_key ); } NtClose( device_key ); } NtClose( subkey ); } NtClose( hkey ); } static void cleanup_devices(void) { char buffer[4096]; KEY_NODE_INFORMATION *key = (void *)buffer; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; WCHAR bufferW[512], *value_str = (WCHAR *)value->Data; unsigned i = 0; DWORD size; HKEY hkey, subkey, device_key, prop_key; hkey = reg_open_ascii_key( enum_key, "PCI" ); while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size )) { unsigned int j = 0; if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue; while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size )) { BOOL present = FALSE; if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue; memcpy( bufferW, key->Name, key->NameLength ); bufferW[key->NameLength / sizeof(WCHAR)] = 0; size = query_reg_ascii_value( device_key, "ClassGUID", value, sizeof(buffer) ); if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW )) { NtClose( device_key ); continue; } if ((prop_key = reg_open_ascii_key( device_key, devpropkey_device_ispresentA ))) { if (query_reg_value( prop_key, NULL, value, sizeof(buffer) ) == sizeof(BOOL)) present = *(const BOOL *)value->Data; NtClose( prop_key ); } NtClose( device_key ); if (!present && reg_delete_tree( subkey, bufferW, lstrlenW( bufferW ) * sizeof(WCHAR) )) j = 0; } NtClose( subkey ); } NtClose( hkey ); } /* see UuidCreate */ static void uuid_create( GUID *uuid ) { char buf[4096]; NtQuerySystemInformation( SystemInterruptInformation, buf, sizeof(buf), NULL ); memcpy( uuid, buf, sizeof(*uuid) ); uuid->Data3 &= 0x0fff; uuid->Data3 |= (4 << 12); uuid->Data4[0] &= 0x3f; uuid->Data4[0] |= 0x80; } #define TICKSPERSEC 10000000 #define SECSPERDAY 86400 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97) #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1) static unsigned int format_date( WCHAR *bufferW, LONGLONG time ) { int cleaps, years, yearday, months, days; unsigned int day, month, year; char buffer[32]; days = time / TICKSPERSEC / SECSPERDAY; /* compute year, month and day of month, see RtlTimeToTimeFields */ cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4; days += 28188 + cleaps; years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM); yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4; months = (64 * yearday) / 1959; if (months < 14) { month = months - 1; year = years + 1524; } else { month = months - 13; year = years + 1525; } day = yearday - (1959 * months) / 64 ; sprintf( buffer, "%u-%u-%u", month, day, year ); return asciiz_to_unicode( bufferW, buffer ); } struct device_manager_ctx { UINT gpu_count; UINT adapter_count; UINT monitor_count; HANDLE mutex; struct gpu gpu; struct adapter adapter; HKEY adapter_key; /* for the virtual desktop settings */ BOOL is_primary; UINT primary_bpp; UINT primary_width; UINT primary_height; }; static void link_device( const char *instance, const char *class ) { char buffer[MAX_PATH], *ptr; HKEY hkey, subkey; unsigned int pos; pos = snprintf( buffer, ARRAY_SIZE(buffer), "DeviceClasses\\%s\\", class ); snprintf( buffer + pos, ARRAY_SIZE(buffer) - pos, "##?#%s#%s", instance, class ); for (ptr = buffer + pos; *ptr; ptr++) if (*ptr == '\\') *ptr = '#'; hkey = reg_create_ascii_key( control_key, buffer, 0, NULL ); set_reg_ascii_value( hkey, "DeviceInstance", instance ); subkey = reg_create_ascii_key( hkey, "#", REG_OPTION_VOLATILE, NULL ); NtClose( hkey ); hkey = subkey; snprintf( buffer, ARRAY_SIZE(buffer), "\\\\?\\%s#%s", instance, class ); for (ptr = buffer + 4; *ptr; ptr++) if (*ptr == '\\') *ptr = '#'; set_reg_ascii_value( hkey, "SymbolicLink", buffer ); if ((subkey = reg_create_ascii_key( hkey, "Control", REG_OPTION_VOLATILE, NULL ))) { const DWORD linked = 1; set_reg_value( subkey, linkedW, REG_DWORD, &linked, sizeof(linked) ); NtClose( subkey ); } } static BOOL read_gpu_from_registry( struct gpu *gpu ) { char buffer[1024]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; WCHAR *value_str = (WCHAR *)value->Data; HKEY hkey, subkey; unsigned int i; if (!(hkey = reg_open_ascii_key( enum_key, gpu->path ))) return FALSE; if (query_reg_ascii_value( hkey, "Driver", value, sizeof(buffer) ) && value->Type == REG_SZ) gpu->index = wcstoul( wcsrchr( value_str, '\\' ) + 1, NULL, 16 ); if (query_reg_ascii_value( hkey, "DeviceDesc", value, sizeof(buffer) ) && value->Type == REG_SZ) memcpy( gpu->name, value->Data, value->DataLength ); if ((subkey = reg_open_ascii_key( hkey, devpropkey_gpu_luidA ))) { if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) == sizeof(LUID)) gpu->luid = *(const LUID *)value->Data; NtClose( subkey ); } if ((subkey = reg_create_ascii_key( hkey, "Device Parameters", 0, NULL ))) { if (query_reg_ascii_value( subkey, "VideoID", value, sizeof(buffer) ) == sizeof(gpu->guid) * sizeof(WCHAR)) { WCHAR *guidW = (WCHAR *)value->Data; for (i = 0; i < sizeof(gpu->guid); i++) gpu->guid[i] = guidW[i]; TRACE( "got guid %s\n", debugstr_a(gpu->guid) ); } NtClose( subkey ); } if ((subkey = reg_open_ascii_key( hkey, devpropkey_gpu_vulkan_uuidA ))) { if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) == sizeof(GUID)) gpu->vulkan_uuid = *(const GUID *)value->Data; NtClose( subkey ); } NtClose( hkey ); return TRUE; } static BOOL write_gpu_to_registry( const struct gpu *gpu, const struct pci_id *pci, ULONGLONG memory_size ) { const WCHAR *desc; char buffer[4096], *tmp; WCHAR bufferW[512]; unsigned int size; HKEY subkey; LARGE_INTEGER ft; ULONG value; HKEY hkey; static const BOOL present = TRUE; static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0}; static const WCHAR driver_date_dataW[] = {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0}; static const WCHAR adapter_stringW[] = {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n', '.','A','d','a','p','t','e','r','S','t','r','i','n','g',0}; static const WCHAR bios_stringW[] = {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.', 'B','i','o','s','S','t','r','i','n','g',0}; static const WCHAR chip_typeW[] = {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.', 'C','h','i','p','T','y','p','e',0}; static const WCHAR qw_memory_sizeW[] = {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.', 'q','w','M','e','m','o','r','y','S','i','z','e',0}; static const WCHAR memory_sizeW[] = {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.', 'M','e','m','o','r','y','S','i','z','e',0}; static const WCHAR dac_typeW[] = {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.', 'D','a','c','T','y','p','e',0}; static const WCHAR ramdacW[] = {'I','n','t','e','r','g','r','a','t','e','d',' ','R','A','M','D','A','C',0}; static const WCHAR driver_dateW[] = {'D','r','i','v','e','r','D','a','t','e',0}; if (!(hkey = reg_create_ascii_key( enum_key, gpu->path, 0, NULL ))) return FALSE; set_reg_ascii_value( hkey, "Class", "Display" ); set_reg_ascii_value( hkey, "ClassGUID", guid_devclass_displayA ); sprintf( buffer, "%s\\%04X", guid_devclass_displayA, gpu->index ); set_reg_ascii_value( hkey, "Driver", buffer ); strcpy( buffer, gpu->path ); if ((tmp = strrchr( buffer, '\\' ))) *tmp = 0; size = asciiz_to_unicode( bufferW, buffer ); bufferW[size / sizeof(WCHAR)] = 0; /* for REG_MULTI_SZ */ set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, size + sizeof(WCHAR) ); if ((subkey = reg_create_ascii_key( hkey, devpkey_device_matching_device_id, 0, NULL ))) { if (pci->vendor && pci->device) set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW, size ); else set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW, asciiz_to_unicode( bufferW, "ROOT\\BasicRender" )); NtClose( subkey ); } if (pci->vendor && pci->device) { if ((subkey = reg_create_ascii_key( hkey, devpkey_device_bus_number, 0, NULL ))) { set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32, &gpu->index, sizeof(gpu->index) ); NtClose( subkey ); } } if ((subkey = reg_create_ascii_key( hkey, devpkey_device_removal_policy, 0, NULL ))) { unsigned int removal_policy = CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL; set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32, &removal_policy, sizeof(removal_policy) ); NtClose( subkey ); } desc = gpu->name; if (!desc[0]) desc = wine_adapterW; set_reg_value( hkey, device_descW, REG_SZ, desc, (lstrlenW( desc ) + 1) * sizeof(WCHAR) ); if ((subkey = reg_create_ascii_key( hkey, "Device Parameters", 0, NULL ))) { set_reg_ascii_value( subkey, "VideoID", gpu->guid ); NtClose( subkey ); } if ((subkey = reg_create_ascii_key( hkey, devpropkey_gpu_vulkan_uuidA, 0, NULL ))) { set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_GUID, &gpu->vulkan_uuid, sizeof(gpu->vulkan_uuid) ); NtClose( subkey ); } if ((subkey = reg_create_ascii_key( hkey, devpropkey_device_ispresentA, 0, NULL ))) { set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN, &present, sizeof(present) ); NtClose( subkey ); } if ((subkey = reg_create_ascii_key( hkey, devpropkey_gpu_luidA, 0, NULL ))) { set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT64, &gpu->luid, sizeof(gpu->luid) ); NtClose( subkey ); } NtClose( hkey ); sprintf( buffer, "Class\\%s\\%04X", guid_devclass_displayA, gpu->index ); if (!(hkey = reg_create_ascii_key( control_key, buffer, 0, NULL ))) return FALSE; NtQuerySystemTime( &ft ); set_reg_value( hkey, driver_dateW, REG_SZ, bufferW, format_date( bufferW, ft.QuadPart )); set_reg_value( hkey, driver_date_dataW, REG_BINARY, &ft, sizeof(ft) ); size = (lstrlenW( desc ) + 1) * sizeof(WCHAR); set_reg_value( hkey, driver_descW, REG_SZ, desc, size ); set_reg_value( hkey, adapter_stringW, REG_SZ, desc, size ); set_reg_value( hkey, bios_stringW, REG_SZ, desc, size ); set_reg_value( hkey, chip_typeW, REG_SZ, desc, size ); set_reg_value( hkey, dac_typeW, REG_SZ, ramdacW, sizeof(ramdacW) ); /* If we failed to retrieve the gpu memory size set a default of 1Gb */ if (!memory_size) memory_size = 1073741824; set_reg_value( hkey, qw_memory_sizeW, REG_QWORD, &memory_size, sizeof(memory_size) ); value = (ULONG)min( memory_size, (ULONGLONG)ULONG_MAX ); set_reg_value( hkey, memory_sizeW, REG_DWORD, &value, sizeof(value) ); if (pci->vendor && pci->device) { /* The last seven digits are the driver number. */ switch (pci->vendor) { /* Intel */ case 0x8086: sprintf( buffer, "31.0.101.4576" ); break; /* AMD */ case 0x1002: sprintf( buffer, "31.0.14051.5006" ); break; /* Nvidia */ case 0x10de: sprintf( buffer, "31.0.15.3625" ); break; /* Default value for any other vendor. */ default: sprintf( buffer, "31.0.10.1000" ); break; } set_reg_ascii_value( hkey, "DriverVersion", buffer ); } NtClose( hkey ); link_device( gpu->path, guid_devinterface_display_adapterA ); link_device( gpu->path, guid_display_device_arrivalA ); return TRUE; } static void add_gpu( const struct gdi_gpu *gpu, void *param ) { const struct pci_id pci_id = { .vendor = gpu->vendor_id, .device = gpu->device_id, .subsystem = gpu->subsys_id, .revision = gpu->revision_id, }; struct device_manager_ctx *ctx = param; char buffer[4096]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; unsigned int i; HKEY hkey, subkey; TRACE( "%s %04X %04X %08X %02X\n", debugstr_w(gpu->name), gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id ); if (!enum_key && !(enum_key = reg_create_ascii_key( NULL, enum_keyA, 0, NULL ))) return; if (!ctx->mutex) { pthread_mutex_lock( &display_lock ); ctx->mutex = get_display_device_init_mutex(); prepare_devices(); } memset( &ctx->gpu, 0, sizeof(ctx->gpu) ); ctx->gpu.index = ctx->gpu_count; lstrcpyW( ctx->gpu.name, gpu->name ); ctx->gpu.vulkan_uuid = gpu->vulkan_uuid; sprintf( ctx->gpu.path, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X\\%08X", gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, ctx->gpu.index ); if (!(hkey = reg_create_ascii_key( enum_key, ctx->gpu.path, 0, NULL ))) return; if ((subkey = reg_create_ascii_key( hkey, "Device Parameters", 0, NULL ))) { if (query_reg_ascii_value( subkey, "VideoID", value, sizeof(buffer) ) != sizeof(ctx->gpu.guid) * sizeof(WCHAR)) { GUID guid; uuid_create( &guid ); sprintf( ctx->gpu.guid, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", (unsigned int)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] ); TRACE( "created guid %s\n", debugstr_a(ctx->gpu.guid) ); } else { WCHAR *guidW = (WCHAR *)value->Data; for (i = 0; i < sizeof(ctx->gpu.guid); i++) ctx->gpu.guid[i] = guidW[i]; TRACE( "got guid %s\n", debugstr_a(ctx->gpu.guid) ); } NtClose( subkey ); } if ((subkey = reg_create_ascii_key( hkey, devpropkey_gpu_luidA, 0, NULL ))) { if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) != sizeof(LUID)) { NtAllocateLocallyUniqueId( &ctx->gpu.luid ); TRACE("allocated luid %08x%08x\n", (int)ctx->gpu.luid.HighPart, (int)ctx->gpu.luid.LowPart ); } else { memcpy( &ctx->gpu.luid, value->Data, sizeof(ctx->gpu.luid) ); TRACE("got luid %08x%08x\n", (int)ctx->gpu.luid.HighPart, (int)ctx->gpu.luid.LowPart ); } NtClose( subkey ); } NtClose( hkey ); if (!write_gpu_to_registry( &ctx->gpu, &pci_id, gpu->memory_size )) WARN( "Failed to write gpu to registry\n" ); else ctx->gpu_count++; } static void add_adapter( const struct gdi_adapter *adapter, void *param ) { struct device_manager_ctx *ctx = param; unsigned int adapter_index, len; char name[64], buffer[MAX_PATH]; WCHAR bufferW[MAX_PATH]; HKEY hkey; TRACE( "\n" ); if (ctx->adapter_key) { NtClose( ctx->adapter_key ); ctx->adapter_key = NULL; } adapter_index = ctx->gpu.adapter_count++; memset( &ctx->adapter, 0, sizeof(ctx->adapter) ); ctx->adapter.gpu = &ctx->gpu; ctx->adapter.id = ctx->adapter_count++; snprintf( buffer, ARRAY_SIZE(buffer), "%s\\Video\\%s\\%04x", control_keyA, ctx->gpu.guid, adapter_index ); len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR); hkey = reg_create_ascii_key( NULL, buffer, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, NULL ); if (!hkey) hkey = reg_create_ascii_key( NULL, buffer, REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK, NULL ); sprintf( name, "\\Device\\Video%u", ctx->adapter.id ); set_reg_ascii_value( video_key, name, buffer ); if (hkey) { sprintf( buffer, "%s\\Class\\%s\\%04X", control_keyA, guid_devclass_displayA, ctx->gpu.index ); len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR); set_reg_value( hkey, symbolic_link_valueW, REG_LINK, bufferW, len ); NtClose( hkey ); } else ERR( "failed to create link key\n" ); /* Following information is Wine specific, it doesn't really exist on Windows. */ snprintf( buffer, ARRAY_SIZE(buffer), "System\\CurrentControlSet\\Control\\Video\\%s\\%04x", ctx->gpu.guid, adapter_index ); ctx->adapter_key = reg_create_ascii_key( config_key, buffer, REG_OPTION_VOLATILE, NULL ); set_reg_ascii_value( ctx->adapter_key, "GPUID", ctx->gpu.path ); set_reg_value( ctx->adapter_key, state_flagsW, REG_DWORD, &adapter->state_flags, sizeof(adapter->state_flags) ); } static BOOL write_monitor_to_registry( struct monitor *monitor, const BYTE *edid, UINT edid_len ) { char buffer[1024], *tmp; WCHAR bufferW[1024]; HKEY hkey, subkey; unsigned int len; if (!(hkey = reg_create_ascii_key( enum_key, monitor->path, 0, NULL ))) return FALSE; set_reg_ascii_value( hkey, "DeviceDesc", "Generic Non-PnP Monitor" ); set_reg_ascii_value( hkey, "Class", "Monitor" ); sprintf( buffer, "%s\\%04X", guid_devclass_monitorA, monitor->output_id ); set_reg_ascii_value( hkey, "Driver", buffer ); set_reg_ascii_value( hkey, "ClassGUID", guid_devclass_monitorA ); sprintf( buffer, "MONITOR\\%s", monitor->path + 8 ); if ((tmp = strrchr( buffer, '\\' ))) *tmp = 0; len = asciiz_to_unicode( bufferW, buffer ); bufferW[len / sizeof(WCHAR)] = 0; set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, len + sizeof(WCHAR) ); if ((subkey = reg_create_ascii_key( hkey, "Device Parameters", 0, NULL ))) { static const WCHAR bad_edidW[] = {'B','A','D','_','E','D','I','D',0}; static const WCHAR edidW[] = {'E','D','I','D',0}; if (edid_len) set_reg_value( subkey, edidW, REG_BINARY, edid, edid_len ); else set_reg_value( subkey, bad_edidW, REG_BINARY, NULL, 0 ); NtClose( subkey ); } /* WINE_DEVPROPKEY_MONITOR_RCMONITOR */ if ((subkey = reg_create_ascii_key( hkey, wine_devpropkey_monitor_rcmonitorA, 0, NULL ))) { set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_monitor, sizeof(monitor->rc_monitor) ); NtClose( subkey ); } /* WINE_DEVPROPKEY_MONITOR_RCWORK */ if ((subkey = reg_create_ascii_key( hkey, wine_devpropkey_monitor_rcworkA, 0, NULL ))) { TRACE( "rc_work %s\n", wine_dbgstr_rect(&monitor->rc_work) ); set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_work, sizeof(monitor->rc_work) ); NtClose( subkey ); } /* DEVPROPKEY_MONITOR_GPU_LUID */ if ((subkey = reg_create_ascii_key( hkey, devpropkey_monitor_gpu_luidA, 0, NULL ))) { set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_INT64, &monitor->adapter->gpu->luid, sizeof(monitor->adapter->gpu->luid) ); NtClose( subkey ); } /* DEVPROPKEY_MONITOR_OUTPUT_ID */ if ((subkey = reg_create_ascii_key( hkey, devpropkey_monitor_output_idA, 0, NULL ))) { set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32, &monitor->output_id, sizeof(monitor->output_id) ); NtClose( subkey ); } NtClose( hkey ); sprintf( buffer, "Class\\%s\\%04X", guid_devclass_monitorA, monitor->output_id ); if (!(hkey = reg_create_ascii_key( control_key, buffer, 0, NULL ))) return FALSE; NtClose( hkey ); link_device( monitor->path, guid_devinterface_monitorA ); return TRUE; } static void add_monitor( const struct gdi_monitor *gdi_monitor, void *param ) { struct device_manager_ctx *ctx = param; struct monitor monitor = {0}; char buffer[MAX_PATH]; char monitor_id_string[16]; monitor.adapter = &ctx->adapter; monitor.id = ctx->adapter.monitor_count; monitor.output_id = ctx->monitor_count; monitor.rc_monitor = gdi_monitor->rc_monitor; monitor.rc_work = gdi_monitor->rc_work; TRACE( "%u %s %s\n", monitor.id, wine_dbgstr_rect(&gdi_monitor->rc_monitor), wine_dbgstr_rect(&gdi_monitor->rc_work) ); get_monitor_info_from_edid( &monitor.edid_info, gdi_monitor->edid, gdi_monitor->edid_len ); if (monitor.edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID) strcpy( monitor_id_string, monitor.edid_info.monitor_id_string ); else strcpy( monitor_id_string, "Default_Monitor" ); sprintf( buffer, "MonitorID%u", monitor.id ); sprintf( monitor.path, "DISPLAY\\%s\\%04X&%04X", monitor_id_string, ctx->adapter.id, monitor.id ); set_reg_ascii_value( ctx->adapter_key, buffer, monitor.path ); if (!write_monitor_to_registry( &monitor, gdi_monitor->edid, gdi_monitor->edid_len )) WARN( "Failed to write monitor to registry\n" ); else { ctx->adapter.monitor_count++; ctx->monitor_count++; } } static void add_mode( const DEVMODEW *mode, BOOL current, void *param ) { struct device_manager_ctx *ctx = param; DEVMODEW nopos_mode; if (!ctx->gpu.adapter_count) { static const struct gdi_adapter default_adapter = { .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE, }; TRACE( "adding default fake adapter\n" ); add_adapter( &default_adapter, ctx ); } nopos_mode = *mode; nopos_mode.dmPosition.x = 0; nopos_mode.dmPosition.y = 0; nopos_mode.dmFields &= ~DM_POSITION; if (write_adapter_mode( ctx->adapter_key, ctx->adapter.mode_count, &nopos_mode )) { ctx->adapter.mode_count++; set_reg_value( ctx->adapter_key, mode_countW, REG_DWORD, &ctx->adapter.mode_count, sizeof(ctx->adapter.mode_count) ); if (current) { if (!read_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, &nopos_mode )) write_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, mode ); write_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, mode ); } } } static const struct gdi_device_manager device_manager = { add_gpu, add_adapter, add_monitor, add_mode, }; static void reset_display_manager_ctx( struct device_manager_ctx *ctx ) { HANDLE mutex = ctx->mutex; if (ctx->adapter_key) { NtClose( ctx->adapter_key ); last_query_display_time = 0; } if (ctx->gpu_count) cleanup_devices(); memset( ctx, 0, sizeof(*ctx) ); if ((ctx->mutex = mutex)) prepare_devices(); } static void release_display_manager_ctx( struct device_manager_ctx *ctx ) { if (ctx->mutex) { pthread_mutex_unlock( &display_lock ); release_display_device_init_mutex( ctx->mutex ); ctx->mutex = 0; } reset_display_manager_ctx( ctx ); } static void clear_display_devices(void) { struct adapter *adapter; struct monitor *monitor; struct gpu *gpu; if (list_head( &monitors ) == &virtual_monitor.entry) { list_init( &monitors ); return; } while (!list_empty( &monitors )) { monitor = LIST_ENTRY( list_head( &monitors ), struct monitor, entry ); if (monitor->adapter) adapter_release( monitor->adapter ); list_remove( &monitor->entry ); free( monitor ); } while (!list_empty( &adapters )) { adapter = LIST_ENTRY( list_head( &adapters ), struct adapter, entry ); list_remove( &adapter->entry ); adapter_release( adapter ); } while (!list_empty( &gpus )) { gpu = LIST_ENTRY( list_head( &gpus ), struct gpu, entry ); list_remove( &gpu->entry ); gpu_release( gpu ); } } static BOOL is_monitor_active( struct monitor *monitor ) { struct adapter *adapter; /* services do not have any adapters, only a virtual monitor */ if (!(adapter = monitor->adapter)) return TRUE; if (!(adapter->state_flags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) return FALSE; return !IsRectEmpty( &monitor->rc_monitor ); } static BOOL is_monitor_primary( struct monitor *monitor ) { struct adapter *adapter; /* services do not have any adapters, only a virtual monitor */ if (!(adapter = monitor->adapter)) return TRUE; return !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE); } static void enum_device_keys( const char *root, const WCHAR *classW, UINT class_size, void (*callback)(const char *) ) { char buffer[1024]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; KEY_BASIC_INFORMATION *key2 = (void *)buffer; HKEY root_key, device_key, instance_key; DWORD size, root_len, i = 0; char path[MAX_PATH]; if (!(root_key = reg_open_ascii_key( enum_key, root ))) return; root_len = sprintf( path, "%s\\", root ); while (!NtEnumerateKey( root_key, i++, KeyBasicInformation, key2, sizeof(buffer), &size )) { DWORD j = 0, k, len, device_len; if (!(device_key = reg_open_key( root_key, key2->Name, key2->NameLength ))) continue; for (k = 0, len = root_len; k < key2->NameLength / sizeof(WCHAR); k++) path[len++] = key2->Name[k]; path[len++] = '\\'; device_len = len; while (!NtEnumerateKey( device_key, j++, KeyBasicInformation, key2, sizeof(buffer), &size )) { if (!(instance_key = reg_open_key( device_key, key2->Name, key2->NameLength ))) continue; for (k = 0, len = device_len; k < key2->NameLength / sizeof(WCHAR); k++) path[len++] = key2->Name[k]; path[len++] = 0; size = query_reg_ascii_value( instance_key, "ClassGUID", value, sizeof(buffer) ); if (size != class_size || wcscmp( (WCHAR *)value->Data, classW )) { NtClose( instance_key ); continue; } callback( path ); NtClose( instance_key ); } NtClose( device_key ); } NtClose( root_key ); } static void enum_gpus( const char *path ) { struct gpu *gpu; if (!(gpu = calloc( 1, sizeof(*gpu) ))) return; gpu->refcount = 1; strcpy( gpu->path, path ); list_add_tail( &gpus, &gpu->entry ); } static struct gpu *find_gpu_from_path( const char *path ) { struct gpu *gpu; LIST_FOR_EACH_ENTRY( gpu, &gpus, struct gpu, entry ) if (!strcmp( gpu->path, path )) return gpu_acquire( gpu ); ERR( "Failed to find gpu with path %s\n", debugstr_a(path) ); return NULL; } static void enum_monitors( const char *path ) { struct monitor *monitor; if (!(monitor = calloc( 1, sizeof(*monitor) ))) return; strcpy( monitor->path, path ); list_add_tail( &monitors, &monitor->entry ); } static struct monitor *find_monitor_from_path( const char *path ) { struct monitor *monitor; LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) if (!strcmp( monitor->path, path )) return monitor; ERR( "Failed to find monitor with path %s\n", debugstr_a(path) ); return NULL; } static BOOL update_display_cache_from_registry(void) { char path[MAX_PATH]; DWORD adapter_id, monitor_id, monitor_count = 0, size; KEY_BASIC_INFORMATION key; struct adapter *adapter; struct monitor *monitor; HANDLE mutex = NULL; NTSTATUS status; struct gpu *gpu; HKEY hkey; BOOL ret; /* If user driver did initialize the registry, then exit */ if (!enum_key && !(enum_key = reg_open_ascii_key( NULL, enum_keyA ))) return FALSE; if (!video_key && !(video_key = reg_open_ascii_key( NULL, devicemap_video_keyA ))) return FALSE; status = NtQueryKey( video_key, KeyBasicInformation, &key, offsetof(KEY_BASIC_INFORMATION, Name), &size ); if (status && status != STATUS_BUFFER_OVERFLOW) return FALSE; if (key.LastWriteTime.QuadPart <= last_query_display_time) return TRUE; pthread_mutex_lock( &display_lock ); mutex = get_display_device_init_mutex(); clear_display_devices(); enum_device_keys( "PCI", guid_devclass_displayW, sizeof(guid_devclass_displayW), enum_gpus ); enum_device_keys( "DISPLAY", guid_devclass_monitorW, sizeof(guid_devclass_monitorW), enum_monitors ); LIST_FOR_EACH_ENTRY( gpu, &gpus, struct gpu, entry ) { if (!read_gpu_from_registry( gpu )) WARN( "Failed to read gpu from registry\n" ); } LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) { if (!read_monitor_from_registry( monitor )) WARN( "Failed to read monitor from registry\n" ); } for (adapter_id = 0;; adapter_id++) { if (!(adapter = calloc( 1, sizeof(*adapter) ))) break; adapter->refcount = 1; adapter->id = adapter_id; if (!read_display_adapter_settings( adapter_id, adapter, path ) || !(adapter->gpu = find_gpu_from_path( path ))) { free( adapter->modes ); free( adapter ); break; } list_add_tail( &adapters, &adapter->entry ); size = lstrlenW( adapter->config_key ) * sizeof(WCHAR); if (!(hkey = reg_open_key( config_key, adapter->config_key, size ))) continue; for (monitor_id = 0;; monitor_id++) { struct monitor *monitor; if (!read_adapter_monitor_path( hkey, monitor_id, path )) break; if (!(monitor = find_monitor_from_path( path ))) continue; monitor->id = monitor_id; monitor->adapter = adapter_acquire( adapter ); monitor->handle = UlongToHandle( ++monitor_count ); if (adapter->monitor_count++) monitor->is_clone = TRUE; } NtClose( hkey ); } if ((ret = !list_empty( &adapters ) && !list_empty( &monitors ))) last_query_display_time = key.LastWriteTime.QuadPart; pthread_mutex_unlock( &display_lock ); release_display_device_init_mutex( mutex ); return ret; } static BOOL is_same_devmode( const DEVMODEW *a, const DEVMODEW *b ) { return a->dmDisplayOrientation == b->dmDisplayOrientation && a->dmBitsPerPel == b->dmBitsPerPel && a->dmPelsWidth == b->dmPelsWidth && a->dmPelsHeight == b->dmPelsHeight && a->dmDisplayFrequency == b->dmDisplayFrequency; } static BOOL default_update_display_devices( const struct gdi_device_manager *manager, BOOL force, struct device_manager_ctx *ctx ) { /* default implementation: expose an adapter and a monitor with a few standard modes, * and read / write current display settings from / to the registry. */ static const DEVMODEW modes[] = { { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, .dmBitsPerPel = 32, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, }, { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, .dmBitsPerPel = 32, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, }, { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, .dmBitsPerPel = 32, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, }, { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, .dmBitsPerPel = 16, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, }, { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, .dmBitsPerPel = 16, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, }, { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, .dmBitsPerPel = 16, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, }, }; static const struct gdi_gpu gpu; static const struct gdi_adapter adapter = { .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE, }; struct gdi_monitor monitor = {0}; DEVMODEW mode = {{0}}; UINT i; if (!force) return TRUE; manager->add_gpu( &gpu, ctx ); manager->add_adapter( &adapter, ctx ); if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, &mode )) { mode = modes[2]; mode.dmFields |= DM_POSITION; } monitor.rc_monitor.right = mode.dmPelsWidth; monitor.rc_monitor.bottom = mode.dmPelsHeight; monitor.rc_work.right = mode.dmPelsWidth; monitor.rc_work.bottom = mode.dmPelsHeight; manager->add_monitor( &monitor, ctx ); for (i = 0; i < ARRAY_SIZE(modes); ++i) { if (is_same_devmode( modes + i, &mode )) manager->add_mode( &mode, TRUE, ctx ); else manager->add_mode( modes + i, FALSE, ctx ); } return TRUE; } static BOOL update_display_devices( const struct gdi_device_manager *manager, BOOL force, struct device_manager_ctx *ctx ) { if (user_driver->pUpdateDisplayDevices( manager, force, ctx )) return TRUE; return default_update_display_devices( manager, force, ctx ); } /* parse the desktop size specification */ static BOOL parse_size( const WCHAR *size, unsigned int *width, unsigned int *height ) { WCHAR *end; *width = wcstoul( size, &end, 10 ); if (end == size) return FALSE; if (*end != 'x') return FALSE; size = end + 1; *height = wcstoul( size, &end, 10 ); return !*end; } /* retrieve the default desktop size from the registry */ static BOOL get_default_desktop_size( unsigned int *width, unsigned int *height ) { WCHAR buffer[4096]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; DWORD size; HKEY hkey; /* @@ Wine registry key: HKCU\Software\Wine\Explorer\Desktops */ if (!(hkey = reg_open_hkcu_key( "Software\\Wine\\Explorer\\Desktops" ))) return FALSE; size = query_reg_ascii_value( hkey, "Default", value, sizeof(buffer) ); NtClose( hkey ); if (!size || value->Type != REG_SZ) return FALSE; if (!parse_size( (const WCHAR *)value->Data, width, height )) return FALSE; return TRUE; } static void desktop_add_gpu( const struct gdi_gpu *gpu, void *param ) { } static void desktop_add_adapter( const struct gdi_adapter *adapter, void *param ) { struct device_manager_ctx *ctx = param; ctx->is_primary = !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE); } static void desktop_add_monitor( const struct gdi_monitor *monitor, void *param ) { } static void desktop_add_mode( const DEVMODEW *mode, BOOL current, void *param ) { struct device_manager_ctx *ctx = param; if (ctx->is_primary && current) { ctx->primary_bpp = mode->dmBitsPerPel; ctx->primary_width = mode->dmPelsWidth; ctx->primary_height = mode->dmPelsHeight; } } static const struct gdi_device_manager desktop_device_manager = { desktop_add_gpu, desktop_add_adapter, desktop_add_monitor, desktop_add_mode, }; static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ctx *ctx ) { static const struct gdi_gpu gpu; static const struct gdi_adapter adapter = { .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE, }; struct gdi_monitor monitor = {0}; static struct screen_size { unsigned int width; unsigned int height; } screen_sizes[] = { /* 4:3 */ { 320, 240}, { 400, 300}, { 512, 384}, { 640, 480}, { 768, 576}, { 800, 600}, {1024, 768}, {1152, 864}, {1280, 960}, {1400, 1050}, {1600, 1200}, {2048, 1536}, /* 5:4 */ {1280, 1024}, {2560, 2048}, /* 16:9 */ {1280, 720}, {1366, 768}, {1600, 900}, {1920, 1080}, {2560, 1440}, {3840, 2160}, /* 16:10 */ { 320, 200}, { 640, 400}, {1280, 800}, {1440, 900}, {1680, 1050}, {1920, 1200}, {2560, 1600} }; struct device_manager_ctx desktop_ctx = {0}; UINT screen_width, screen_height, max_width, max_height; unsigned int depths[] = {8, 16, 0}; DEVMODEW current, mode = { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, .dmDisplayFrequency = 60, }; UINT i, j; if (!force) return TRUE; /* in virtual desktop mode, read the device list from the user driver but expose virtual devices */ if (!update_display_devices( &desktop_device_manager, TRUE, &desktop_ctx )) return FALSE; max_width = desktop_ctx.primary_width; max_height = desktop_ctx.primary_height; depths[ARRAY_SIZE(depths) - 1] = desktop_ctx.primary_bpp; if (!get_default_desktop_size( &screen_width, &screen_height )) { screen_width = max_width; screen_height = max_height; } add_gpu( &gpu, ctx ); add_adapter( &adapter, ctx ); if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, ¤t )) { current = mode; current.dmFields |= DM_POSITION; current.dmBitsPerPel = desktop_ctx.primary_bpp; current.dmPelsWidth = screen_width; current.dmPelsHeight = screen_height; } monitor.rc_monitor.right = current.dmPelsWidth; monitor.rc_monitor.bottom = current.dmPelsHeight; monitor.rc_work.right = current.dmPelsWidth; monitor.rc_work.bottom = current.dmPelsHeight; add_monitor( &monitor, ctx ); for (i = 0; i < ARRAY_SIZE(depths); ++i) { mode.dmBitsPerPel = depths[i]; for (j = 0; j < ARRAY_SIZE(screen_sizes); ++j) { mode.dmPelsWidth = screen_sizes[j].width; mode.dmPelsHeight = screen_sizes[j].height; if (mode.dmPelsWidth > max_width || mode.dmPelsHeight > max_height) continue; if (mode.dmPelsWidth == max_width && mode.dmPelsHeight == max_height) continue; if (mode.dmPelsWidth == screen_width && mode.dmPelsHeight == screen_height) continue; if (is_same_devmode( &mode, ¤t )) add_mode( ¤t, TRUE, ctx ); else add_mode( &mode, FALSE, ctx ); } mode.dmPelsWidth = screen_width; mode.dmPelsHeight = screen_height; if (is_same_devmode( &mode, ¤t )) add_mode( ¤t, TRUE, ctx ); else add_mode( &mode, FALSE, ctx ); if (max_width != screen_width || max_height != screen_height) { mode.dmPelsWidth = max_width; mode.dmPelsHeight = max_height; if (is_same_devmode( &mode, ¤t )) add_mode( ¤t, TRUE, ctx ); else add_mode( &mode, FALSE, ctx ); } } return TRUE; } BOOL update_display_cache( BOOL force ) { static const WCHAR wine_service_station_name[] = {'_','_','w','i','n','e','s','e','r','v','i','c','e','_','w','i','n','s','t','a','t','i','o','n',0}; HWINSTA winstation = NtUserGetProcessWindowStation(); struct device_manager_ctx ctx = {0}; BOOL was_virtual_desktop, ret; WCHAR name[MAX_PATH]; /* services do not have any adapters, only a virtual monitor */ if (NtUserGetObjectInformation( winstation, UOI_NAME, name, sizeof(name), NULL ) && !wcscmp( name, wine_service_station_name )) { pthread_mutex_lock( &display_lock ); clear_display_devices(); list_add_tail( &monitors, &virtual_monitor.entry ); pthread_mutex_unlock( &display_lock ); return TRUE; } if ((was_virtual_desktop = is_virtual_desktop())) ret = TRUE; else ret = update_display_devices( &device_manager, force, &ctx ); /* as update_display_devices calls the user driver, it starts explorer and may change the virtual desktop state */ if (ret && is_virtual_desktop()) { reset_display_manager_ctx( &ctx ); ret = desktop_update_display_devices( force || !was_virtual_desktop, &ctx ); } release_display_manager_ctx( &ctx ); if (!ret) WARN( "Failed to update display devices\n" ); if (!update_display_cache_from_registry()) { if (force) { ERR( "Failed to read display config.\n" ); return FALSE; } if (ctx.gpu_count) { ERR( "Driver reported devices, but we failed to read them.\n" ); return FALSE; } return update_display_cache( TRUE ); } return TRUE; } static BOOL lock_display_devices(void) { if (!update_display_cache( FALSE )) return FALSE; pthread_mutex_lock( &display_lock ); return TRUE; } static void unlock_display_devices(void) { pthread_mutex_unlock( &display_lock ); } static HDC get_display_dc(void) { pthread_mutex_lock( &display_dc_lock ); if (!display_dc) { HDC dc; pthread_mutex_unlock( &display_dc_lock ); dc = NtGdiOpenDCW( NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL ); pthread_mutex_lock( &display_dc_lock ); if (display_dc) NtGdiDeleteObjectApp( dc ); else display_dc = dc; } return display_dc; } static void release_display_dc( HDC hdc ) { pthread_mutex_unlock( &display_dc_lock ); } /********************************************************************** * get_monitor_dpi */ UINT get_monitor_dpi( HMONITOR monitor ) { /* FIXME: use the monitor DPI instead */ return system_dpi; } /********************************************************************** * get_win_monitor_dpi */ UINT get_win_monitor_dpi( HWND hwnd ) { /* FIXME: use the monitor DPI instead */ return system_dpi; } /* copied from user32 GetAwarenessFromDpiAwarenessContext, make sure to keep that in sync */ static DPI_AWARENESS get_awareness_from_dpi_awareness_context( DPI_AWARENESS_CONTEXT context ) { switch ((ULONG_PTR)context) { case 0x10: case 0x11: case 0x12: case 0x22: case 0x80000010: case 0x80000011: case 0x80000012: case 0x80000022: return (ULONG_PTR)context & 3; case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE: case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE: case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE: return ~(ULONG_PTR)context; case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: return ~(ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; default: return DPI_AWARENESS_INVALID; } } /********************************************************************** * get_thread_dpi_awareness */ DPI_AWARENESS get_thread_dpi_awareness(void) { struct ntuser_thread_info *info = NtUserGetThreadInfo(); ULONG_PTR context = info->dpi_awareness; if (context == 0) /* process default */ return NtUserGetProcessDpiAwarenessContext( NULL ) & 3; return get_awareness_from_dpi_awareness_context((DPI_AWARENESS_CONTEXT)context); } DWORD get_process_layout(void) { return process_layout == ~0u ? 0 : process_layout; } /********************************************************************** * get_thread_dpi */ UINT get_thread_dpi(void) { switch (get_thread_dpi_awareness()) { case DPI_AWARENESS_UNAWARE: return USER_DEFAULT_SCREEN_DPI; case DPI_AWARENESS_SYSTEM_AWARE: return system_dpi; default: return 0; /* no scaling */ } } /* see GetDpiForSystem */ UINT get_system_dpi(void) { if (get_thread_dpi_awareness() == DPI_AWARENESS_UNAWARE) return USER_DEFAULT_SCREEN_DPI; return system_dpi; } /********************************************************************** * SetThreadDpiAwarenessContext (win32u.so) * copied from user32, make sure to keep that in sync */ DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT context ) { struct ntuser_thread_info *info = NtUserGetThreadInfo(); DPI_AWARENESS prev, val = get_awareness_from_dpi_awareness_context( context ); if (val == DPI_AWARENESS_INVALID) { RtlSetLastWin32Error( ERROR_INVALID_PARAMETER ); return 0; } if (!(prev = info->dpi_awareness)) { prev = NtUserGetProcessDpiAwarenessContext( GetCurrentProcess() ) & 3; prev |= 0x80000010; /* restore to process default */ } if (((ULONG_PTR)context & ~(ULONG_PTR)0x33) == 0x80000000) info->dpi_awareness = 0; else if (context == DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 || context == (DPI_AWARENESS_CONTEXT)0x22) info->dpi_awareness = 0x22; else info->dpi_awareness = val | 0x10; return ULongToHandle( prev ); } /********************************************************************** * map_dpi_rect */ RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to ) { if (dpi_from && dpi_to && dpi_from != dpi_to) { rect.left = muldiv( rect.left, dpi_to, dpi_from ); rect.top = muldiv( rect.top, dpi_to, dpi_from ); rect.right = muldiv( rect.right, dpi_to, dpi_from ); rect.bottom = muldiv( rect.bottom, dpi_to, dpi_from ); } return rect; } /********************************************************************** * map_dpi_point */ POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to ) { if (dpi_from && dpi_to && dpi_from != dpi_to) { pt.x = muldiv( pt.x, dpi_to, dpi_from ); pt.y = muldiv( pt.y, dpi_to, dpi_from ); } return pt; } /********************************************************************** * point_win_to_phys_dpi */ static POINT point_win_to_phys_dpi( HWND hwnd, POINT pt ) { return map_dpi_point( pt, get_dpi_for_window( hwnd ), get_win_monitor_dpi( hwnd ) ); } /********************************************************************** * point_phys_to_win_dpi */ POINT point_phys_to_win_dpi( HWND hwnd, POINT pt ) { return map_dpi_point( pt, get_win_monitor_dpi( hwnd ), get_dpi_for_window( hwnd )); } /********************************************************************** * point_thread_to_win_dpi */ POINT point_thread_to_win_dpi( HWND hwnd, POINT pt ) { UINT dpi = get_thread_dpi(); if (!dpi) dpi = get_win_monitor_dpi( hwnd ); return map_dpi_point( pt, dpi, get_dpi_for_window( hwnd )); } /********************************************************************** * rect_thread_to_win_dpi */ RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect ) { UINT dpi = get_thread_dpi(); if (!dpi) dpi = get_win_monitor_dpi( hwnd ); return map_dpi_rect( rect, dpi, get_dpi_for_window( hwnd ) ); } /* map value from system dpi to standard 96 dpi for storing in the registry */ static int map_from_system_dpi( int val ) { return muldiv( val, USER_DEFAULT_SCREEN_DPI, get_system_dpi() ); } /* map value from 96 dpi to system or custom dpi */ static int map_to_dpi( int val, UINT dpi ) { if (!dpi) dpi = get_system_dpi(); return muldiv( val, dpi, USER_DEFAULT_SCREEN_DPI ); } RECT get_virtual_screen_rect( UINT dpi ) { struct monitor *monitor; RECT rect = {0}; if (!lock_display_devices()) return rect; LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) { if (!is_monitor_active( monitor ) || monitor->is_clone) continue; union_rect( &rect, &rect, &monitor->rc_monitor ); } unlock_display_devices(); if (dpi) rect = map_dpi_rect( rect, system_dpi, dpi ); return rect; } static BOOL is_window_rect_full_screen( const RECT *rect ) { struct monitor *monitor; BOOL ret = FALSE; if (!lock_display_devices()) return FALSE; LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) { RECT monrect; if (!is_monitor_active( monitor ) || monitor->is_clone) continue; monrect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ), get_thread_dpi() ); if (rect->left <= monrect.left && rect->right >= monrect.right && rect->top <= monrect.top && rect->bottom >= monrect.bottom) { ret = TRUE; break; } } unlock_display_devices(); return ret; } static UINT get_display_index( const UNICODE_STRING *name ) { static const WCHAR displayW[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y'}; WCHAR *end, display[64] = {0}; int index; memcpy( display, name->Buffer, min( name->Length, 63 * sizeof(WCHAR) ) ); if (wcsnicmp( display, displayW, ARRAY_SIZE(displayW) )) return 0; if (!(index = wcstoul( display + ARRAY_SIZE(displayW), &end, 10 )) || *end) return 0; return index; } RECT get_display_rect( const WCHAR *display ) { struct monitor *monitor; UNICODE_STRING name; RECT rect = {0}; UINT index; RtlInitUnicodeString( &name, display ); if (!(index = get_display_index( &name ))) return rect; if (!lock_display_devices()) return rect; LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) { if (!monitor->adapter || monitor->adapter->id + 1 != index) continue; rect = monitor->rc_monitor; break; } unlock_display_devices(); return map_dpi_rect( rect, system_dpi, get_thread_dpi() ); } RECT get_primary_monitor_rect( UINT dpi ) { struct monitor *monitor; RECT rect = {0}; if (!lock_display_devices()) return rect; LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) { if (!is_monitor_primary( monitor )) continue; rect = monitor->rc_monitor; break; } unlock_display_devices(); return map_dpi_rect( rect, system_dpi, dpi ); } /********************************************************************** * NtUserGetDisplayConfigBufferSizes (win32u.@) */ LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_info, UINT32 *num_mode_info ) { struct monitor *monitor; UINT32 count = 0; TRACE( "(0x%x %p %p)\n", flags, num_path_info, num_mode_info ); if (!num_path_info || !num_mode_info) return ERROR_INVALID_PARAMETER; *num_path_info = 0; switch (flags) { case QDC_ALL_PATHS: case QDC_ONLY_ACTIVE_PATHS: case QDC_DATABASE_CURRENT: break; default: return ERROR_INVALID_PARAMETER; } /* FIXME: semi-stub */ if (flags != QDC_ONLY_ACTIVE_PATHS) FIXME( "only returning active paths\n" ); if (lock_display_devices()) { LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) { if (!is_monitor_active( monitor )) continue; count++; } unlock_display_devices(); } *num_path_info = count; *num_mode_info = count * 2; TRACE( "returning %u paths %u modes\n", *num_path_info, *num_mode_info ); return ERROR_SUCCESS; } static DISPLAYCONFIG_ROTATION get_dc_rotation( const DEVMODEW *devmode ) { if (devmode->dmFields & DM_DISPLAYORIENTATION) return devmode->dmDisplayOrientation + 1; else return DISPLAYCONFIG_ROTATION_IDENTITY; } static DISPLAYCONFIG_SCANLINE_ORDERING get_dc_scanline_ordering( const DEVMODEW *devmode ) { if (!(devmode->dmFields & DM_DISPLAYFLAGS)) return DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED; else if (devmode->dmDisplayFlags & DM_INTERLACED) return DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED; else return DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; } static DISPLAYCONFIG_PIXELFORMAT get_dc_pixelformat( DWORD dmBitsPerPel ) { if ((dmBitsPerPel == 8) || (dmBitsPerPel == 16) || (dmBitsPerPel == 24) || (dmBitsPerPel == 32)) return dmBitsPerPel / 8; else return DISPLAYCONFIG_PIXELFORMAT_NONGDI; } static void set_mode_target_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid, UINT32 target_id, UINT32 flags, const DEVMODEW *devmode ) { DISPLAYCONFIG_TARGET_MODE *mode = &info->targetMode; info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET; info->adapterId = *gpu_luid; info->id = target_id; /* FIXME: Populate pixelRate/hSyncFreq/totalSize with real data */ mode->targetVideoSignalInfo.pixelRate = devmode->dmDisplayFrequency * devmode->dmPelsWidth * devmode->dmPelsHeight; mode->targetVideoSignalInfo.hSyncFreq.Numerator = devmode->dmDisplayFrequency * devmode->dmPelsWidth; mode->targetVideoSignalInfo.hSyncFreq.Denominator = 1; mode->targetVideoSignalInfo.vSyncFreq.Numerator = devmode->dmDisplayFrequency; mode->targetVideoSignalInfo.vSyncFreq.Denominator = 1; mode->targetVideoSignalInfo.activeSize.cx = devmode->dmPelsWidth; mode->targetVideoSignalInfo.activeSize.cy = devmode->dmPelsHeight; if (flags & QDC_DATABASE_CURRENT) { mode->targetVideoSignalInfo.totalSize.cx = 0; mode->targetVideoSignalInfo.totalSize.cy = 0; } else { mode->targetVideoSignalInfo.totalSize.cx = devmode->dmPelsWidth; mode->targetVideoSignalInfo.totalSize.cy = devmode->dmPelsHeight; } mode->targetVideoSignalInfo.videoStandard = D3DKMDT_VSS_OTHER; mode->targetVideoSignalInfo.scanLineOrdering = get_dc_scanline_ordering( devmode ); } static void set_path_target_info( DISPLAYCONFIG_PATH_TARGET_INFO *info, const LUID *gpu_luid, UINT32 target_id, UINT32 mode_index, const DEVMODEW *devmode ) { info->adapterId = *gpu_luid; info->id = target_id; info->modeInfoIdx = mode_index; info->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL; info->rotation = get_dc_rotation( devmode ); info->scaling = DISPLAYCONFIG_SCALING_IDENTITY; info->refreshRate.Numerator = devmode->dmDisplayFrequency; info->refreshRate.Denominator = 1; info->scanLineOrdering = get_dc_scanline_ordering( devmode ); info->targetAvailable = TRUE; info->statusFlags = DISPLAYCONFIG_TARGET_IN_USE; } static void set_mode_source_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid, UINT32 source_id, const DEVMODEW *devmode ) { DISPLAYCONFIG_SOURCE_MODE *mode = &info->sourceMode; info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE; info->adapterId = *gpu_luid; info->id = source_id; mode->width = devmode->dmPelsWidth; mode->height = devmode->dmPelsHeight; mode->pixelFormat = get_dc_pixelformat( devmode->dmBitsPerPel ); if (devmode->dmFields & DM_POSITION) { mode->position = devmode->dmPosition; } else { mode->position.x = 0; mode->position.y = 0; } } static void set_path_source_info( DISPLAYCONFIG_PATH_SOURCE_INFO *info, const LUID *gpu_luid, UINT32 source_id, UINT32 mode_index ) { info->adapterId = *gpu_luid; info->id = source_id; info->modeInfoIdx = mode_index; info->statusFlags = DISPLAYCONFIG_SOURCE_IN_USE; } static BOOL source_mode_exists( const DISPLAYCONFIG_MODE_INFO *modes, UINT32 modes_count, UINT32 source_id, UINT32 *found_mode_index ) { UINT32 i; for (i = 0; i < modes_count; i++) { if (modes[i].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE && modes[i].id == source_id) { *found_mode_index = i; return TRUE; } } return FALSE; } /*********************************************************************** * NtUserQueryDisplayConfig (win32u.@) */ LONG WINAPI NtUserQueryDisplayConfig( UINT32 flags, UINT32 *paths_count, DISPLAYCONFIG_PATH_INFO *paths, UINT32 *modes_count, DISPLAYCONFIG_MODE_INFO *modes, DISPLAYCONFIG_TOPOLOGY_ID *topology_id ) { ULONG adapter_index; LONG ret; UINT32 output_id, source_mode_index, path_index = 0, mode_index = 0; const LUID *gpu_luid; DEVMODEW devmode; struct monitor *monitor; FIXME( "flags %#x, paths_count %p, paths %p, modes_count %p, modes %p, topology_id %p semi-stub\n", flags, paths_count, paths, modes_count, modes, topology_id ); if (!paths_count || !modes_count) return ERROR_INVALID_PARAMETER; if (!*paths_count || !*modes_count) return ERROR_INVALID_PARAMETER; if (flags != QDC_ALL_PATHS && flags != QDC_ONLY_ACTIVE_PATHS && flags != QDC_DATABASE_CURRENT) return ERROR_INVALID_PARAMETER; if (((flags == QDC_DATABASE_CURRENT) && !topology_id) || ((flags != QDC_DATABASE_CURRENT) && topology_id)) return ERROR_INVALID_PARAMETER; if (flags != QDC_ONLY_ACTIVE_PATHS) FIXME( "only returning active paths\n" ); if (topology_id) { FIXME( "setting toplogyid to DISPLAYCONFIG_TOPOLOGY_INTERNAL\n" ); *topology_id = DISPLAYCONFIG_TOPOLOGY_INTERNAL; } if (!lock_display_devices()) return ERROR_GEN_FAILURE; ret = ERROR_GEN_FAILURE; LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) { if (!is_monitor_active( monitor )) continue; if (!monitor->adapter) continue; adapter_index = monitor->adapter->id; gpu_luid = &monitor->adapter->gpu->luid; output_id = monitor->output_id; memset( &devmode, 0, sizeof(devmode) ); devmode.dmSize = sizeof(devmode); if (!adapter_get_current_settings( monitor->adapter, &devmode )) { goto done; } if (path_index == *paths_count || mode_index == *modes_count) { ret = ERROR_INSUFFICIENT_BUFFER; goto done; } paths[path_index].flags = DISPLAYCONFIG_PATH_ACTIVE; set_mode_target_info( &modes[mode_index], gpu_luid, output_id, flags, &devmode ); set_path_target_info( &paths[path_index].targetInfo, gpu_luid, output_id, mode_index, &devmode ); mode_index++; if (mode_index == *modes_count) { ret = ERROR_INSUFFICIENT_BUFFER; goto done; } /* Multiple targets can be driven by the same source, ensure a mode * hasn't already been added for this source. */ if (!source_mode_exists( modes, mode_index, adapter_index, &source_mode_index )) { set_mode_source_info( &modes[mode_index], gpu_luid, adapter_index, &devmode ); source_mode_index = mode_index; mode_index++; } set_path_source_info( &paths[path_index].sourceInfo, gpu_luid, adapter_index, source_mode_index ); path_index++; } *paths_count = path_index; *modes_count = mode_index; ret = ERROR_SUCCESS; done: unlock_display_devices(); return ret; } /* display_lock mutex must be held */ static struct monitor *find_monitor_by_index( struct adapter *adapter, UINT index ) { struct monitor *monitor; LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry) if (monitor->adapter == adapter && index == monitor->id) return monitor; WARN( "Failed to find adapter %u monitor with id %u.\n", adapter->id, index ); return NULL; } /* display_lock mutex must be held */ static struct adapter *find_adapter_by_index( UINT index ) { struct adapter *adapter; LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry) if (index == adapter->id) return adapter; WARN( "Failed to find adapter with id %u.\n", index ); return NULL; } /* display_lock mutex must be held */ static struct adapter *find_primary_adapter(void) { struct adapter *adapter; LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry) if (adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE) return adapter; WARN( "Failed to find primary adapter.\n" ); return NULL; } /* display_lock mutex must be held */ static struct adapter *find_adapter_by_name( UNICODE_STRING *name ) { struct adapter *adapter; UINT index; if (!(index = get_display_index( name ))) return NULL; LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry) if (adapter->id + 1 == index) return adapter; WARN( "Failed to find adapter with name %s.\n", debugstr_us(name) ); return NULL; } /* Find and acquire the adapter matching name, or primary adapter if name is NULL. * If not NULL, the returned adapter needs to be released with adapter_release. */ static struct adapter *find_adapter( UNICODE_STRING *name ) { struct adapter *adapter; if (!lock_display_devices()) return NULL; if (name && name->Length) adapter = find_adapter_by_name( name ); else adapter = find_primary_adapter(); if (adapter) adapter = adapter_acquire( adapter ); unlock_display_devices(); return adapter; } static void monitor_get_interface_name( struct monitor *monitor, WCHAR *interface_name ) { char buffer[MAX_PATH] = {0}, *tmp; const char *id; *interface_name = 0; if (!monitor->adapter) return; if (!(monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID)) id = "Default_Monitor"; else id = monitor->edid_info.monitor_id_string; sprintf( buffer, "\\\\?\\DISPLAY\\%s\\%04X&%04X#%s", id, monitor->adapter->id, monitor->id, guid_devinterface_monitorA ); for (tmp = buffer + 4; *tmp; tmp++) if (*tmp == '\\') *tmp = '#'; asciiz_to_unicode( interface_name, buffer ); } /*********************************************************************** * NtUserEnumDisplayDevices (win32u.@) */ NTSTATUS WINAPI NtUserEnumDisplayDevices( UNICODE_STRING *device, DWORD index, DISPLAY_DEVICEW *info, DWORD flags ) { struct monitor *monitor = NULL; struct adapter *adapter = NULL; BOOL found = FALSE; TRACE( "%s %u %p %#x\n", debugstr_us( device ), (int)index, info, (int)flags ); if (!info || !info->cb) return STATUS_UNSUCCESSFUL; if (!lock_display_devices()) return STATUS_UNSUCCESSFUL; if (!device || !device->Length) { if ((adapter = find_adapter_by_index( index ))) found = TRUE; } else if ((adapter = find_adapter_by_name( device ))) { if ((monitor = find_monitor_by_index( adapter, index ))) found = TRUE; } if (found) { char buffer[MAX_PATH], *tmp; if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceName) + sizeof(info->DeviceName)) { if (monitor) sprintf( buffer, "\\\\.\\DISPLAY%d\\Monitor%d", adapter->id + 1, monitor->id ); else sprintf( buffer, "\\\\.\\DISPLAY%d", adapter->id + 1 ); asciiz_to_unicode( info->DeviceName, buffer ); } if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceString) + sizeof(info->DeviceString)) { if (monitor) asciiz_to_unicode( info->DeviceString, "Generic Non-PnP Monitor" ); else lstrcpynW( info->DeviceString, adapter->gpu->name, ARRAY_SIZE(info->DeviceString) ); } if (info->cb >= offsetof(DISPLAY_DEVICEW, StateFlags) + sizeof(info->StateFlags)) { if (!monitor) info->StateFlags = adapter->state_flags; else { info->StateFlags = DISPLAY_DEVICE_ATTACHED; if (is_monitor_active( monitor )) info->StateFlags |= DISPLAY_DEVICE_ACTIVE; } } if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID)) { if (flags & EDD_GET_DEVICE_INTERFACE_NAME) { if (monitor) monitor_get_interface_name( monitor, info->DeviceID ); else *info->DeviceID = 0; } else { if (monitor) { sprintf( buffer, "MONITOR\\%s", monitor->path + 8 ); if (!(tmp = strrchr( buffer, '\\' ))) tmp = buffer + strlen( buffer ); sprintf( tmp, "\\%s\\%04X", guid_devclass_monitorA, monitor->output_id ); } else { strcpy( buffer, adapter->gpu->path ); if ((tmp = strrchr( buffer, '\\' ))) *tmp = 0; } asciiz_to_unicode( info->DeviceID, buffer ); } } if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey)) { if (monitor) sprintf( buffer, "%s\\Class\\%s\\%04X", control_keyA, guid_devclass_monitorA, monitor->output_id ); else sprintf( buffer, "%s\\Video\\%s\\%04x", control_keyA, adapter->gpu->guid, adapter->id ); asciiz_to_unicode( info->DeviceKey, buffer ); } } unlock_display_devices(); return found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; } #define _X_FIELD(prefix, bits) \ if ((fields) & prefix##_##bits) \ { \ p += sprintf( p, "%s%s", first ? "" : ",", #bits ); \ first = FALSE; \ } static const char *_CDS_flags( DWORD fields ) { BOOL first = TRUE; CHAR buf[128]; CHAR *p = buf; _X_FIELD(CDS, UPDATEREGISTRY) _X_FIELD(CDS, TEST) _X_FIELD(CDS, FULLSCREEN) _X_FIELD(CDS, GLOBAL) _X_FIELD(CDS, SET_PRIMARY) _X_FIELD(CDS, VIDEOPARAMETERS) _X_FIELD(CDS, ENABLE_UNSAFE_MODES) _X_FIELD(CDS, DISABLE_UNSAFE_MODES) _X_FIELD(CDS, RESET) _X_FIELD(CDS, RESET_EX) _X_FIELD(CDS, NORESET) *p = 0; return wine_dbg_sprintf( "%s", buf ); } static const char *_DM_fields( DWORD fields ) { BOOL first = TRUE; CHAR buf[128]; CHAR *p = buf; _X_FIELD(DM, BITSPERPEL) _X_FIELD(DM, PELSWIDTH) _X_FIELD(DM, PELSHEIGHT) _X_FIELD(DM, DISPLAYFLAGS) _X_FIELD(DM, DISPLAYFREQUENCY) _X_FIELD(DM, POSITION) _X_FIELD(DM, DISPLAYORIENTATION) *p = 0; return wine_dbg_sprintf( "%s", buf ); } #undef _X_FIELD static void trace_devmode( const DEVMODEW *devmode ) { TRACE( "dmFields=%s ", _DM_fields(devmode->dmFields) ); if (devmode->dmFields & DM_BITSPERPEL) TRACE( "dmBitsPerPel=%u ", (int)devmode->dmBitsPerPel ); if (devmode->dmFields & DM_PELSWIDTH) TRACE( "dmPelsWidth=%u ", (int)devmode->dmPelsWidth ); if (devmode->dmFields & DM_PELSHEIGHT) TRACE( "dmPelsHeight=%u ", (int)devmode->dmPelsHeight ); if (devmode->dmFields & DM_DISPLAYFREQUENCY) TRACE( "dmDisplayFrequency=%u ", (int)devmode->dmDisplayFrequency ); if (devmode->dmFields & DM_POSITION) TRACE( "dmPosition=(%d,%d) ", (int)devmode->dmPosition.x, (int)devmode->dmPosition.y ); if (devmode->dmFields & DM_DISPLAYFLAGS) TRACE( "dmDisplayFlags=%#x ", (int)devmode->dmDisplayFlags ); if (devmode->dmFields & DM_DISPLAYORIENTATION) TRACE( "dmDisplayOrientation=%u ", (int)devmode->dmDisplayOrientation ); TRACE("\n"); } static BOOL is_detached_mode( const DEVMODEW *mode ) { return mode->dmFields & DM_POSITION && mode->dmFields & DM_PELSWIDTH && mode->dmFields & DM_PELSHEIGHT && mode->dmPelsWidth == 0 && mode->dmPelsHeight == 0; } static const DEVMODEW *find_display_mode( const DEVMODEW *modes, DEVMODEW *devmode ) { const DEVMODEW *mode; if (is_detached_mode( devmode )) return devmode; for (mode = modes; mode && mode->dmSize; mode = NEXT_DEVMODEW(mode)) { if ((mode->dmFields & DM_DISPLAYFLAGS) && (mode->dmDisplayFlags & WINE_DM_UNSUPPORTED)) continue; if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel && devmode->dmBitsPerPel != mode->dmBitsPerPel) continue; if ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth != mode->dmPelsWidth) continue; if ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight != mode->dmPelsHeight) continue; if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != mode->dmDisplayFrequency && devmode->dmDisplayFrequency > 1 && mode->dmDisplayFrequency) continue; if ((devmode->dmFields & DM_DISPLAYORIENTATION) && devmode->dmDisplayOrientation != mode->dmDisplayOrientation) continue; if ((devmode->dmFields & DM_DISPLAYFLAGS) && (mode->dmFields & DM_DISPLAYFLAGS) && (devmode->dmDisplayFlags & DM_INTERLACED) != (mode->dmDisplayFlags & DM_INTERLACED)) continue; if ((devmode->dmFields & DM_DISPLAYFIXEDOUTPUT) && (mode->dmFields & DM_DISPLAYFIXEDOUTPUT) && devmode->dmDisplayFixedOutput != mode->dmDisplayFixedOutput) continue; return mode; } return NULL; } static BOOL adapter_get_full_mode( const struct adapter *adapter, const DEVMODEW *devmode, DEVMODEW *full_mode ) { const DEVMODEW *adapter_mode; if (devmode) { trace_devmode( devmode ); if (devmode->dmSize < offsetof(DEVMODEW, dmICMMethod)) return FALSE; if (!is_detached_mode( devmode ) && (!(devmode->dmFields & DM_BITSPERPEL) || !devmode->dmBitsPerPel) && (!(devmode->dmFields & DM_PELSWIDTH) || !devmode->dmPelsWidth) && (!(devmode->dmFields & DM_PELSHEIGHT) || !devmode->dmPelsHeight) && (!(devmode->dmFields & DM_DISPLAYFREQUENCY) || !devmode->dmDisplayFrequency)) devmode = NULL; } if (devmode) memcpy( full_mode, devmode, devmode->dmSize ); else { if (!adapter_get_registry_settings( adapter, full_mode )) return FALSE; TRACE( "Return to original display mode\n" ); } if ((full_mode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT)) { WARN( "devmode doesn't specify the resolution: %#x\n", (int)full_mode->dmFields ); return FALSE; } if (!is_detached_mode( full_mode ) && (!full_mode->dmPelsWidth || !full_mode->dmPelsHeight || !(full_mode->dmFields & DM_POSITION))) { DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}; if (!adapter_get_current_settings( adapter, ¤t_mode )) return FALSE; if (!full_mode->dmPelsWidth) full_mode->dmPelsWidth = current_mode.dmPelsWidth; if (!full_mode->dmPelsHeight) full_mode->dmPelsHeight = current_mode.dmPelsHeight; if (!(full_mode->dmFields & DM_POSITION)) { full_mode->dmPosition = current_mode.dmPosition; full_mode->dmFields |= DM_POSITION; } } if ((adapter_mode = find_display_mode( adapter->modes, full_mode )) && adapter_mode != full_mode) { POINTL position = full_mode->dmPosition; *full_mode = *adapter_mode; full_mode->dmFields |= DM_POSITION; full_mode->dmPosition = position; } return adapter_mode != NULL; } static DEVMODEW *get_display_settings( struct adapter *target, const DEVMODEW *devmode ) { DEVMODEW *mode, *displays; struct adapter *adapter; BOOL ret; /* allocate an extra mode for easier iteration */ if (!(displays = calloc( list_count( &adapters ) + 1, sizeof(DEVMODEW) ))) return NULL; mode = displays; LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry ) { char buffer[CCHDEVICENAME]; mode->dmSize = sizeof(DEVMODEW); if (devmode && adapter->id == target->id) memcpy( &mode->dmFields, &devmode->dmFields, devmode->dmSize - offsetof(DEVMODEW, dmFields) ); else { if (!target) ret = adapter_get_registry_settings( adapter, mode ); else ret = adapter_get_current_settings( adapter, mode ); if (!ret) { free( displays ); return NULL; } } sprintf( buffer, "\\\\.\\DISPLAY%d", adapter->id + 1 ); asciiz_to_unicode( mode->dmDeviceName, buffer ); mode = NEXT_DEVMODEW(mode); } return displays; } static INT offset_length( POINT offset ) { return offset.x * offset.x + offset.y * offset.y; } static void set_rect_from_devmode( RECT *rect, const DEVMODEW *mode ) { SetRect( rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth, mode->dmPosition.y + mode->dmPelsHeight ); } /* Check if a rect overlaps with placed display rects */ static BOOL overlap_placed_displays( const RECT *rect, const DEVMODEW *displays ) { const DEVMODEW *mode; RECT intersect; for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) { set_rect_from_devmode( &intersect, mode ); if ((mode->dmFields & DM_POSITION) && intersect_rect( &intersect, &intersect, rect )) return TRUE; } return FALSE; } /* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */ static POINT get_placement_offset( const DEVMODEW *displays, const DEVMODEW *placing ) { POINT points[8], left_top, offset, min_offset = {0, 0}; INT point_idx, point_count, vertex_idx; BOOL has_placed = FALSE, first = TRUE; RECT desired_rect, rect; const DEVMODEW *mode; INT width, height; set_rect_from_devmode( &desired_rect, placing ); /* If the display to be placed is detached, no offset is needed to place it */ if (IsRectEmpty( &desired_rect )) return min_offset; /* If there is no placed and attached display, place this display as it is */ for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) { set_rect_from_devmode( &rect, mode ); if ((mode->dmFields & DM_POSITION) && !IsRectEmpty( &rect )) { has_placed = TRUE; break; } } if (!has_placed) return min_offset; /* Try to place this display with each of its four vertices at every vertex of the placed * displays and see which combination has the minimum offset length */ width = desired_rect.right - desired_rect.left; height = desired_rect.bottom - desired_rect.top; for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) { set_rect_from_devmode( &rect, mode ); if (!(mode->dmFields & DM_POSITION) || IsRectEmpty( &rect )) continue; /* Get four vertices of the placed display rectangle */ points[0].x = rect.left; points[0].y = rect.top; points[1].x = rect.left; points[1].y = rect.bottom; points[2].x = rect.right; points[2].y = rect.top; points[3].x = rect.right; points[3].y = rect.bottom; point_count = 4; /* Intersected points when moving the display to be placed horizontally */ if (desired_rect.bottom >= rect.top && desired_rect.top <= rect.bottom) { points[point_count].x = rect.left; points[point_count++].y = desired_rect.top; points[point_count].x = rect.right; points[point_count++].y = desired_rect.top; } /* Intersected points when moving the display to be placed vertically */ if (desired_rect.left <= rect.right && desired_rect.right >= rect.left) { points[point_count].x = desired_rect.left; points[point_count++].y = rect.top; points[point_count].x = desired_rect.left; points[point_count++].y = rect.bottom; } /* Try moving each vertex of the display rectangle to each points */ for (point_idx = 0; point_idx < point_count; ++point_idx) { for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx) { switch (vertex_idx) { /* Move the bottom right vertex to the point */ case 0: left_top.x = points[point_idx].x - width; left_top.y = points[point_idx].y - height; break; /* Move the bottom left vertex to the point */ case 1: left_top.x = points[point_idx].x; left_top.y = points[point_idx].y - height; break; /* Move the top right vertex to the point */ case 2: left_top.x = points[point_idx].x - width; left_top.y = points[point_idx].y; break; /* Move the top left vertex to the point */ case 3: left_top.x = points[point_idx].x; left_top.y = points[point_idx].y; break; } offset.x = left_top.x - desired_rect.left; offset.y = left_top.y - desired_rect.top; rect = desired_rect; OffsetRect( &rect, offset.x, offset.y ); if (!overlap_placed_displays( &rect, displays )) { if (first) { min_offset = offset; first = FALSE; continue; } if (offset_length( offset ) < offset_length( min_offset )) min_offset = offset; } } } } return min_offset; } static void place_all_displays( DEVMODEW *displays ) { POINT min_offset, offset; DEVMODEW *mode, *placing; for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) mode->dmFields &= ~DM_POSITION; /* Place all displays with no extra space between them and no overlapping */ while (1) { /* Place the unplaced display with the minimum offset length first */ placing = NULL; for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) { if (mode->dmFields & DM_POSITION) continue; offset = get_placement_offset( displays, mode ); if (!placing || offset_length( offset ) < offset_length( min_offset )) { min_offset = offset; placing = mode; } } /* If all displays are placed */ if (!placing) break; placing->dmPosition.x += min_offset.x; placing->dmPosition.y += min_offset.y; placing->dmFields |= DM_POSITION; } } static BOOL all_detached_settings( const DEVMODEW *displays ) { const DEVMODEW *mode; for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode)) if (!is_detached_mode( mode )) return FALSE; return TRUE; } static LONG apply_display_settings( struct adapter *target, const DEVMODEW *devmode, HWND hwnd, DWORD flags, void *lparam ) { WCHAR primary_name[CCHDEVICENAME]; struct adapter *primary, *adapter; DEVMODEW *mode, *displays; LONG ret; if (!lock_display_devices()) return DISP_CHANGE_FAILED; if (!(displays = get_display_settings( target, devmode ))) { unlock_display_devices(); return DISP_CHANGE_FAILED; } if (all_detached_settings( displays )) { unlock_display_devices(); WARN( "Detaching all modes is not permitted.\n" ); free( displays ); return DISP_CHANGE_SUCCESSFUL; } place_all_displays( displays ); if (!(primary = find_primary_adapter())) primary_name[0] = 0; else { char device_name[CCHDEVICENAME]; sprintf( device_name, "\\\\.\\DISPLAY%d", primary->id + 1 ); asciiz_to_unicode( primary_name, device_name ); } /* use the default implementation in virtual desktop mode */ if (is_virtual_desktop()) ret = E_NOTIMPL; else ret = user_driver->pChangeDisplaySettings( displays, primary_name, hwnd, flags, lparam ); if (ret == E_NOTIMPL) { /* default implementation: write current display settings to the registry. */ mode = displays; LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry ) { if (!adapter_set_current_settings( adapter, mode )) WARN( "Failed to write adapter %u current mode.\n", adapter->id ); mode = NEXT_DEVMODEW(mode); } ret = DISP_CHANGE_SUCCESSFUL; } unlock_display_devices(); free( displays ); if (ret) return ret; if (!update_display_cache( TRUE )) WARN( "Failed to update display cache after mode change.\n" ); if ((adapter = find_adapter( NULL ))) { DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}; if (!adapter_get_current_settings( adapter, ¤t_mode )) WARN( "Failed to get primary adapter current display settings.\n" ); adapter_release( adapter ); NtUserClipCursor( NULL ); send_notify_message( NtUserGetDesktopWindow(), WM_DISPLAYCHANGE, current_mode.dmBitsPerPel, MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), FALSE ); send_message_timeout( HWND_BROADCAST, WM_DISPLAYCHANGE, current_mode.dmBitsPerPel, MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), SMTO_ABORTIFHUNG, 2000, FALSE ); /* post clip_fullscreen_window request to the foreground window */ NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_CLIPCURSOR, SET_CURSOR_FSCLIP, 0 ); } return ret; } /*********************************************************************** * NtUserChangeDisplaySettings (win32u.@) */ LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devmode, HWND hwnd, DWORD flags, void *lparam ) { DEVMODEW full_mode = {.dmSize = sizeof(DEVMODEW)}; int ret = DISP_CHANGE_SUCCESSFUL; struct adapter *adapter; TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, (int)flags, lparam ); TRACE( "flags=%s\n", _CDS_flags(flags) ); if ((!devname || !devname->Length) && !devmode) return apply_display_settings( NULL, NULL, hwnd, flags, lparam ); if (!(adapter = find_adapter( devname ))) return DISP_CHANGE_BADPARAM; if (!adapter_get_full_mode( adapter, devmode, &full_mode )) ret = DISP_CHANGE_BADMODE; else if ((flags & CDS_UPDATEREGISTRY) && !adapter_set_registry_settings( adapter, &full_mode )) ret = DISP_CHANGE_NOTUPDATED; else if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL; else ret = apply_display_settings( adapter, &full_mode, hwnd, flags, lparam ); adapter_release( adapter ); if (ret) ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret ); return ret; } static BOOL adapter_enum_display_settings( const struct adapter *adapter, UINT index, DEVMODEW *devmode, UINT flags ) { DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}; const DEVMODEW *adapter_mode; if (!(flags & EDS_ROTATEDMODE) && !adapter_get_current_settings( adapter, ¤t_mode )) { WARN( "Failed to query current display mode for EDS_ROTATEDMODE flag.\n" ); return FALSE; } for (adapter_mode = adapter->modes; adapter_mode->dmSize; adapter_mode = NEXT_DEVMODEW(adapter_mode)) { if (!(flags & EDS_ROTATEDMODE) && (adapter_mode->dmFields & DM_DISPLAYORIENTATION) && adapter_mode->dmDisplayOrientation != current_mode.dmDisplayOrientation) continue; if (!(flags & EDS_RAWMODE) && (adapter_mode->dmFields & DM_DISPLAYFLAGS) && (adapter_mode->dmDisplayFlags & WINE_DM_UNSUPPORTED)) continue; if (!index--) { memcpy( &devmode->dmFields, &adapter_mode->dmFields, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields) ); devmode->dmDisplayFlags &= ~WINE_DM_UNSUPPORTED; return TRUE; } } WARN( "device %d, index %#x, flags %#x display mode not found.\n", adapter->id, index, flags ); RtlSetLastWin32Error( ERROR_NO_MORE_FILES ); return FALSE; } /*********************************************************************** * NtUserEnumDisplaySettings (win32u.@) */ BOOL WINAPI NtUserEnumDisplaySettings( UNICODE_STRING *device, DWORD index, DEVMODEW *devmode, DWORD flags ) { static const WCHAR wine_display_driverW[] = {'W','i','n','e',' ','D','i','s','p','l','a','y',' ','D','r','i','v','e','r',0}; struct adapter *adapter; BOOL ret; TRACE( "device %s, index %#x, devmode %p, flags %#x\n", debugstr_us(device), (int)index, devmode, (int)flags ); if (!(adapter = find_adapter( device ))) return FALSE; lstrcpynW( devmode->dmDeviceName, wine_display_driverW, ARRAY_SIZE(devmode->dmDeviceName) ); devmode->dmSpecVersion = DM_SPECVERSION; devmode->dmDriverVersion = DM_SPECVERSION; devmode->dmSize = offsetof(DEVMODEW, dmICMMethod); devmode->dmDriverExtra = 0; if (index == ENUM_REGISTRY_SETTINGS) ret = adapter_get_registry_settings( adapter, devmode ); else if (index == ENUM_CURRENT_SETTINGS) ret = adapter_get_current_settings( adapter, devmode ); else ret = adapter_enum_display_settings( adapter, index, devmode, flags ); adapter_release( adapter ); if (!ret) WARN( "Failed to query %s display settings.\n", debugstr_us(device) ); else TRACE( "position %dx%d, resolution %ux%u, frequency %u, depth %u, orientation %#x.\n", (int)devmode->dmPosition.x, (int)devmode->dmPosition.y, (int)devmode->dmPelsWidth, (int)devmode->dmPelsHeight, (int)devmode->dmDisplayFrequency, (int)devmode->dmBitsPerPel, (int)devmode->dmDisplayOrientation ); return ret; } struct monitor_enum_info { HANDLE handle; RECT rect; }; static unsigned int active_unique_monitor_count(void) { struct monitor *monitor; unsigned int count = 0; LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry) { if (is_monitor_active( monitor ) && !monitor->is_clone) count++; } return count; } INT get_display_depth( UNICODE_STRING *name ) { WCHAR device_nameW[CCHDEVICENAME]; char device_name[CCHDEVICENAME]; struct adapter *adapter; BOOL is_primary; INT depth; if (!lock_display_devices()) return 32; if (name && name->Length) adapter = find_adapter_by_name( name ); else adapter = find_primary_adapter(); if (!adapter) { unlock_display_devices(); return 32; } is_primary = !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE); sprintf( device_name, "\\\\.\\DISPLAY%d", adapter->id + 1 ); asciiz_to_unicode( device_nameW, device_name ); /* use the default implementation in virtual desktop mode */ if (is_virtual_desktop()) depth = -1; else depth = user_driver->pGetDisplayDepth( device_nameW, is_primary ); if (depth < 0) { DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}; if (!adapter_get_current_settings( adapter, ¤t_mode )) depth = 32; else depth = current_mode.dmBitsPerPel; } unlock_display_devices(); return depth; } static BOOL should_enumerate_monitor( struct monitor *monitor, const POINT *origin, const RECT *limit, RECT *rect ) { if (!is_monitor_active( monitor )) return FALSE; if (monitor->is_clone) return FALSE; *rect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ), get_thread_dpi() ); OffsetRect( rect, -origin->x, -origin->y ); return intersect_rect( rect, rect, limit ); } /*********************************************************************** * NtUserEnumDisplayMonitors (win32u.@) */ BOOL WINAPI NtUserEnumDisplayMonitors( HDC hdc, RECT *rect, MONITORENUMPROC proc, LPARAM lparam ) { struct monitor_enum_info enum_buf[8], *enum_info = enum_buf; struct enum_display_monitor_params params; struct monitor *monitor; unsigned int count = 0, i; POINT origin; RECT limit; BOOL ret = TRUE; if (hdc) { DC *dc; if (!(dc = get_dc_ptr( hdc ))) return FALSE; origin.x = dc->attr->vis_rect.left; origin.y = dc->attr->vis_rect.top; release_dc_ptr( dc ); if (NtGdiGetAppClipBox( hdc, &limit ) == ERROR) return FALSE; } else { origin.x = origin.y = 0; limit.left = limit.top = INT_MIN; limit.right = limit.bottom = INT_MAX; } if (rect && !intersect_rect( &limit, &limit, rect )) return TRUE; if (!lock_display_devices()) return FALSE; count = list_count( &monitors ); if (!count || (count > ARRAYSIZE(enum_buf) && !(enum_info = malloc( count * sizeof(*enum_info) )))) { unlock_display_devices(); return FALSE; } count = 0; /* enumerate primary monitors first */ LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry) { if (!is_monitor_primary( monitor )) continue; if (should_enumerate_monitor( monitor, &origin, &limit, &enum_info[count].rect )) { enum_info[count++].handle = monitor->handle; break; } } /* then non-primary monitors */ LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry) { if (is_monitor_primary( monitor )) continue; if (should_enumerate_monitor( monitor, &origin, &limit, &enum_info[count].rect )) enum_info[count++].handle = monitor->handle; } unlock_display_devices(); params.proc = proc; params.hdc = hdc; params.lparam = lparam; for (i = 0; i < count && ret; i++) { void *ret_ptr; ULONG ret_len; NTSTATUS status; params.monitor = enum_info[i].handle; params.rect = enum_info[i].rect; status = KeUserModeCallback( NtUserCallEnumDisplayMonitor, ¶ms, sizeof(params), &ret_ptr, &ret_len ); if (!status && ret_len == sizeof(ret)) ret = *(BOOL *)ret_ptr; else ret = FALSE; } if (enum_info != enum_buf) free( enum_info ); return ret; } BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info ) { struct monitor *monitor; UINT dpi_from, dpi_to; if (info->cbSize != sizeof(MONITORINFOEXW) && info->cbSize != sizeof(MONITORINFO)) return FALSE; if (!lock_display_devices()) return FALSE; LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) { if (monitor->handle != handle) continue; if (!is_monitor_active( monitor )) continue; /* FIXME: map dpi */ info->rcMonitor = monitor->rc_monitor; info->rcWork = monitor->rc_work; info->dwFlags = is_monitor_primary( monitor ) ? MONITORINFOF_PRIMARY : 0; if (info->cbSize >= sizeof(MONITORINFOEXW)) { char buffer[CCHDEVICENAME]; if (monitor->adapter) sprintf( buffer, "\\\\.\\DISPLAY%d", monitor->adapter->id + 1 ); else strcpy( buffer, "WinDisc" ); asciiz_to_unicode( ((MONITORINFOEXW *)info)->szDevice, buffer ); } unlock_display_devices(); if ((dpi_to = get_thread_dpi())) { dpi_from = get_monitor_dpi( handle ); info->rcMonitor = map_dpi_rect( info->rcMonitor, dpi_from, dpi_to ); info->rcWork = map_dpi_rect( info->rcWork, dpi_from, dpi_to ); } TRACE( "flags %04x, monitor %s, work %s\n", (int)info->dwFlags, wine_dbgstr_rect(&info->rcMonitor), wine_dbgstr_rect(&info->rcWork)); return TRUE; } unlock_display_devices(); WARN( "invalid handle %p\n", handle ); RtlSetLastWin32Error( ERROR_INVALID_MONITOR_HANDLE ); return FALSE; } HMONITOR monitor_from_rect( const RECT *rect, UINT flags, UINT dpi ) { HMONITOR primary = 0, nearest = 0, ret = 0; UINT max_area = 0, min_distance = ~0u; struct monitor *monitor; RECT r; r = map_dpi_rect( *rect, dpi, system_dpi ); if (IsRectEmpty( &r )) { r.right = r.left + 1; r.bottom = r.top + 1; } if (!lock_display_devices()) return 0; LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry) { RECT intersect, monitor_rect; if (!is_monitor_active( monitor ) || monitor->is_clone) continue; monitor_rect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ), system_dpi ); if (intersect_rect( &intersect, &monitor_rect, &r )) { /* check for larger intersecting area */ UINT area = (intersect.right - intersect.left) * (intersect.bottom - intersect.top); if (area > max_area) { max_area = area; ret = monitor->handle; } } else if (!max_area) /* if not intersecting, check for min distance */ { UINT distance; UINT x, y; if (r.right <= monitor_rect.left) x = monitor_rect.left - r.right; else if (monitor_rect.right <= r.left) x = r.left - monitor_rect.right; else x = 0; if (r.bottom <= monitor_rect.top) y = monitor_rect.top - r.bottom; else if (monitor_rect.bottom <= r.top) y = r.top - monitor_rect.bottom; else y = 0; distance = x * x + y * y; if (distance < min_distance) { min_distance = distance; nearest = monitor->handle; } } if (is_monitor_primary( monitor )) primary = monitor->handle; } unlock_display_devices(); if (!ret) { if (flags & MONITOR_DEFAULTTOPRIMARY) ret = primary; else if (flags & MONITOR_DEFAULTTONEAREST) ret = nearest; } TRACE( "%s flags %x returning %p\n", wine_dbgstr_rect(rect), flags, ret ); return ret; } HMONITOR monitor_from_point( POINT pt, UINT flags, UINT dpi ) { RECT rect; SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 ); return monitor_from_rect( &rect, flags, dpi ); } /* see MonitorFromWindow */ HMONITOR monitor_from_window( HWND hwnd, UINT flags, UINT dpi ) { RECT rect; WINDOWPLACEMENT wp; TRACE( "(%p, 0x%08x)\n", hwnd, flags ); wp.length = sizeof(wp); if (is_iconic( hwnd ) && NtUserGetWindowPlacement( hwnd, &wp )) return monitor_from_rect( &wp.rcNormalPosition, flags, dpi ); if (get_window_rect( hwnd, &rect, dpi )) return monitor_from_rect( &rect, flags, dpi ); if (!(flags & (MONITOR_DEFAULTTOPRIMARY|MONITOR_DEFAULTTONEAREST))) return 0; /* retrieve the primary */ SetRect( &rect, 0, 0, 1, 1 ); return monitor_from_rect( &rect, flags, dpi ); } /*********************************************************************** * NtUserGetSystemDpiForProcess (win32u.@) */ ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process ) { if (process && process != GetCurrentProcess()) { FIXME( "not supported on other process %p\n", process ); return 0; } return system_dpi; } /*********************************************************************** * NtUserGetDpiForMonitor (win32u.@) */ BOOL WINAPI NtUserGetDpiForMonitor( HMONITOR monitor, UINT type, UINT *x, UINT *y ) { if (type > 2) { RtlSetLastWin32Error( ERROR_BAD_ARGUMENTS ); return FALSE; } if (!x || !y) { RtlSetLastWin32Error( ERROR_INVALID_ADDRESS ); return FALSE; } switch (get_thread_dpi_awareness()) { case DPI_AWARENESS_UNAWARE: *x = *y = USER_DEFAULT_SCREEN_DPI; break; case DPI_AWARENESS_SYSTEM_AWARE: *x = *y = system_dpi; break; default: *x = *y = get_monitor_dpi( monitor ); break; } return TRUE; } /********************************************************************** * LogicalToPhysicalPointForPerMonitorDPI (win32u.@) */ BOOL WINAPI NtUserLogicalToPerMonitorDPIPhysicalPoint( HWND hwnd, POINT *pt ) { RECT rect; if (!get_window_rect( hwnd, &rect, get_thread_dpi() )) return FALSE; if (pt->x < rect.left || pt->y < rect.top || pt->x > rect.right || pt->y > rect.bottom) return FALSE; *pt = point_win_to_phys_dpi( hwnd, *pt ); return TRUE; } /********************************************************************** * NtUserPerMonitorDPIPhysicalToLogicalPoint (win32u.@) */ BOOL WINAPI NtUserPerMonitorDPIPhysicalToLogicalPoint( HWND hwnd, POINT *pt ) { RECT rect; BOOL ret = FALSE; if (get_window_rect( hwnd, &rect, 0 ) && pt->x >= rect.left && pt->y >= rect.top && pt->x <= rect.right && pt->y <= rect.bottom) { *pt = point_phys_to_win_dpi( hwnd, *pt ); ret = TRUE; } return ret; } /* retrieve the cached base keys for a given entry */ static BOOL get_base_keys( enum parameter_key index, HKEY *base_key, HKEY *volatile_key ) { static HKEY base_keys[NB_PARAM_KEYS]; static HKEY volatile_keys[NB_PARAM_KEYS]; HKEY key; if (!base_keys[index] && base_key) { if (!(key = reg_create_ascii_key( hkcu_key, parameter_key_names[index], 0, NULL ))) return FALSE; if (InterlockedCompareExchangePointer( (void **)&base_keys[index], key, 0 )) NtClose( key ); } if (!volatile_keys[index] && volatile_key) { if (!(key = reg_create_ascii_key( volatile_base_key, parameter_key_names[index], REG_OPTION_VOLATILE, NULL ))) return FALSE; if (InterlockedCompareExchangePointer( (void **)&volatile_keys[index], key, 0 )) NtClose( key ); } if (base_key) *base_key = base_keys[index]; if (volatile_key) *volatile_key = volatile_keys[index]; return TRUE; } /* load a value to a registry entry */ static DWORD load_entry( struct sysparam_entry *entry, void *data, DWORD size ) { char buffer[4096]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; DWORD count; HKEY base_key, volatile_key; if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE; if (!(count = query_reg_ascii_value( volatile_key, entry->regval, value, sizeof(buffer) ))) count = query_reg_ascii_value( base_key, entry->regval, value, sizeof(buffer) ); if (count > size) { count = size; /* make sure strings are null-terminated */ if (value->Type == REG_SZ) ((WCHAR *)value->Data)[count / sizeof(WCHAR) - 1] = 0; } if (count) memcpy( data, value->Data, count ); entry->loaded = TRUE; return count; } /* save a value to a registry entry */ static BOOL save_entry( const struct sysparam_entry *entry, const void *data, DWORD size, DWORD type, UINT flags ) { HKEY base_key, volatile_key; WCHAR nameW[64]; asciiz_to_unicode( nameW, entry->regval ); if (flags & SPIF_UPDATEINIFILE) { if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE; if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE; reg_delete_value( volatile_key, nameW ); if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL )) { asciiz_to_unicode( nameW, entry->mirror ); set_reg_value( base_key, nameW, type, data, size ); } } else { if (!get_base_keys( entry->base_key, NULL, &volatile_key )) return FALSE; if (!set_reg_value( volatile_key, nameW, type, data, size )) return FALSE; } return TRUE; } /* save a string value to a registry entry */ static BOOL save_entry_string( const struct sysparam_entry *entry, const WCHAR *str, UINT flags ) { return save_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ, flags ); } /* initialize an entry in the registry if missing */ static BOOL init_entry( struct sysparam_entry *entry, const void *data, DWORD size, DWORD type ) { KEY_VALUE_PARTIAL_INFORMATION value; UNICODE_STRING name; WCHAR nameW[64]; HKEY base_key; DWORD count; NTSTATUS status; if (!get_base_keys( entry->base_key, &base_key, NULL )) return FALSE; name.Buffer = nameW; name.MaximumLength = asciiz_to_unicode( nameW, entry->regval ); name.Length = name.MaximumLength - sizeof(WCHAR); status = NtQueryValueKey( base_key, &name, KeyValuePartialInformation, &value, sizeof(value), &count ); if (!status || status == STATUS_BUFFER_OVERFLOW) return TRUE; if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE; if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL )) { asciiz_to_unicode( nameW, entry->mirror ); set_reg_value( base_key, nameW, type, data, size ); } entry->loaded = TRUE; return TRUE; } /* initialize a string value in the registry if missing */ static BOOL init_entry_string( struct sysparam_entry *entry, const WCHAR *str ) { return init_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ ); } /* set an int parameter in the registry */ static BOOL set_int_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { WCHAR bufW[32]; char buf[32]; sprintf( buf, "%d", int_param ); asciiz_to_unicode( bufW, buf ); if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE; entry->uint.val = int_param; entry->hdr.loaded = TRUE; return TRUE; } /* initialize an int parameter */ static BOOL init_int_entry( union sysparam_all_entry *entry ) { WCHAR bufW[32]; char buf[32]; sprintf( buf, "%d", entry->uint.val ); asciiz_to_unicode( bufW, buf ); return init_entry_string( &entry->hdr, bufW ); } /* load a uint parameter from the registry */ static BOOL get_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { if (!ptr_param) return FALSE; if (!entry->hdr.loaded) { WCHAR buf[32]; if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 ); } *(UINT *)ptr_param = entry->uint.val; return TRUE; } /* set a uint parameter in the registry */ static BOOL set_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { WCHAR bufW[32]; char buf[32]; sprintf( buf, "%u", int_param ); asciiz_to_unicode( bufW, buf ); if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE; entry->uint.val = int_param; entry->hdr.loaded = TRUE; return TRUE; } /* initialize a uint parameter */ static BOOL init_uint_entry( union sysparam_all_entry *entry ) { WCHAR bufW[32]; char buf[32]; sprintf( buf, "%u", entry->uint.val ); asciiz_to_unicode( bufW, buf ); return init_entry_string( &entry->hdr, bufW ); } /* load a twips parameter from the registry */ static BOOL get_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { int val; if (!ptr_param) return FALSE; if (!entry->hdr.loaded) { WCHAR buf[32]; if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 ); } /* Dimensions are quoted as being "twips" values if negative and pixels if positive. * One inch is 1440 twips. * See for example * Technical Reference to the Windows 2000 Registry -> * HKEY_CURRENT_USER -> Control Panel -> Desktop -> WindowMetrics */ val = entry->uint.val; if (val < 0) val = muldiv( -val, dpi, 1440 ); else val = map_to_dpi( val, dpi ); *(int *)ptr_param = val; return TRUE; } /* set a twips parameter in the registry */ static BOOL set_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { int val = int_param; if (val > 0) val = map_from_system_dpi( val ); return set_int_entry( entry, val, ptr_param, flags ); } /* load a bool parameter from the registry */ static BOOL get_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { if (!ptr_param) return FALSE; if (!entry->hdr.loaded) { WCHAR buf[32]; if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = wcstol( buf, NULL, 10 ) != 0; } *(UINT *)ptr_param = entry->bool.val; return TRUE; } /* set a bool parameter in the registry */ static BOOL set_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { WCHAR buf[] = { int_param ? '1' : '0', 0 }; if (!save_entry_string( &entry->hdr, buf, flags )) return FALSE; entry->bool.val = int_param != 0; entry->hdr.loaded = TRUE; return TRUE; } /* initialize a bool parameter */ static BOOL init_bool_entry( union sysparam_all_entry *entry ) { WCHAR buf[] = { entry->bool.val ? '1' : '0', 0 }; return init_entry_string( &entry->hdr, buf ); } /* load a bool parameter using Yes/No strings from the registry */ static BOOL get_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { if (!ptr_param) return FALSE; if (!entry->hdr.loaded) { WCHAR buf[32]; if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = !wcsicmp( yesW, buf ); } *(UINT *)ptr_param = entry->bool.val; return TRUE; } /* set a bool parameter using Yes/No strings from the registry */ static BOOL set_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { const WCHAR *str = int_param ? yesW : noW; if (!save_entry_string( &entry->hdr, str, flags )) return FALSE; entry->bool.val = int_param != 0; entry->hdr.loaded = TRUE; return TRUE; } /* initialize a bool parameter using Yes/No strings */ static BOOL init_yesno_entry( union sysparam_all_entry *entry ) { return init_entry_string( &entry->hdr, entry->bool.val ? yesW : noW ); } /* load a dword (binary) parameter from the registry */ static BOOL get_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { if (!ptr_param) return FALSE; if (!entry->hdr.loaded) { DWORD val; if (load_entry( &entry->hdr, &val, sizeof(val) ) == sizeof(DWORD)) entry->dword.val = val; } *(DWORD *)ptr_param = entry->dword.val; return TRUE; } /* set a dword (binary) parameter in the registry */ static BOOL set_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { DWORD val = PtrToUlong( ptr_param ); if (!save_entry( &entry->hdr, &val, sizeof(val), REG_DWORD, flags )) return FALSE; entry->dword.val = val; entry->hdr.loaded = TRUE; return TRUE; } /* initialize a dword parameter */ static BOOL init_dword_entry( union sysparam_all_entry *entry ) { return init_entry( &entry->hdr, &entry->dword.val, sizeof(entry->dword.val), REG_DWORD ); } /* load an RGB parameter from the registry */ static BOOL get_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { if (!ptr_param) return FALSE; if (!entry->hdr.loaded) { WCHAR buf[32]; if (load_entry( &entry->hdr, buf, sizeof(buf) )) { DWORD r, g, b; WCHAR *end, *str = buf; r = wcstoul( str, &end, 10 ); if (end == str || !*end) goto done; str = end + 1; g = wcstoul( str, &end, 10 ); if (end == str || !*end) goto done; str = end + 1; b = wcstoul( str, &end, 10 ); if (end == str) goto done; if (r > 255 || g > 255 || b > 255) goto done; entry->rgb.val = RGB( r, g, b ); } } done: *(COLORREF *)ptr_param = entry->rgb.val; return TRUE; } /* set an RGB parameter in the registry */ static BOOL set_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { WCHAR bufW[32]; char buf[32]; HBRUSH brush; HPEN pen; sprintf( buf, "%u %u %u", GetRValue(int_param), GetGValue(int_param), GetBValue(int_param) ); asciiz_to_unicode( bufW, buf ); if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE; entry->rgb.val = int_param; entry->hdr.loaded = TRUE; if ((brush = InterlockedExchangePointer( (void **)&entry->rgb.brush, 0 ))) { make_gdi_object_system( brush, FALSE ); NtGdiDeleteObjectApp( brush ); } if ((pen = InterlockedExchangePointer( (void **)&entry->rgb.pen, 0 ))) { make_gdi_object_system( pen, FALSE ); NtGdiDeleteObjectApp( pen ); } return TRUE; } /* initialize an RGB parameter */ static BOOL init_rgb_entry( union sysparam_all_entry *entry ) { WCHAR bufW[32]; char buf[32]; sprintf( buf, "%u %u %u", GetRValue(entry->rgb.val), GetGValue(entry->rgb.val), GetBValue(entry->rgb.val) ); asciiz_to_unicode( bufW, buf ); return init_entry_string( &entry->hdr, bufW ); } /* get a path parameter in the registry */ static BOOL get_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { if (!ptr_param) return FALSE; if (!entry->hdr.loaded) { WCHAR buffer[MAX_PATH]; if (load_entry( &entry->hdr, buffer, sizeof(buffer) )) lstrcpynW( entry->path.path, buffer, MAX_PATH ); } lstrcpynW( ptr_param, entry->path.path, int_param ); return TRUE; } /* set a path parameter in the registry */ static BOOL set_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { WCHAR buffer[MAX_PATH]; BOOL ret; lstrcpynW( buffer, ptr_param, MAX_PATH ); ret = save_entry_string( &entry->hdr, buffer, flags ); if (ret) { lstrcpyW( entry->path.path, buffer ); entry->hdr.loaded = TRUE; } return ret; } /* initialize a path parameter */ static BOOL init_path_entry( union sysparam_all_entry *entry ) { return init_entry_string( &entry->hdr, entry->path.path ); } /* get a binary parameter in the registry */ static BOOL get_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { if (!ptr_param) return FALSE; if (!entry->hdr.loaded) { void *buffer = malloc( entry->bin.size ); DWORD len = load_entry( &entry->hdr, buffer, entry->bin.size ); if (len) { memcpy( entry->bin.ptr, buffer, entry->bin.size ); memset( (char *)entry->bin.ptr + len, 0, entry->bin.size - len ); } free( buffer ); } memcpy( ptr_param, entry->bin.ptr, min( int_param, entry->bin.size ) ); return TRUE; } /* set a binary parameter in the registry */ static BOOL set_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { BOOL ret; void *buffer = malloc( entry->bin.size ); memcpy( buffer, entry->bin.ptr, entry->bin.size ); memcpy( buffer, ptr_param, min( int_param, entry->bin.size )); ret = save_entry( &entry->hdr, buffer, entry->bin.size, REG_BINARY, flags ); if (ret) { memcpy( entry->bin.ptr, buffer, entry->bin.size ); entry->hdr.loaded = TRUE; } free( buffer ); return ret; } /* initialize a binary parameter */ static BOOL init_binary_entry( union sysparam_all_entry *entry ) { return init_entry( &entry->hdr, entry->bin.ptr, entry->bin.size, REG_BINARY ); } static void logfont16to32( const LOGFONT16 *font16, LPLOGFONTW font32 ) { font32->lfHeight = font16->lfHeight; font32->lfWidth = font16->lfWidth; font32->lfEscapement = font16->lfEscapement; font32->lfOrientation = font16->lfOrientation; font32->lfWeight = font16->lfWeight; font32->lfItalic = font16->lfItalic; font32->lfUnderline = font16->lfUnderline; font32->lfStrikeOut = font16->lfStrikeOut; font32->lfCharSet = font16->lfCharSet; font32->lfOutPrecision = font16->lfOutPrecision; font32->lfClipPrecision = font16->lfClipPrecision; font32->lfQuality = font16->lfQuality; font32->lfPitchAndFamily = font16->lfPitchAndFamily; win32u_mbtowc( &ansi_cp, font32->lfFaceName, LF_FACESIZE, font16->lfFaceName, strlen( font16->lfFaceName )); font32->lfFaceName[LF_FACESIZE-1] = 0; } static void get_real_fontname( LOGFONTW *lf, WCHAR fullname[LF_FACESIZE] ) { struct font_enum_entry enum_entry; ULONG count = sizeof(enum_entry); HDC hdc; hdc = get_display_dc(); NtGdiEnumFonts( hdc, 0, 0, lstrlenW( lf->lfFaceName ), lf->lfFaceName, lf->lfCharSet, &count, &enum_entry ); release_display_dc( hdc ); if (count) lstrcpyW( fullname, enum_entry.lf.elfFullName ); else lstrcpyW( fullname, lf->lfFaceName ); } LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, int *height ) { SIZE sz; static const WCHAR abcdW[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q', 'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H', 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}; if (metric && !NtGdiGetTextMetricsW( hdc, metric, 0 )) return 0; if (!NtGdiGetTextExtentExW( hdc, abcdW, ARRAYSIZE(abcdW), 0, NULL, NULL, &sz, 0 )) return 0; if (height) *height = sz.cy; return (sz.cx / 26 + 1) / 2; } /* get text metrics and/or "average" char width of the specified logfont * for the specified dc */ static void get_text_metr_size( HDC hdc, LOGFONTW *lf, TEXTMETRICW *metric, UINT *psz ) { HFONT hfont, hfontsav; TEXTMETRICW tm; UINT ret; if (!metric) metric = &tm; hfont = NtGdiHfontCreate( lf, sizeof(*lf), 0, 0, NULL ); if (!hfont || !(hfontsav = NtGdiSelectFont( hdc, hfont ))) { metric->tmHeight = -1; if (psz) *psz = 10; if (hfont) NtGdiDeleteObjectApp( hfont ); return; } ret = get_char_dimensions( hdc, metric, NULL ); if (psz) *psz = ret ? ret : 10; NtGdiSelectFont( hdc, hfontsav ); NtGdiDeleteObjectApp( hfont ); } DWORD get_dialog_base_units(void) { static int cx, cy; if (!cx) { HDC hdc; if ((hdc = NtUserGetDC( 0 ))) { cx = get_char_dimensions( hdc, NULL, &cy ); NtUserReleaseDC( 0, hdc ); } TRACE( "base units = %d,%d\n", cx, cy ); } return MAKELONG( muldiv( cx, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ), muldiv( cy, get_system_dpi(), USER_DEFAULT_SCREEN_DPI )); } /* adjust some of the raw values found in the registry */ static void normalize_nonclientmetrics( NONCLIENTMETRICSW *pncm) { TEXTMETRICW tm; HDC hdc = get_display_dc(); if( pncm->iBorderWidth < 1) pncm->iBorderWidth = 1; if( pncm->iCaptionWidth < 8) pncm->iCaptionWidth = 8; if( pncm->iScrollWidth < 8) pncm->iScrollWidth = 8; if( pncm->iScrollHeight < 8) pncm->iScrollHeight = 8; /* adjust some heights to the corresponding font */ get_text_metr_size( hdc, &pncm->lfMenuFont, &tm, NULL); pncm->iMenuHeight = max( pncm->iMenuHeight, 2 + tm.tmHeight + tm.tmExternalLeading ); get_text_metr_size( hdc, &pncm->lfCaptionFont, &tm, NULL); pncm->iCaptionHeight = max( pncm->iCaptionHeight, 2 + tm.tmHeight); get_text_metr_size( hdc, &pncm->lfSmCaptionFont, &tm, NULL); pncm->iSmCaptionHeight = max( pncm->iSmCaptionHeight, 2 + tm.tmHeight); release_display_dc( hdc ); } /* load a font (binary) parameter from the registry */ static BOOL get_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { LOGFONTW font; if (!ptr_param) return FALSE; if (!entry->hdr.loaded) { switch (load_entry( &entry->hdr, &font, sizeof(font) )) { case sizeof(font): font.lfCharSet = DEFAULT_CHARSET; if (font.lfHeight > 0) /* positive height value means points ( inch/72 ) */ font.lfHeight = -muldiv( font.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 ); entry->font.val = font; break; case sizeof(LOGFONT16): /* win9x-winME format */ logfont16to32( (LOGFONT16 *)&font, &entry->font.val ); entry->font.val.lfCharSet = DEFAULT_CHARSET; if (entry->font.val.lfHeight > 0) entry->font.val.lfHeight = -muldiv( entry->font.val.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 ); break; default: WARN( "Unknown format in key %s value %s\n", debugstr_a( parameter_key_names[entry->hdr.base_key] ), debugstr_a( entry->hdr.regval )); /* fall through */ case 0: /* use the default GUI font */ NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(font), &font ); font.lfCharSet = DEFAULT_CHARSET; font.lfHeight = map_from_system_dpi( font.lfHeight ); font.lfWeight = entry->font.weight; entry->font.val = font; break; } get_real_fontname( &entry->font.val, entry->font.fullname ); entry->hdr.loaded = TRUE; } font = entry->font.val; font.lfHeight = map_to_dpi( font.lfHeight, dpi ); lstrcpyW( font.lfFaceName, entry->font.fullname ); *(LOGFONTW *)ptr_param = font; return TRUE; } /* set a font (binary) parameter in the registry */ static BOOL set_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { LOGFONTW font; WCHAR *ptr; memcpy( &font, ptr_param, sizeof(font) ); /* zero pad the end of lfFaceName so we don't save uninitialised data */ for (ptr = font.lfFaceName; ptr < font.lfFaceName + LF_FACESIZE && *ptr; ptr++); if (ptr < font.lfFaceName + LF_FACESIZE) memset( ptr, 0, (font.lfFaceName + LF_FACESIZE - ptr) * sizeof(WCHAR) ); if (font.lfHeight < 0) font.lfHeight = map_from_system_dpi( font.lfHeight ); if (!save_entry( &entry->hdr, &font, sizeof(font), REG_BINARY, flags )) return FALSE; entry->font.val = font; get_real_fontname( &entry->font.val, entry->font.fullname ); entry->hdr.loaded = TRUE; return TRUE; } /* initialize a font (binary) parameter */ static BOOL init_font_entry( union sysparam_all_entry *entry ) { NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(entry->font.val), &entry->font.val ); entry->font.val.lfCharSet = DEFAULT_CHARSET; entry->font.val.lfHeight = map_from_system_dpi( entry->font.val.lfHeight ); entry->font.val.lfWeight = entry->font.weight; get_real_fontname( &entry->font.val, entry->font.fullname ); return init_entry( &entry->hdr, &entry->font.val, sizeof(entry->font.val), REG_BINARY ); } /* get a user pref parameter in the registry */ static BOOL get_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi ) { union sysparam_all_entry *parent_entry = entry->pref.parent; BYTE prefs[8]; if (!ptr_param) return FALSE; if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, dpi )) return FALSE; *(BOOL *)ptr_param = (prefs[entry->pref.offset] & entry->pref.mask) != 0; return TRUE; } /* set a user pref parameter in the registry */ static BOOL set_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags ) { union sysparam_all_entry *parent_entry = entry->pref.parent; BYTE prefs[8]; parent_entry->hdr.loaded = FALSE; /* force loading it again */ if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, get_system_dpi() )) return FALSE; if (PtrToUlong( ptr_param )) prefs[entry->pref.offset] |= entry->pref.mask; else prefs[entry->pref.offset] &= ~entry->pref.mask; return parent_entry->hdr.set( parent_entry, sizeof(prefs), prefs, flags ); } static BOOL get_entry_dpi( void *ptr, UINT int_param, void *ptr_param, UINT dpi ) { union sysparam_all_entry *entry = ptr; return entry->hdr.get( entry, int_param, ptr_param, dpi ); } static BOOL get_entry( void *ptr, UINT int_param, void *ptr_param ) { return get_entry_dpi( ptr, int_param, ptr_param, get_system_dpi() ); } static BOOL set_entry( void *ptr, UINT int_param, void *ptr_param, UINT flags ) { union sysparam_all_entry *entry = ptr; return entry->hdr.set( entry, int_param, ptr_param, flags ); } #define UINT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \ { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg }, (val) } } #define UINT_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \ { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg, mirror_base, reg }, (val) } } #define INT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \ { .uint = { { get_uint_entry, set_int_entry, init_int_entry, base, reg }, (val) } } #define BOOL_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \ { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg }, (val) } } #define BOOL_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \ { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg, mirror_base, reg }, (val) } } #define TWIPS_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \ { .uint = { { get_twips_entry, set_twips_entry, init_int_entry, base, reg }, (val) } } #define YESNO_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \ { .bool = { { get_yesno_entry, set_yesno_entry, init_yesno_entry, base, reg }, (val) } } #define DWORD_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \ { .dword = { { get_dword_entry, set_dword_entry, init_dword_entry, base, reg }, (val) } } #define BINARY_ENTRY(name,data,base,reg) union sysparam_all_entry entry_##name = \ { .bin = { { get_binary_entry, set_binary_entry, init_binary_entry, base, reg }, data, sizeof(data) } } #define PATH_ENTRY(name,base,reg,buffer) union sysparam_all_entry entry_##name = \ { .path = { { get_path_entry, set_path_entry, init_path_entry, base, reg }, buffer } } #define FONT_ENTRY(name,weight,base,reg) union sysparam_all_entry entry_##name = \ { .font = { { get_font_entry, set_font_entry, init_font_entry, base, reg }, (weight) } } #define USERPREF_ENTRY(name,offset,mask) union sysparam_all_entry entry_##name = \ { .pref = { { get_userpref_entry, set_userpref_entry }, &entry_USERPREFERENCESMASK, (offset), (mask) } } static UINT_ENTRY( DRAGWIDTH, 4, DESKTOP_KEY, "DragWidth" ); static UINT_ENTRY( DRAGHEIGHT, 4, DESKTOP_KEY, "DragHeight" ); static UINT_ENTRY( DOUBLECLICKTIME, 500, MOUSE_KEY, "DoubleClickSpeed" ); static UINT_ENTRY( FONTSMOOTHING, 2, DESKTOP_KEY, "FontSmoothing" ); static UINT_ENTRY( GRIDGRANULARITY, 0, DESKTOP_KEY, "GridGranularity" ); static UINT_ENTRY( KEYBOARDDELAY, 1, KEYBOARD_KEY, "KeyboardDelay" ); static UINT_ENTRY( KEYBOARDSPEED, 31, KEYBOARD_KEY, "KeyboardSpeed" ); static UINT_ENTRY( MENUSHOWDELAY, 400, DESKTOP_KEY, "MenuShowDelay" ); static UINT_ENTRY( MINARRANGE, ARW_HIDE, METRICS_KEY, "MinArrange" ); static UINT_ENTRY( MINHORZGAP, 0, METRICS_KEY, "MinHorzGap" ); static UINT_ENTRY( MINVERTGAP, 0, METRICS_KEY, "MinVertGap" ); static UINT_ENTRY( MINWIDTH, 154, METRICS_KEY, "MinWidth" ); static UINT_ENTRY( MOUSEHOVERHEIGHT, 4, MOUSE_KEY, "MouseHoverHeight" ); static UINT_ENTRY( MOUSEHOVERTIME, 400, MOUSE_KEY, "MouseHoverTime" ); static UINT_ENTRY( MOUSEHOVERWIDTH, 4, MOUSE_KEY, "MouseHoverWidth" ); static UINT_ENTRY( MOUSESPEED, 10, MOUSE_KEY, "MouseSensitivity" ); static UINT_ENTRY( MOUSETRAILS, 0, MOUSE_KEY, "MouseTrails" ); static UINT_ENTRY( SCREENSAVETIMEOUT, 300, DESKTOP_KEY, "ScreenSaveTimeOut" ); static UINT_ENTRY( WHEELSCROLLCHARS, 3, DESKTOP_KEY, "WheelScrollChars" ); static UINT_ENTRY( WHEELSCROLLLINES, 3, DESKTOP_KEY, "WheelScrollLines" ); static UINT_ENTRY_MIRROR( DOUBLECLKHEIGHT, 4, MOUSE_KEY, "DoubleClickHeight", DESKTOP_KEY ); static UINT_ENTRY_MIRROR( DOUBLECLKWIDTH, 4, MOUSE_KEY, "DoubleClickWidth", DESKTOP_KEY ); static UINT_ENTRY_MIRROR( MENUDROPALIGNMENT, 0, DESKTOP_KEY, "MenuDropAlignment", VERSION_KEY ); static INT_ENTRY( MOUSETHRESHOLD1, 6, MOUSE_KEY, "MouseThreshold1" ); static INT_ENTRY( MOUSETHRESHOLD2, 10, MOUSE_KEY, "MouseThreshold2" ); static INT_ENTRY( MOUSEACCELERATION, 1, MOUSE_KEY, "MouseSpeed" ); static BOOL_ENTRY( BLOCKSENDINPUTRESETS, FALSE, DESKTOP_KEY, "BlockSendInputResets" ); static BOOL_ENTRY( DRAGFULLWINDOWS, FALSE, DESKTOP_KEY, "DragFullWindows" ); static BOOL_ENTRY( KEYBOARDPREF, TRUE, KEYBOARDPREF_KEY, "On" ); static BOOL_ENTRY( LOWPOWERACTIVE, FALSE, DESKTOP_KEY, "LowPowerActive" ); static BOOL_ENTRY( MOUSEBUTTONSWAP, FALSE, MOUSE_KEY, "SwapMouseButtons" ); static BOOL_ENTRY( POWEROFFACTIVE, FALSE, DESKTOP_KEY, "PowerOffActive" ); static BOOL_ENTRY( SCREENREADER, FALSE, SCREENREADER_KEY, "On" ); static BOOL_ENTRY( SCREENSAVEACTIVE, TRUE, DESKTOP_KEY, "ScreenSaveActive" ); static BOOL_ENTRY( SCREENSAVERRUNNING, FALSE, DESKTOP_KEY, "WINE_ScreenSaverRunning" ); /* FIXME - real value */ static BOOL_ENTRY( SHOWSOUNDS, FALSE, SHOWSOUNDS_KEY, "On" ); static BOOL_ENTRY( SNAPTODEFBUTTON, FALSE, MOUSE_KEY, "SnapToDefaultButton" ); static BOOL_ENTRY_MIRROR( ICONTITLEWRAP, TRUE, DESKTOP_KEY, "IconTitleWrap", METRICS_KEY ); static BOOL_ENTRY( AUDIODESC_ON, FALSE, AUDIODESC_KEY, "On" ); static TWIPS_ENTRY( BORDER, -15, METRICS_KEY, "BorderWidth" ); static TWIPS_ENTRY( CAPTIONHEIGHT, -270, METRICS_KEY, "CaptionHeight" ); static TWIPS_ENTRY( CAPTIONWIDTH, -270, METRICS_KEY, "CaptionWidth" ); static TWIPS_ENTRY( ICONHORIZONTALSPACING, -1125, METRICS_KEY, "IconSpacing" ); static TWIPS_ENTRY( ICONVERTICALSPACING, -1125, METRICS_KEY, "IconVerticalSpacing" ); static TWIPS_ENTRY( MENUHEIGHT, -270, METRICS_KEY, "MenuHeight" ); static TWIPS_ENTRY( MENUWIDTH, -270, METRICS_KEY, "MenuWidth" ); static TWIPS_ENTRY( PADDEDBORDERWIDTH, 0, METRICS_KEY, "PaddedBorderWidth" ); static TWIPS_ENTRY( SCROLLHEIGHT, -240, METRICS_KEY, "ScrollHeight" ); static TWIPS_ENTRY( SCROLLWIDTH, -240, METRICS_KEY, "ScrollWidth" ); static TWIPS_ENTRY( SMCAPTIONHEIGHT, -225, METRICS_KEY, "SmCaptionHeight" ); static TWIPS_ENTRY( SMCAPTIONWIDTH, -225, METRICS_KEY, "SmCaptionWidth" ); static YESNO_ENTRY( BEEP, TRUE, SOUND_KEY, "Beep" ); static DWORD_ENTRY( ACTIVEWINDOWTRACKING, 0, MOUSE_KEY, "ActiveWindowTracking" ); static DWORD_ENTRY( ACTIVEWNDTRKTIMEOUT, 0, DESKTOP_KEY, "ActiveWndTrackTimeout" ); static DWORD_ENTRY( CARETWIDTH, 1, DESKTOP_KEY, "CaretWidth" ); static DWORD_ENTRY( DPISCALINGVER, 0, DESKTOP_KEY, "DpiScalingVer" ); static DWORD_ENTRY( FOCUSBORDERHEIGHT, 1, DESKTOP_KEY, "FocusBorderHeight" ); static DWORD_ENTRY( FOCUSBORDERWIDTH, 1, DESKTOP_KEY, "FocusBorderWidth" ); static DWORD_ENTRY( FONTSMOOTHINGCONTRAST, 0, DESKTOP_KEY, "FontSmoothingGamma" ); static DWORD_ENTRY( FONTSMOOTHINGORIENTATION, FE_FONTSMOOTHINGORIENTATIONRGB, DESKTOP_KEY, "FontSmoothingOrientation" ); static DWORD_ENTRY( FONTSMOOTHINGTYPE, FE_FONTSMOOTHINGSTANDARD, DESKTOP_KEY, "FontSmoothingType" ); static DWORD_ENTRY( FOREGROUNDFLASHCOUNT, 3, DESKTOP_KEY, "ForegroundFlashCount" ); static DWORD_ENTRY( FOREGROUNDLOCKTIMEOUT, 0, DESKTOP_KEY, "ForegroundLockTimeout" ); static DWORD_ENTRY( LOGPIXELS, 0, DESKTOP_KEY, "LogPixels" ); static DWORD_ENTRY( MOUSECLICKLOCKTIME, 1200, DESKTOP_KEY, "ClickLockTime" ); static DWORD_ENTRY( AUDIODESC_LOCALE, 0, AUDIODESC_KEY, "Locale" ); static WCHAR desk_pattern_path[MAX_PATH]; static WCHAR desk_wallpaper_path[MAX_PATH]; static PATH_ENTRY( DESKPATTERN, DESKTOP_KEY, "Pattern", desk_pattern_path ); static PATH_ENTRY( DESKWALLPAPER, DESKTOP_KEY, "Wallpaper", desk_wallpaper_path ); static BYTE user_prefs[8] = { 0x30, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, 0x00 }; static BINARY_ENTRY( USERPREFERENCESMASK, user_prefs, DESKTOP_KEY, "UserPreferencesMask" ); static FONT_ENTRY( CAPTIONLOGFONT, FW_BOLD, METRICS_KEY, "CaptionFont" ); static FONT_ENTRY( ICONTITLELOGFONT, FW_NORMAL, METRICS_KEY, "IconFont" ); static FONT_ENTRY( MENULOGFONT, FW_NORMAL, METRICS_KEY, "MenuFont" ); static FONT_ENTRY( MESSAGELOGFONT, FW_NORMAL, METRICS_KEY, "MessageFont" ); static FONT_ENTRY( SMCAPTIONLOGFONT, FW_NORMAL, METRICS_KEY, "SmCaptionFont" ); static FONT_ENTRY( STATUSLOGFONT, FW_NORMAL, METRICS_KEY, "StatusFont" ); static USERPREF_ENTRY( MENUANIMATION, 0, 0x02 ); static USERPREF_ENTRY( COMBOBOXANIMATION, 0, 0x04 ); static USERPREF_ENTRY( LISTBOXSMOOTHSCROLLING, 0, 0x08 ); static USERPREF_ENTRY( GRADIENTCAPTIONS, 0, 0x10 ); static USERPREF_ENTRY( KEYBOARDCUES, 0, 0x20 ); static USERPREF_ENTRY( ACTIVEWNDTRKZORDER, 0, 0x40 ); static USERPREF_ENTRY( HOTTRACKING, 0, 0x80 ); static USERPREF_ENTRY( MENUFADE, 1, 0x02 ); static USERPREF_ENTRY( SELECTIONFADE, 1, 0x04 ); static USERPREF_ENTRY( TOOLTIPANIMATION, 1, 0x08 ); static USERPREF_ENTRY( TOOLTIPFADE, 1, 0x10 ); static USERPREF_ENTRY( CURSORSHADOW, 1, 0x20 ); static USERPREF_ENTRY( MOUSESONAR, 1, 0x40 ); static USERPREF_ENTRY( MOUSECLICKLOCK, 1, 0x80 ); static USERPREF_ENTRY( MOUSEVANISH, 2, 0x01 ); static USERPREF_ENTRY( FLATMENU, 2, 0x02 ); static USERPREF_ENTRY( DROPSHADOW, 2, 0x04 ); static USERPREF_ENTRY( UIEFFECTS, 3, 0x80 ); static USERPREF_ENTRY( DISABLEOVERLAPPEDCONTENT, 4, 0x01 ); static USERPREF_ENTRY( CLIENTAREAANIMATION, 4, 0x02 ); static USERPREF_ENTRY( CLEARTYPE, 4, 0x10 ); static USERPREF_ENTRY( SPEECHRECOGNITION, 4, 0x20 ); /* System parameter indexes */ enum spi_index { SPI_SETWORKAREA_IDX, SPI_INDEX_COUNT }; /* indicators whether system parameter value is loaded */ static char spi_loaded[SPI_INDEX_COUNT]; static struct sysparam_rgb_entry system_colors[] = { #define RGB_ENTRY(name,val,reg) { { get_rgb_entry, set_rgb_entry, init_rgb_entry, COLORS_KEY, reg }, (val) } RGB_ENTRY( COLOR_SCROLLBAR, RGB(212, 208, 200), "Scrollbar" ), RGB_ENTRY( COLOR_BACKGROUND, RGB(58, 110, 165), "Background" ), RGB_ENTRY( COLOR_ACTIVECAPTION, RGB(10, 36, 106), "ActiveTitle" ), RGB_ENTRY( COLOR_INACTIVECAPTION, RGB(128, 128, 128), "InactiveTitle" ), RGB_ENTRY( COLOR_MENU, RGB(212, 208, 200), "Menu" ), RGB_ENTRY( COLOR_WINDOW, RGB(255, 255, 255), "Window" ), RGB_ENTRY( COLOR_WINDOWFRAME, RGB(0, 0, 0), "WindowFrame" ), RGB_ENTRY( COLOR_MENUTEXT, RGB(0, 0, 0), "MenuText" ), RGB_ENTRY( COLOR_WINDOWTEXT, RGB(0, 0, 0), "WindowText" ), RGB_ENTRY( COLOR_CAPTIONTEXT, RGB(255, 255, 255), "TitleText" ), RGB_ENTRY( COLOR_ACTIVEBORDER, RGB(212, 208, 200), "ActiveBorder" ), RGB_ENTRY( COLOR_INACTIVEBORDER, RGB(212, 208, 200), "InactiveBorder" ), RGB_ENTRY( COLOR_APPWORKSPACE, RGB(128, 128, 128), "AppWorkSpace" ), RGB_ENTRY( COLOR_HIGHLIGHT, RGB(10, 36, 106), "Hilight" ), RGB_ENTRY( COLOR_HIGHLIGHTTEXT, RGB(255, 255, 255), "HilightText" ), RGB_ENTRY( COLOR_BTNFACE, RGB(212, 208, 200), "ButtonFace" ), RGB_ENTRY( COLOR_BTNSHADOW, RGB(128, 128, 128), "ButtonShadow" ), RGB_ENTRY( COLOR_GRAYTEXT, RGB(128, 128, 128), "GrayText" ), RGB_ENTRY( COLOR_BTNTEXT, RGB(0, 0, 0), "ButtonText" ), RGB_ENTRY( COLOR_INACTIVECAPTIONTEXT, RGB(212, 208, 200), "InactiveTitleText" ), RGB_ENTRY( COLOR_BTNHIGHLIGHT, RGB(255, 255, 255), "ButtonHilight" ), RGB_ENTRY( COLOR_3DDKSHADOW, RGB(64, 64, 64), "ButtonDkShadow" ), RGB_ENTRY( COLOR_3DLIGHT, RGB(212, 208, 200), "ButtonLight" ), RGB_ENTRY( COLOR_INFOTEXT, RGB(0, 0, 0), "InfoText" ), RGB_ENTRY( COLOR_INFOBK, RGB(255, 255, 225), "InfoWindow" ), RGB_ENTRY( COLOR_ALTERNATEBTNFACE, RGB(181, 181, 181), "ButtonAlternateFace" ), RGB_ENTRY( COLOR_HOTLIGHT, RGB(0, 0, 200), "HotTrackingColor" ), RGB_ENTRY( COLOR_GRADIENTACTIVECAPTION, RGB(166, 202, 240), "GradientActiveTitle" ), RGB_ENTRY( COLOR_GRADIENTINACTIVECAPTION, RGB(192, 192, 192), "GradientInactiveTitle" ), RGB_ENTRY( COLOR_MENUHILIGHT, RGB(10, 36, 106), "MenuHilight" ), RGB_ENTRY( COLOR_MENUBAR, RGB(212, 208, 200), "MenuBar" ) #undef RGB_ENTRY }; /* entries that are initialized by default in the registry */ static union sysparam_all_entry * const default_entries[] = { (union sysparam_all_entry *)&entry_ACTIVEWINDOWTRACKING, (union sysparam_all_entry *)&entry_ACTIVEWNDTRKTIMEOUT, (union sysparam_all_entry *)&entry_BEEP, (union sysparam_all_entry *)&entry_BLOCKSENDINPUTRESETS, (union sysparam_all_entry *)&entry_BORDER, (union sysparam_all_entry *)&entry_CAPTIONHEIGHT, (union sysparam_all_entry *)&entry_CAPTIONWIDTH, (union sysparam_all_entry *)&entry_CARETWIDTH, (union sysparam_all_entry *)&entry_DESKWALLPAPER, (union sysparam_all_entry *)&entry_DOUBLECLICKTIME, (union sysparam_all_entry *)&entry_DOUBLECLKHEIGHT, (union sysparam_all_entry *)&entry_DOUBLECLKWIDTH, (union sysparam_all_entry *)&entry_DRAGFULLWINDOWS, (union sysparam_all_entry *)&entry_DRAGHEIGHT, (union sysparam_all_entry *)&entry_DRAGWIDTH, (union sysparam_all_entry *)&entry_FOCUSBORDERHEIGHT, (union sysparam_all_entry *)&entry_FOCUSBORDERWIDTH, (union sysparam_all_entry *)&entry_FONTSMOOTHING, (union sysparam_all_entry *)&entry_FONTSMOOTHINGCONTRAST, (union sysparam_all_entry *)&entry_FONTSMOOTHINGORIENTATION, (union sysparam_all_entry *)&entry_FONTSMOOTHINGTYPE, (union sysparam_all_entry *)&entry_FOREGROUNDFLASHCOUNT, (union sysparam_all_entry *)&entry_FOREGROUNDLOCKTIMEOUT, (union sysparam_all_entry *)&entry_ICONHORIZONTALSPACING, (union sysparam_all_entry *)&entry_ICONTITLEWRAP, (union sysparam_all_entry *)&entry_ICONVERTICALSPACING, (union sysparam_all_entry *)&entry_KEYBOARDDELAY, (union sysparam_all_entry *)&entry_KEYBOARDPREF, (union sysparam_all_entry *)&entry_KEYBOARDSPEED, (union sysparam_all_entry *)&entry_LOWPOWERACTIVE, (union sysparam_all_entry *)&entry_MENUHEIGHT, (union sysparam_all_entry *)&entry_MENUSHOWDELAY, (union sysparam_all_entry *)&entry_MENUWIDTH, (union sysparam_all_entry *)&entry_MOUSEACCELERATION, (union sysparam_all_entry *)&entry_MOUSEBUTTONSWAP, (union sysparam_all_entry *)&entry_MOUSECLICKLOCKTIME, (union sysparam_all_entry *)&entry_MOUSEHOVERHEIGHT, (union sysparam_all_entry *)&entry_MOUSEHOVERTIME, (union sysparam_all_entry *)&entry_MOUSEHOVERWIDTH, (union sysparam_all_entry *)&entry_MOUSESPEED, (union sysparam_all_entry *)&entry_MOUSETHRESHOLD1, (union sysparam_all_entry *)&entry_MOUSETHRESHOLD2, (union sysparam_all_entry *)&entry_PADDEDBORDERWIDTH, (union sysparam_all_entry *)&entry_SCREENREADER, (union sysparam_all_entry *)&entry_SCROLLHEIGHT, (union sysparam_all_entry *)&entry_SCROLLWIDTH, (union sysparam_all_entry *)&entry_SHOWSOUNDS, (union sysparam_all_entry *)&entry_SMCAPTIONHEIGHT, (union sysparam_all_entry *)&entry_SMCAPTIONWIDTH, (union sysparam_all_entry *)&entry_SNAPTODEFBUTTON, (union sysparam_all_entry *)&entry_USERPREFERENCESMASK, (union sysparam_all_entry *)&entry_WHEELSCROLLCHARS, (union sysparam_all_entry *)&entry_WHEELSCROLLLINES, (union sysparam_all_entry *)&entry_AUDIODESC_LOCALE, (union sysparam_all_entry *)&entry_AUDIODESC_ON, }; /*********************************************************************** * get_config_key * * Get a config key from either the app-specific or the default config */ static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name, WCHAR *buffer, DWORD size ) { char buf[2048]; KEY_VALUE_PARTIAL_INFORMATION *info = (void *)buf; if (appkey && query_reg_ascii_value( appkey, name, info, sizeof(buf) )) { size = min( info->DataLength, size - sizeof(WCHAR) ); memcpy( buffer, info->Data, size ); buffer[size / sizeof(WCHAR)] = 0; return 0; } if (defkey && query_reg_ascii_value( defkey, name, info, sizeof(buf) )) { size = min( info->DataLength, size - sizeof(WCHAR) ); memcpy( buffer, info->Data, size ); buffer[size / sizeof(WCHAR)] = 0; return 0; } return ERROR_FILE_NOT_FOUND; } void sysparams_init(void) { WCHAR buffer[MAX_PATH+16], *p, *appname; DWORD i, dispos, dpi_scaling; WCHAR layout[KL_NAMELENGTH]; pthread_mutexattr_t attr; HKEY hkey, appkey = 0; DWORD len; static const WCHAR oneW[] = {'1',0}; static const WCHAR x11driverW[] = {'\\','X','1','1',' ','D','r','i','v','e','r',0}; pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); pthread_mutex_init( &user_mutex, &attr ); pthread_mutexattr_destroy( &attr ); if ((hkey = reg_create_ascii_key( hkcu_key, "Keyboard Layout\\Preload", 0, NULL ))) { if (NtUserGetKeyboardLayoutName( layout )) set_reg_value( hkey, oneW, REG_SZ, (const BYTE *)layout, (lstrlenW(layout) + 1) * sizeof(WCHAR) ); NtClose( hkey ); } /* this one must be non-volatile */ if (!(hkey = reg_create_ascii_key( hkcu_key, "Software\\Wine", 0, NULL ))) { ERR("Can't create wine registry branch\n"); return; } /* @@ Wine registry key: HKCU\Software\Wine\Temporary System Parameters */ if (!(volatile_base_key = reg_create_ascii_key( hkey, "Temporary System Parameters", REG_OPTION_VOLATILE, &dispos ))) ERR("Can't create non-permanent wine registry branch\n"); NtClose( hkey ); config_key = reg_create_ascii_key( NULL, config_keyA, 0, NULL ); get_dword_entry( (union sysparam_all_entry *)&entry_LOGPIXELS, 0, &system_dpi, 0 ); if (!system_dpi) /* check fallback key */ { if ((hkey = reg_open_ascii_key( config_key, "Software\\Fonts" ))) { char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(DWORD)])]; KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer; if (query_reg_ascii_value( hkey, "LogPixels", value, sizeof(buffer) ) && value->Type == REG_DWORD) system_dpi = *(const DWORD *)value->Data; NtClose( hkey ); } } if (!system_dpi) system_dpi = USER_DEFAULT_SCREEN_DPI; /* FIXME: what do the DpiScalingVer flags mean? */ get_dword_entry( (union sysparam_all_entry *)&entry_DPISCALINGVER, 0, &dpi_scaling, 0 ); if (!dpi_scaling) NtUserSetProcessDpiAwarenessContext( NTUSER_DPI_PER_MONITOR_AWARE, 0 ); if (volatile_base_key && dispos == REG_CREATED_NEW_KEY) /* first process, initialize entries */ { for (i = 0; i < ARRAY_SIZE( default_entries ); i++) default_entries[i]->hdr.init( default_entries[i] ); } /* @@ Wine registry key: HKCU\Software\Wine\X11 Driver */ hkey = reg_open_hkcu_key( "Software\\Wine\\X11 Driver" ); /* open the app-specific key */ appname = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; if ((p = wcsrchr( appname, '/' ))) appname = p + 1; if ((p = wcsrchr( appname, '\\' ))) appname = p + 1; len = lstrlenW( appname ); if (len && len < MAX_PATH) { HKEY tmpkey; int i; for (i = 0; appname[i]; i++) buffer[i] = RtlDowncaseUnicodeChar( appname[i] ); buffer[i] = 0; appname = buffer; memcpy( appname + i, x11driverW, sizeof(x11driverW) ); /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\X11 Driver */ if ((tmpkey = reg_open_hkcu_key( "Software\\Wine\\AppDefaults" ))) { appkey = reg_open_key( tmpkey, appname, lstrlenW( appname ) * sizeof(WCHAR) ); NtClose( tmpkey ); } } #define IS_OPTION_TRUE(ch) \ ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1') if (!get_config_key( hkey, appkey, "GrabPointer", buffer, sizeof(buffer) )) grab_pointer = IS_OPTION_TRUE( buffer[0] ); if (!get_config_key( hkey, appkey, "GrabFullscreen", buffer, sizeof(buffer) )) grab_fullscreen = IS_OPTION_TRUE( buffer[0] ); #undef IS_OPTION_TRUE } static BOOL update_desktop_wallpaper(void) { /* FIXME: move implementation from user32 */ entry_DESKWALLPAPER.hdr.loaded = entry_DESKPATTERN.hdr.loaded = FALSE; return TRUE; } /*********************************************************************** * NtUserSystemParametersInfoForDpi (win32u.@) */ BOOL WINAPI NtUserSystemParametersInfoForDpi( UINT action, UINT val, PVOID ptr, UINT winini, UINT dpi ) { BOOL ret = FALSE; switch (action) { case SPI_GETICONTITLELOGFONT: ret = get_entry_dpi( &entry_ICONTITLELOGFONT, val, ptr, dpi ); break; case SPI_GETNONCLIENTMETRICS: { NONCLIENTMETRICSW *ncm = ptr; if (!ncm) break; ret = get_entry_dpi( &entry_BORDER, 0, &ncm->iBorderWidth, dpi ) && get_entry_dpi( &entry_SCROLLWIDTH, 0, &ncm->iScrollWidth, dpi ) && get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ncm->iScrollHeight, dpi ) && get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ncm->iCaptionWidth, dpi ) && get_entry_dpi( &entry_CAPTIONHEIGHT, 0, &ncm->iCaptionHeight, dpi ) && get_entry_dpi( &entry_CAPTIONLOGFONT, 0, &ncm->lfCaptionFont, dpi ) && get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ncm->iSmCaptionWidth, dpi ) && get_entry_dpi( &entry_SMCAPTIONHEIGHT, 0, &ncm->iSmCaptionHeight, dpi ) && get_entry_dpi( &entry_SMCAPTIONLOGFONT, 0, &ncm->lfSmCaptionFont, dpi ) && get_entry_dpi( &entry_MENUWIDTH, 0, &ncm->iMenuWidth, dpi ) && get_entry_dpi( &entry_MENUHEIGHT, 0, &ncm->iMenuHeight, dpi ) && get_entry_dpi( &entry_MENULOGFONT, 0, &ncm->lfMenuFont, dpi ) && get_entry_dpi( &entry_STATUSLOGFONT, 0, &ncm->lfStatusFont, dpi ) && get_entry_dpi( &entry_MESSAGELOGFONT, 0, &ncm->lfMessageFont, dpi ); if (ret && ncm->cbSize == sizeof(NONCLIENTMETRICSW)) ret = get_entry_dpi( &entry_PADDEDBORDERWIDTH, 0, &ncm->iPaddedBorderWidth, dpi ); normalize_nonclientmetrics( ncm ); break; } case SPI_GETICONMETRICS: { ICONMETRICSW *im = ptr; if (im && im->cbSize == sizeof(*im)) ret = get_entry_dpi( &entry_ICONHORIZONTALSPACING, 0, &im->iHorzSpacing, dpi ) && get_entry_dpi( &entry_ICONVERTICALSPACING, 0, &im->iVertSpacing, dpi ) && get_entry_dpi( &entry_ICONTITLEWRAP, 0, &im->iTitleWrap, dpi ) && get_entry_dpi( &entry_ICONTITLELOGFONT, 0, &im->lfFont, dpi ); break; } default: RtlSetLastWin32Error( ERROR_INVALID_PARAMETER ); break; } return ret; } /*********************************************************************** * NtUserSystemParametersInfo (win32u.@) * * Each system parameter has flag which shows whether the parameter * is loaded or not. Parameters, stored directly in SysParametersInfo are * loaded from registry only when they are requested and the flag is * "false", after the loading the flag is set to "true". On interprocess * notification of the parameter change the corresponding parameter flag is * set to "false". The parameter value will be reloaded when it is requested * the next time. * Parameters, backed by or depend on GetSystemMetrics are processed * differently. These parameters are always loaded. They are reloaded right * away on interprocess change notification. We can't do lazy loading because * we don't want to complicate GetSystemMetrics. * Parameters backed by driver settings are read from corresponding setting. * On the parameter change request the setting is changed. Interprocess change * notifications are ignored. * When parameter value is updated the changed value is stored in permanent * registry branch if saving is requested. Otherwise it is stored * in temporary branch * * Some SPI values can also be stored as Twips values in the registry, * don't forget the conversion! */ BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT winini ) { #define WINE_SPI_FIXME(x) \ case x: \ { \ static BOOL warn = TRUE; \ if (warn) \ { \ warn = FALSE; \ FIXME( "Unimplemented action: %u (%s)\n", x, #x ); \ } \ } \ RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE ); \ ret = FALSE; \ break #define WINE_SPI_WARN(x) \ case x: \ WARN( "Ignored action: %u (%s)\n", x, #x ); \ ret = TRUE; \ break BOOL ret = user_driver->pSystemParametersInfo( action, val, ptr, winini ); unsigned spi_idx = 0; if (!ret) switch (action) { case SPI_GETBEEP: ret = get_entry( &entry_BEEP, val, ptr ); break; case SPI_SETBEEP: ret = set_entry( &entry_BEEP, val, ptr, winini ); break; case SPI_GETMOUSE: ret = get_entry( &entry_MOUSETHRESHOLD1, val, (INT *)ptr ) && get_entry( &entry_MOUSETHRESHOLD2, val, (INT *)ptr + 1 ) && get_entry( &entry_MOUSEACCELERATION, val, (INT *)ptr + 2 ); break; case SPI_SETMOUSE: ret = set_entry( &entry_MOUSETHRESHOLD1, ((INT *)ptr)[0], ptr, winini ) && set_entry( &entry_MOUSETHRESHOLD2, ((INT *)ptr)[1], ptr, winini ) && set_entry( &entry_MOUSEACCELERATION, ((INT *)ptr)[2], ptr, winini ); break; case SPI_GETBORDER: ret = get_entry( &entry_BORDER, val, ptr ); if (*(INT*)ptr < 1) *(INT*)ptr = 1; break; case SPI_SETBORDER: ret = set_entry( &entry_BORDER, val, ptr, winini ); break; case SPI_GETKEYBOARDSPEED: ret = get_entry( &entry_KEYBOARDSPEED, val, ptr ); break; case SPI_SETKEYBOARDSPEED: if (val > 31) val = 31; ret = set_entry( &entry_KEYBOARDSPEED, val, ptr, winini ); break; WINE_SPI_WARN(SPI_LANGDRIVER); /* not implemented in Windows */ case SPI_ICONHORIZONTALSPACING: if (ptr != NULL) ret = get_entry( &entry_ICONHORIZONTALSPACING, val, ptr ); else { int min_val = map_to_dpi( 32, get_system_dpi() ); ret = set_entry( &entry_ICONHORIZONTALSPACING, max( min_val, val ), ptr, winini ); } break; case SPI_GETSCREENSAVETIMEOUT: ret = get_entry( &entry_SCREENSAVETIMEOUT, val, ptr ); break; case SPI_SETSCREENSAVETIMEOUT: ret = set_entry( &entry_SCREENSAVETIMEOUT, val, ptr, winini ); break; case SPI_GETSCREENSAVEACTIVE: ret = get_entry( &entry_SCREENSAVEACTIVE, val, ptr ); break; case SPI_SETSCREENSAVEACTIVE: ret = set_entry( &entry_SCREENSAVEACTIVE, val, ptr, winini ); break; case SPI_GETGRIDGRANULARITY: ret = get_entry( &entry_GRIDGRANULARITY, val, ptr ); break; case SPI_SETGRIDGRANULARITY: ret = set_entry( &entry_GRIDGRANULARITY, val, ptr, winini ); break; case SPI_SETDESKWALLPAPER: if (!ptr || set_entry( &entry_DESKWALLPAPER, val, ptr, winini )) ret = update_desktop_wallpaper(); break; case SPI_SETDESKPATTERN: if (!ptr || set_entry( &entry_DESKPATTERN, val, ptr, winini )) ret = update_desktop_wallpaper(); break; case SPI_GETKEYBOARDDELAY: ret = get_entry( &entry_KEYBOARDDELAY, val, ptr ); break; case SPI_SETKEYBOARDDELAY: ret = set_entry( &entry_KEYBOARDDELAY, val, ptr, winini ); break; case SPI_ICONVERTICALSPACING: if (ptr != NULL) ret = get_entry( &entry_ICONVERTICALSPACING, val, ptr ); else { int min_val = map_to_dpi( 32, get_system_dpi() ); ret = set_entry( &entry_ICONVERTICALSPACING, max( min_val, val ), ptr, winini ); } break; case SPI_GETICONTITLEWRAP: ret = get_entry( &entry_ICONTITLEWRAP, val, ptr ); break; case SPI_SETICONTITLEWRAP: ret = set_entry( &entry_ICONTITLEWRAP, val, ptr, winini ); break; case SPI_GETMENUDROPALIGNMENT: ret = get_entry( &entry_MENUDROPALIGNMENT, val, ptr ); break; case SPI_SETMENUDROPALIGNMENT: ret = set_entry( &entry_MENUDROPALIGNMENT, val, ptr, winini ); break; case SPI_SETDOUBLECLKWIDTH: ret = set_entry( &entry_DOUBLECLKWIDTH, val, ptr, winini ); break; case SPI_SETDOUBLECLKHEIGHT: ret = set_entry( &entry_DOUBLECLKHEIGHT, val, ptr, winini ); break; case SPI_GETICONTITLELOGFONT: ret = get_entry( &entry_ICONTITLELOGFONT, val, ptr ); break; case SPI_SETDOUBLECLICKTIME: ret = set_entry( &entry_DOUBLECLICKTIME, val, ptr, winini ); break; case SPI_SETMOUSEBUTTONSWAP: ret = set_entry( &entry_MOUSEBUTTONSWAP, val, ptr, winini ); break; case SPI_SETICONTITLELOGFONT: ret = set_entry( &entry_ICONTITLELOGFONT, val, ptr, winini ); break; case SPI_GETFASTTASKSWITCH: if (!ptr) return FALSE; *(BOOL *)ptr = TRUE; ret = TRUE; break; case SPI_SETFASTTASKSWITCH: /* the action is disabled */ ret = FALSE; break; case SPI_SETDRAGFULLWINDOWS: ret = set_entry( &entry_DRAGFULLWINDOWS, val, ptr, winini ); break; case SPI_GETDRAGFULLWINDOWS: ret = get_entry( &entry_DRAGFULLWINDOWS, val, ptr ); break; case SPI_GETNONCLIENTMETRICS: { NONCLIENTMETRICSW *nm = ptr; int padded_border; if (!ptr) return FALSE; ret = get_entry( &entry_BORDER, 0, &nm->iBorderWidth ) && get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border ) && get_entry( &entry_SCROLLWIDTH, 0, &nm->iScrollWidth ) && get_entry( &entry_SCROLLHEIGHT, 0, &nm->iScrollHeight ) && get_entry( &entry_CAPTIONWIDTH, 0, &nm->iCaptionWidth ) && get_entry( &entry_CAPTIONHEIGHT, 0, &nm->iCaptionHeight ) && get_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont ) && get_entry( &entry_SMCAPTIONWIDTH, 0, &nm->iSmCaptionWidth ) && get_entry( &entry_SMCAPTIONHEIGHT, 0, &nm->iSmCaptionHeight ) && get_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont ) && get_entry( &entry_MENUWIDTH, 0, &nm->iMenuWidth ) && get_entry( &entry_MENUHEIGHT, 0, &nm->iMenuHeight ) && get_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont ) && get_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont ) && get_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont ); if (ret) { nm->iBorderWidth += padded_border; if (nm->cbSize == sizeof(NONCLIENTMETRICSW)) nm->iPaddedBorderWidth = 0; } normalize_nonclientmetrics( nm ); break; } case SPI_SETNONCLIENTMETRICS: { LPNONCLIENTMETRICSW nm = ptr; int padded_border; if (nm && (nm->cbSize == sizeof(NONCLIENTMETRICSW) || nm->cbSize == FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth))) { get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border ); ret = set_entry( &entry_BORDER, nm->iBorderWidth - padded_border, NULL, winini ) && set_entry( &entry_SCROLLWIDTH, nm->iScrollWidth, NULL, winini ) && set_entry( &entry_SCROLLHEIGHT, nm->iScrollHeight, NULL, winini ) && set_entry( &entry_CAPTIONWIDTH, nm->iCaptionWidth, NULL, winini ) && set_entry( &entry_CAPTIONHEIGHT, nm->iCaptionHeight, NULL, winini ) && set_entry( &entry_SMCAPTIONWIDTH, nm->iSmCaptionWidth, NULL, winini ) && set_entry( &entry_SMCAPTIONHEIGHT, nm->iSmCaptionHeight, NULL, winini ) && set_entry( &entry_MENUWIDTH, nm->iMenuWidth, NULL, winini ) && set_entry( &entry_MENUHEIGHT, nm->iMenuHeight, NULL, winini ) && set_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont, winini ) && set_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont, winini ) && set_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont, winini ) && set_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont, winini ) && set_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont, winini ); } break; } case SPI_GETMINIMIZEDMETRICS: { MINIMIZEDMETRICS *mm = ptr; if (mm && mm->cbSize == sizeof(*mm)) { ret = get_entry( &entry_MINWIDTH, 0, &mm->iWidth ) && get_entry( &entry_MINHORZGAP, 0, &mm->iHorzGap ) && get_entry( &entry_MINVERTGAP, 0, &mm->iVertGap ) && get_entry( &entry_MINARRANGE, 0, &mm->iArrange ); mm->iWidth = max( 0, mm->iWidth ); mm->iHorzGap = max( 0, mm->iHorzGap ); mm->iVertGap = max( 0, mm->iVertGap ); mm->iArrange &= 0x0f; } break; } case SPI_SETMINIMIZEDMETRICS: { MINIMIZEDMETRICS *mm = ptr; if (mm && mm->cbSize == sizeof(*mm)) ret = set_entry( &entry_MINWIDTH, max( 0, mm->iWidth ), NULL, winini ) && set_entry( &entry_MINHORZGAP, max( 0, mm->iHorzGap ), NULL, winini ) && set_entry( &entry_MINVERTGAP, max( 0, mm->iVertGap ), NULL, winini ) && set_entry( &entry_MINARRANGE, mm->iArrange & 0x0f, NULL, winini ); break; } case SPI_GETICONMETRICS: { ICONMETRICSW *icon = ptr; if(icon && icon->cbSize == sizeof(*icon)) { ret = get_entry( &entry_ICONHORIZONTALSPACING, 0, &icon->iHorzSpacing ) && get_entry( &entry_ICONVERTICALSPACING, 0, &icon->iVertSpacing ) && get_entry( &entry_ICONTITLEWRAP, 0, &icon->iTitleWrap ) && get_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont ); } break; } case SPI_SETICONMETRICS: { ICONMETRICSW *icon = ptr; if (icon && icon->cbSize == sizeof(*icon)) ret = set_entry( &entry_ICONVERTICALSPACING, max(32,icon->iVertSpacing), NULL, winini ) && set_entry( &entry_ICONHORIZONTALSPACING, max(32,icon->iHorzSpacing), NULL, winini ) && set_entry( &entry_ICONTITLEWRAP, icon->iTitleWrap, NULL, winini ) && set_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont, winini ); break; } case SPI_SETWORKAREA: { if (!ptr) return FALSE; spi_idx = SPI_SETWORKAREA_IDX; work_area = *(RECT*)ptr; spi_loaded[spi_idx] = TRUE; ret = TRUE; break; } case SPI_GETWORKAREA: { if (!ptr) return FALSE; spi_idx = SPI_SETWORKAREA_IDX; if (!spi_loaded[spi_idx]) { struct monitor *monitor; if (!lock_display_devices()) return FALSE; LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) { if (!is_monitor_primary( monitor )) continue; work_area = monitor->rc_work; break; } unlock_display_devices(); spi_loaded[spi_idx] = TRUE; } *(RECT *)ptr = map_dpi_rect( work_area, system_dpi, get_thread_dpi() ); ret = TRUE; TRACE("work area %s\n", wine_dbgstr_rect( &work_area )); break; } WINE_SPI_FIXME(SPI_SETPENWINDOWS); case SPI_GETFILTERKEYS: { LPFILTERKEYS filter_keys = ptr; WARN("SPI_GETFILTERKEYS not fully implemented\n"); if (filter_keys && filter_keys->cbSize == sizeof(FILTERKEYS)) { /* Indicate that no FilterKeys feature available */ filter_keys->dwFlags = 0; filter_keys->iWaitMSec = 0; filter_keys->iDelayMSec = 0; filter_keys->iRepeatMSec = 0; filter_keys->iBounceMSec = 0; ret = TRUE; } break; } WINE_SPI_FIXME(SPI_SETFILTERKEYS); case SPI_GETTOGGLEKEYS: { LPTOGGLEKEYS toggle_keys = ptr; WARN("SPI_GETTOGGLEKEYS not fully implemented\n"); if (toggle_keys && toggle_keys->cbSize == sizeof(TOGGLEKEYS)) { /* Indicate that no ToggleKeys feature available */ toggle_keys->dwFlags = 0; ret = TRUE; } break; } WINE_SPI_FIXME(SPI_SETTOGGLEKEYS); case SPI_GETMOUSEKEYS: { MOUSEKEYS *mouse_keys = ptr; WARN("SPI_GETMOUSEKEYS not fully implemented\n"); if (mouse_keys && mouse_keys->cbSize == sizeof(MOUSEKEYS)) { /* Indicate that no MouseKeys feature available */ mouse_keys->dwFlags = 0; mouse_keys->iMaxSpeed = 360; mouse_keys->iTimeToMaxSpeed = 1000; mouse_keys->iCtrlSpeed = 0; mouse_keys->dwReserved1 = 0; mouse_keys->dwReserved2 = 0; ret = TRUE; } break; } WINE_SPI_FIXME(SPI_SETMOUSEKEYS); case SPI_GETSHOWSOUNDS: ret = get_entry( &entry_SHOWSOUNDS, val, ptr ); break; case SPI_SETSHOWSOUNDS: ret = set_entry( &entry_SHOWSOUNDS, val, ptr, winini ); break; case SPI_GETSTICKYKEYS: { STICKYKEYS *sticky_keys = ptr; WARN("SPI_GETSTICKYKEYS not fully implemented\n"); if (sticky_keys && sticky_keys->cbSize == sizeof(STICKYKEYS)) { /* Indicate that no StickyKeys feature available */ sticky_keys->dwFlags = 0; ret = TRUE; } break; } WINE_SPI_FIXME(SPI_SETSTICKYKEYS); case SPI_GETACCESSTIMEOUT: { ACCESSTIMEOUT *access_timeout = ptr; WARN("SPI_GETACCESSTIMEOUT not fully implemented\n"); if (access_timeout && access_timeout->cbSize == sizeof(ACCESSTIMEOUT)) { /* Indicate that no accessibility features timeout is available */ access_timeout->dwFlags = 0; access_timeout->iTimeOutMSec = 0; ret = TRUE; } break; } WINE_SPI_FIXME(SPI_SETACCESSTIMEOUT); case SPI_GETSERIALKEYS: { LPSERIALKEYSW serial_keys = ptr; WARN("SPI_GETSERIALKEYS not fully implemented\n"); if (serial_keys && serial_keys->cbSize == sizeof(SERIALKEYSW)) { /* Indicate that no SerialKeys feature available */ serial_keys->dwFlags = 0; serial_keys->lpszActivePort = NULL; serial_keys->lpszPort = NULL; serial_keys->iBaudRate = 0; serial_keys->iPortState = 0; ret = TRUE; } break; } WINE_SPI_FIXME(SPI_SETSERIALKEYS); case SPI_GETSOUNDSENTRY: { SOUNDSENTRYW *sound_sentry = ptr; WARN("SPI_GETSOUNDSENTRY not fully implemented\n"); if (sound_sentry && sound_sentry->cbSize == sizeof(SOUNDSENTRYW)) { /* Indicate that no SoundSentry feature available */ sound_sentry->dwFlags = 0; sound_sentry->iFSTextEffect = 0; sound_sentry->iFSTextEffectMSec = 0; sound_sentry->iFSTextEffectColorBits = 0; sound_sentry->iFSGrafEffect = 0; sound_sentry->iFSGrafEffectMSec = 0; sound_sentry->iFSGrafEffectColor = 0; sound_sentry->iWindowsEffect = 0; sound_sentry->iWindowsEffectMSec = 0; sound_sentry->lpszWindowsEffectDLL = 0; sound_sentry->iWindowsEffectOrdinal = 0; ret = TRUE; } break; } WINE_SPI_FIXME(SPI_SETSOUNDSENTRY); case SPI_GETHIGHCONTRAST: { HIGHCONTRASTW *high_contrast = ptr; WARN("SPI_GETHIGHCONTRAST not fully implemented\n"); if (high_contrast && high_contrast->cbSize == sizeof(HIGHCONTRASTW)) { /* Indicate that no high contrast feature available */ high_contrast->dwFlags = 0; high_contrast->lpszDefaultScheme = NULL; ret = TRUE; } break; } WINE_SPI_FIXME(SPI_SETHIGHCONTRAST); case SPI_GETKEYBOARDPREF: ret = get_entry( &entry_KEYBOARDPREF, val, ptr ); break; case SPI_SETKEYBOARDPREF: ret = set_entry( &entry_KEYBOARDPREF, val, ptr, winini ); break; case SPI_GETSCREENREADER: ret = get_entry( &entry_SCREENREADER, val, ptr ); break; case SPI_SETSCREENREADER: ret = set_entry( &entry_SCREENREADER, val, ptr, winini ); break; case SPI_GETANIMATION: { ANIMATIONINFO *anim_info = ptr; /* Tell it "disabled" */ if (anim_info && anim_info->cbSize == sizeof(ANIMATIONINFO)) { /* Minimize and restore animation is disabled (nonzero == enabled) */ anim_info->iMinAnimate = 0; ret = TRUE; } break; } WINE_SPI_WARN(SPI_SETANIMATION); case SPI_GETFONTSMOOTHING: ret = get_entry( &entry_FONTSMOOTHING, val, ptr ); if (ret) *(UINT *)ptr = (*(UINT *)ptr != 0); break; case SPI_SETFONTSMOOTHING: val = val ? 2 : 0; /* Win NT4/2k/XP behavior */ ret = set_entry( &entry_FONTSMOOTHING, val, ptr, winini ); break; case SPI_SETDRAGWIDTH: ret = set_entry( &entry_DRAGWIDTH, val, ptr, winini ); break; case SPI_SETDRAGHEIGHT: ret = set_entry( &entry_DRAGHEIGHT, val, ptr, winini ); break; WINE_SPI_FIXME(SPI_SETHANDHELD); WINE_SPI_FIXME(SPI_GETLOWPOWERTIMEOUT); WINE_SPI_FIXME(SPI_GETPOWEROFFTIMEOUT); WINE_SPI_FIXME(SPI_SETLOWPOWERTIMEOUT); WINE_SPI_FIXME(SPI_SETPOWEROFFTIMEOUT); case SPI_GETLOWPOWERACTIVE: ret = get_entry( &entry_LOWPOWERACTIVE, val, ptr ); break; case SPI_SETLOWPOWERACTIVE: ret = set_entry( &entry_LOWPOWERACTIVE, val, ptr, winini ); break; case SPI_GETPOWEROFFACTIVE: ret = get_entry( &entry_POWEROFFACTIVE, val, ptr ); break; case SPI_SETPOWEROFFACTIVE: ret = set_entry( &entry_POWEROFFACTIVE, val, ptr, winini ); break; WINE_SPI_FIXME(SPI_SETCURSORS); WINE_SPI_FIXME(SPI_SETICONS); case SPI_GETDEFAULTINPUTLANG: ret = NtUserGetKeyboardLayout(0) != 0; break; WINE_SPI_FIXME(SPI_SETDEFAULTINPUTLANG); WINE_SPI_FIXME(SPI_SETLANGTOGGLE); case SPI_GETWINDOWSEXTENSION: WARN( "pretend no support for Win9x Plus! for now.\n" ); ret = FALSE; /* yes, this is the result value */ break; case SPI_SETMOUSETRAILS: ret = set_entry( &entry_MOUSETRAILS, val, ptr, winini ); break; case SPI_GETMOUSETRAILS: ret = get_entry( &entry_MOUSETRAILS, val, ptr ); break; case SPI_GETSNAPTODEFBUTTON: ret = get_entry( &entry_SNAPTODEFBUTTON, val, ptr ); break; case SPI_SETSNAPTODEFBUTTON: ret = set_entry( &entry_SNAPTODEFBUTTON, val, ptr, winini ); break; case SPI_SETSCREENSAVERRUNNING: ret = set_entry( &entry_SCREENSAVERRUNNING, val, ptr, winini ); break; case SPI_GETMOUSEHOVERWIDTH: ret = get_entry( &entry_MOUSEHOVERWIDTH, val, ptr ); break; case SPI_SETMOUSEHOVERWIDTH: ret = set_entry( &entry_MOUSEHOVERWIDTH, val, ptr, winini ); break; case SPI_GETMOUSEHOVERHEIGHT: ret = get_entry( &entry_MOUSEHOVERHEIGHT, val, ptr ); break; case SPI_SETMOUSEHOVERHEIGHT: ret = set_entry( &entry_MOUSEHOVERHEIGHT, val, ptr, winini ); break; case SPI_GETMOUSEHOVERTIME: ret = get_entry( &entry_MOUSEHOVERTIME, val, ptr ); break; case SPI_SETMOUSEHOVERTIME: ret = set_entry( &entry_MOUSEHOVERTIME, val, ptr, winini ); break; case SPI_GETWHEELSCROLLLINES: ret = get_entry( &entry_WHEELSCROLLLINES, val, ptr ); break; case SPI_SETWHEELSCROLLLINES: ret = set_entry( &entry_WHEELSCROLLLINES, val, ptr, winini ); break; case SPI_GETMENUSHOWDELAY: ret = get_entry( &entry_MENUSHOWDELAY, val, ptr ); break; case SPI_SETMENUSHOWDELAY: ret = set_entry( &entry_MENUSHOWDELAY, val, ptr, winini ); break; case SPI_GETWHEELSCROLLCHARS: ret = get_entry( &entry_WHEELSCROLLCHARS, val, ptr ); break; case SPI_SETWHEELSCROLLCHARS: ret = set_entry( &entry_WHEELSCROLLCHARS, val, ptr, winini ); break; WINE_SPI_FIXME(SPI_GETSHOWIMEUI); WINE_SPI_FIXME(SPI_SETSHOWIMEUI); case SPI_GETMOUSESPEED: ret = get_entry( &entry_MOUSESPEED, val, ptr ); break; case SPI_SETMOUSESPEED: ret = set_entry( &entry_MOUSESPEED, val, ptr, winini ); break; case SPI_GETSCREENSAVERRUNNING: ret = get_entry( &entry_SCREENSAVERRUNNING, val, ptr ); break; case SPI_GETDESKWALLPAPER: ret = get_entry( &entry_DESKWALLPAPER, val, ptr ); break; case SPI_GETACTIVEWINDOWTRACKING: ret = get_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr ); break; case SPI_SETACTIVEWINDOWTRACKING: ret = set_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr, winini ); break; case SPI_GETMENUANIMATION: ret = get_entry( &entry_MENUANIMATION, val, ptr ); break; case SPI_SETMENUANIMATION: ret = set_entry( &entry_MENUANIMATION, val, ptr, winini ); break; case SPI_GETCOMBOBOXANIMATION: ret = get_entry( &entry_COMBOBOXANIMATION, val, ptr ); break; case SPI_SETCOMBOBOXANIMATION: ret = set_entry( &entry_COMBOBOXANIMATION, val, ptr, winini ); break; case SPI_GETLISTBOXSMOOTHSCROLLING: ret = get_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr ); break; case SPI_SETLISTBOXSMOOTHSCROLLING: ret = set_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr, winini ); break; case SPI_GETGRADIENTCAPTIONS: ret = get_entry( &entry_GRADIENTCAPTIONS, val, ptr ); break; case SPI_SETGRADIENTCAPTIONS: ret = set_entry( &entry_GRADIENTCAPTIONS, val, ptr, winini ); break; case SPI_GETKEYBOARDCUES: ret = get_entry( &entry_KEYBOARDCUES, val, ptr ); break; case SPI_SETKEYBOARDCUES: ret = set_entry( &entry_KEYBOARDCUES, val, ptr, winini ); break; case SPI_GETACTIVEWNDTRKZORDER: ret = get_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr ); break; case SPI_SETACTIVEWNDTRKZORDER: ret = set_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr, winini ); break; case SPI_GETHOTTRACKING: ret = get_entry( &entry_HOTTRACKING, val, ptr ); break; case SPI_SETHOTTRACKING: ret = set_entry( &entry_HOTTRACKING, val, ptr, winini ); break; case SPI_GETMENUFADE: ret = get_entry( &entry_MENUFADE, val, ptr ); break; case SPI_SETMENUFADE: ret = set_entry( &entry_MENUFADE, val, ptr, winini ); break; case SPI_GETSELECTIONFADE: ret = get_entry( &entry_SELECTIONFADE, val, ptr ); break; case SPI_SETSELECTIONFADE: ret = set_entry( &entry_SELECTIONFADE, val, ptr, winini ); break; case SPI_GETTOOLTIPANIMATION: ret = get_entry( &entry_TOOLTIPANIMATION, val, ptr ); break; case SPI_SETTOOLTIPANIMATION: ret = set_entry( &entry_TOOLTIPANIMATION, val, ptr, winini ); break; case SPI_GETTOOLTIPFADE: ret = get_entry( &entry_TOOLTIPFADE, val, ptr ); break; case SPI_SETTOOLTIPFADE: ret = set_entry( &entry_TOOLTIPFADE, val, ptr, winini ); break; case SPI_GETCURSORSHADOW: ret = get_entry( &entry_CURSORSHADOW, val, ptr ); break; case SPI_SETCURSORSHADOW: ret = set_entry( &entry_CURSORSHADOW, val, ptr, winini ); break; case SPI_GETMOUSESONAR: ret = get_entry( &entry_MOUSESONAR, val, ptr ); break; case SPI_SETMOUSESONAR: ret = set_entry( &entry_MOUSESONAR, val, ptr, winini ); break; case SPI_GETMOUSECLICKLOCK: ret = get_entry( &entry_MOUSECLICKLOCK, val, ptr ); break; case SPI_SETMOUSECLICKLOCK: ret = set_entry( &entry_MOUSECLICKLOCK, val, ptr, winini ); break; case SPI_GETMOUSEVANISH: ret = get_entry( &entry_MOUSEVANISH, val, ptr ); break; case SPI_SETMOUSEVANISH: ret = set_entry( &entry_MOUSEVANISH, val, ptr, winini ); break; case SPI_GETFLATMENU: ret = get_entry( &entry_FLATMENU, val, ptr ); break; case SPI_SETFLATMENU: ret = set_entry( &entry_FLATMENU, val, ptr, winini ); break; case SPI_GETDROPSHADOW: ret = get_entry( &entry_DROPSHADOW, val, ptr ); break; case SPI_SETDROPSHADOW: ret = set_entry( &entry_DROPSHADOW, val, ptr, winini ); break; case SPI_GETBLOCKSENDINPUTRESETS: ret = get_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr ); break; case SPI_SETBLOCKSENDINPUTRESETS: ret = set_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr, winini ); break; case SPI_GETUIEFFECTS: ret = get_entry( &entry_UIEFFECTS, val, ptr ); break; case SPI_SETUIEFFECTS: /* FIXME: this probably should mask other UI effect values when unset */ ret = set_entry( &entry_UIEFFECTS, val, ptr, winini ); break; case SPI_GETDISABLEOVERLAPPEDCONTENT: ret = get_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr ); break; case SPI_SETDISABLEOVERLAPPEDCONTENT: ret = set_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr, winini ); break; case SPI_GETCLIENTAREAANIMATION: ret = get_entry( &entry_CLIENTAREAANIMATION, val, ptr ); break; case SPI_SETCLIENTAREAANIMATION: ret = set_entry( &entry_CLIENTAREAANIMATION, val, ptr, winini ); break; case SPI_GETCLEARTYPE: ret = get_entry( &entry_CLEARTYPE, val, ptr ); break; case SPI_SETCLEARTYPE: ret = set_entry( &entry_CLEARTYPE, val, ptr, winini ); break; case SPI_GETSPEECHRECOGNITION: ret = get_entry( &entry_SPEECHRECOGNITION, val, ptr ); break; case SPI_SETSPEECHRECOGNITION: ret = set_entry( &entry_SPEECHRECOGNITION, val, ptr, winini ); break; case SPI_GETFOREGROUNDLOCKTIMEOUT: ret = get_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr ); break; case SPI_SETFOREGROUNDLOCKTIMEOUT: /* FIXME: this should check that the calling thread * is able to change the foreground window */ ret = set_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr, winini ); break; case SPI_GETACTIVEWNDTRKTIMEOUT: ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr ); break; case SPI_SETACTIVEWNDTRKTIMEOUT: ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr ); break; case SPI_GETFOREGROUNDFLASHCOUNT: ret = get_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr ); break; case SPI_SETFOREGROUNDFLASHCOUNT: ret = set_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr, winini ); break; case SPI_GETCARETWIDTH: ret = get_entry( &entry_CARETWIDTH, val, ptr ); break; case SPI_SETCARETWIDTH: ret = set_entry( &entry_CARETWIDTH, val, ptr, winini ); break; case SPI_GETMOUSECLICKLOCKTIME: ret = get_entry( &entry_MOUSECLICKLOCKTIME, val, ptr ); break; case SPI_SETMOUSECLICKLOCKTIME: ret = set_entry( &entry_MOUSECLICKLOCKTIME, val, ptr, winini ); break; case SPI_GETFONTSMOOTHINGTYPE: ret = get_entry( &entry_FONTSMOOTHINGTYPE, val, ptr ); break; case SPI_SETFONTSMOOTHINGTYPE: ret = set_entry( &entry_FONTSMOOTHINGTYPE, val, ptr, winini ); break; case SPI_GETFONTSMOOTHINGCONTRAST: ret = get_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr ); break; case SPI_SETFONTSMOOTHINGCONTRAST: ret = set_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr, winini ); break; case SPI_GETFOCUSBORDERWIDTH: ret = get_entry( &entry_FOCUSBORDERWIDTH, val, ptr ); break; case SPI_GETFOCUSBORDERHEIGHT: ret = get_entry( &entry_FOCUSBORDERHEIGHT, val, ptr ); break; case SPI_SETFOCUSBORDERWIDTH: ret = set_entry( &entry_FOCUSBORDERWIDTH, val, ptr, winini ); break; case SPI_SETFOCUSBORDERHEIGHT: ret = set_entry( &entry_FOCUSBORDERHEIGHT, val, ptr, winini ); break; case SPI_GETFONTSMOOTHINGORIENTATION: ret = get_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr ); break; case SPI_SETFONTSMOOTHINGORIENTATION: ret = set_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr, winini ); break; case SPI_GETAUDIODESCRIPTION: { AUDIODESCRIPTION *audio = ptr; if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) ) { ret = get_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled ) && get_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale ); } break; } case SPI_SETAUDIODESCRIPTION: { AUDIODESCRIPTION *audio = ptr; if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) ) { ret = set_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled, winini) && set_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale, winini ); } break; } default: FIXME( "Unknown action: %u\n", action ); RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE ); ret = FALSE; break; } if (ret && (winini & SPIF_UPDATEINIFILE)) { static const WCHAR emptyW[1]; if (winini & (SPIF_SENDWININICHANGE | SPIF_SENDCHANGE)) send_message_timeout( HWND_BROADCAST, WM_SETTINGCHANGE, action, (LPARAM) emptyW, SMTO_ABORTIFHUNG, 2000, FALSE ); } TRACE( "(%u, %u, %p, %u) ret %d\n", action, val, ptr, winini, ret ); return ret; #undef WINE_SPI_FIXME #undef WINE_SPI_WARN } int get_system_metrics( int index ) { NONCLIENTMETRICSW ncm; MINIMIZEDMETRICS mm; ICONMETRICSW im; RECT rect; UINT ret; HDC hdc; /* some metrics are dynamic */ switch (index) { case SM_CXVSCROLL: case SM_CYHSCROLL: get_entry( &entry_SCROLLWIDTH, 0, &ret ); return max( ret, 8 ); case SM_CYCAPTION: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); return ncm.iCaptionHeight + 1; case SM_CXBORDER: case SM_CYBORDER: /* SM_C{X,Y}BORDER always returns 1 regardless of 'BorderWidth' value in registry */ return 1; case SM_CXDLGFRAME: case SM_CYDLGFRAME: return 3; case SM_CYVTHUMB: case SM_CXHTHUMB: case SM_CYVSCROLL: case SM_CXHSCROLL: get_entry( &entry_SCROLLHEIGHT, 0, &ret ); return max( ret, 8 ); case SM_CXICON: case SM_CYICON: return map_to_dpi( 32, get_system_dpi() ); case SM_CXCURSOR: case SM_CYCURSOR: ret = map_to_dpi( 32, get_system_dpi() ); if (ret >= 64) return 64; if (ret >= 48) return 48; return 32; case SM_CYMENU: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); return ncm.iMenuHeight + 1; case SM_CXFULLSCREEN: /* see the remark for SM_CXMAXIMIZED, at least this formulation is correct */ return get_system_metrics( SM_CXMAXIMIZED ) - 2 * get_system_metrics( SM_CXFRAME ); case SM_CYFULLSCREEN: /* see the remark for SM_CYMAXIMIZED, at least this formulation is * correct */ return get_system_metrics( SM_CYMAXIMIZED ) - get_system_metrics( SM_CYMIN ); case SM_CYKANJIWINDOW: return 0; case SM_MOUSEPRESENT: return 1; case SM_DEBUG: return 0; case SM_SWAPBUTTON: get_entry( &entry_MOUSEBUTTONSWAP, 0, &ret ); return ret; case SM_RESERVED1: case SM_RESERVED2: case SM_RESERVED3: case SM_RESERVED4: return 0; case SM_CXMIN: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); hdc = get_display_dc(); get_text_metr_size( hdc, &ncm.lfCaptionFont, NULL, &ret ); release_display_dc( hdc ); return 3 * ncm.iCaptionWidth + ncm.iCaptionHeight + 4 * ret + 2 * get_system_metrics( SM_CXFRAME ) + 4; case SM_CYMIN: return get_system_metrics( SM_CYCAPTION ) + 2 * get_system_metrics( SM_CYFRAME ); case SM_CXSIZE: get_entry( &entry_CAPTIONWIDTH, 0, &ret ); return max( ret, 8 ); case SM_CYSIZE: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); return ncm.iCaptionHeight; case SM_CXFRAME: get_entry( &entry_BORDER, 0, &ret ); ret = max( ret, 1 ); return get_system_metrics( SM_CXDLGFRAME ) + ret; case SM_CYFRAME: get_entry( &entry_BORDER, 0, &ret ); ret = max( ret, 1 ); return get_system_metrics( SM_CYDLGFRAME ) + ret; case SM_CXMINTRACK: return get_system_metrics( SM_CXMIN ); case SM_CYMINTRACK: return get_system_metrics( SM_CYMIN ); case SM_CXDOUBLECLK: get_entry( &entry_DOUBLECLKWIDTH, 0, &ret ); return ret; case SM_CYDOUBLECLK: get_entry( &entry_DOUBLECLKHEIGHT, 0, &ret ); return ret; case SM_CXICONSPACING: im.cbSize = sizeof(im); NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 ); return im.iHorzSpacing; case SM_CYICONSPACING: im.cbSize = sizeof(im); NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 ); return im.iVertSpacing; case SM_MENUDROPALIGNMENT: NtUserSystemParametersInfo( SPI_GETMENUDROPALIGNMENT, 0, &ret, 0 ); return ret; case SM_PENWINDOWS: return 0; case SM_DBCSENABLED: return ansi_cp.MaximumCharacterSize > 1; case SM_CMOUSEBUTTONS: return 3; case SM_SECURE: return 0; case SM_CXEDGE: return get_system_metrics( SM_CXBORDER ) + 1; case SM_CYEDGE: return get_system_metrics( SM_CYBORDER ) + 1; case SM_CXMINSPACING: mm.cbSize = sizeof(mm); NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 ); return get_system_metrics( SM_CXMINIMIZED ) + mm.iHorzGap; case SM_CYMINSPACING: mm.cbSize = sizeof(mm); NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 ); return get_system_metrics( SM_CYMINIMIZED ) + mm.iVertGap; case SM_CXSMICON: case SM_CYSMICON: return map_to_dpi( 16, get_system_dpi() ) & ~1; case SM_CYSMCAPTION: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); return ncm.iSmCaptionHeight + 1; case SM_CXSMSIZE: get_entry( &entry_SMCAPTIONWIDTH, 0, &ret ); return ret; case SM_CYSMSIZE: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); return ncm.iSmCaptionHeight; case SM_CXMENUSIZE: get_entry( &entry_MENUWIDTH, 0, &ret ); return ret; case SM_CYMENUSIZE: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); return ncm.iMenuHeight; case SM_ARRANGE: mm.cbSize = sizeof(mm); NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 ); return mm.iArrange; case SM_CXMINIMIZED: mm.cbSize = sizeof(mm); NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 ); return mm.iWidth + 6; case SM_CYMINIMIZED: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); return ncm.iCaptionHeight + 6; case SM_CXMAXTRACK: return get_system_metrics( SM_CXVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CXFRAME ); case SM_CYMAXTRACK: return get_system_metrics( SM_CYVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CYFRAME ); case SM_CXMAXIMIZED: /* FIXME: subtract the width of any vertical application toolbars*/ return get_system_metrics( SM_CXSCREEN ) + 2 * get_system_metrics( SM_CXFRAME ); case SM_CYMAXIMIZED: /* FIXME: subtract the width of any horizontal application toolbars*/ return get_system_metrics( SM_CYSCREEN ) + 2 * get_system_metrics( SM_CYCAPTION ); case SM_NETWORK: return 3; /* FIXME */ case SM_CLEANBOOT: return 0; /* 0 = ok, 1 = failsafe, 2 = failsafe + network */ case SM_CXDRAG: get_entry( &entry_DRAGWIDTH, 0, &ret ); return ret; case SM_CYDRAG: get_entry( &entry_DRAGHEIGHT, 0, &ret ); return ret; case SM_SHOWSOUNDS: get_entry( &entry_SHOWSOUNDS, 0, &ret ); return ret; case SM_CXMENUCHECK: case SM_CYMENUCHECK: { TEXTMETRICW tm; ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); hdc = get_display_dc(); get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL ); release_display_dc( hdc ); return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading + 1) / 2) * 2 - 1; } case SM_SLOWMACHINE: return 0; /* Never true */ case SM_MIDEASTENABLED: return 0; /* FIXME */ case SM_MOUSEWHEELPRESENT: return 1; case SM_CXSCREEN: rect = get_primary_monitor_rect( get_thread_dpi() ); return rect.right - rect.left; case SM_CYSCREEN: rect = get_primary_monitor_rect( get_thread_dpi() ); return rect.bottom - rect.top; case SM_XVIRTUALSCREEN: rect = get_virtual_screen_rect( get_thread_dpi() ); return rect.left; case SM_YVIRTUALSCREEN: rect = get_virtual_screen_rect( get_thread_dpi() ); return rect.top; case SM_CXVIRTUALSCREEN: rect = get_virtual_screen_rect( get_thread_dpi() ); return rect.right - rect.left; case SM_CYVIRTUALSCREEN: rect = get_virtual_screen_rect( get_thread_dpi() ); return rect.bottom - rect.top; case SM_CMONITORS: if (!lock_display_devices()) return FALSE; ret = active_unique_monitor_count(); unlock_display_devices(); return ret; case SM_SAMEDISPLAYFORMAT: return 1; case SM_IMMENABLED: return 0; /* FIXME */ case SM_CXFOCUSBORDER: case SM_CYFOCUSBORDER: return 1; case SM_TABLETPC: case SM_MEDIACENTER: return 0; case SM_CMETRICS: return SM_CMETRICS; default: return 0; } } static int get_system_metrics_for_dpi( int index, unsigned int dpi ) { NONCLIENTMETRICSW ncm; ICONMETRICSW im; UINT ret; HDC hdc; /* some metrics are dynamic */ switch (index) { case SM_CXVSCROLL: case SM_CYHSCROLL: get_entry_dpi( &entry_SCROLLWIDTH, 0, &ret, dpi ); return max( ret, 8 ); case SM_CYCAPTION: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi ); return ncm.iCaptionHeight + 1; case SM_CYVTHUMB: case SM_CXHTHUMB: case SM_CYVSCROLL: case SM_CXHSCROLL: get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ret, dpi ); return max( ret, 8 ); case SM_CXICON: case SM_CYICON: return map_to_dpi( 32, dpi ); case SM_CXCURSOR: case SM_CYCURSOR: ret = map_to_dpi( 32, dpi ); if (ret >= 64) return 64; if (ret >= 48) return 48; return 32; case SM_CYMENU: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi ); return ncm.iMenuHeight + 1; case SM_CXSIZE: get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ret, dpi ); return max( ret, 8 ); case SM_CYSIZE: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi ); return ncm.iCaptionHeight; case SM_CXFRAME: get_entry_dpi( &entry_BORDER, 0, &ret, dpi ); ret = max( ret, 1 ); return get_system_metrics_for_dpi( SM_CXDLGFRAME, dpi ) + ret; case SM_CYFRAME: get_entry_dpi( &entry_BORDER, 0, &ret, dpi ); ret = max( ret, 1 ); return get_system_metrics_for_dpi( SM_CYDLGFRAME, dpi ) + ret; case SM_CXICONSPACING: im.cbSize = sizeof(im); NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi ); return im.iHorzSpacing; case SM_CYICONSPACING: im.cbSize = sizeof(im); NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi ); return im.iVertSpacing; case SM_CXSMICON: case SM_CYSMICON: return map_to_dpi( 16, dpi ) & ~1; case SM_CYSMCAPTION: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi ); return ncm.iSmCaptionHeight + 1; case SM_CXSMSIZE: get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ret, dpi ); return ret; case SM_CYSMSIZE: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi ); return ncm.iSmCaptionHeight; case SM_CXMENUSIZE: get_entry_dpi( &entry_MENUWIDTH, 0, &ret, dpi ); return ret; case SM_CYMENUSIZE: ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi ); return ncm.iMenuHeight; case SM_CXMENUCHECK: case SM_CYMENUCHECK: { TEXTMETRICW tm; ncm.cbSize = sizeof(ncm); NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi ); hdc = get_display_dc(); get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL); release_display_dc( hdc ); return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading - 1) | 1); } default: return get_system_metrics( index ); } } COLORREF get_sys_color( int index ) { COLORREF ret = 0; if (index >= 0 && index < ARRAY_SIZE( system_colors )) get_entry( &system_colors[index], 0, &ret ); return ret; } HBRUSH get_55aa_brush(void) { static const WORD pattern[] = { 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa }; static HBRUSH brush_55aa; if (!brush_55aa) { HBITMAP bitmap = NtGdiCreateBitmap( 8, 8, 1, 1, pattern ); HBRUSH brush = NtGdiCreatePatternBrushInternal( bitmap, FALSE, FALSE ); NtGdiDeleteObjectApp( bitmap ); make_gdi_object_system( brush, TRUE ); if (InterlockedCompareExchangePointer( (void **)&brush_55aa, brush, 0 )) { make_gdi_object_system( brush, FALSE ); NtGdiDeleteObjectApp( brush ); } } return brush_55aa; } HBRUSH get_sys_color_brush( unsigned int index ) { if (index == COLOR_55AA_BRUSH) return get_55aa_brush(); if (index >= ARRAY_SIZE( system_colors )) return 0; if (!system_colors[index].brush) { HBRUSH brush = NtGdiCreateSolidBrush( get_sys_color( index ), NULL ); make_gdi_object_system( brush, TRUE ); if (InterlockedCompareExchangePointer( (void **)&system_colors[index].brush, brush, 0 )) { make_gdi_object_system( brush, FALSE ); NtGdiDeleteObjectApp( brush ); } } return system_colors[index].brush; } HPEN get_sys_color_pen( unsigned int index ) { if (index >= ARRAY_SIZE( system_colors )) return 0; if (!system_colors[index].pen) { HPEN pen = NtGdiCreatePen( PS_SOLID, 1, get_sys_color( index ), NULL ); make_gdi_object_system( pen, TRUE ); if (InterlockedCompareExchangePointer( (void **)&system_colors[index].pen, pen, 0 )) { make_gdi_object_system( pen, FALSE ); NtGdiDeleteObjectApp( pen ); } } return system_colors[index].pen; } /********************************************************************** * NtUserGetDoubleClickTime (win32u.@) */ UINT WINAPI NtUserGetDoubleClickTime(void) { UINT time = 0; get_entry( &entry_DOUBLECLICKTIME, 0, &time ); if (!time) time = 500; return time; } /************************************************************************* * NtUserSetSysColors (win32u.@) */ BOOL WINAPI NtUserSetSysColors( INT count, const INT *colors, const COLORREF *values ) { int i; if (IS_INTRESOURCE(colors)) return FALSE; /* stupid app passes a color instead of an array */ for (i = 0; i < count; i++) if (colors[i] >= 0 && colors[i] < ARRAY_SIZE( system_colors )) set_entry( &system_colors[colors[i]], values[i], 0, 0 ); /* Send WM_SYSCOLORCHANGE message to all windows */ send_message_timeout( HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0, SMTO_ABORTIFHUNG, 2000, FALSE ); /* Repaint affected portions of all visible windows */ NtUserRedrawWindow( 0, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN ); return TRUE; } static LONG dpi_awareness; /*********************************************************************** * NtUserSetProcessDpiAwarenessContext (win32u.@) */ BOOL WINAPI NtUserSetProcessDpiAwarenessContext( ULONG awareness, ULONG unknown ) { switch (awareness) { case NTUSER_DPI_UNAWARE: case NTUSER_DPI_SYSTEM_AWARE: case NTUSER_DPI_PER_MONITOR_AWARE: case NTUSER_DPI_PER_MONITOR_AWARE_V2: case NTUSER_DPI_PER_UNAWARE_GDISCALED: break; default: RtlSetLastWin32Error( ERROR_INVALID_PARAMETER ); return FALSE; } return !InterlockedCompareExchange( &dpi_awareness, awareness, 0 ); } /*********************************************************************** * NtUserGetProcessDpiAwarenessContext (win32u.@) */ ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process ) { DPI_AWARENESS val; if (process && process != GetCurrentProcess()) { WARN( "not supported on other process %p\n", process ); return NTUSER_DPI_UNAWARE; } val = ReadNoFence( &dpi_awareness ); if (!val) return NTUSER_DPI_UNAWARE; return val; } BOOL message_beep( UINT i ) { BOOL active = TRUE; NtUserSystemParametersInfo( SPI_GETBEEP, 0, &active, FALSE ); if (active) user_driver->pBeep(); return TRUE; } static DWORD exiting_thread_id; /********************************************************************** * is_exiting_thread */ BOOL is_exiting_thread( DWORD tid ) { return tid == exiting_thread_id; } static void thread_detach(void) { struct user_thread_info *thread_info = get_user_thread_info(); user_driver->pThreadDetach(); free( thread_info->key_state ); thread_info->key_state = 0; free( thread_info->rawinput ); destroy_thread_windows(); cleanup_imm_thread(); NtClose( thread_info->server_queue ); exiting_thread_id = 0; } /*********************************************************************** * NtUserCallNoParam (win32u.@) */ ULONG_PTR WINAPI NtUserCallNoParam( ULONG code ) { switch(code) { case NtUserCallNoParam_DestroyCaret: return destroy_caret(); case NtUserCallNoParam_GetDesktopWindow: return HandleToUlong( get_desktop_window() ); case NtUserCallNoParam_GetDialogBaseUnits: return get_dialog_base_units(); case NtUserCallNoParam_GetInputState: return get_input_state(); case NtUserCallNoParam_GetProcessDefaultLayout: return process_layout; case NtUserCallNoParam_GetProgmanWindow: return HandleToUlong( get_progman_window() ); case NtUserCallNoParam_GetShellWindow: return HandleToUlong( get_shell_window() ); case NtUserCallNoParam_GetTaskmanWindow: return HandleToUlong( get_taskman_window() ); case NtUserCallNoParam_ReleaseCapture: return release_capture(); /* temporary exports */ case NtUserExitingThread: exiting_thread_id = GetCurrentThreadId(); return 0; case NtUserThreadDetach: thread_detach(); return 0; default: FIXME( "invalid code %u\n", (int)code ); return 0; } } /*********************************************************************** * NtUserCallOneParam (win32u.@) */ ULONG_PTR WINAPI NtUserCallOneParam( ULONG_PTR arg, ULONG code ) { switch(code) { case NtUserCallOneParam_BeginDeferWindowPos: return HandleToUlong( begin_defer_window_pos( arg )); case NtUserCallOneParam_CreateCursorIcon: return HandleToUlong( alloc_cursoricon_handle( arg )); case NtUserCallOneParam_CreateMenu: return HandleToUlong( create_menu( arg ) ); case NtUserCallOneParam_EnableDC: return set_dce_flags( UlongToHandle(arg), DCHF_ENABLEDC ); case NtUserCallOneParam_EnableThunkLock: enable_thunk_lock = arg; return 0; case NtUserCallOneParam_EnumClipboardFormats: return enum_clipboard_formats( arg ); case NtUserCallOneParam_GetClipCursor: return get_clip_cursor( (RECT *)arg ); case NtUserCallOneParam_GetCursorPos: return get_cursor_pos( (POINT *)arg ); case NtUserCallOneParam_GetIconParam: return get_icon_param( UlongToHandle(arg) ); case NtUserCallOneParam_GetMenuItemCount: return get_menu_item_count( UlongToHandle(arg) ); case NtUserCallOneParam_GetSysColor: return get_sys_color( arg ); case NtUserCallOneParam_IsWindowRectFullScreen: return is_window_rect_full_screen( (const RECT *)arg ); case NtUserCallOneParam_RealizePalette: return realize_palette( UlongToHandle(arg) ); case NtUserCallOneParam_GetPrimaryMonitorRect: *(RECT *)arg = get_primary_monitor_rect( 0 ); return 1; case NtUserCallOneParam_GetSysColorBrush: return HandleToUlong( get_sys_color_brush(arg) ); case NtUserCallOneParam_GetSysColorPen: return HandleToUlong( get_sys_color_pen(arg) ); case NtUserCallOneParam_GetSystemMetrics: return get_system_metrics( arg ); case NtUserCallOneParam_GetVirtualScreenRect: *(RECT *)arg = get_virtual_screen_rect( 0 ); return 1; case NtUserCallOneParam_MessageBeep: return message_beep( arg ); case NtUserCallOneParam_ReplyMessage: return reply_message_result( arg ); case NtUserCallOneParam_SetCaretBlinkTime: return set_caret_blink_time( arg ); case NtUserCallOneParam_SetProcessDefaultLayout: process_layout = arg; return TRUE; case NtUserCallOneParam_SetKeyboardAutoRepeat: return set_keyboard_auto_repeat( arg ); /* temporary exports */ case NtUserGetDeskPattern: return get_entry( &entry_DESKPATTERN, 256, (WCHAR *)arg ); default: FIXME( "invalid code %u\n", (int)code ); return 0; } } /*********************************************************************** * NtUserCallTwoParam (win32u.@) */ ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code ) { switch(code) { case NtUserCallTwoParam_GetDialogProc: return (ULONG_PTR)get_dialog_proc( (DLGPROC)arg1, arg2 ); case NtUserCallTwoParam_GetMenuInfo: return get_menu_info( UlongToHandle(arg1), (MENUINFO *)arg2 ); case NtUserCallTwoParam_GetMonitorInfo: return get_monitor_info( UlongToHandle(arg1), (MONITORINFO *)arg2 ); case NtUserCallTwoParam_GetSystemMetricsForDpi: return get_system_metrics_for_dpi( arg1, arg2 ); case NtUserCallTwoParam_MonitorFromRect: return HandleToUlong( monitor_from_rect( (const RECT *)arg1, arg2, get_thread_dpi() )); case NtUserCallTwoParam_SetCaretPos: return set_caret_pos( arg1, arg2 ); case NtUserCallTwoParam_SetIconParam: return set_icon_param( UlongToHandle(arg1), arg2 ); case NtUserCallTwoParam_UnhookWindowsHook: return unhook_windows_hook( arg1, (HOOKPROC)arg2 ); /* temporary exports */ case NtUserAllocWinProc: return (UINT_PTR)alloc_winproc( (WNDPROC)arg1, arg2 ); default: FIXME( "invalid code %u\n", (int)code ); return 0; } } /*********************************************************************** * NtUserDisplayConfigGetDeviceInfo (win32u.@) */ NTSTATUS WINAPI NtUserDisplayConfigGetDeviceInfo( DISPLAYCONFIG_DEVICE_INFO_HEADER *packet ) { NTSTATUS ret = STATUS_UNSUCCESSFUL; char buffer[CCHDEVICENAME]; TRACE( "packet %p.\n", packet ); if (!packet || packet->size < sizeof(*packet)) return STATUS_UNSUCCESSFUL; switch (packet->type) { case DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME: { DISPLAYCONFIG_SOURCE_DEVICE_NAME *source_name = (DISPLAYCONFIG_SOURCE_DEVICE_NAME *)packet; struct adapter *adapter; TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME.\n" ); if (packet->size < sizeof(*source_name)) return STATUS_INVALID_PARAMETER; if (!lock_display_devices()) return STATUS_UNSUCCESSFUL; LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry) { if (source_name->header.id != adapter->id) continue; if (memcmp( &source_name->header.adapterId, &adapter->gpu->luid, sizeof(adapter->gpu->luid) )) continue; sprintf( buffer, "\\\\.\\DISPLAY%d", adapter->id + 1 ); asciiz_to_unicode( source_name->viewGdiDeviceName, buffer ); ret = STATUS_SUCCESS; break; } unlock_display_devices(); return ret; } case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME: { DISPLAYCONFIG_TARGET_DEVICE_NAME *target_name = (DISPLAYCONFIG_TARGET_DEVICE_NAME *)packet; char buffer[ARRAY_SIZE(target_name->monitorFriendlyDeviceName)]; struct monitor *monitor; TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME.\n" ); if (packet->size < sizeof(*target_name)) return STATUS_INVALID_PARAMETER; if (!lock_display_devices()) return STATUS_UNSUCCESSFUL; memset( &target_name->flags, 0, sizeof(*target_name) - offsetof(DISPLAYCONFIG_TARGET_DEVICE_NAME, flags) ); LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry) { if (target_name->header.id != monitor->output_id) continue; if (memcmp( &target_name->header.adapterId, &monitor->adapter->gpu->luid, sizeof(monitor->adapter->gpu->luid) )) continue; target_name->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL; snprintf( buffer, ARRAY_SIZE(buffer), "Display%u", monitor->output_id + 1 ); asciiz_to_unicode( target_name->monitorFriendlyDeviceName, buffer ); monitor_get_interface_name( monitor, target_name->monitorDevicePath ); if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID) { target_name->edidManufactureId = monitor->edid_info.manufacturer; target_name->edidProductCodeId = monitor->edid_info.product_code; target_name->flags.edidIdsValid = 1; } if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_NAME) { wcscpy( target_name->monitorFriendlyDeviceName, monitor->edid_info.monitor_name ); target_name->flags.friendlyNameFromEdid = 1; } ret = STATUS_SUCCESS; break; } unlock_display_devices(); return ret; } case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE: { DISPLAYCONFIG_TARGET_PREFERRED_MODE *preferred_mode = (DISPLAYCONFIG_TARGET_PREFERRED_MODE *)packet; DISPLAYCONFIG_VIDEO_SIGNAL_INFO *signal_info = &preferred_mode->targetMode.targetVideoSignalInfo; unsigned int i, display_freq; DEVMODEW *found_mode = NULL; BOOL have_edid_mode = FALSE; struct monitor *monitor; FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE semi-stub.\n" ); if (packet->size < sizeof(*preferred_mode)) return STATUS_INVALID_PARAMETER; if (!lock_display_devices()) return STATUS_UNSUCCESSFUL; memset( &preferred_mode->width, 0, sizeof(*preferred_mode) - offsetof(DISPLAYCONFIG_TARGET_PREFERRED_MODE, width) ); LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry) { if (preferred_mode->header.id != monitor->output_id) continue; if (memcmp( &preferred_mode->header.adapterId, &monitor->adapter->gpu->luid, sizeof(monitor->adapter->gpu->luid) )) continue; for (i = 0; i < monitor->adapter->mode_count; ++i) { DEVMODEW *mode = &monitor->adapter->modes[i]; if (!have_edid_mode && monitor->edid_info.flags & MONITOR_INFO_HAS_PREFERRED_MODE && mode->dmPelsWidth == monitor->edid_info.preferred_width && mode->dmPelsHeight == monitor->edid_info.preferred_height) { found_mode = mode; have_edid_mode = TRUE; } if (!have_edid_mode && (!found_mode || (mode->dmPelsWidth > found_mode->dmPelsWidth && mode->dmPelsHeight >= found_mode->dmPelsHeight) || (mode->dmPelsHeight > found_mode->dmPelsHeight && mode->dmPelsWidth >= found_mode->dmPelsWidth))) found_mode = mode; if (mode->dmPelsWidth == found_mode->dmPelsWidth && mode->dmPelsHeight == found_mode->dmPelsHeight && mode->dmDisplayFrequency > found_mode->dmDisplayFrequency) found_mode = mode; } if (!found_mode) { ERR( "No mode found.\n" ); break; } preferred_mode->width = found_mode->dmPelsWidth; preferred_mode->height = found_mode->dmPelsHeight; display_freq = found_mode->dmDisplayFrequency; signal_info->pixelRate = display_freq * preferred_mode->width * preferred_mode->height; signal_info->hSyncFreq.Numerator = display_freq * preferred_mode->width; signal_info->hSyncFreq.Denominator = 1; signal_info->vSyncFreq.Numerator = display_freq; signal_info->vSyncFreq.Denominator = 1; signal_info->activeSize.cx = preferred_mode->width; signal_info->activeSize.cy = preferred_mode->height; signal_info->totalSize.cx = preferred_mode->width; signal_info->totalSize.cy = preferred_mode->height; signal_info->videoStandard = D3DKMDT_VSS_OTHER; if (!(found_mode->dmFields & DM_DISPLAYFLAGS)) signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED; else if (found_mode->dmDisplayFlags & DM_INTERLACED) signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED; else signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; ret = STATUS_SUCCESS; break; } unlock_display_devices(); return ret; } case DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME: { DISPLAYCONFIG_ADAPTER_NAME *adapter_name = (DISPLAYCONFIG_ADAPTER_NAME *)packet; FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME stub.\n" ); if (packet->size < sizeof(*adapter_name)) return STATUS_INVALID_PARAMETER; return STATUS_NOT_SUPPORTED; } case DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE: case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE: case DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION: case DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION: case DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO: case DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE: case DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL: default: FIXME( "Unimplemented packet type %u.\n", packet->type ); return STATUS_INVALID_PARAMETER; } } /****************************************************************************** * NtGdiDdDDIEnumAdapters2 (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDIEnumAdapters2( D3DKMT_ENUMADAPTERS2 *desc ) { D3DKMT_OPENADAPTERFROMLUID open_adapter_from_luid; struct gpu *gpu, *current_gpus[34]; D3DKMT_CLOSEADAPTER close_adapter; NTSTATUS status = STATUS_SUCCESS; UINT idx = 0, count = 0; TRACE( "(%p)\n", desc ); if (!desc) return STATUS_INVALID_PARAMETER; if (!desc->pAdapters) { desc->NumAdapters = ARRAY_SIZE(current_gpus); return STATUS_SUCCESS; } if (!lock_display_devices()) return STATUS_UNSUCCESSFUL; LIST_FOR_EACH_ENTRY( gpu, &gpus, struct gpu, entry ) { if (count >= ARRAY_SIZE(current_gpus)) { WARN( "Too many adapters (%u), only up to %zu can be enumerated.\n", count, ARRAY_SIZE(current_gpus) ); break; } current_gpus[count++] = gpu_acquire( gpu ); } unlock_display_devices(); if (count > desc->NumAdapters) { status = STATUS_BUFFER_TOO_SMALL; goto done; } for (idx = 0; idx < count; idx++) { gpu = current_gpus[idx]; if (!gpu->luid.LowPart && !gpu->luid.HighPart) { ERR( "Adapter %u does not have a LUID.\n", idx ); status = STATUS_UNSUCCESSFUL; break; } open_adapter_from_luid.AdapterLuid = gpu->luid; /* give the GDI driver a chance to be notified about new adapter handle */ if ((status = NtGdiDdDDIOpenAdapterFromLuid( &open_adapter_from_luid ))) { ERR( "Failed to open adapter %u from LUID, status %#x.\n", idx, (UINT)status ); break; } desc->pAdapters[idx].hAdapter = open_adapter_from_luid.hAdapter; desc->pAdapters[idx].AdapterLuid = gpu->luid; desc->pAdapters[idx].NumOfSources = gpu->adapter_count; desc->pAdapters[idx].bPrecisePresentRegionsPreferred = FALSE; } if (idx != count) { while (idx) { close_adapter.hAdapter = desc->pAdapters[--idx].hAdapter; NtGdiDdDDICloseAdapter( &close_adapter ); } memset( desc->pAdapters, 0, desc->NumAdapters * sizeof(D3DKMT_ADAPTERINFO) ); } desc->NumAdapters = idx; done: while (count) gpu_release( current_gpus[--count] ); return status; } /* Find the Vulkan device UUID corresponding to a LUID */ BOOL get_vulkan_uuid_from_luid( const LUID *luid, GUID *uuid ) { BOOL found = FALSE; struct gpu *gpu; if (!lock_display_devices()) return FALSE; LIST_FOR_EACH_ENTRY( gpu, &gpus, struct gpu, entry ) { if ((found = !memcmp( &gpu->luid, luid, sizeof(*luid) ))) { *uuid = gpu->vulkan_uuid; break; } } unlock_display_devices(); return found; }