win32u: Set DEVPKEY_Device_MatchingDeviceId for GPUs.
[wine.git] / dlls / win32u / sysparams.c
blob3967976f54dc561a8b9af38aacbfaed5e77fb1fc
1 /*
2 * System parameters functions
4 * Copyright 1994 Alexandre Julliard
5 * Copyright 2019 Zhiyi Zhang for CodeWeavers
6 * Copyright 2021 Jacek Caban for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #if 0
24 #pragma makedep unix
25 #endif
27 #include <pthread.h>
28 #include <assert.h>
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32 #include "ntgdi_private.h"
33 #include "ntuser_private.h"
34 #include "devpropdef.h"
35 #include "wine/wingdi16.h"
36 #include "wine/server.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(system);
41 static HKEY video_key, enum_key, control_key, config_key, volatile_base_key;
43 static const WCHAR devicemap_video_keyW[] =
45 '\\','R','e','g','i','s','t','r','y',
46 '\\','M','a','c','h','i','n','e',
47 '\\','H','A','R','D','W','A','R','E',
48 '\\','D','E','V','I','C','E','M','A','P',
49 '\\','V','I','D','E','O'
52 static const WCHAR enum_keyW[] =
54 '\\','R','e','g','i','s','t','r','y',
55 '\\','M','a','c','h','i','n','e',
56 '\\','S','y','s','t','e','m',
57 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
58 '\\','E','n','u','m'
61 static const WCHAR control_keyW[] =
63 '\\','R','e','g','i','s','t','r','y',
64 '\\','M','a','c','h','i','n','e',
65 '\\','S','y','s','t','e','m',
66 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
67 '\\','C','o','n','t','r','o','l'
70 static const WCHAR config_keyW[] =
72 '\\','R','e','g','i','s','t','r','y',
73 '\\','M','a','c','h','i','n','e',
74 '\\','S','y','s','t','e','m',
75 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
76 '\\','H','a','r','d','w','a','r','e',' ','P','r','o','f','i','l','e','s',
77 '\\','C','u','r','r','e','n','t'
80 static const WCHAR devpropkey_gpu_vulkan_uuidW[] =
82 'P','r','o','p','e','r','t','i','e','s',
83 '\\','{','2','3','3','A','9','E','F','3','-','A','F','C','4','-','4','A','B','D',
84 '-','B','5','6','4','-','C','3','2','F','2','1','F','1','5','3','5','C','}',
85 '\\','0','0','0','2'
88 static const WCHAR devpropkey_gpu_luidW[] =
90 'P','r','o','p','e','r','t','i','e','s',
91 '\\','{','6','0','B','1','9','3','C','B','-','5','2','7','6','-','4','D','0','F',
92 '-','9','6','F','C','-','F','1','7','3','A','B','A','D','3','E','C','6','}',
93 '\\','0','0','0','2'
96 static const WCHAR devpkey_device_matching_device_id[] =
98 'P','r','o','p','e','r','t','i','e','s',
99 '\\','{','A','8','B','8','6','5','D','D','-','2','E','3','D','-','4','0','9','4',
100 '-','A','D','9','7','-','E','5','9','3','A','7','0','C','7','5','D','6','}',
101 '\\','0','0','0','8'
104 static const WCHAR devpropkey_device_ispresentW[] =
106 'P','r','o','p','e','r','t','i','e','s',
107 '\\','{','5','4','0','B','9','4','7','E','-','8','B','4','0','-','4','5','B','C',
108 '-','A','8','A','2','-','6','A','0','B','8','9','4','C','B','D','A','2','}',
109 '\\','0','0','0','5'
112 static const WCHAR devpropkey_monitor_gpu_luidW[] =
114 'P','r','o','p','e','r','t','i','e','s',
115 '\\','{','C','A','0','8','5','8','5','3','-','1','6','C','E','-','4','8','A','A',
116 '-','B','1','1','4','-','D','E','9','C','7','2','3','3','4','2','2','3','}',
117 '\\','0','0','0','1'
120 static const WCHAR devpropkey_monitor_output_idW[] =
122 'P','r','o','p','e','r','t','i','e','s',
123 '\\','{','C','A','0','8','5','8','5','3','-','1','6','C','E','-','4','8','A','A',
124 '-','B','1','1','4','-','D','E','9','C','7','2','3','3','4','2','2','3','}',
125 '\\','0','0','0','2'
128 static const WCHAR wine_devpropkey_monitor_stateflagsW[] =
130 'P','r','o','p','e','r','t','i','e','s','\\',
131 '{','2','3','3','a','9','e','f','3','-','a','f','c','4','-','4','a','b','d',
132 '-','b','5','6','4','-','c','3','2','f','2','1','f','1','5','3','5','b','}',
133 '\\','0','0','0','2'
136 static const WCHAR wine_devpropkey_monitor_rcmonitorW[] =
138 'P','r','o','p','e','r','t','i','e','s','\\',
139 '{','2','3','3','a','9','e','f','3','-','a','f','c','4','-','4','a','b','d',
140 '-','b','5','6','4','-','c','3','2','f','2','1','f','1','5','3','5','b','}',
141 '\\','0','0','0','3'
144 static const WCHAR wine_devpropkey_monitor_rcworkW[] =
146 'P','r','o','p','e','r','t','i','e','s','\\',
147 '{','2','3','3','a','9','e','f','3','-','a','f','c','4','-','4','a','b','d',
148 '-','b','5','6','4','-','c','3','2','f','2','1','f','1','5','3','5','b','}',
149 '\\','0','0','0','4'
152 static const WCHAR wine_devpropkey_monitor_adapternameW[] =
154 'P','r','o','p','e','r','t','i','e','s','\\',
155 '{','2','3','3','a','9','e','f','3','-','a','f','c','4','-','4','a','b','d',
156 '-','b','5','6','4','-','c','3','2','f','2','1','f','1','5','3','5','b','}',
157 '\\','0','0','0','5'
160 static const WCHAR device_instanceW[] = {'D','e','v','i','c','e','I','n','s','t','a','n','c','e',0};
161 static const WCHAR controlW[] = {'C','o','n','t','r','o','l'};
162 static const WCHAR device_parametersW[] =
163 {'D','e','v','i','c','e',' ','P','a','r','a','m','e','t','e','r','s'};
164 static const WCHAR linkedW[] = {'L','i','n','k','e','d',0};
165 static const WCHAR symbolic_link_valueW[] =
166 {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
167 static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0};
168 static const WCHAR gpu_idW[] = {'G','P','U','I','D',0};
169 static const WCHAR hardware_idW[] = {'H','a','r','d','w','a','r','e','I','D',0};
170 static const WCHAR device_descW[] = {'D','e','v','i','c','e','D','e','s','c',0};
171 static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0};
172 static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
173 static const WCHAR class_guidW[] = {'C','l','a','s','s','G','U','I','D',0};
174 static const WCHAR pciW[] = {'P','C','I'};
175 static const WCHAR classW[] = {'C','l','a','s','s',0};
176 static const WCHAR displayW[] = {'D','i','s','p','l','a','y',0};
177 static const WCHAR monitorW[] = {'M','o','n','i','t','o','r',0};
178 static const WCHAR yesW[] = {'Y','e','s',0};
179 static const WCHAR noW[] = {'N','o',0};
180 static const WCHAR mode_countW[] = {'M','o','d','e','C','o','u','n','t',0};
181 static const WCHAR edidW[] = {'E','D','I','D',0};
183 static const char guid_devclass_displayA[] = "{4D36E968-E325-11CE-BFC1-08002BE10318}";
184 static const WCHAR guid_devclass_displayW[] =
185 {'{','4','D','3','6','E','9','6','8','-','E','3','2','5','-','1','1','C','E','-',
186 'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0};
188 static const char guid_devclass_monitorA[] = "{4D36E96E-E325-11CE-BFC1-08002BE10318}";
189 static const WCHAR guid_devclass_monitorW[] =
190 {'{','4','D','3','6','E','9','6','E','-','E','3','2','5','-','1','1','C','E','-',
191 'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0};
193 static const WCHAR guid_devinterface_display_adapterW[] =
194 {'{','5','B','4','5','2','0','1','D','-','F','2','F','2','-','4','F','3','B','-',
195 '8','5','B','B','-','3','0','F','F','1','F','9','5','3','5','9','9','}',0};
197 static const WCHAR guid_display_device_arrivalW[] =
198 {'{','1','C','A','0','5','1','8','0','-','A','6','9','9','-','4','5','0','A','-',
199 '9','A','0','C','-','D','E','4','F','B','E','3','D','D','D','8','9','}',0};
201 static const WCHAR guid_devinterface_monitorW[] =
202 {'{','E','6','F','0','7','B','5','F','-','E','E','9','7','-','4','A','9','0','-',
203 'B','0','7','6','-','3','3','F','5','7','B','F','4','E','A','A','7','}',0};
205 #define NEXT_DEVMODEW(mode) ((DEVMODEW *)((char *)((mode) + 1) + (mode)->dmDriverExtra))
207 /* Cached display device information */
208 struct display_device
210 WCHAR device_name[32]; /* DeviceName in DISPLAY_DEVICEW */
211 WCHAR device_string[128]; /* DeviceString in DISPLAY_DEVICEW */
212 DWORD state_flags; /* StateFlags in DISPLAY_DEVICEW */
213 WCHAR device_id[128]; /* DeviceID in DISPLAY_DEVICEW */
214 WCHAR interface_name[128]; /* DeviceID in DISPLAY_DEVICEW when EDD_GET_DEVICE_INTERFACE_NAME is set */
215 WCHAR device_key[128]; /* DeviceKey in DISPLAY_DEVICEW */
218 struct adapter
220 LONG refcount;
221 struct list entry;
222 struct display_device dev;
223 LUID gpu_luid;
224 unsigned int id;
225 const WCHAR *config_key;
226 unsigned int mode_count;
227 DEVMODEW *modes;
230 #define MONITOR_INFO_HAS_MONITOR_ID 0x00000001
231 #define MONITOR_INFO_HAS_MONITOR_NAME 0x00000002
232 struct edid_monitor_info
234 unsigned int flags;
235 /* MONITOR_INFO_HAS_MONITOR_ID */
236 unsigned short manufacturer, product_code;
237 char monitor_id_string[8];
238 /* MONITOR_INFO_HAS_MONITOR_NAME */
239 WCHAR monitor_name[14];
242 struct monitor
244 struct list entry;
245 struct display_device dev;
246 struct adapter *adapter;
247 HANDLE handle;
248 unsigned int id;
249 unsigned int flags;
250 unsigned int output_id;
251 RECT rc_monitor;
252 RECT rc_work;
253 BOOL is_clone;
254 struct edid_monitor_info edid_info;
257 static struct list adapters = LIST_INIT(adapters);
258 static struct list monitors = LIST_INIT(monitors);
259 static INT64 last_query_display_time;
260 static pthread_mutex_t display_lock = PTHREAD_MUTEX_INITIALIZER;
262 BOOL enable_thunk_lock = FALSE;
264 #define VIRTUAL_HMONITOR ((HMONITOR)(UINT_PTR)(0x10000 + 1))
265 static struct monitor virtual_monitor =
267 .handle = VIRTUAL_HMONITOR,
268 .flags = MONITORINFOF_PRIMARY,
269 .rc_monitor.right = 1024,
270 .rc_monitor.bottom = 768,
271 .rc_work.right = 1024,
272 .rc_work.bottom = 768,
273 .dev.state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED,
276 /* the various registry keys that are used to store parameters */
277 enum parameter_key
279 COLORS_KEY,
280 DESKTOP_KEY,
281 KEYBOARD_KEY,
282 MOUSE_KEY,
283 METRICS_KEY,
284 SOUND_KEY,
285 VERSION_KEY,
286 SHOWSOUNDS_KEY,
287 KEYBOARDPREF_KEY,
288 SCREENREADER_KEY,
289 AUDIODESC_KEY,
290 NB_PARAM_KEYS
293 static const char *parameter_key_names[NB_PARAM_KEYS] =
295 "Control Panel\\Colors",
296 "Control Panel\\Desktop",
297 "Control Panel\\Keyboard",
298 "Control Panel\\Mouse",
299 "Control Panel\\Desktop\\WindowMetrics",
300 "Control Panel\\Sound",
301 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
302 "Control Panel\\Accessibility\\ShowSounds",
303 "Control Panel\\Accessibility\\Keyboard Preference",
304 "Control Panel\\Accessibility\\Blind Access",
305 "Control Panel\\Accessibility\\AudioDescription",
308 /* System parameters storage */
309 union sysparam_all_entry;
311 struct sysparam_entry
313 BOOL (*get)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi );
314 BOOL (*set)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags );
315 BOOL (*init)( union sysparam_all_entry *entry );
316 enum parameter_key base_key;
317 const char *regval;
318 enum parameter_key mirror_key;
319 const char *mirror;
320 BOOL loaded;
323 struct sysparam_uint_entry
325 struct sysparam_entry hdr;
326 UINT val;
329 struct sysparam_bool_entry
331 struct sysparam_entry hdr;
332 BOOL val;
335 struct sysparam_dword_entry
337 struct sysparam_entry hdr;
338 DWORD val;
341 struct sysparam_rgb_entry
343 struct sysparam_entry hdr;
344 COLORREF val;
345 HBRUSH brush;
346 HPEN pen;
349 struct sysparam_binary_entry
351 struct sysparam_entry hdr;
352 void *ptr;
353 size_t size;
356 struct sysparam_path_entry
358 struct sysparam_entry hdr;
359 WCHAR *path;
362 struct sysparam_font_entry
364 struct sysparam_entry hdr;
365 UINT weight;
366 LOGFONTW val;
367 WCHAR fullname[LF_FACESIZE];
370 struct sysparam_pref_entry
372 struct sysparam_entry hdr;
373 union sysparam_all_entry *parent;
374 UINT offset;
375 UINT mask;
378 union sysparam_all_entry
380 struct sysparam_entry hdr;
381 struct sysparam_uint_entry uint;
382 struct sysparam_bool_entry bool;
383 struct sysparam_dword_entry dword;
384 struct sysparam_rgb_entry rgb;
385 struct sysparam_binary_entry bin;
386 struct sysparam_path_entry path;
387 struct sysparam_font_entry font;
388 struct sysparam_pref_entry pref;
391 static UINT system_dpi;
392 static RECT work_area;
393 static DWORD process_layout = ~0u;
395 static HDC display_dc;
396 static pthread_mutex_t display_dc_lock = PTHREAD_MUTEX_INITIALIZER;
398 static pthread_mutex_t user_mutex;
399 static unsigned int user_lock_thread, user_lock_rec;
401 void user_lock(void)
403 pthread_mutex_lock( &user_mutex );
404 if (!user_lock_rec++) user_lock_thread = GetCurrentThreadId();
407 void user_unlock(void)
409 if (!--user_lock_rec) user_lock_thread = 0;
410 pthread_mutex_unlock( &user_mutex );
413 void user_check_not_lock(void)
415 if (user_lock_thread == GetCurrentThreadId())
417 ERR( "BUG: holding USER lock\n" );
418 assert( 0 );
422 static HANDLE get_display_device_init_mutex( void )
424 WCHAR bufferW[256];
425 UNICODE_STRING name = {.Buffer = bufferW};
426 OBJECT_ATTRIBUTES attr;
427 char buffer[256];
428 HANDLE mutex;
430 snprintf( buffer, ARRAY_SIZE(buffer), "\\Sessions\\%u\\BaseNamedObjects\\display_device_init",
431 (int)NtCurrentTeb()->Peb->SessionId );
432 name.Length = name.MaximumLength = asciiz_to_unicode( bufferW, buffer );
434 InitializeObjectAttributes( &attr, &name, OBJ_OPENIF, NULL, NULL );
435 if (NtCreateMutant( &mutex, MUTEX_ALL_ACCESS, &attr, FALSE ) < 0) return 0;
436 NtWaitForSingleObject( mutex, FALSE, NULL );
437 return mutex;
440 static void release_display_device_init_mutex( HANDLE mutex )
442 NtReleaseMutant( mutex, NULL );
443 NtClose( mutex );
446 static struct adapter *adapter_acquire( struct adapter *adapter )
448 InterlockedIncrement( &adapter->refcount );
449 return adapter;
452 static void adapter_release( struct adapter *adapter )
454 if (!InterlockedDecrement( &adapter->refcount ))
456 free( adapter->modes );
457 free( adapter );
461 C_ASSERT(sizeof(DEVMODEW) - offsetof(DEVMODEW, dmFields) == 0x94);
463 static void get_monitor_info_from_edid( struct edid_monitor_info *info, const unsigned char *edid, unsigned int edid_len )
465 unsigned int i, j;
466 unsigned short w;
467 unsigned char d;
468 const char *s;
470 info->flags = 0;
471 if (!edid || edid_len < 128) return;
473 w = (edid[8] << 8) | edid[9]; /* Manufacturer ID, big endian. */
474 for (i = 0; i < 3; ++i)
476 d = w & 0x1f;
477 if (!d || d - 1 > 'Z' - 'A') return;
478 info->monitor_id_string[2 - i] = 'A' + d - 1;
479 w >>= 5;
481 if (w) return;
482 w = edid[10] | (edid[11] << 8); /* Product code, little endian. */
483 info->manufacturer = *(unsigned short *)(edid + 8);
484 info->product_code = w;
485 sprintf( info->monitor_id_string + 3, "%04X", w );
486 info->flags = MONITOR_INFO_HAS_MONITOR_ID;
487 TRACE( "Monitor id %s.\n", info->monitor_id_string );
489 for (i = 0; i < 4; ++i)
491 if (edid[54 + i * 18 + 3] != 0xfc) continue;
492 /* "Display name" ASCII descriptor. */
493 s = (const char *)&edid[54 + i * 18 + 5];
494 for (j = 0; s[j] && j < 13; ++j)
495 info->monitor_name[j] = s[j];
496 while (j && isspace(s[j - 1])) --j;
497 info->monitor_name[j] = 0;
498 info->flags |= MONITOR_INFO_HAS_MONITOR_NAME;
499 break;
503 static BOOL write_adapter_mode( HKEY adapter_key, UINT index, const DEVMODEW *mode )
505 WCHAR bufferW[MAX_PATH] = {0};
506 char buffer[MAX_PATH];
508 sprintf( buffer, "Modes\\%08X", index );
509 asciiz_to_unicode( bufferW, buffer );
510 return set_reg_value( adapter_key, bufferW, REG_BINARY, &mode->dmFields, sizeof(*mode) - offsetof(DEVMODEW, dmFields) );
513 static BOOL read_adapter_mode( HKEY adapter_key, UINT index, DEVMODEW *mode )
515 char value_buf[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(*mode)])];
516 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)value_buf;
517 WCHAR bufferW[MAX_PATH] = {0};
518 char buffer[MAX_PATH];
520 sprintf( buffer, "Modes\\%08X", index );
521 asciiz_to_unicode( bufferW, buffer );
522 if (!query_reg_value( adapter_key, bufferW, value, sizeof(value_buf) )) return FALSE;
524 memcpy( &mode->dmFields, value->Data, sizeof(*mode) - offsetof(DEVMODEW, dmFields) );
525 return TRUE;
528 static BOOL adapter_get_registry_settings( const struct adapter *adapter, DEVMODEW *mode )
530 BOOL ret = FALSE;
531 HANDLE mutex;
532 HKEY hkey;
534 mutex = get_display_device_init_mutex();
536 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
537 else if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
538 else
540 ret = read_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode );
541 NtClose( hkey );
544 release_display_device_init_mutex( mutex );
545 return ret;
548 static BOOL adapter_set_registry_settings( const struct adapter *adapter, const DEVMODEW *mode )
550 HANDLE mutex;
551 HKEY hkey;
552 BOOL ret;
554 mutex = get_display_device_init_mutex();
556 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
557 if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
558 else
560 ret = write_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode );
561 NtClose( hkey );
564 release_display_device_init_mutex( mutex );
565 return ret;
568 static BOOL adapter_get_current_settings( const struct adapter *adapter, DEVMODEW *mode )
570 BOOL is_primary = !!(adapter->dev.state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
571 HANDLE mutex;
572 HKEY hkey;
573 BOOL ret;
575 if ((ret = user_driver->pGetCurrentDisplaySettings( adapter->dev.device_name, is_primary, mode ))) return TRUE;
577 /* default implementation: read current display settings from the registry. */
579 mutex = get_display_device_init_mutex();
581 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
582 else if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
583 else
585 ret = read_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
586 NtClose( hkey );
589 release_display_device_init_mutex( mutex );
590 return ret;
593 static BOOL adapter_set_current_settings( const struct adapter *adapter, const DEVMODEW *mode )
595 HANDLE mutex;
596 HKEY hkey;
597 BOOL ret;
599 mutex = get_display_device_init_mutex();
601 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
602 if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
603 else
605 ret = write_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
606 NtClose( hkey );
609 release_display_device_init_mutex( mutex );
610 return ret;
613 static int mode_compare(const void *p1, const void *p2)
615 BOOL a_interlaced, b_interlaced, a_stretched, b_stretched;
616 DWORD a_width, a_height, b_width, b_height;
617 const DEVMODEW *a = p1, *b = p2;
618 int ret;
620 /* Depth in descending order */
621 if ((ret = b->dmBitsPerPel - a->dmBitsPerPel)) return ret;
623 /* Use the width and height in landscape mode for comparison */
624 if (a->dmDisplayOrientation == DMDO_DEFAULT || a->dmDisplayOrientation == DMDO_180)
626 a_width = a->dmPelsWidth;
627 a_height = a->dmPelsHeight;
629 else
631 a_width = a->dmPelsHeight;
632 a_height = a->dmPelsWidth;
635 if (b->dmDisplayOrientation == DMDO_DEFAULT || b->dmDisplayOrientation == DMDO_180)
637 b_width = b->dmPelsWidth;
638 b_height = b->dmPelsHeight;
640 else
642 b_width = b->dmPelsHeight;
643 b_height = b->dmPelsWidth;
646 /* Width in ascending order */
647 if ((ret = a_width - b_width)) return ret;
649 /* Height in ascending order */
650 if ((ret = a_height - b_height)) return ret;
652 /* Frequency in descending order */
653 if ((ret = b->dmDisplayFrequency - a->dmDisplayFrequency)) return ret;
655 /* Orientation in ascending order */
656 if ((ret = a->dmDisplayOrientation - b->dmDisplayOrientation)) return ret;
658 if (!(a->dmFields & DM_DISPLAYFLAGS)) a_interlaced = FALSE;
659 else a_interlaced = !!(a->dmDisplayFlags & DM_INTERLACED);
660 if (!(b->dmFields & DM_DISPLAYFLAGS)) b_interlaced = FALSE;
661 else b_interlaced = !!(b->dmDisplayFlags & DM_INTERLACED);
663 /* Interlaced in ascending order */
664 if ((ret = a_interlaced - b_interlaced)) return ret;
666 if (!(a->dmFields & DM_DISPLAYFIXEDOUTPUT)) a_stretched = FALSE;
667 else a_stretched = a->dmDisplayFixedOutput == DMDFO_STRETCH;
668 if (!(b->dmFields & DM_DISPLAYFIXEDOUTPUT)) b_stretched = FALSE;
669 else b_stretched = b->dmDisplayFixedOutput == DMDFO_STRETCH;
671 /* Stretched in ascending order */
672 if ((ret = a_stretched - b_stretched)) return ret;
674 return 0;
677 static unsigned int query_reg_subkey_value( HKEY hkey, const WCHAR *name, unsigned int name_size,
678 KEY_VALUE_PARTIAL_INFORMATION *value, unsigned int size )
680 HKEY subkey;
682 if (!(subkey = reg_open_key( hkey, name, name_size ))) return 0;
683 size = query_reg_value( subkey, NULL, value, size );
684 NtClose( subkey );
685 return size;
688 static BOOL read_display_adapter_settings( unsigned int index, struct adapter *info )
690 char buffer[4096];
691 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
692 WCHAR *value_str = (WCHAR *)value->Data;
693 DEVMODEW *mode;
694 DWORD i, size;
695 HKEY hkey;
697 if (!enum_key && !(enum_key = reg_open_key( NULL, enum_keyW, sizeof(enum_keyW) )))
698 return FALSE;
700 /* Find adapter */
701 sprintf( buffer, "\\Device\\Video%d", index );
702 size = query_reg_ascii_value( video_key, buffer, value, sizeof(buffer) );
703 if (!size || value->Type != REG_SZ ||
704 value->DataLength <= sizeof("\\Registry\\Machine\\") * sizeof(WCHAR))
705 return FALSE;
707 /* DeviceKey */
708 memcpy( info->dev.device_key, value_str, value->DataLength );
709 info->config_key = info->dev.device_key + sizeof("\\Registry\\Machine\\") - 1;
711 if (!(hkey = reg_open_key( NULL, value_str, value->DataLength - sizeof(WCHAR) )))
712 return FALSE;
714 /* DeviceString */
715 if (query_reg_value( hkey, driver_descW, value, sizeof(buffer) ) && value->Type == REG_SZ)
716 memcpy( info->dev.device_string, value_str, value->DataLength );
717 NtClose( hkey );
719 /* DeviceName */
720 sprintf( buffer, "\\\\.\\DISPLAY%d", index + 1 );
721 asciiz_to_unicode( info->dev.device_name, buffer );
723 if (!(hkey = reg_open_key( config_key, info->config_key,
724 lstrlenW( info->config_key ) * sizeof(WCHAR) )))
725 return FALSE;
727 /* StateFlags */
728 if (query_reg_value( hkey, state_flagsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
729 info->dev.state_flags = *(const DWORD *)value->Data;
731 /* Interface name */
732 info->dev.interface_name[0] = 0;
734 /* ModeCount */
735 if (query_reg_value( hkey, mode_countW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
736 info->mode_count = *(const DWORD *)value->Data;
738 /* Modes, allocate an extra mode for easier iteration */
739 if ((info->modes = calloc( info->mode_count + 1, sizeof(DEVMODEW) )))
741 for (i = 0, mode = info->modes; i < info->mode_count; i++)
743 mode->dmSize = offsetof(DEVMODEW, dmICMMethod);
744 if (!read_adapter_mode( hkey, i, mode )) break;
745 mode = NEXT_DEVMODEW(mode);
747 info->mode_count = i;
749 qsort(info->modes, info->mode_count, sizeof(*info->modes) + info->modes->dmDriverExtra, mode_compare);
752 /* DeviceID */
753 size = query_reg_value( hkey, gpu_idW, value, sizeof(buffer) );
754 NtClose( hkey );
755 if (!size || value->Type != REG_SZ || !info->mode_count || !info->modes) return FALSE;
757 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
758 return FALSE;
760 size = query_reg_subkey_value( hkey, devpropkey_gpu_luidW, sizeof(devpropkey_gpu_luidW), value, sizeof(buffer) );
761 if (size != sizeof(info->gpu_luid))
763 NtClose( hkey );
764 return FALSE;
766 memcpy( &info->gpu_luid, value->Data, sizeof(info->gpu_luid) );
768 size = query_reg_value( hkey, hardware_idW, value, sizeof(buffer) );
769 NtClose( hkey );
770 if (!size || (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
771 return FALSE;
773 lstrcpyW( info->dev.device_id, value_str );
774 return TRUE;
777 static BOOL read_monitor_settings( struct adapter *adapter, UINT index, struct monitor *monitor )
779 char buffer[4096];
780 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
781 WCHAR *device_name, *value_str = (WCHAR *)value->Data, *ptr;
782 HKEY hkey, subkey;
783 DWORD size, len;
785 monitor->flags = adapter->id ? 0 : MONITORINFOF_PRIMARY;
787 /* DeviceName */
788 sprintf( buffer, "\\\\.\\DISPLAY%d\\Monitor%d", adapter->id + 1, index );
789 asciiz_to_unicode( monitor->dev.device_name, buffer );
791 if (!(hkey = reg_open_key( config_key, adapter->config_key,
792 lstrlenW( adapter->config_key ) * sizeof(WCHAR) )))
793 return FALSE;
795 /* Interface name */
796 sprintf( buffer, "MonitorID%u", index );
797 size = query_reg_ascii_value( hkey, buffer, value, sizeof(buffer) );
798 NtClose( hkey );
799 if (!size || value->Type != REG_SZ) return FALSE;
800 len = asciiz_to_unicode( monitor->dev.interface_name, "\\\\\?\\" ) / sizeof(WCHAR) - 1;
801 memcpy( monitor->dev.interface_name + len, value_str, value->DataLength - sizeof(WCHAR) );
802 len += value->DataLength / sizeof(WCHAR) - 1;
803 monitor->dev.interface_name[len++] = '#';
804 memcpy( monitor->dev.interface_name + len, guid_devinterface_monitorW,
805 sizeof(guid_devinterface_monitorW) );
807 /* Replace '\\' with '#' after prefix */
808 for (ptr = monitor->dev.interface_name + ARRAYSIZE("\\\\\?\\") - 1; *ptr; ptr++)
809 if (*ptr == '\\') *ptr = '#';
811 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
812 return FALSE;
814 /* StateFlags, WINE_DEVPROPKEY_MONITOR_STATEFLAGS */
815 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_stateflagsW,
816 sizeof(wine_devpropkey_monitor_stateflagsW),
817 value, sizeof(buffer) );
818 if (size != sizeof(monitor->dev.state_flags))
820 NtClose( hkey );
821 return FALSE;
823 monitor->dev.state_flags = *(const DWORD *)value->Data;
825 /* Output ID */
826 size = query_reg_subkey_value( hkey, devpropkey_monitor_output_idW,
827 sizeof(devpropkey_monitor_output_idW),
828 value, sizeof(buffer) );
829 if (size != sizeof(monitor->output_id))
831 NtClose( hkey );
832 return FALSE;
834 monitor->output_id = *(const unsigned int *)value->Data;
836 /* rc_monitor, WINE_DEVPROPKEY_MONITOR_RCMONITOR */
837 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcmonitorW,
838 sizeof(wine_devpropkey_monitor_rcmonitorW),
839 value, sizeof(buffer) );
840 if (size != sizeof(monitor->rc_monitor))
842 NtClose( hkey );
843 return FALSE;
845 monitor->rc_monitor = *(const RECT *)value->Data;
847 /* rc_work, WINE_DEVPROPKEY_MONITOR_RCWORK */
848 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcworkW,
849 sizeof(wine_devpropkey_monitor_rcworkW),
850 value, sizeof(buffer) );
851 if (size != sizeof(monitor->rc_work))
853 NtClose( hkey );
854 return FALSE;
856 monitor->rc_work = *(const RECT *)value->Data;
858 /* DeviceString */
859 if (!query_reg_value( hkey, device_descW, value, sizeof(buffer) ) || value->Type != REG_SZ)
861 NtClose( hkey );
862 return FALSE;
864 memcpy( monitor->dev.device_string, value->Data, value->DataLength );
866 /* DeviceKey */
867 if (!query_reg_value( hkey, driverW, value, sizeof(buffer) ) || value->Type != REG_SZ)
869 NtClose( hkey );
870 return FALSE;
872 size = asciiz_to_unicode( monitor->dev.device_key,
873 "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\" );
874 device_name = &monitor->dev.device_key[size / sizeof(WCHAR) - 1];
875 memcpy( device_name, value_str, value->DataLength );
877 /* DeviceID */
878 if (!query_reg_value( hkey, hardware_idW, value, sizeof(buffer) ) ||
879 (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
881 NtClose( hkey );
882 return FALSE;
884 size = lstrlenW( value_str );
885 memcpy( monitor->dev.device_id, value_str, size * sizeof(WCHAR) );
886 monitor->dev.device_id[size++] = '\\';
887 lstrcpyW( monitor->dev.device_id + size, device_name );
889 /* EDID */
890 if ((subkey = reg_open_key( hkey, device_parametersW, sizeof(device_parametersW) )))
892 if (query_reg_value( subkey, edidW, value, sizeof(buffer) ))
893 get_monitor_info_from_edid( &monitor->edid_info, value->Data, value->DataLength );
894 NtClose( subkey );
897 NtClose( hkey );
898 return TRUE;
901 static void reg_empty_key( HKEY root, const char *key_name )
903 char buffer[4096];
904 KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer;
905 KEY_VALUE_FULL_INFORMATION *value = (KEY_VALUE_FULL_INFORMATION *)buffer;
906 WCHAR bufferW[512];
907 DWORD size;
908 HKEY hkey;
910 if (key_name)
911 hkey = reg_open_key( root, bufferW, asciiz_to_unicode( bufferW, key_name ) - sizeof(WCHAR) );
912 else
913 hkey = root;
915 while (!NtEnumerateKey( hkey, 0, KeyNodeInformation, key, sizeof(buffer), &size ))
916 reg_delete_tree( hkey, key->Name, key->NameLength );
918 while (!NtEnumerateValueKey( hkey, 0, KeyValueFullInformation, value, sizeof(buffer), &size ))
920 UNICODE_STRING name = { value->NameLength, value->NameLength, value->Name };
921 NtDeleteValueKey( hkey, &name );
924 if (hkey != root) NtClose( hkey );
927 static void prepare_devices(void)
929 char buffer[4096];
930 KEY_NODE_INFORMATION *key = (void *)buffer;
931 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
932 WCHAR *value_str = (WCHAR *)value->Data;
933 WCHAR bufferW[128];
934 unsigned i = 0;
935 DWORD size;
936 HKEY hkey, subkey, device_key, prop_key;
938 if (!enum_key) enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL );
939 if (!control_key) control_key = reg_create_key( NULL, control_keyW, sizeof(control_keyW), 0, NULL );
940 if (!video_key) video_key = reg_create_key( NULL, devicemap_video_keyW, sizeof(devicemap_video_keyW),
941 REG_OPTION_VOLATILE, NULL );
943 /* delete monitors */
944 reg_empty_key( enum_key, "DISPLAY" );
945 sprintf( buffer, "Class\\%s", guid_devclass_monitorA );
946 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
947 0, NULL );
948 reg_empty_key( hkey, NULL );
949 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
950 NtClose( hkey );
952 /* delete adapters */
953 reg_empty_key( video_key, NULL );
955 /* clean GPUs */
956 sprintf( buffer, "Class\\%s", guid_devclass_displayA );
957 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
958 0, NULL );
959 reg_empty_key( hkey, NULL );
960 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
961 NtClose( hkey );
963 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
965 /* To preserve GPU GUIDs, mark them as not present and delete them in cleanup_devices if needed. */
966 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
968 unsigned int j = 0;
970 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
972 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
974 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
975 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
976 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
978 NtClose( device_key );
979 continue;
982 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
983 if (size == sizeof(guid_devclass_displayW) &&
984 !wcscmp( (const WCHAR *)value->Data, guid_devclass_displayW ) &&
985 (prop_key = reg_create_key( device_key, devpropkey_device_ispresentW,
986 sizeof(devpropkey_device_ispresentW), 0, NULL )))
988 BOOL present = FALSE;
989 set_reg_value( prop_key, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
990 &present, sizeof(present) );
991 NtClose( prop_key );
994 NtClose( device_key );
997 NtClose( subkey );
1000 NtClose( hkey );
1003 static void cleanup_devices(void)
1005 char buffer[4096];
1006 KEY_NODE_INFORMATION *key = (void *)buffer;
1007 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1008 WCHAR bufferW[512], *value_str = (WCHAR *)value->Data;
1009 unsigned i = 0;
1010 DWORD size;
1011 HKEY hkey, subkey, device_key, prop_key;
1013 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
1015 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
1017 unsigned int j = 0;
1019 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
1021 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
1023 BOOL present = FALSE;
1025 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
1026 memcpy( bufferW, key->Name, key->NameLength );
1027 bufferW[key->NameLength / sizeof(WCHAR)] = 0;
1029 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1030 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
1032 NtClose( device_key );
1033 continue;
1036 if ((prop_key = reg_open_key( device_key, devpropkey_device_ispresentW,
1037 sizeof(devpropkey_device_ispresentW) )))
1039 if (query_reg_value( prop_key, NULL, value, sizeof(buffer) ) == sizeof(BOOL))
1040 present = *(const BOOL *)value->Data;
1041 NtClose( prop_key );
1044 NtClose( device_key );
1046 if (!present && reg_delete_tree( subkey, bufferW, lstrlenW( bufferW ) * sizeof(WCHAR) ))
1047 j = 0;
1050 NtClose( subkey );
1053 NtClose( hkey );
1056 /* see UuidCreate */
1057 static void uuid_create( GUID *uuid )
1059 char buf[4096];
1060 NtQuerySystemInformation( SystemInterruptInformation, buf, sizeof(buf), NULL );
1061 memcpy( uuid, buf, sizeof(*uuid) );
1062 uuid->Data3 &= 0x0fff;
1063 uuid->Data3 |= (4 << 12);
1064 uuid->Data4[0] &= 0x3f;
1065 uuid->Data4[0] |= 0x80;
1068 #define TICKSPERSEC 10000000
1069 #define SECSPERDAY 86400
1070 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
1071 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
1073 static unsigned int format_date( WCHAR *bufferW, LONGLONG time )
1075 int cleaps, years, yearday, months, days;
1076 unsigned int day, month, year;
1077 char buffer[32];
1079 days = time / TICKSPERSEC / SECSPERDAY;
1081 /* compute year, month and day of month, see RtlTimeToTimeFields */
1082 cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
1083 days += 28188 + cleaps;
1084 years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
1085 yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
1086 months = (64 * yearday) / 1959;
1087 if (months < 14)
1089 month = months - 1;
1090 year = years + 1524;
1092 else
1094 month = months - 13;
1095 year = years + 1525;
1097 day = yearday - (1959 * months) / 64 ;
1099 sprintf( buffer, "%u-%u-%u", month, day, year );
1100 return asciiz_to_unicode( bufferW, buffer );
1103 struct device_manager_ctx
1105 unsigned int gpu_count;
1106 unsigned int adapter_count;
1107 unsigned int video_count;
1108 unsigned int monitor_count;
1109 unsigned int output_count;
1110 unsigned int mode_count;
1111 HANDLE mutex;
1112 WCHAR gpuid[128];
1113 WCHAR gpu_guid[64];
1114 LUID gpu_luid;
1115 HKEY adapter_key;
1118 static void link_device( const WCHAR *instance, const WCHAR *class )
1120 unsigned int instance_len = lstrlenW( instance ), len;
1121 unsigned int class_len = lstrlenW( class );
1122 WCHAR buffer[MAX_PATH], *ptr;
1123 HKEY hkey, subkey;
1125 static const WCHAR symbolic_linkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k',0};
1126 static const WCHAR hashW[] = {'#'};
1128 len = asciiz_to_unicode( buffer, "DeviceClasses\\" ) / sizeof(WCHAR) - 1;
1129 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
1130 len += class_len;
1131 len += asciiz_to_unicode( buffer + len, "\\##?#" ) / sizeof(WCHAR) - 1;
1132 memcpy( buffer + len, instance, instance_len * sizeof(WCHAR) );
1133 for (ptr = buffer + len; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
1134 len += instance_len;
1135 buffer[len++] = '#';
1136 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
1137 len += class_len;
1138 hkey = reg_create_key( control_key, buffer, len * sizeof(WCHAR), 0, NULL );
1140 set_reg_value( hkey, device_instanceW, REG_SZ, instance, (instance_len + 1) * sizeof(WCHAR) );
1142 subkey = reg_create_key( hkey, hashW, sizeof(hashW), REG_OPTION_VOLATILE, NULL );
1143 NtClose( hkey );
1144 hkey = subkey;
1146 len = asciiz_to_unicode( buffer, "\\\\?\\" ) / sizeof(WCHAR) - 1;
1147 memcpy( buffer + len, instance, (instance_len + 1) * sizeof(WCHAR) );
1148 len += instance_len;
1149 memcpy( buffer + len, class, (class_len + 1) * sizeof(WCHAR) );
1150 len += class_len + 1;
1151 for (ptr = buffer + 4; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
1152 set_reg_value( hkey, symbolic_linkW, REG_SZ, buffer, len * sizeof(WCHAR) );
1154 if ((subkey = reg_create_key( hkey, controlW, sizeof(controlW), REG_OPTION_VOLATILE, NULL )))
1156 const DWORD linked = 1;
1157 set_reg_value( subkey, linkedW, REG_DWORD, &linked, sizeof(linked) );
1158 NtClose( subkey );
1162 static void add_gpu( const struct gdi_gpu *gpu, void *param )
1164 struct device_manager_ctx *ctx = param;
1165 const WCHAR *desc;
1166 char buffer[4096];
1167 WCHAR bufferW[512];
1168 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1169 unsigned int gpu_index, size;
1170 HKEY hkey, subkey;
1171 LARGE_INTEGER ft;
1173 static const BOOL present = TRUE;
1174 static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0};
1175 static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0};
1176 static const WCHAR driver_date_dataW[] =
1177 {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
1178 static const WCHAR adapter_stringW[] =
1179 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n',
1180 '.','A','d','a','p','t','e','r','S','t','r','i','n','g',0};
1181 static const WCHAR bios_stringW[] =
1182 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1183 'B','i','o','s','S','t','r','i','n','g',0};
1184 static const WCHAR chip_typeW[] =
1185 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1186 'C','h','i','p','T','y','p','e',0};
1187 static const WCHAR dac_typeW[] =
1188 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1189 'D','a','c','T','y','p','e',0};
1190 static const WCHAR ramdacW[] =
1191 {'I','n','t','e','r','g','r','a','t','e','d',' ','R','A','M','D','A','C',0};
1192 static const WCHAR driver_dateW[] = {'D','r','i','v','e','r','D','a','t','e',0};
1194 TRACE( "%s %04X %04X %08X %02X\n", debugstr_w(gpu->name),
1195 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
1197 gpu_index = ctx->gpu_count++;
1198 ctx->adapter_count = 0;
1199 ctx->monitor_count = 0;
1200 ctx->mode_count = 0;
1202 if (!enum_key && !(enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL )))
1203 return;
1205 if (!ctx->mutex)
1207 ctx->mutex = get_display_device_init_mutex();
1208 pthread_mutex_lock( &display_lock );
1209 prepare_devices();
1212 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X\\%08X",
1213 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index );
1214 size = asciiz_to_unicode( ctx->gpuid, buffer );
1215 if (!(hkey = reg_create_key( enum_key, ctx->gpuid, size - sizeof(WCHAR), 0, NULL ))) return;
1217 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
1218 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_displayW,
1219 sizeof(guid_devclass_displayW) );
1220 sprintf( buffer, "%s\\%04X", guid_devclass_displayA, gpu_index );
1221 set_reg_value( hkey, driverW, REG_SZ, bufferW, asciiz_to_unicode( bufferW, buffer ));
1223 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X",
1224 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
1225 size = asciiz_to_unicode( bufferW, buffer );
1226 bufferW[size / sizeof(WCHAR)] = 0; /* for REG_MULTI_SZ */
1227 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, size + sizeof(WCHAR) );
1229 if ((subkey = reg_create_key( hkey, devpkey_device_matching_device_id,
1230 sizeof(devpkey_device_matching_device_id), 0, NULL )))
1232 if (gpu->vendor_id && gpu->device_id)
1233 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW, size );
1234 else
1235 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
1236 asciiz_to_unicode( bufferW, "ROOT\\BasicRender" ));
1237 NtClose( subkey );
1240 desc = gpu->name;
1241 if (!desc[0]) desc = wine_adapterW;
1242 set_reg_value( hkey, device_descW, REG_SZ, desc, (lstrlenW( desc ) + 1) * sizeof(WCHAR) );
1244 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
1246 if (!query_reg_value( subkey, video_idW, value, sizeof(buffer) ))
1248 GUID guid;
1249 uuid_create( &guid );
1250 sprintf( buffer, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
1251 (unsigned int)guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
1252 guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] );
1253 size = asciiz_to_unicode( ctx->gpu_guid, buffer );
1254 TRACE( "created guid %s\n", debugstr_w(ctx->gpu_guid) );
1255 set_reg_value( subkey, video_idW, REG_SZ, ctx->gpu_guid, size );
1257 else
1259 memcpy( ctx->gpu_guid, value->Data, value->DataLength );
1260 TRACE( "got guid %s\n", debugstr_w(ctx->gpu_guid) );
1262 NtClose( subkey );
1265 if ((subkey = reg_create_key( hkey, devpropkey_gpu_vulkan_uuidW,
1266 sizeof(devpropkey_gpu_vulkan_uuidW), 0, NULL )))
1268 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_GUID,
1269 &gpu->vulkan_uuid, sizeof(gpu->vulkan_uuid) );
1270 NtClose( subkey );
1273 if ((subkey = reg_create_key( hkey, devpropkey_device_ispresentW,
1274 sizeof(devpropkey_device_ispresentW), 0, NULL )))
1276 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
1277 &present, sizeof(present) );
1278 NtClose( subkey );
1281 if ((subkey = reg_create_key( hkey, devpropkey_gpu_luidW, sizeof(devpropkey_gpu_luidW), 0, NULL )))
1283 if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) != sizeof(LUID))
1285 NtAllocateLocallyUniqueId( &ctx->gpu_luid );
1286 TRACE("allocated luid %08x%08x\n", (int)ctx->gpu_luid.HighPart, (int)ctx->gpu_luid.LowPart );
1287 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT64,
1288 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
1290 else
1292 memcpy( &ctx->gpu_luid, value->Data, sizeof(ctx->gpu_luid) );
1293 TRACE("got luid %08x%08x\n", (int)ctx->gpu_luid.HighPart, (int)ctx->gpu_luid.LowPart );
1295 NtClose( subkey );
1298 NtClose( hkey );
1300 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_displayA, gpu_index );
1301 hkey = reg_create_key( control_key, bufferW,
1302 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
1304 NtQuerySystemTime( &ft );
1305 set_reg_value( hkey, driver_dateW, REG_SZ, bufferW, format_date( bufferW, ft.QuadPart ));
1307 set_reg_value( hkey, driver_date_dataW, REG_BINARY, &ft, sizeof(ft) );
1309 size = (lstrlenW( desc ) + 1) * sizeof(WCHAR);
1310 set_reg_value( hkey, driver_descW, REG_SZ, desc, size );
1311 set_reg_value( hkey, adapter_stringW, REG_BINARY, desc, size );
1312 set_reg_value( hkey, bios_stringW, REG_BINARY, desc, size );
1313 set_reg_value( hkey, chip_typeW, REG_BINARY, desc, size );
1314 set_reg_value( hkey, dac_typeW, REG_BINARY, ramdacW, sizeof(ramdacW) );
1316 NtClose( hkey );
1318 link_device( ctx->gpuid, guid_devinterface_display_adapterW );
1319 link_device( ctx->gpuid, guid_display_device_arrivalW );
1322 static void add_adapter( const struct gdi_adapter *adapter, void *param )
1324 struct device_manager_ctx *ctx = param;
1325 unsigned int adapter_index, video_index, len;
1326 char name[64], buffer[MAX_PATH];
1327 WCHAR nameW[64], bufferW[MAX_PATH];
1328 HKEY hkey;
1330 TRACE( "\n" );
1332 if (ctx->adapter_key)
1334 NtClose( ctx->adapter_key );
1335 ctx->adapter_key = NULL;
1338 adapter_index = ctx->adapter_count++;
1339 video_index = ctx->video_count++;
1340 ctx->monitor_count = 0;
1341 ctx->mode_count = 0;
1343 len = asciiz_to_unicode( bufferW, "\\Registry\\Machine\\System\\CurrentControlSet\\"
1344 "Control\\Video\\" ) / sizeof(WCHAR) - 1;
1345 lstrcpyW( bufferW + len, ctx->gpu_guid );
1346 len += lstrlenW( bufferW + len );
1347 sprintf( buffer, "\\%04x", adapter_index );
1348 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1349 hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1350 REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, NULL );
1351 if (!hkey) hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1352 REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK, NULL );
1354 sprintf( name, "\\Device\\Video%u", video_index );
1355 asciiz_to_unicode( nameW, name );
1356 set_reg_value( video_key, nameW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1358 if (hkey)
1360 sprintf( buffer, "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\"
1361 "%s\\%04X", guid_devclass_displayA, ctx->gpu_count - 1 );
1362 len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR);
1363 set_reg_value( hkey, symbolic_link_valueW, REG_LINK, bufferW, len );
1364 NtClose( hkey );
1366 else ERR( "failed to create link key\n" );
1368 /* Following information is Wine specific, it doesn't really exist on Windows. */
1369 len = asciiz_to_unicode( bufferW, "System\\CurrentControlSet\\Control\\Video\\" )
1370 / sizeof(WCHAR) - 1;
1371 lstrcpyW( bufferW + len, ctx->gpu_guid );
1372 len += lstrlenW( bufferW + len );
1373 sprintf( buffer, "\\%04x", adapter_index );
1374 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1375 ctx->adapter_key = reg_create_key( config_key, bufferW, len * sizeof(WCHAR),
1376 REG_OPTION_VOLATILE, NULL );
1378 set_reg_value( ctx->adapter_key, gpu_idW, REG_SZ, ctx->gpuid,
1379 (lstrlenW( ctx->gpuid ) + 1) * sizeof(WCHAR) );
1380 set_reg_value( ctx->adapter_key, state_flagsW, REG_DWORD, &adapter->state_flags,
1381 sizeof(adapter->state_flags) );
1384 static void add_monitor( const struct gdi_monitor *monitor, void *param )
1386 struct device_manager_ctx *ctx = param;
1387 char buffer[MAX_PATH], instance[64];
1388 unsigned int monitor_index, output_index;
1389 struct edid_monitor_info monitor_info;
1390 char monitor_id_string[16];
1391 WCHAR bufferW[MAX_PATH];
1392 HKEY hkey, subkey;
1393 unsigned int len;
1395 monitor_index = ctx->monitor_count++;
1396 output_index = ctx->output_count++;
1398 TRACE( "%u %s %s\n", monitor_index, wine_dbgstr_rect(&monitor->rc_monitor), wine_dbgstr_rect(&monitor->rc_work) );
1400 get_monitor_info_from_edid( &monitor_info, monitor->edid, monitor->edid_len );
1401 if (monitor_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
1402 strcpy( monitor_id_string, monitor_info.monitor_id_string );
1403 else
1404 strcpy( monitor_id_string, "Default_Monitor" );
1406 sprintf( buffer, "MonitorID%u", monitor_index );
1407 sprintf( instance, "DISPLAY\\%s\\%04X&%04X", monitor_id_string, ctx->video_count - 1, monitor_index );
1408 set_reg_ascii_value( ctx->adapter_key, buffer, instance );
1410 hkey = reg_create_key( enum_key, bufferW, asciiz_to_unicode( bufferW, instance ) - sizeof(WCHAR),
1411 0, NULL );
1412 if (!hkey) return;
1414 link_device( bufferW, guid_devinterface_monitorW );
1416 asciiz_to_unicode( bufferW, "Generic Non-PnP Monitor" );
1417 set_reg_value( hkey, device_descW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1419 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
1420 sprintf( buffer, "%s\\%04X", guid_devclass_monitorA, output_index );
1421 set_reg_ascii_value( hkey, "Driver", buffer );
1422 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_monitorW, sizeof(guid_devclass_monitorW) );
1424 sprintf( buffer, "MONITOR\\%s", monitor_id_string );
1425 len = asciiz_to_unicode( bufferW, buffer );
1426 bufferW[len / sizeof(WCHAR)] = 0;
1427 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, len + sizeof(WCHAR) );
1429 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
1431 static const WCHAR bad_edidW[] = {'B','A','D','_','E','D','I','D',0};
1433 if (monitor->edid_len)
1434 set_reg_value( subkey, edidW, REG_BINARY, monitor->edid, monitor->edid_len );
1435 else
1436 set_reg_value( subkey, bad_edidW, REG_BINARY, NULL, 0 );
1437 NtClose( subkey );
1440 /* StateFlags */
1441 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_stateflagsW,
1442 sizeof(wine_devpropkey_monitor_stateflagsW), 0, NULL )))
1444 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32, &monitor->state_flags,
1445 sizeof(monitor->state_flags) );
1446 NtClose( subkey );
1449 /* WINE_DEVPROPKEY_MONITOR_RCMONITOR */
1450 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcmonitorW,
1451 sizeof(wine_devpropkey_monitor_rcmonitorW), 0, NULL )))
1453 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_monitor,
1454 sizeof(monitor->rc_monitor) );
1455 NtClose( subkey );
1458 /* WINE_DEVPROPKEY_MONITOR_RCWORK */
1459 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcworkW,
1460 sizeof(wine_devpropkey_monitor_rcworkW), 0, NULL )))
1462 TRACE( "rc_work %s\n", wine_dbgstr_rect(&monitor->rc_work) );
1463 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_work,
1464 sizeof(monitor->rc_work) );
1465 NtClose( subkey );
1468 /* WINE_DEVPROPKEY_MONITOR_ADAPTERNAME */
1469 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_adapternameW,
1470 sizeof(wine_devpropkey_monitor_adapternameW), 0, NULL )))
1472 sprintf( buffer, "\\\\.\\DISPLAY%u", ctx->video_count );
1473 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
1474 asciiz_to_unicode( bufferW, buffer ));
1475 NtClose( subkey );
1478 /* DEVPROPKEY_MONITOR_GPU_LUID */
1479 if ((subkey = reg_create_key( hkey, devpropkey_monitor_gpu_luidW,
1480 sizeof(devpropkey_monitor_gpu_luidW), 0, NULL )))
1482 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_INT64,
1483 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
1484 NtClose( subkey );
1487 /* DEVPROPKEY_MONITOR_OUTPUT_ID */
1488 if ((subkey = reg_create_key( hkey, devpropkey_monitor_output_idW,
1489 sizeof(devpropkey_monitor_output_idW), 0, NULL )))
1491 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1492 &output_index, sizeof(output_index) );
1493 NtClose( subkey );
1496 NtClose( hkey );
1498 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_monitorA, output_index );
1499 hkey = reg_create_key( control_key, bufferW,
1500 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
1501 if (hkey) NtClose( hkey );
1504 static void add_mode( const DEVMODEW *mode, BOOL current, void *param )
1506 struct device_manager_ctx *ctx = param;
1507 DEVMODEW nopos_mode;
1509 if (!ctx->adapter_count)
1511 static const struct gdi_adapter default_adapter =
1513 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1515 TRACE( "adding default fake adapter\n" );
1516 add_adapter( &default_adapter, ctx );
1519 nopos_mode = *mode;
1520 nopos_mode.dmPosition.x = 0;
1521 nopos_mode.dmPosition.y = 0;
1522 nopos_mode.dmFields &= ~DM_POSITION;
1524 if (write_adapter_mode( ctx->adapter_key, ctx->mode_count, &nopos_mode ))
1526 ctx->mode_count++;
1527 set_reg_value( ctx->adapter_key, mode_countW, REG_DWORD, &ctx->mode_count, sizeof(ctx->mode_count) );
1528 if (current)
1530 if (!read_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, &nopos_mode ))
1531 write_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, mode );
1532 write_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, mode );
1537 static const struct gdi_device_manager device_manager =
1539 add_gpu,
1540 add_adapter,
1541 add_monitor,
1542 add_mode,
1545 static void release_display_manager_ctx( struct device_manager_ctx *ctx )
1547 if (ctx->mutex)
1549 pthread_mutex_unlock( &display_lock );
1550 release_display_device_init_mutex( ctx->mutex );
1552 if (ctx->adapter_key)
1554 NtClose( ctx->adapter_key );
1555 last_query_display_time = 0;
1557 if (ctx->gpu_count) cleanup_devices();
1560 static void clear_display_devices(void)
1562 struct adapter *adapter;
1563 struct monitor *monitor;
1565 if (list_head( &monitors ) == &virtual_monitor.entry)
1567 list_init( &monitors );
1568 return;
1571 while (!list_empty( &monitors ))
1573 monitor = LIST_ENTRY( list_head( &monitors ), struct monitor, entry );
1574 adapter_release( monitor->adapter );
1575 list_remove( &monitor->entry );
1576 free( monitor );
1579 while (!list_empty( &adapters ))
1581 adapter = LIST_ENTRY( list_head( &adapters ), struct adapter, entry );
1582 list_remove( &adapter->entry );
1583 adapter_release( adapter );
1587 static BOOL update_display_cache_from_registry(void)
1589 DWORD adapter_id, monitor_id, monitor_count = 0, size;
1590 KEY_BASIC_INFORMATION key;
1591 struct adapter *adapter;
1592 struct monitor *monitor, *monitor2;
1593 HANDLE mutex = NULL;
1594 NTSTATUS status;
1595 BOOL ret;
1597 /* If user driver did initialize the registry, then exit */
1598 if (!video_key && !(video_key = reg_open_key( NULL, devicemap_video_keyW,
1599 sizeof(devicemap_video_keyW) )))
1600 return FALSE;
1602 status = NtQueryKey( video_key, KeyBasicInformation, &key,
1603 offsetof(KEY_BASIC_INFORMATION, Name), &size );
1604 if (status && status != STATUS_BUFFER_OVERFLOW)
1605 return FALSE;
1607 if (key.LastWriteTime.QuadPart <= last_query_display_time) return TRUE;
1609 mutex = get_display_device_init_mutex();
1610 pthread_mutex_lock( &display_lock );
1612 clear_display_devices();
1614 for (adapter_id = 0;; adapter_id++)
1616 if (!(adapter = calloc( 1, sizeof(*adapter) ))) break;
1617 adapter->refcount = 1;
1618 adapter->id = adapter_id;
1620 if (!read_display_adapter_settings( adapter_id, adapter ))
1622 adapter_release( adapter );
1623 break;
1626 list_add_tail( &adapters, &adapter->entry );
1627 for (monitor_id = 0;; monitor_id++)
1629 if (!(monitor = calloc( 1, sizeof(*monitor) ))) break;
1630 if (!read_monitor_settings( adapter, monitor_id, monitor ))
1632 free( monitor );
1633 break;
1636 monitor->id = monitor_id;
1637 monitor->adapter = adapter_acquire( adapter );
1639 LIST_FOR_EACH_ENTRY(monitor2, &monitors, struct monitor, entry)
1641 if (EqualRect(&monitor2->rc_monitor, &monitor->rc_monitor))
1643 monitor->is_clone = TRUE;
1644 break;
1648 monitor->handle = UlongToHandle( ++monitor_count );
1649 list_add_tail( &monitors, &monitor->entry );
1653 if ((ret = !list_empty( &adapters ) && !list_empty( &monitors )))
1654 last_query_display_time = key.LastWriteTime.QuadPart;
1655 pthread_mutex_unlock( &display_lock );
1656 release_display_device_init_mutex( mutex );
1657 return ret;
1660 static BOOL is_same_devmode( const DEVMODEW *a, const DEVMODEW *b )
1662 return a->dmDisplayOrientation == b->dmDisplayOrientation &&
1663 a->dmBitsPerPel == b->dmBitsPerPel &&
1664 a->dmPelsWidth == b->dmPelsWidth &&
1665 a->dmPelsHeight == b->dmPelsHeight &&
1666 a->dmDisplayFrequency == b->dmDisplayFrequency;
1669 static BOOL update_display_cache( BOOL force )
1671 HWINSTA winstation = NtUserGetProcessWindowStation();
1672 struct device_manager_ctx ctx = {0};
1673 USEROBJECTFLAGS flags;
1675 /* services do not have any adapters, only a virtual monitor */
1676 if (NtUserGetObjectInformation( winstation, UOI_FLAGS, &flags, sizeof(flags), NULL )
1677 && !(flags.dwFlags & WSF_VISIBLE))
1679 pthread_mutex_lock( &display_lock );
1680 clear_display_devices();
1681 list_add_tail( &monitors, &virtual_monitor.entry );
1682 pthread_mutex_unlock( &display_lock );
1683 return TRUE;
1686 if (!user_driver->pUpdateDisplayDevices( &device_manager, force, &ctx ) && force)
1688 /* default implementation: expose an adapter and a monitor with a few standard modes,
1689 * and read / write current display settings from / to the registry.
1691 static const DEVMODEW modes[] =
1693 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1694 .dmBitsPerPel = 32, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
1695 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1696 .dmBitsPerPel = 32, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
1697 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1698 .dmBitsPerPel = 32, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
1699 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1700 .dmBitsPerPel = 16, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
1701 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1702 .dmBitsPerPel = 16, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
1703 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1704 .dmBitsPerPel = 16, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
1706 static const struct gdi_gpu gpu;
1707 static const struct gdi_adapter adapter =
1709 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1711 struct gdi_monitor monitor =
1713 .state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED,
1715 DEVMODEW mode = {{0}};
1716 UINT i;
1718 add_gpu( &gpu, &ctx );
1719 add_adapter( &adapter, &ctx );
1721 if (!read_adapter_mode( ctx.adapter_key, ENUM_CURRENT_SETTINGS, &mode ))
1723 mode = modes[2];
1724 mode.dmFields |= DM_POSITION;
1726 monitor.rc_monitor.right = mode.dmPelsWidth;
1727 monitor.rc_monitor.bottom = mode.dmPelsHeight;
1728 monitor.rc_work.right = mode.dmPelsWidth;
1729 monitor.rc_work.bottom = mode.dmPelsHeight;
1731 add_monitor( &monitor, &ctx );
1732 for (i = 0; i < ARRAY_SIZE(modes); ++i)
1734 if (is_same_devmode( modes + i, &mode )) add_mode( &mode, TRUE, &ctx );
1735 else add_mode( modes + i, FALSE, &ctx );
1738 release_display_manager_ctx( &ctx );
1740 if (!update_display_cache_from_registry())
1742 if (force)
1744 ERR( "Failed to read display config.\n" );
1745 return FALSE;
1748 if (ctx.gpu_count)
1750 ERR( "Driver reported devices, but we failed to read them.\n" );
1751 return FALSE;
1754 return update_display_cache( TRUE );
1757 return TRUE;
1760 static BOOL lock_display_devices(void)
1762 if (!update_display_cache( FALSE )) return FALSE;
1763 pthread_mutex_lock( &display_lock );
1764 return TRUE;
1767 static void unlock_display_devices(void)
1769 pthread_mutex_unlock( &display_lock );
1772 static HDC get_display_dc(void)
1774 pthread_mutex_lock( &display_dc_lock );
1775 if (!display_dc)
1777 HDC dc;
1779 pthread_mutex_unlock( &display_dc_lock );
1780 dc = NtGdiOpenDCW( NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL );
1781 pthread_mutex_lock( &display_dc_lock );
1782 if (display_dc)
1783 NtGdiDeleteObjectApp( dc );
1784 else
1785 display_dc = dc;
1787 return display_dc;
1790 static void release_display_dc( HDC hdc )
1792 pthread_mutex_unlock( &display_dc_lock );
1795 /**********************************************************************
1796 * get_monitor_dpi
1798 UINT get_monitor_dpi( HMONITOR monitor )
1800 /* FIXME: use the monitor DPI instead */
1801 return system_dpi;
1804 /**********************************************************************
1805 * get_win_monitor_dpi
1807 UINT get_win_monitor_dpi( HWND hwnd )
1809 /* FIXME: use the monitor DPI instead */
1810 return system_dpi;
1813 /**********************************************************************
1814 * get_thread_dpi_awareness
1816 DPI_AWARENESS get_thread_dpi_awareness(void)
1818 struct ntuser_thread_info *info = NtUserGetThreadInfo();
1819 ULONG_PTR context = info->dpi_awareness;
1821 if (!context) context = NtUserGetProcessDpiAwarenessContext( NULL );
1823 switch (context)
1825 case 0x10:
1826 case 0x11:
1827 case 0x12:
1828 case 0x80000010:
1829 case 0x80000011:
1830 case 0x80000012:
1831 return context & 3;
1832 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
1833 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
1834 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
1835 return ~context;
1836 default:
1837 return DPI_AWARENESS_INVALID;
1841 DWORD get_process_layout(void)
1843 return process_layout == ~0u ? 0 : process_layout;
1846 /**********************************************************************
1847 * get_thread_dpi
1849 UINT get_thread_dpi(void)
1851 switch (get_thread_dpi_awareness())
1853 case DPI_AWARENESS_UNAWARE: return USER_DEFAULT_SCREEN_DPI;
1854 case DPI_AWARENESS_SYSTEM_AWARE: return system_dpi;
1855 default: return 0; /* no scaling */
1859 /* see GetDpiForSystem */
1860 UINT get_system_dpi(void)
1862 if (get_thread_dpi_awareness() == DPI_AWARENESS_UNAWARE) return USER_DEFAULT_SCREEN_DPI;
1863 return system_dpi;
1866 /* see GetAwarenessFromDpiAwarenessContext */
1867 static DPI_AWARENESS get_awareness_from_dpi_awareness_context( DPI_AWARENESS_CONTEXT context )
1869 switch ((ULONG_PTR)context)
1871 case 0x10:
1872 case 0x11:
1873 case 0x12:
1874 case 0x80000010:
1875 case 0x80000011:
1876 case 0x80000012:
1877 return (ULONG_PTR)context & 3;
1878 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
1879 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
1880 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
1881 return ~(ULONG_PTR)context;
1882 default:
1883 return DPI_AWARENESS_INVALID;
1887 /**********************************************************************
1888 * SetThreadDpiAwarenessContext (win32u.so)
1890 DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT context )
1892 struct ntuser_thread_info *info = NtUserGetThreadInfo();
1893 DPI_AWARENESS prev, val = get_awareness_from_dpi_awareness_context( context );
1895 if (val == DPI_AWARENESS_INVALID)
1897 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
1898 return 0;
1900 if (!(prev = info->dpi_awareness))
1902 prev = NtUserGetProcessDpiAwarenessContext( GetCurrentProcess() ) & 3;
1903 prev |= 0x80000010; /* restore to process default */
1905 if (((ULONG_PTR)context & ~(ULONG_PTR)0x13) == 0x80000000) info->dpi_awareness = 0;
1906 else info->dpi_awareness = val | 0x10;
1907 return ULongToHandle( prev );
1910 /**********************************************************************
1911 * map_dpi_rect
1913 RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to )
1915 if (dpi_from && dpi_to && dpi_from != dpi_to)
1917 rect.left = muldiv( rect.left, dpi_to, dpi_from );
1918 rect.top = muldiv( rect.top, dpi_to, dpi_from );
1919 rect.right = muldiv( rect.right, dpi_to, dpi_from );
1920 rect.bottom = muldiv( rect.bottom, dpi_to, dpi_from );
1922 return rect;
1925 /**********************************************************************
1926 * map_dpi_point
1928 POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to )
1930 if (dpi_from && dpi_to && dpi_from != dpi_to)
1932 pt.x = muldiv( pt.x, dpi_to, dpi_from );
1933 pt.y = muldiv( pt.y, dpi_to, dpi_from );
1935 return pt;
1938 /**********************************************************************
1939 * point_win_to_phys_dpi
1941 static POINT point_win_to_phys_dpi( HWND hwnd, POINT pt )
1943 return map_dpi_point( pt, get_dpi_for_window( hwnd ), get_win_monitor_dpi( hwnd ) );
1946 /**********************************************************************
1947 * point_phys_to_win_dpi
1949 POINT point_phys_to_win_dpi( HWND hwnd, POINT pt )
1951 return map_dpi_point( pt, get_win_monitor_dpi( hwnd ), get_dpi_for_window( hwnd ));
1954 /**********************************************************************
1955 * point_thread_to_win_dpi
1957 POINT point_thread_to_win_dpi( HWND hwnd, POINT pt )
1959 UINT dpi = get_thread_dpi();
1960 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
1961 return map_dpi_point( pt, dpi, get_dpi_for_window( hwnd ));
1964 /**********************************************************************
1965 * rect_thread_to_win_dpi
1967 RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect )
1969 UINT dpi = get_thread_dpi();
1970 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
1971 return map_dpi_rect( rect, dpi, get_dpi_for_window( hwnd ) );
1974 /* map value from system dpi to standard 96 dpi for storing in the registry */
1975 static int map_from_system_dpi( int val )
1977 return muldiv( val, USER_DEFAULT_SCREEN_DPI, get_system_dpi() );
1980 /* map value from 96 dpi to system or custom dpi */
1981 static int map_to_dpi( int val, UINT dpi )
1983 if (!dpi) dpi = get_system_dpi();
1984 return muldiv( val, dpi, USER_DEFAULT_SCREEN_DPI );
1987 RECT get_virtual_screen_rect( UINT dpi )
1989 struct monitor *monitor;
1990 RECT rect = {0};
1992 if (!lock_display_devices()) return rect;
1994 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
1996 union_rect( &rect, &rect, &monitor->rc_monitor );
1999 unlock_display_devices();
2001 if (dpi) rect = map_dpi_rect( rect, system_dpi, dpi );
2002 return rect;
2005 static BOOL is_window_rect_full_screen( const RECT *rect )
2007 struct monitor *monitor;
2008 BOOL ret = FALSE;
2010 if (!lock_display_devices()) return FALSE;
2012 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2014 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2015 continue;
2017 if (rect->left <= monitor->rc_monitor.left && rect->right >= monitor->rc_monitor.right &&
2018 rect->top <= monitor->rc_monitor.top && rect->bottom >= monitor->rc_monitor.bottom)
2020 ret = TRUE;
2021 break;
2025 unlock_display_devices();
2026 return ret;
2029 RECT get_display_rect( const WCHAR *display )
2031 struct monitor *monitor;
2032 RECT rect = {0};
2034 if (!lock_display_devices()) return rect;
2036 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2038 if (!monitor->adapter || wcsicmp( monitor->adapter->dev.device_name, display )) continue;
2039 rect = monitor->rc_monitor;
2040 break;
2043 unlock_display_devices();
2044 return map_dpi_rect( rect, system_dpi, get_thread_dpi() );
2047 RECT get_primary_monitor_rect( UINT dpi )
2049 struct monitor *monitor;
2050 RECT rect = {0};
2052 if (!lock_display_devices()) return rect;
2054 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2056 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
2057 rect = monitor->rc_monitor;
2058 break;
2061 unlock_display_devices();
2062 return map_dpi_rect( rect, system_dpi, dpi );
2065 /**********************************************************************
2066 * NtUserGetDisplayConfigBufferSizes (win32u.@)
2068 LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_info,
2069 UINT32 *num_mode_info )
2071 struct monitor *monitor;
2072 UINT32 count = 0;
2074 TRACE( "(0x%x %p %p)\n", flags, num_path_info, num_mode_info );
2076 if (!num_path_info || !num_mode_info)
2077 return ERROR_INVALID_PARAMETER;
2079 *num_path_info = 0;
2081 switch (flags)
2083 case QDC_ALL_PATHS:
2084 case QDC_ONLY_ACTIVE_PATHS:
2085 case QDC_DATABASE_CURRENT:
2086 break;
2087 default:
2088 return ERROR_INVALID_PARAMETER;
2091 /* FIXME: semi-stub */
2092 if (flags != QDC_ONLY_ACTIVE_PATHS)
2093 FIXME( "only returning active paths\n" );
2095 if (lock_display_devices())
2097 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2099 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2100 continue;
2101 count++;
2103 unlock_display_devices();
2106 *num_path_info = count;
2107 *num_mode_info = count * 2;
2108 TRACE( "returning %u paths %u modes\n", *num_path_info, *num_mode_info );
2109 return ERROR_SUCCESS;
2112 /* display_lock mutex must be held */
2113 static struct display_device *find_monitor_device( struct display_device *adapter, UINT index )
2115 struct monitor *monitor;
2117 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2118 if (&monitor->adapter->dev == adapter && index == monitor->id)
2119 return &monitor->dev;
2121 WARN( "Failed to find adapter %s monitor with id %u.\n", debugstr_w(adapter->device_name), index );
2122 return NULL;
2125 /* display_lock mutex must be held */
2126 static struct display_device *find_adapter_device_by_id( UINT index )
2128 struct adapter *adapter;
2130 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
2131 if (index == adapter->id) return &adapter->dev;
2133 WARN( "Failed to find adapter with id %u.\n", index );
2134 return NULL;
2137 /* display_lock mutex must be held */
2138 static struct display_device *find_adapter_device_by_name( UNICODE_STRING *name )
2140 SIZE_T len = name->Length / sizeof(WCHAR);
2141 struct adapter *adapter;
2143 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
2144 if (!wcsnicmp( name->Buffer, adapter->dev.device_name, len ) && !adapter->dev.device_name[len])
2145 return &adapter->dev;
2147 WARN( "Failed to find adapter with name %s.\n", debugstr_us(name) );
2148 return NULL;
2151 /* Find and acquire the adapter matching name, or primary adapter if name is NULL.
2152 * If not NULL, the returned adapter needs to be released with adapter_release.
2154 static struct adapter *find_adapter( UNICODE_STRING *name )
2156 struct display_device *device;
2157 struct adapter *adapter;
2159 if (!lock_display_devices()) return NULL;
2161 if (name && name->Length) device = find_adapter_device_by_name( name );
2162 else device = find_adapter_device_by_id( 0 ); /* use primary adapter */
2164 if (!device) adapter = NULL;
2165 else adapter = adapter_acquire( CONTAINING_RECORD( device, struct adapter, dev ) );
2167 unlock_display_devices();
2168 return adapter;
2171 /***********************************************************************
2172 * NtUserEnumDisplayDevices (win32u.@)
2174 NTSTATUS WINAPI NtUserEnumDisplayDevices( UNICODE_STRING *device, DWORD index,
2175 DISPLAY_DEVICEW *info, DWORD flags )
2177 struct display_device *found = NULL;
2179 TRACE( "%s %u %p %#x\n", debugstr_us( device ), (int)index, info, (int)flags );
2181 if (!info || !info->cb) return STATUS_UNSUCCESSFUL;
2183 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
2185 if (!device || !device->Length) found = find_adapter_device_by_id( index );
2186 else if ((found = find_adapter_device_by_name( device ))) found = find_monitor_device( found, index );
2188 if (found)
2190 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceName) + sizeof(info->DeviceName))
2191 lstrcpyW( info->DeviceName, found->device_name );
2192 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceString) + sizeof(info->DeviceString))
2193 lstrcpyW( info->DeviceString, found->device_string );
2194 if (info->cb >= offsetof(DISPLAY_DEVICEW, StateFlags) + sizeof(info->StateFlags))
2195 info->StateFlags = found->state_flags;
2196 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID))
2197 lstrcpyW( info->DeviceID, (flags & EDD_GET_DEVICE_INTERFACE_NAME)
2198 ? found->interface_name : found->device_id );
2199 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey))
2200 lstrcpyW( info->DeviceKey, found->device_key );
2202 unlock_display_devices();
2203 return found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
2206 #define _X_FIELD(prefix, bits) \
2207 if ((fields) & prefix##_##bits) \
2209 p += sprintf( p, "%s%s", first ? "" : ",", #bits ); \
2210 first = FALSE; \
2213 static const char *_CDS_flags( DWORD fields )
2215 BOOL first = TRUE;
2216 CHAR buf[128];
2217 CHAR *p = buf;
2219 _X_FIELD(CDS, UPDATEREGISTRY)
2220 _X_FIELD(CDS, TEST)
2221 _X_FIELD(CDS, FULLSCREEN)
2222 _X_FIELD(CDS, GLOBAL)
2223 _X_FIELD(CDS, SET_PRIMARY)
2224 _X_FIELD(CDS, VIDEOPARAMETERS)
2225 _X_FIELD(CDS, ENABLE_UNSAFE_MODES)
2226 _X_FIELD(CDS, DISABLE_UNSAFE_MODES)
2227 _X_FIELD(CDS, RESET)
2228 _X_FIELD(CDS, RESET_EX)
2229 _X_FIELD(CDS, NORESET)
2231 *p = 0;
2232 return wine_dbg_sprintf( "%s", buf );
2235 static const char *_DM_fields( DWORD fields )
2237 BOOL first = TRUE;
2238 CHAR buf[128];
2239 CHAR *p = buf;
2241 _X_FIELD(DM, BITSPERPEL)
2242 _X_FIELD(DM, PELSWIDTH)
2243 _X_FIELD(DM, PELSHEIGHT)
2244 _X_FIELD(DM, DISPLAYFLAGS)
2245 _X_FIELD(DM, DISPLAYFREQUENCY)
2246 _X_FIELD(DM, POSITION)
2247 _X_FIELD(DM, DISPLAYORIENTATION)
2249 *p = 0;
2250 return wine_dbg_sprintf( "%s", buf );
2253 #undef _X_FIELD
2255 static void trace_devmode( const DEVMODEW *devmode )
2257 TRACE( "dmFields=%s ", _DM_fields(devmode->dmFields) );
2258 if (devmode->dmFields & DM_BITSPERPEL)
2259 TRACE( "dmBitsPerPel=%u ", (int)devmode->dmBitsPerPel );
2260 if (devmode->dmFields & DM_PELSWIDTH)
2261 TRACE( "dmPelsWidth=%u ", (int)devmode->dmPelsWidth );
2262 if (devmode->dmFields & DM_PELSHEIGHT)
2263 TRACE( "dmPelsHeight=%u ", (int)devmode->dmPelsHeight );
2264 if (devmode->dmFields & DM_DISPLAYFREQUENCY)
2265 TRACE( "dmDisplayFrequency=%u ", (int)devmode->dmDisplayFrequency );
2266 if (devmode->dmFields & DM_POSITION)
2267 TRACE( "dmPosition=(%d,%d) ", (int)devmode->dmPosition.x, (int)devmode->dmPosition.y );
2268 if (devmode->dmFields & DM_DISPLAYFLAGS)
2269 TRACE( "dmDisplayFlags=%#x ", (int)devmode->dmDisplayFlags );
2270 if (devmode->dmFields & DM_DISPLAYORIENTATION)
2271 TRACE( "dmDisplayOrientation=%u ", (int)devmode->dmDisplayOrientation );
2272 TRACE("\n");
2275 static BOOL is_detached_mode( const DEVMODEW *mode )
2277 return mode->dmFields & DM_POSITION &&
2278 mode->dmFields & DM_PELSWIDTH &&
2279 mode->dmFields & DM_PELSHEIGHT &&
2280 mode->dmPelsWidth == 0 &&
2281 mode->dmPelsHeight == 0;
2284 static const DEVMODEW *find_display_mode( const DEVMODEW *modes, DEVMODEW *devmode )
2286 const DEVMODEW *mode;
2288 if (is_detached_mode( devmode )) return devmode;
2290 for (mode = modes; mode && mode->dmSize; mode = NEXT_DEVMODEW(mode))
2292 if ((mode->dmFields & DM_DISPLAYFLAGS) && (mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
2293 continue;
2294 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel && devmode->dmBitsPerPel != mode->dmBitsPerPel)
2295 continue;
2296 if ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth != mode->dmPelsWidth)
2297 continue;
2298 if ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight != mode->dmPelsHeight)
2299 continue;
2300 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != mode->dmDisplayFrequency
2301 && devmode->dmDisplayFrequency > 1 && mode->dmDisplayFrequency)
2302 continue;
2303 if ((devmode->dmFields & DM_DISPLAYORIENTATION) && devmode->dmDisplayOrientation != mode->dmDisplayOrientation)
2304 continue;
2305 if ((devmode->dmFields & DM_DISPLAYFLAGS) && (mode->dmFields & DM_DISPLAYFLAGS) &&
2306 (devmode->dmDisplayFlags & DM_INTERLACED) != (mode->dmDisplayFlags & DM_INTERLACED))
2307 continue;
2308 if ((devmode->dmFields & DM_DISPLAYFIXEDOUTPUT) && (mode->dmFields & DM_DISPLAYFIXEDOUTPUT) &&
2309 devmode->dmDisplayFixedOutput != mode->dmDisplayFixedOutput)
2310 continue;
2312 return mode;
2315 return NULL;
2318 static BOOL adapter_get_full_mode( const struct adapter *adapter, const DEVMODEW *devmode, DEVMODEW *full_mode )
2320 const DEVMODEW *adapter_mode;
2322 if (devmode)
2324 trace_devmode( devmode );
2326 if (devmode->dmSize < offsetof(DEVMODEW, dmICMMethod)) return FALSE;
2327 if (!is_detached_mode( devmode ) &&
2328 (!(devmode->dmFields & DM_BITSPERPEL) || !devmode->dmBitsPerPel) &&
2329 (!(devmode->dmFields & DM_PELSWIDTH) || !devmode->dmPelsWidth) &&
2330 (!(devmode->dmFields & DM_PELSHEIGHT) || !devmode->dmPelsHeight) &&
2331 (!(devmode->dmFields & DM_DISPLAYFREQUENCY) || !devmode->dmDisplayFrequency))
2332 devmode = NULL;
2335 if (devmode) memcpy( full_mode, devmode, devmode->dmSize );
2336 else
2338 if (!adapter_get_registry_settings( adapter, full_mode )) return FALSE;
2339 TRACE( "Return to original display mode\n" );
2342 if ((full_mode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
2344 WARN( "devmode doesn't specify the resolution: %#x\n", (int)full_mode->dmFields );
2345 return FALSE;
2348 if (!is_detached_mode( full_mode ) && (!full_mode->dmPelsWidth || !full_mode->dmPelsHeight || !(full_mode->dmFields & DM_POSITION)))
2350 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2352 if (!adapter_get_current_settings( adapter, &current_mode )) return FALSE;
2353 if (!full_mode->dmPelsWidth) full_mode->dmPelsWidth = current_mode.dmPelsWidth;
2354 if (!full_mode->dmPelsHeight) full_mode->dmPelsHeight = current_mode.dmPelsHeight;
2355 if (!(full_mode->dmFields & DM_POSITION))
2357 full_mode->dmPosition = current_mode.dmPosition;
2358 full_mode->dmFields |= DM_POSITION;
2362 if ((adapter_mode = find_display_mode( adapter->modes, full_mode )) && adapter_mode != full_mode)
2364 POINTL position = full_mode->dmPosition;
2365 *full_mode = *adapter_mode;
2366 full_mode->dmFields |= DM_POSITION;
2367 full_mode->dmPosition = position;
2370 return adapter_mode != NULL;
2373 static DEVMODEW *get_display_settings( const WCHAR *devname, const DEVMODEW *devmode )
2375 DEVMODEW *mode, *displays;
2376 struct adapter *adapter;
2377 BOOL ret;
2379 /* allocate an extra mode for easier iteration */
2380 if (!(displays = calloc( list_count( &adapters ) + 1, sizeof(DEVMODEW) ))) return NULL;
2381 mode = displays;
2383 LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
2385 mode->dmSize = sizeof(DEVMODEW);
2386 if (devmode && !wcsicmp( devname, adapter->dev.device_name ))
2387 memcpy( &mode->dmFields, &devmode->dmFields, devmode->dmSize - offsetof(DEVMODEW, dmFields) );
2388 else
2390 if (!devname) ret = adapter_get_registry_settings( adapter, mode );
2391 else ret = adapter_get_current_settings( adapter, mode );
2392 if (!ret)
2394 free( displays );
2395 return NULL;
2399 lstrcpyW( mode->dmDeviceName, adapter->dev.device_name );
2400 mode = NEXT_DEVMODEW(mode);
2403 return displays;
2406 static INT offset_length( POINT offset )
2408 return offset.x * offset.x + offset.y * offset.y;
2411 static void set_rect_from_devmode( RECT *rect, const DEVMODEW *mode )
2413 SetRect( rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth,
2414 mode->dmPosition.y + mode->dmPelsHeight );
2417 /* Check if a rect overlaps with placed display rects */
2418 static BOOL overlap_placed_displays( const RECT *rect, const DEVMODEW *displays )
2420 const DEVMODEW *mode;
2421 RECT intersect;
2423 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2425 set_rect_from_devmode( &intersect, mode );
2426 if ((mode->dmFields & DM_POSITION) && intersect_rect( &intersect, &intersect, rect )) return TRUE;
2429 return FALSE;
2432 /* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */
2433 static POINT get_placement_offset( const DEVMODEW *displays, const DEVMODEW *placing )
2435 POINT points[8], left_top, offset, min_offset = {0, 0};
2436 INT point_idx, point_count, vertex_idx;
2437 BOOL has_placed = FALSE, first = TRUE;
2438 RECT desired_rect, rect;
2439 const DEVMODEW *mode;
2440 INT width, height;
2442 set_rect_from_devmode( &desired_rect, placing );
2444 /* If the display to be placed is detached, no offset is needed to place it */
2445 if (IsRectEmpty( &desired_rect )) return min_offset;
2447 /* If there is no placed and attached display, place this display as it is */
2448 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2450 set_rect_from_devmode( &rect, mode );
2451 if ((mode->dmFields & DM_POSITION) && !IsRectEmpty( &rect ))
2453 has_placed = TRUE;
2454 break;
2458 if (!has_placed) return min_offset;
2460 /* Try to place this display with each of its four vertices at every vertex of the placed
2461 * displays and see which combination has the minimum offset length */
2462 width = desired_rect.right - desired_rect.left;
2463 height = desired_rect.bottom - desired_rect.top;
2465 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2467 set_rect_from_devmode( &rect, mode );
2468 if (!(mode->dmFields & DM_POSITION) || IsRectEmpty( &rect )) continue;
2470 /* Get four vertices of the placed display rectangle */
2471 points[0].x = rect.left;
2472 points[0].y = rect.top;
2473 points[1].x = rect.left;
2474 points[1].y = rect.bottom;
2475 points[2].x = rect.right;
2476 points[2].y = rect.top;
2477 points[3].x = rect.right;
2478 points[3].y = rect.bottom;
2479 point_count = 4;
2481 /* Intersected points when moving the display to be placed horizontally */
2482 if (desired_rect.bottom >= rect.top && desired_rect.top <= rect.bottom)
2484 points[point_count].x = rect.left;
2485 points[point_count++].y = desired_rect.top;
2486 points[point_count].x = rect.right;
2487 points[point_count++].y = desired_rect.top;
2489 /* Intersected points when moving the display to be placed vertically */
2490 if (desired_rect.left <= rect.right && desired_rect.right >= rect.left)
2492 points[point_count].x = desired_rect.left;
2493 points[point_count++].y = rect.top;
2494 points[point_count].x = desired_rect.left;
2495 points[point_count++].y = rect.bottom;
2498 /* Try moving each vertex of the display rectangle to each points */
2499 for (point_idx = 0; point_idx < point_count; ++point_idx)
2501 for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx)
2503 switch (vertex_idx)
2505 /* Move the bottom right vertex to the point */
2506 case 0:
2507 left_top.x = points[point_idx].x - width;
2508 left_top.y = points[point_idx].y - height;
2509 break;
2510 /* Move the bottom left vertex to the point */
2511 case 1:
2512 left_top.x = points[point_idx].x;
2513 left_top.y = points[point_idx].y - height;
2514 break;
2515 /* Move the top right vertex to the point */
2516 case 2:
2517 left_top.x = points[point_idx].x - width;
2518 left_top.y = points[point_idx].y;
2519 break;
2520 /* Move the top left vertex to the point */
2521 case 3:
2522 left_top.x = points[point_idx].x;
2523 left_top.y = points[point_idx].y;
2524 break;
2527 offset.x = left_top.x - desired_rect.left;
2528 offset.y = left_top.y - desired_rect.top;
2529 rect = desired_rect;
2530 OffsetRect( &rect, offset.x, offset.y );
2531 if (!overlap_placed_displays( &rect, displays ))
2533 if (first)
2535 min_offset = offset;
2536 first = FALSE;
2537 continue;
2540 if (offset_length( offset ) < offset_length( min_offset )) min_offset = offset;
2546 return min_offset;
2549 static void place_all_displays( DEVMODEW *displays )
2551 POINT min_offset, offset;
2552 DEVMODEW *mode, *placing;
2554 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2555 mode->dmFields &= ~DM_POSITION;
2557 /* Place all displays with no extra space between them and no overlapping */
2558 while (1)
2560 /* Place the unplaced display with the minimum offset length first */
2561 placing = NULL;
2562 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2564 if (mode->dmFields & DM_POSITION) continue;
2566 offset = get_placement_offset( displays, mode );
2567 if (!placing || offset_length( offset ) < offset_length( min_offset ))
2569 min_offset = offset;
2570 placing = mode;
2574 /* If all displays are placed */
2575 if (!placing) break;
2577 placing->dmPosition.x += min_offset.x;
2578 placing->dmPosition.y += min_offset.y;
2579 placing->dmFields |= DM_POSITION;
2583 static BOOL all_detached_settings( const DEVMODEW *displays )
2585 const DEVMODEW *mode;
2587 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2588 if (!is_detached_mode( mode )) return FALSE;
2590 return TRUE;
2593 static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmode,
2594 HWND hwnd, DWORD flags, void *lparam )
2596 WCHAR primary_name[CCHDEVICENAME];
2597 struct display_device *primary;
2598 DEVMODEW *mode, *displays;
2599 struct adapter *adapter;
2600 LONG ret;
2602 if (!lock_display_devices()) return DISP_CHANGE_FAILED;
2603 if (!(displays = get_display_settings( devname, devmode )))
2605 unlock_display_devices();
2606 return DISP_CHANGE_FAILED;
2609 if (all_detached_settings( displays ))
2611 unlock_display_devices();
2612 WARN( "Detaching all modes is not permitted.\n" );
2613 free( displays );
2614 return DISP_CHANGE_SUCCESSFUL;
2617 place_all_displays( displays );
2619 if (!(primary = find_adapter_device_by_id( 0 ))) primary_name[0] = 0;
2620 else wcscpy( primary_name, primary->device_name );
2622 if ((ret = user_driver->pChangeDisplaySettings( displays, primary_name, hwnd, flags, lparam )) == E_NOTIMPL)
2624 /* default implementation: write current display settings to the registry. */
2625 mode = displays;
2626 LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
2628 if (!adapter_set_current_settings( adapter, mode ))
2629 WARN( "Failed to write adapter %s current mode.\n", debugstr_w(adapter->dev.device_name) );
2630 mode = NEXT_DEVMODEW(mode);
2632 ret = DISP_CHANGE_SUCCESSFUL;
2634 unlock_display_devices();
2636 free( displays );
2637 if (ret) return ret;
2639 if (!update_display_cache( TRUE ))
2640 WARN( "Failed to update display cache after mode change.\n" );
2642 if ((adapter = find_adapter( NULL )))
2644 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2646 if (!adapter_get_current_settings( adapter, &current_mode )) WARN( "Failed to get primary adapter current display settings.\n" );
2647 adapter_release( adapter );
2649 send_notify_message( NtUserGetDesktopWindow(), WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
2650 MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), FALSE );
2651 send_message_timeout( HWND_BROADCAST, WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
2652 MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ),
2653 SMTO_ABORTIFHUNG, 2000, FALSE );
2656 return ret;
2659 /***********************************************************************
2660 * NtUserChangeDisplaySettings (win32u.@)
2662 LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devmode, HWND hwnd,
2663 DWORD flags, void *lparam )
2665 DEVMODEW full_mode = {.dmSize = sizeof(DEVMODEW)};
2666 int ret = DISP_CHANGE_SUCCESSFUL;
2667 struct adapter *adapter;
2669 TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, (int)flags, lparam );
2670 TRACE( "flags=%s\n", _CDS_flags(flags) );
2672 if ((!devname || !devname->Length) && !devmode) return apply_display_settings( NULL, NULL, hwnd, flags, lparam );
2674 if (!(adapter = find_adapter( devname ))) return DISP_CHANGE_BADPARAM;
2676 if (!adapter_get_full_mode( adapter, devmode, &full_mode )) ret = DISP_CHANGE_BADMODE;
2677 else if ((flags & CDS_UPDATEREGISTRY) && !adapter_set_registry_settings( adapter, &full_mode )) ret = DISP_CHANGE_NOTUPDATED;
2678 else if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL;
2679 else ret = apply_display_settings( adapter->dev.device_name, &full_mode, hwnd, flags, lparam );
2680 adapter_release( adapter );
2682 if (ret) ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret );
2683 return ret;
2686 static BOOL adapter_enum_display_settings( const struct adapter *adapter, UINT index, DEVMODEW *devmode, UINT flags )
2688 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2689 const DEVMODEW *adapter_mode;
2691 if (!(flags & EDS_ROTATEDMODE) && !adapter_get_current_settings( adapter, &current_mode ))
2693 WARN( "Failed to query current display mode for EDS_ROTATEDMODE flag.\n" );
2694 return FALSE;
2697 for (adapter_mode = adapter->modes; adapter_mode->dmSize; adapter_mode = NEXT_DEVMODEW(adapter_mode))
2699 if (!(flags & EDS_ROTATEDMODE) && (adapter_mode->dmFields & DM_DISPLAYORIENTATION) &&
2700 adapter_mode->dmDisplayOrientation != current_mode.dmDisplayOrientation)
2701 continue;
2702 if (!(flags & EDS_RAWMODE) && (adapter_mode->dmFields & DM_DISPLAYFLAGS) &&
2703 (adapter_mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
2704 continue;
2705 if (!index--)
2707 memcpy( &devmode->dmFields, &adapter_mode->dmFields, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields) );
2708 devmode->dmDisplayFlags &= ~WINE_DM_UNSUPPORTED;
2709 return TRUE;
2713 WARN( "device %s, index %#x, flags %#x display mode not found.\n",
2714 debugstr_w( adapter->dev.device_name ), index, flags );
2715 RtlSetLastWin32Error( ERROR_NO_MORE_FILES );
2716 return FALSE;
2719 /***********************************************************************
2720 * NtUserEnumDisplaySettings (win32u.@)
2722 BOOL WINAPI NtUserEnumDisplaySettings( UNICODE_STRING *device, DWORD index, DEVMODEW *devmode, DWORD flags )
2724 static const WCHAR wine_display_driverW[] = {'W','i','n','e',' ','D','i','s','p','l','a','y',' ','D','r','i','v','e','r',0};
2725 struct adapter *adapter;
2726 BOOL ret;
2728 TRACE( "device %s, index %#x, devmode %p, flags %#x\n",
2729 debugstr_us(device), (int)index, devmode, (int)flags );
2731 if (!(adapter = find_adapter( device ))) return FALSE;
2733 lstrcpynW( devmode->dmDeviceName, wine_display_driverW, ARRAY_SIZE(devmode->dmDeviceName) );
2734 devmode->dmSpecVersion = DM_SPECVERSION;
2735 devmode->dmDriverVersion = DM_SPECVERSION;
2736 devmode->dmSize = offsetof(DEVMODEW, dmICMMethod);
2737 devmode->dmDriverExtra = 0;
2739 if (index == ENUM_REGISTRY_SETTINGS) ret = adapter_get_registry_settings( adapter, devmode );
2740 else if (index == ENUM_CURRENT_SETTINGS) ret = adapter_get_current_settings( adapter, devmode );
2741 else ret = adapter_enum_display_settings( adapter, index, devmode, flags );
2742 adapter_release( adapter );
2744 if (!ret) WARN( "Failed to query %s display settings.\n", debugstr_us(device) );
2745 else TRACE( "position %dx%d, resolution %ux%u, frequency %u, depth %u, orientation %#x.\n",
2746 (int)devmode->dmPosition.x, (int)devmode->dmPosition.y, (int)devmode->dmPelsWidth,
2747 (int)devmode->dmPelsHeight, (int)devmode->dmDisplayFrequency,
2748 (int)devmode->dmBitsPerPel, (int)devmode->dmDisplayOrientation );
2749 return ret;
2752 struct monitor_enum_info
2754 HANDLE handle;
2755 RECT rect;
2758 static unsigned int active_monitor_count(void)
2760 struct monitor *monitor;
2761 unsigned int count = 0;
2763 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2765 if ((monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) count++;
2767 return count;
2770 INT get_display_depth( UNICODE_STRING *name )
2772 struct display_device *device;
2773 BOOL is_primary;
2774 INT depth;
2776 if (!lock_display_devices())
2777 return 32;
2779 if (name && name->Length)
2780 device = find_adapter_device_by_name( name );
2781 else
2782 device = find_adapter_device_by_id( 0 ); /* use primary adapter */
2784 if (!device)
2786 unlock_display_devices();
2787 return 32;
2790 is_primary = !!(device->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
2791 if ((depth = user_driver->pGetDisplayDepth( device->device_name, is_primary )) < 0)
2793 struct adapter *adapter = CONTAINING_RECORD( device, struct adapter, dev );
2794 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2796 if (!adapter_get_current_settings( adapter, &current_mode )) depth = 32;
2797 else depth = current_mode.dmBitsPerPel;
2800 unlock_display_devices();
2801 return depth;
2804 /***********************************************************************
2805 * NtUserEnumDisplayMonitors (win32u.@)
2807 BOOL WINAPI NtUserEnumDisplayMonitors( HDC hdc, RECT *rect, MONITORENUMPROC proc, LPARAM lparam )
2809 struct monitor_enum_info enum_buf[8], *enum_info = enum_buf;
2810 struct enum_display_monitor_params params;
2811 struct monitor *monitor;
2812 unsigned int count = 0, i;
2813 POINT origin;
2814 RECT limit;
2815 BOOL ret = TRUE;
2817 if (hdc)
2819 DC *dc;
2820 if (!(dc = get_dc_ptr( hdc ))) return FALSE;
2821 origin.x = dc->attr->vis_rect.left;
2822 origin.y = dc->attr->vis_rect.top;
2823 release_dc_ptr( dc );
2824 if (NtGdiGetAppClipBox( hdc, &limit ) == ERROR) return FALSE;
2826 else
2828 origin.x = origin.y = 0;
2829 limit.left = limit.top = INT_MIN;
2830 limit.right = limit.bottom = INT_MAX;
2832 if (rect && !intersect_rect( &limit, &limit, rect )) return TRUE;
2834 if (!lock_display_devices()) return FALSE;
2836 count = list_count( &monitors );
2837 if (!count || (count > ARRAYSIZE(enum_buf) &&
2838 !(enum_info = malloc( count * sizeof(*enum_info) ))))
2840 unlock_display_devices();
2841 return FALSE;
2844 count = 0;
2845 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2847 RECT monrect;
2849 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) continue;
2851 monrect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
2852 get_thread_dpi() );
2853 OffsetRect( &monrect, -origin.x, -origin.y );
2854 if (!intersect_rect( &monrect, &monrect, &limit )) continue;
2855 if (monitor->is_clone) continue;
2857 enum_info[count].handle = monitor->handle;
2858 enum_info[count].rect = monrect;
2859 count++;
2862 unlock_display_devices();
2864 params.proc = proc;
2865 params.hdc = hdc;
2866 params.lparam = lparam;
2867 for (i = 0; i < count; i++)
2869 void *ret_ptr;
2870 ULONG ret_len;
2871 params.monitor = enum_info[i].handle;
2872 params.rect = enum_info[i].rect;
2873 if (!KeUserModeCallback( NtUserCallEnumDisplayMonitor, &params, sizeof(params),
2874 &ret_ptr, &ret_len ))
2876 ret = FALSE;
2877 break;
2880 if (enum_info != enum_buf) free( enum_info );
2881 return ret;
2884 BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info )
2886 struct monitor *monitor;
2887 UINT dpi_from, dpi_to;
2889 if (info->cbSize != sizeof(MONITORINFOEXW) && info->cbSize != sizeof(MONITORINFO)) return FALSE;
2891 if (!lock_display_devices()) return FALSE;
2893 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2895 if (monitor->handle != handle) continue;
2896 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) break;
2898 /* FIXME: map dpi */
2899 info->rcMonitor = monitor->rc_monitor;
2900 info->rcWork = monitor->rc_work;
2901 info->dwFlags = monitor->flags;
2902 if (info->cbSize >= sizeof(MONITORINFOEXW))
2904 if (monitor->adapter)
2905 lstrcpyW( ((MONITORINFOEXW *)info)->szDevice, monitor->adapter->dev.device_name );
2906 else
2907 asciiz_to_unicode( ((MONITORINFOEXW *)info)->szDevice, "WinDisc" );
2909 unlock_display_devices();
2911 if ((dpi_to = get_thread_dpi()))
2913 dpi_from = get_monitor_dpi( handle );
2914 info->rcMonitor = map_dpi_rect( info->rcMonitor, dpi_from, dpi_to );
2915 info->rcWork = map_dpi_rect( info->rcWork, dpi_from, dpi_to );
2917 TRACE( "flags %04x, monitor %s, work %s\n", (int)info->dwFlags,
2918 wine_dbgstr_rect(&info->rcMonitor), wine_dbgstr_rect(&info->rcWork));
2919 return TRUE;
2922 unlock_display_devices();
2923 WARN( "invalid handle %p\n", handle );
2924 RtlSetLastWin32Error( ERROR_INVALID_MONITOR_HANDLE );
2925 return FALSE;
2928 HMONITOR monitor_from_rect( const RECT *rect, UINT flags, UINT dpi )
2930 HMONITOR primary = 0, nearest = 0, ret = 0;
2931 UINT max_area = 0, min_distance = ~0u;
2932 struct monitor *monitor;
2933 RECT r;
2935 r = map_dpi_rect( *rect, dpi, system_dpi );
2936 if (IsRectEmpty( &r ))
2938 r.right = r.left + 1;
2939 r.bottom = r.top + 1;
2942 if (!lock_display_devices()) return 0;
2944 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2946 RECT intersect;
2947 RECT monitor_rect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
2948 system_dpi );
2950 if (intersect_rect( &intersect, &monitor_rect, &r ))
2952 /* check for larger intersecting area */
2953 UINT area = (intersect.right - intersect.left) * (intersect.bottom - intersect.top);
2954 if (area > max_area)
2956 max_area = area;
2957 ret = monitor->handle;
2960 else if (!max_area) /* if not intersecting, check for min distance */
2962 UINT distance;
2963 UINT x, y;
2965 if (r.right <= monitor_rect.left) x = monitor_rect.left - r.right;
2966 else if (monitor_rect.right <= r.left) x = r.left - monitor_rect.right;
2967 else x = 0;
2968 if (r.bottom <= monitor_rect.top) y = monitor_rect.top - r.bottom;
2969 else if (monitor_rect.bottom <= r.top) y = r.top - monitor_rect.bottom;
2970 else y = 0;
2971 distance = x * x + y * y;
2972 if (distance < min_distance)
2974 min_distance = distance;
2975 nearest = monitor->handle;
2979 if (monitor->flags & MONITORINFOF_PRIMARY) primary = monitor->handle;
2982 unlock_display_devices();
2984 if (!ret)
2986 if (flags & MONITOR_DEFAULTTOPRIMARY) ret = primary;
2987 else if (flags & MONITOR_DEFAULTTONEAREST) ret = nearest;
2990 TRACE( "%s flags %x returning %p\n", wine_dbgstr_rect(rect), flags, ret );
2991 return ret;
2994 HMONITOR monitor_from_point( POINT pt, UINT flags, UINT dpi )
2996 RECT rect;
2997 SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 );
2998 return monitor_from_rect( &rect, flags, dpi );
3001 /* see MonitorFromWindow */
3002 HMONITOR monitor_from_window( HWND hwnd, UINT flags, UINT dpi )
3004 RECT rect;
3005 WINDOWPLACEMENT wp;
3007 TRACE( "(%p, 0x%08x)\n", hwnd, flags );
3009 wp.length = sizeof(wp);
3010 if (is_iconic( hwnd ) && NtUserGetWindowPlacement( hwnd, &wp ))
3011 return monitor_from_rect( &wp.rcNormalPosition, flags, dpi );
3013 if (get_window_rect( hwnd, &rect, dpi ))
3014 return monitor_from_rect( &rect, flags, dpi );
3016 if (!(flags & (MONITOR_DEFAULTTOPRIMARY|MONITOR_DEFAULTTONEAREST))) return 0;
3017 /* retrieve the primary */
3018 SetRect( &rect, 0, 0, 1, 1 );
3019 return monitor_from_rect( &rect, flags, dpi );
3022 /***********************************************************************
3023 * NtUserGetSystemDpiForProcess (win32u.@)
3025 ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process )
3027 if (process && process != GetCurrentProcess())
3029 FIXME( "not supported on other process %p\n", process );
3030 return 0;
3033 return system_dpi;
3036 /***********************************************************************
3037 * NtUserGetDpiForMonitor (win32u.@)
3039 BOOL WINAPI NtUserGetDpiForMonitor( HMONITOR monitor, UINT type, UINT *x, UINT *y )
3041 if (type > 2)
3043 RtlSetLastWin32Error( ERROR_BAD_ARGUMENTS );
3044 return FALSE;
3046 if (!x || !y)
3048 RtlSetLastWin32Error( ERROR_INVALID_ADDRESS );
3049 return FALSE;
3051 switch (get_thread_dpi_awareness())
3053 case DPI_AWARENESS_UNAWARE: *x = *y = USER_DEFAULT_SCREEN_DPI; break;
3054 case DPI_AWARENESS_SYSTEM_AWARE: *x = *y = system_dpi; break;
3055 default: *x = *y = get_monitor_dpi( monitor ); break;
3057 return TRUE;
3060 /**********************************************************************
3061 * LogicalToPhysicalPointForPerMonitorDPI (win32u.@)
3063 BOOL WINAPI NtUserLogicalToPerMonitorDPIPhysicalPoint( HWND hwnd, POINT *pt )
3065 RECT rect;
3067 if (!get_window_rect( hwnd, &rect, get_thread_dpi() )) return FALSE;
3068 if (pt->x < rect.left || pt->y < rect.top || pt->x > rect.right || pt->y > rect.bottom) return FALSE;
3069 *pt = point_win_to_phys_dpi( hwnd, *pt );
3070 return TRUE;
3073 /**********************************************************************
3074 * NtUserPerMonitorDPIPhysicalToLogicalPoint (win32u.@)
3076 BOOL WINAPI NtUserPerMonitorDPIPhysicalToLogicalPoint( HWND hwnd, POINT *pt )
3078 RECT rect;
3079 BOOL ret = FALSE;
3081 if (get_window_rect( hwnd, &rect, 0 ) &&
3082 pt->x >= rect.left && pt->y >= rect.top && pt->x <= rect.right && pt->y <= rect.bottom)
3084 *pt = point_phys_to_win_dpi( hwnd, *pt );
3085 ret = TRUE;
3087 return ret;
3090 /* retrieve the cached base keys for a given entry */
3091 static BOOL get_base_keys( enum parameter_key index, HKEY *base_key, HKEY *volatile_key )
3093 static HKEY base_keys[NB_PARAM_KEYS];
3094 static HKEY volatile_keys[NB_PARAM_KEYS];
3095 WCHAR bufferW[128];
3096 HKEY key;
3098 if (!base_keys[index] && base_key)
3100 if (!(key = reg_create_key( hkcu_key, bufferW,
3101 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
3102 0, NULL )))
3103 return FALSE;
3104 if (InterlockedCompareExchangePointer( (void **)&base_keys[index], key, 0 ))
3105 NtClose( key );
3107 if (!volatile_keys[index] && volatile_key)
3109 if (!(key = reg_create_key( volatile_base_key, bufferW,
3110 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
3111 REG_OPTION_VOLATILE, NULL )))
3112 return FALSE;
3113 if (InterlockedCompareExchangePointer( (void **)&volatile_keys[index], key, 0 ))
3114 NtClose( key );
3116 if (base_key) *base_key = base_keys[index];
3117 if (volatile_key) *volatile_key = volatile_keys[index];
3118 return TRUE;
3121 /* load a value to a registry entry */
3122 static DWORD load_entry( struct sysparam_entry *entry, void *data, DWORD size )
3124 char buffer[4096];
3125 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
3126 DWORD count;
3127 HKEY base_key, volatile_key;
3129 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
3131 if (!(count = query_reg_ascii_value( volatile_key, entry->regval, value, sizeof(buffer) )))
3132 count = query_reg_ascii_value( base_key, entry->regval, value, sizeof(buffer) );
3133 if (count > size)
3135 count = size;
3136 /* make sure strings are null-terminated */
3137 if (value->Type == REG_SZ) ((WCHAR *)value->Data)[count / sizeof(WCHAR) - 1] = 0;
3139 if (count) memcpy( data, value->Data, count );
3140 entry->loaded = TRUE;
3141 return count;
3144 /* save a value to a registry entry */
3145 static BOOL save_entry( const struct sysparam_entry *entry, const void *data, DWORD size,
3146 DWORD type, UINT flags )
3148 HKEY base_key, volatile_key;
3149 WCHAR nameW[64];
3151 asciiz_to_unicode( nameW, entry->regval );
3152 if (flags & SPIF_UPDATEINIFILE)
3154 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
3155 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
3156 reg_delete_value( volatile_key, nameW );
3158 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
3160 asciiz_to_unicode( nameW, entry->mirror );
3161 set_reg_value( base_key, nameW, type, data, size );
3164 else
3166 if (!get_base_keys( entry->base_key, NULL, &volatile_key )) return FALSE;
3167 if (!set_reg_value( volatile_key, nameW, type, data, size )) return FALSE;
3169 return TRUE;
3172 /* save a string value to a registry entry */
3173 static BOOL save_entry_string( const struct sysparam_entry *entry, const WCHAR *str, UINT flags )
3175 return save_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ, flags );
3178 /* initialize an entry in the registry if missing */
3179 static BOOL init_entry( struct sysparam_entry *entry, const void *data, DWORD size, DWORD type )
3181 KEY_VALUE_PARTIAL_INFORMATION value;
3182 UNICODE_STRING name;
3183 WCHAR nameW[64];
3184 HKEY base_key;
3185 DWORD count;
3186 NTSTATUS status;
3188 if (!get_base_keys( entry->base_key, &base_key, NULL )) return FALSE;
3190 name.Buffer = nameW;
3191 name.MaximumLength = asciiz_to_unicode( nameW, entry->regval );
3192 name.Length = name.MaximumLength - sizeof(WCHAR);
3193 status = NtQueryValueKey( base_key, &name, KeyValuePartialInformation,
3194 &value, sizeof(value), &count );
3195 if (!status || status == STATUS_BUFFER_OVERFLOW) return TRUE;
3197 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
3198 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
3200 asciiz_to_unicode( nameW, entry->mirror );
3201 set_reg_value( base_key, nameW, type, data, size );
3203 entry->loaded = TRUE;
3204 return TRUE;
3207 /* initialize a string value in the registry if missing */
3208 static BOOL init_entry_string( struct sysparam_entry *entry, const WCHAR *str )
3210 return init_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ );
3213 /* set an int parameter in the registry */
3214 static BOOL set_int_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3216 WCHAR bufW[32];
3217 char buf[32];
3219 sprintf( buf, "%d", int_param );
3220 asciiz_to_unicode( bufW, buf );
3221 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3222 entry->uint.val = int_param;
3223 entry->hdr.loaded = TRUE;
3224 return TRUE;
3227 /* initialize an int parameter */
3228 static BOOL init_int_entry( union sysparam_all_entry *entry )
3230 WCHAR bufW[32];
3231 char buf[32];
3233 sprintf( buf, "%d", entry->uint.val );
3234 asciiz_to_unicode( bufW, buf );
3235 return init_entry_string( &entry->hdr, bufW );
3238 /* load a uint parameter from the registry */
3239 static BOOL get_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3241 if (!ptr_param) return FALSE;
3243 if (!entry->hdr.loaded)
3245 WCHAR buf[32];
3246 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
3248 *(UINT *)ptr_param = entry->uint.val;
3249 return TRUE;
3252 /* set a uint parameter in the registry */
3253 static BOOL set_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3255 WCHAR bufW[32];
3256 char buf[32];
3258 sprintf( buf, "%u", int_param );
3259 asciiz_to_unicode( bufW, buf );
3260 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3261 entry->uint.val = int_param;
3262 entry->hdr.loaded = TRUE;
3263 return TRUE;
3266 /* initialize a uint parameter */
3267 static BOOL init_uint_entry( union sysparam_all_entry *entry )
3269 WCHAR bufW[32];
3270 char buf[32];
3272 sprintf( buf, "%u", entry->uint.val );
3273 asciiz_to_unicode( bufW, buf );
3274 return init_entry_string( &entry->hdr, bufW );
3277 /* load a twips parameter from the registry */
3278 static BOOL get_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3280 int val;
3282 if (!ptr_param) return FALSE;
3284 if (!entry->hdr.loaded)
3286 WCHAR buf[32];
3287 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
3290 /* Dimensions are quoted as being "twips" values if negative and pixels if positive.
3291 * One inch is 1440 twips.
3292 * See for example
3293 * Technical Reference to the Windows 2000 Registry ->
3294 * HKEY_CURRENT_USER -> Control Panel -> Desktop -> WindowMetrics
3296 val = entry->uint.val;
3297 if (val < 0)
3298 val = muldiv( -val, dpi, 1440 );
3299 else
3300 val = map_to_dpi( val, dpi );
3302 *(int *)ptr_param = val;
3303 return TRUE;
3306 /* set a twips parameter in the registry */
3307 static BOOL set_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3309 int val = int_param;
3310 if (val > 0) val = map_from_system_dpi( val );
3311 return set_int_entry( entry, val, ptr_param, flags );
3314 /* load a bool parameter from the registry */
3315 static BOOL get_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3317 if (!ptr_param) return FALSE;
3319 if (!entry->hdr.loaded)
3321 WCHAR buf[32];
3322 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = wcstol( buf, NULL, 10 ) != 0;
3324 *(UINT *)ptr_param = entry->bool.val;
3325 return TRUE;
3328 /* set a bool parameter in the registry */
3329 static BOOL set_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3331 WCHAR buf[] = { int_param ? '1' : '0', 0 };
3333 if (!save_entry_string( &entry->hdr, buf, flags )) return FALSE;
3334 entry->bool.val = int_param != 0;
3335 entry->hdr.loaded = TRUE;
3336 return TRUE;
3339 /* initialize a bool parameter */
3340 static BOOL init_bool_entry( union sysparam_all_entry *entry )
3342 WCHAR buf[] = { entry->bool.val ? '1' : '0', 0 };
3344 return init_entry_string( &entry->hdr, buf );
3347 /* load a bool parameter using Yes/No strings from the registry */
3348 static BOOL get_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3350 if (!ptr_param) return FALSE;
3352 if (!entry->hdr.loaded)
3354 WCHAR buf[32];
3355 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = !wcsicmp( yesW, buf );
3357 *(UINT *)ptr_param = entry->bool.val;
3358 return TRUE;
3361 /* set a bool parameter using Yes/No strings from the registry */
3362 static BOOL set_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3364 const WCHAR *str = int_param ? yesW : noW;
3366 if (!save_entry_string( &entry->hdr, str, flags )) return FALSE;
3367 entry->bool.val = int_param != 0;
3368 entry->hdr.loaded = TRUE;
3369 return TRUE;
3372 /* initialize a bool parameter using Yes/No strings */
3373 static BOOL init_yesno_entry( union sysparam_all_entry *entry )
3375 return init_entry_string( &entry->hdr, entry->bool.val ? yesW : noW );
3378 /* load a dword (binary) parameter from the registry */
3379 static BOOL get_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3381 if (!ptr_param) return FALSE;
3383 if (!entry->hdr.loaded)
3385 DWORD val;
3386 if (load_entry( &entry->hdr, &val, sizeof(val) ) == sizeof(DWORD)) entry->dword.val = val;
3388 *(DWORD *)ptr_param = entry->dword.val;
3389 return TRUE;
3392 /* set a dword (binary) parameter in the registry */
3393 static BOOL set_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3395 DWORD val = PtrToUlong( ptr_param );
3397 if (!save_entry( &entry->hdr, &val, sizeof(val), REG_DWORD, flags )) return FALSE;
3398 entry->dword.val = val;
3399 entry->hdr.loaded = TRUE;
3400 return TRUE;
3403 /* initialize a dword parameter */
3404 static BOOL init_dword_entry( union sysparam_all_entry *entry )
3406 return init_entry( &entry->hdr, &entry->dword.val, sizeof(entry->dword.val), REG_DWORD );
3409 /* load an RGB parameter from the registry */
3410 static BOOL get_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3412 if (!ptr_param) return FALSE;
3414 if (!entry->hdr.loaded)
3416 WCHAR buf[32];
3418 if (load_entry( &entry->hdr, buf, sizeof(buf) ))
3420 DWORD r, g, b;
3421 WCHAR *end, *str = buf;
3423 r = wcstoul( str, &end, 10 );
3424 if (end == str || !*end) goto done;
3425 str = end + 1;
3426 g = wcstoul( str, &end, 10 );
3427 if (end == str || !*end) goto done;
3428 str = end + 1;
3429 b = wcstoul( str, &end, 10 );
3430 if (end == str) goto done;
3431 if (r > 255 || g > 255 || b > 255) goto done;
3432 entry->rgb.val = RGB( r, g, b );
3435 done:
3436 *(COLORREF *)ptr_param = entry->rgb.val;
3437 return TRUE;
3440 /* set an RGB parameter in the registry */
3441 static BOOL set_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3443 WCHAR bufW[32];
3444 char buf[32];
3445 HBRUSH brush;
3446 HPEN pen;
3448 sprintf( buf, "%u %u %u", GetRValue(int_param), GetGValue(int_param), GetBValue(int_param) );
3449 asciiz_to_unicode( bufW, buf );
3450 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3451 entry->rgb.val = int_param;
3452 entry->hdr.loaded = TRUE;
3453 if ((brush = InterlockedExchangePointer( (void **)&entry->rgb.brush, 0 )))
3455 make_gdi_object_system( brush, FALSE );
3456 NtGdiDeleteObjectApp( brush );
3458 if ((pen = InterlockedExchangePointer( (void **)&entry->rgb.pen, 0 )))
3460 make_gdi_object_system( pen, FALSE );
3461 NtGdiDeleteObjectApp( pen );
3463 return TRUE;
3466 /* initialize an RGB parameter */
3467 static BOOL init_rgb_entry( union sysparam_all_entry *entry )
3469 WCHAR bufW[32];
3470 char buf[32];
3472 sprintf( buf, "%u %u %u", GetRValue(entry->rgb.val), GetGValue(entry->rgb.val),
3473 GetBValue(entry->rgb.val) );
3474 asciiz_to_unicode( bufW, buf );
3475 return init_entry_string( &entry->hdr, bufW );
3478 /* get a path parameter in the registry */
3479 static BOOL get_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3481 if (!ptr_param) return FALSE;
3483 if (!entry->hdr.loaded)
3485 WCHAR buffer[MAX_PATH];
3487 if (load_entry( &entry->hdr, buffer, sizeof(buffer) ))
3488 lstrcpynW( entry->path.path, buffer, MAX_PATH );
3490 lstrcpynW( ptr_param, entry->path.path, int_param );
3491 return TRUE;
3494 /* set a path parameter in the registry */
3495 static BOOL set_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3497 WCHAR buffer[MAX_PATH];
3498 BOOL ret;
3500 lstrcpynW( buffer, ptr_param, MAX_PATH );
3501 ret = save_entry_string( &entry->hdr, buffer, flags );
3502 if (ret)
3504 lstrcpyW( entry->path.path, buffer );
3505 entry->hdr.loaded = TRUE;
3507 return ret;
3510 /* initialize a path parameter */
3511 static BOOL init_path_entry( union sysparam_all_entry *entry )
3513 return init_entry_string( &entry->hdr, entry->path.path );
3516 /* get a binary parameter in the registry */
3517 static BOOL get_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3519 if (!ptr_param) return FALSE;
3521 if (!entry->hdr.loaded)
3523 void *buffer = malloc( entry->bin.size );
3524 DWORD len = load_entry( &entry->hdr, buffer, entry->bin.size );
3526 if (len)
3528 memcpy( entry->bin.ptr, buffer, entry->bin.size );
3529 memset( (char *)entry->bin.ptr + len, 0, entry->bin.size - len );
3531 free( buffer );
3533 memcpy( ptr_param, entry->bin.ptr, min( int_param, entry->bin.size ) );
3534 return TRUE;
3537 /* set a binary parameter in the registry */
3538 static BOOL set_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3540 BOOL ret;
3541 void *buffer = malloc( entry->bin.size );
3543 memcpy( buffer, entry->bin.ptr, entry->bin.size );
3544 memcpy( buffer, ptr_param, min( int_param, entry->bin.size ));
3545 ret = save_entry( &entry->hdr, buffer, entry->bin.size, REG_BINARY, flags );
3546 if (ret)
3548 memcpy( entry->bin.ptr, buffer, entry->bin.size );
3549 entry->hdr.loaded = TRUE;
3551 free( buffer );
3552 return ret;
3555 /* initialize a binary parameter */
3556 static BOOL init_binary_entry( union sysparam_all_entry *entry )
3558 return init_entry( &entry->hdr, entry->bin.ptr, entry->bin.size, REG_BINARY );
3561 static void logfont16to32( const LOGFONT16 *font16, LPLOGFONTW font32 )
3563 font32->lfHeight = font16->lfHeight;
3564 font32->lfWidth = font16->lfWidth;
3565 font32->lfEscapement = font16->lfEscapement;
3566 font32->lfOrientation = font16->lfOrientation;
3567 font32->lfWeight = font16->lfWeight;
3568 font32->lfItalic = font16->lfItalic;
3569 font32->lfUnderline = font16->lfUnderline;
3570 font32->lfStrikeOut = font16->lfStrikeOut;
3571 font32->lfCharSet = font16->lfCharSet;
3572 font32->lfOutPrecision = font16->lfOutPrecision;
3573 font32->lfClipPrecision = font16->lfClipPrecision;
3574 font32->lfQuality = font16->lfQuality;
3575 font32->lfPitchAndFamily = font16->lfPitchAndFamily;
3576 win32u_mbtowc( &ansi_cp, font32->lfFaceName, LF_FACESIZE, font16->lfFaceName,
3577 strlen( font16->lfFaceName ));
3578 font32->lfFaceName[LF_FACESIZE-1] = 0;
3581 static void get_real_fontname( LOGFONTW *lf, WCHAR fullname[LF_FACESIZE] )
3583 struct font_enum_entry enum_entry;
3584 ULONG count = sizeof(enum_entry);
3585 HDC hdc;
3587 hdc = get_display_dc();
3588 NtGdiEnumFonts( hdc, 0, 0, lstrlenW( lf->lfFaceName ), lf->lfFaceName, lf->lfCharSet,
3589 &count, &enum_entry );
3590 release_display_dc( hdc );
3592 if (count)
3593 lstrcpyW( fullname, enum_entry.lf.elfFullName );
3594 else
3595 lstrcpyW( fullname, lf->lfFaceName );
3598 LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, int *height )
3600 SIZE sz;
3601 static const WCHAR abcdW[] =
3602 {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
3603 'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H',
3604 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
3606 if (metric && !NtGdiGetTextMetricsW( hdc, metric, 0 )) return 0;
3608 if (!NtGdiGetTextExtentExW( hdc, abcdW, ARRAYSIZE(abcdW), 0, NULL, NULL, &sz, 0 ))
3609 return 0;
3611 if (height) *height = sz.cy;
3612 return (sz.cx / 26 + 1) / 2;
3615 /* get text metrics and/or "average" char width of the specified logfont
3616 * for the specified dc */
3617 static void get_text_metr_size( HDC hdc, LOGFONTW *lf, TEXTMETRICW *metric, UINT *psz )
3619 HFONT hfont, hfontsav;
3620 TEXTMETRICW tm;
3621 UINT ret;
3622 if (!metric) metric = &tm;
3623 hfont = NtGdiHfontCreate( lf, sizeof(*lf), 0, 0, NULL );
3624 if (!hfont || !(hfontsav = NtGdiSelectFont( hdc, hfont )))
3626 metric->tmHeight = -1;
3627 if (psz) *psz = 10;
3628 if (hfont) NtGdiDeleteObjectApp( hfont );
3629 return;
3631 ret = get_char_dimensions( hdc, metric, NULL );
3632 if (psz) *psz = ret ? ret : 10;
3633 NtGdiSelectFont( hdc, hfontsav );
3634 NtGdiDeleteObjectApp( hfont );
3637 DWORD get_dialog_base_units(void)
3639 static int cx, cy;
3641 if (!cx)
3643 HDC hdc;
3645 if ((hdc = NtUserGetDC( 0 )))
3647 cx = get_char_dimensions( hdc, NULL, &cy );
3648 NtUserReleaseDC( 0, hdc );
3650 TRACE( "base units = %d,%d\n", cx, cy );
3653 return MAKELONG( muldiv( cx, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ),
3654 muldiv( cy, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ));
3657 /* adjust some of the raw values found in the registry */
3658 static void normalize_nonclientmetrics( NONCLIENTMETRICSW *pncm)
3660 TEXTMETRICW tm;
3661 HDC hdc = get_display_dc();
3663 if( pncm->iBorderWidth < 1) pncm->iBorderWidth = 1;
3664 if( pncm->iCaptionWidth < 8) pncm->iCaptionWidth = 8;
3665 if( pncm->iScrollWidth < 8) pncm->iScrollWidth = 8;
3666 if( pncm->iScrollHeight < 8) pncm->iScrollHeight = 8;
3668 /* adjust some heights to the corresponding font */
3669 get_text_metr_size( hdc, &pncm->lfMenuFont, &tm, NULL);
3670 pncm->iMenuHeight = max( pncm->iMenuHeight, 2 + tm.tmHeight + tm.tmExternalLeading );
3671 get_text_metr_size( hdc, &pncm->lfCaptionFont, &tm, NULL);
3672 pncm->iCaptionHeight = max( pncm->iCaptionHeight, 2 + tm.tmHeight);
3673 get_text_metr_size( hdc, &pncm->lfSmCaptionFont, &tm, NULL);
3674 pncm->iSmCaptionHeight = max( pncm->iSmCaptionHeight, 2 + tm.tmHeight);
3675 release_display_dc( hdc );
3678 /* load a font (binary) parameter from the registry */
3679 static BOOL get_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3681 LOGFONTW font;
3683 if (!ptr_param) return FALSE;
3685 if (!entry->hdr.loaded)
3687 switch (load_entry( &entry->hdr, &font, sizeof(font) ))
3689 case sizeof(font):
3690 if (font.lfHeight > 0) /* positive height value means points ( inch/72 ) */
3691 font.lfHeight = -muldiv( font.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
3692 entry->font.val = font;
3693 break;
3694 case sizeof(LOGFONT16): /* win9x-winME format */
3695 logfont16to32( (LOGFONT16 *)&font, &entry->font.val );
3696 if (entry->font.val.lfHeight > 0)
3697 entry->font.val.lfHeight = -muldiv( entry->font.val.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
3698 break;
3699 default:
3700 WARN( "Unknown format in key %s value %s\n",
3701 debugstr_a( parameter_key_names[entry->hdr.base_key] ),
3702 debugstr_a( entry->hdr.regval ));
3703 /* fall through */
3704 case 0: /* use the default GUI font */
3705 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(font), &font );
3706 font.lfHeight = map_from_system_dpi( font.lfHeight );
3707 font.lfWeight = entry->font.weight;
3708 entry->font.val = font;
3709 break;
3711 get_real_fontname( &entry->font.val, entry->font.fullname );
3712 entry->hdr.loaded = TRUE;
3714 font = entry->font.val;
3715 font.lfHeight = map_to_dpi( font.lfHeight, dpi );
3716 lstrcpyW( font.lfFaceName, entry->font.fullname );
3717 *(LOGFONTW *)ptr_param = font;
3718 return TRUE;
3721 /* set a font (binary) parameter in the registry */
3722 static BOOL set_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3724 LOGFONTW font;
3725 WCHAR *ptr;
3727 memcpy( &font, ptr_param, sizeof(font) );
3728 /* zero pad the end of lfFaceName so we don't save uninitialised data */
3729 for (ptr = font.lfFaceName; ptr < font.lfFaceName + LF_FACESIZE && *ptr; ptr++);
3730 if (ptr < font.lfFaceName + LF_FACESIZE)
3731 memset( ptr, 0, (font.lfFaceName + LF_FACESIZE - ptr) * sizeof(WCHAR) );
3732 if (font.lfHeight < 0) font.lfHeight = map_from_system_dpi( font.lfHeight );
3734 if (!save_entry( &entry->hdr, &font, sizeof(font), REG_BINARY, flags )) return FALSE;
3735 entry->font.val = font;
3736 get_real_fontname( &entry->font.val, entry->font.fullname );
3737 entry->hdr.loaded = TRUE;
3738 return TRUE;
3741 /* initialize a font (binary) parameter */
3742 static BOOL init_font_entry( union sysparam_all_entry *entry )
3744 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(entry->font.val), &entry->font.val );
3745 entry->font.val.lfHeight = map_from_system_dpi( entry->font.val.lfHeight );
3746 entry->font.val.lfWeight = entry->font.weight;
3747 get_real_fontname( &entry->font.val, entry->font.fullname );
3748 return init_entry( &entry->hdr, &entry->font.val, sizeof(entry->font.val), REG_BINARY );
3751 /* get a user pref parameter in the registry */
3752 static BOOL get_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3754 union sysparam_all_entry *parent_entry = entry->pref.parent;
3755 BYTE prefs[8];
3757 if (!ptr_param) return FALSE;
3759 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, dpi )) return FALSE;
3760 *(BOOL *)ptr_param = (prefs[entry->pref.offset] & entry->pref.mask) != 0;
3761 return TRUE;
3764 /* set a user pref parameter in the registry */
3765 static BOOL set_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3767 union sysparam_all_entry *parent_entry = entry->pref.parent;
3768 BYTE prefs[8];
3770 parent_entry->hdr.loaded = FALSE; /* force loading it again */
3771 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, get_system_dpi() )) return FALSE;
3773 if (PtrToUlong( ptr_param )) prefs[entry->pref.offset] |= entry->pref.mask;
3774 else prefs[entry->pref.offset] &= ~entry->pref.mask;
3776 return parent_entry->hdr.set( parent_entry, sizeof(prefs), prefs, flags );
3779 static BOOL get_entry_dpi( void *ptr, UINT int_param, void *ptr_param, UINT dpi )
3781 union sysparam_all_entry *entry = ptr;
3782 return entry->hdr.get( entry, int_param, ptr_param, dpi );
3785 static BOOL get_entry( void *ptr, UINT int_param, void *ptr_param )
3787 return get_entry_dpi( ptr, int_param, ptr_param, get_system_dpi() );
3790 static BOOL set_entry( void *ptr, UINT int_param, void *ptr_param, UINT flags )
3792 union sysparam_all_entry *entry = ptr;
3793 return entry->hdr.set( entry, int_param, ptr_param, flags );
3796 #define UINT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3797 { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg }, (val) } }
3799 #define UINT_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
3800 { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg, mirror_base, reg }, (val) } }
3802 #define INT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3803 { .uint = { { get_uint_entry, set_int_entry, init_int_entry, base, reg }, (val) } }
3805 #define BOOL_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3806 { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg }, (val) } }
3808 #define BOOL_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
3809 { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg, mirror_base, reg }, (val) } }
3811 #define TWIPS_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3812 { .uint = { { get_twips_entry, set_twips_entry, init_int_entry, base, reg }, (val) } }
3814 #define YESNO_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3815 { .bool = { { get_yesno_entry, set_yesno_entry, init_yesno_entry, base, reg }, (val) } }
3817 #define DWORD_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3818 { .dword = { { get_dword_entry, set_dword_entry, init_dword_entry, base, reg }, (val) } }
3820 #define BINARY_ENTRY(name,data,base,reg) union sysparam_all_entry entry_##name = \
3821 { .bin = { { get_binary_entry, set_binary_entry, init_binary_entry, base, reg }, data, sizeof(data) } }
3823 #define PATH_ENTRY(name,base,reg,buffer) union sysparam_all_entry entry_##name = \
3824 { .path = { { get_path_entry, set_path_entry, init_path_entry, base, reg }, buffer } }
3826 #define FONT_ENTRY(name,weight,base,reg) union sysparam_all_entry entry_##name = \
3827 { .font = { { get_font_entry, set_font_entry, init_font_entry, base, reg }, (weight) } }
3829 #define USERPREF_ENTRY(name,offset,mask) union sysparam_all_entry entry_##name = \
3830 { .pref = { { get_userpref_entry, set_userpref_entry }, &entry_USERPREFERENCESMASK, (offset), (mask) } }
3832 static UINT_ENTRY( DRAGWIDTH, 4, DESKTOP_KEY, "DragWidth" );
3833 static UINT_ENTRY( DRAGHEIGHT, 4, DESKTOP_KEY, "DragHeight" );
3834 static UINT_ENTRY( DOUBLECLICKTIME, 500, MOUSE_KEY, "DoubleClickSpeed" );
3835 static UINT_ENTRY( FONTSMOOTHING, 2, DESKTOP_KEY, "FontSmoothing" );
3836 static UINT_ENTRY( GRIDGRANULARITY, 0, DESKTOP_KEY, "GridGranularity" );
3837 static UINT_ENTRY( KEYBOARDDELAY, 1, KEYBOARD_KEY, "KeyboardDelay" );
3838 static UINT_ENTRY( KEYBOARDSPEED, 31, KEYBOARD_KEY, "KeyboardSpeed" );
3839 static UINT_ENTRY( MENUSHOWDELAY, 400, DESKTOP_KEY, "MenuShowDelay" );
3840 static UINT_ENTRY( MINARRANGE, ARW_HIDE, METRICS_KEY, "MinArrange" );
3841 static UINT_ENTRY( MINHORZGAP, 0, METRICS_KEY, "MinHorzGap" );
3842 static UINT_ENTRY( MINVERTGAP, 0, METRICS_KEY, "MinVertGap" );
3843 static UINT_ENTRY( MINWIDTH, 154, METRICS_KEY, "MinWidth" );
3844 static UINT_ENTRY( MOUSEHOVERHEIGHT, 4, MOUSE_KEY, "MouseHoverHeight" );
3845 static UINT_ENTRY( MOUSEHOVERTIME, 400, MOUSE_KEY, "MouseHoverTime" );
3846 static UINT_ENTRY( MOUSEHOVERWIDTH, 4, MOUSE_KEY, "MouseHoverWidth" );
3847 static UINT_ENTRY( MOUSESPEED, 10, MOUSE_KEY, "MouseSensitivity" );
3848 static UINT_ENTRY( MOUSETRAILS, 0, MOUSE_KEY, "MouseTrails" );
3849 static UINT_ENTRY( SCREENSAVETIMEOUT, 300, DESKTOP_KEY, "ScreenSaveTimeOut" );
3850 static UINT_ENTRY( WHEELSCROLLCHARS, 3, DESKTOP_KEY, "WheelScrollChars" );
3851 static UINT_ENTRY( WHEELSCROLLLINES, 3, DESKTOP_KEY, "WheelScrollLines" );
3852 static UINT_ENTRY_MIRROR( DOUBLECLKHEIGHT, 4, MOUSE_KEY, "DoubleClickHeight", DESKTOP_KEY );
3853 static UINT_ENTRY_MIRROR( DOUBLECLKWIDTH, 4, MOUSE_KEY, "DoubleClickWidth", DESKTOP_KEY );
3854 static UINT_ENTRY_MIRROR( MENUDROPALIGNMENT, 0, DESKTOP_KEY, "MenuDropAlignment", VERSION_KEY );
3856 static INT_ENTRY( MOUSETHRESHOLD1, 6, MOUSE_KEY, "MouseThreshold1" );
3857 static INT_ENTRY( MOUSETHRESHOLD2, 10, MOUSE_KEY, "MouseThreshold2" );
3858 static INT_ENTRY( MOUSEACCELERATION, 1, MOUSE_KEY, "MouseSpeed" );
3860 static BOOL_ENTRY( BLOCKSENDINPUTRESETS, FALSE, DESKTOP_KEY, "BlockSendInputResets" );
3861 static BOOL_ENTRY( DRAGFULLWINDOWS, FALSE, DESKTOP_KEY, "DragFullWindows" );
3862 static BOOL_ENTRY( KEYBOARDPREF, TRUE, KEYBOARDPREF_KEY, "On" );
3863 static BOOL_ENTRY( LOWPOWERACTIVE, FALSE, DESKTOP_KEY, "LowPowerActive" );
3864 static BOOL_ENTRY( MOUSEBUTTONSWAP, FALSE, MOUSE_KEY, "SwapMouseButtons" );
3865 static BOOL_ENTRY( POWEROFFACTIVE, FALSE, DESKTOP_KEY, "PowerOffActive" );
3866 static BOOL_ENTRY( SCREENREADER, FALSE, SCREENREADER_KEY, "On" );
3867 static BOOL_ENTRY( SCREENSAVEACTIVE, TRUE, DESKTOP_KEY, "ScreenSaveActive" );
3868 static BOOL_ENTRY( SCREENSAVERRUNNING, FALSE, DESKTOP_KEY, "WINE_ScreenSaverRunning" ); /* FIXME - real value */
3869 static BOOL_ENTRY( SHOWSOUNDS, FALSE, SHOWSOUNDS_KEY, "On" );
3870 static BOOL_ENTRY( SNAPTODEFBUTTON, FALSE, MOUSE_KEY, "SnapToDefaultButton" );
3871 static BOOL_ENTRY_MIRROR( ICONTITLEWRAP, TRUE, DESKTOP_KEY, "IconTitleWrap", METRICS_KEY );
3872 static BOOL_ENTRY( AUDIODESC_ON, FALSE, AUDIODESC_KEY, "On" );
3874 static TWIPS_ENTRY( BORDER, -15, METRICS_KEY, "BorderWidth" );
3875 static TWIPS_ENTRY( CAPTIONHEIGHT, -270, METRICS_KEY, "CaptionHeight" );
3876 static TWIPS_ENTRY( CAPTIONWIDTH, -270, METRICS_KEY, "CaptionWidth" );
3877 static TWIPS_ENTRY( ICONHORIZONTALSPACING, -1125, METRICS_KEY, "IconSpacing" );
3878 static TWIPS_ENTRY( ICONVERTICALSPACING, -1125, METRICS_KEY, "IconVerticalSpacing" );
3879 static TWIPS_ENTRY( MENUHEIGHT, -270, METRICS_KEY, "MenuHeight" );
3880 static TWIPS_ENTRY( MENUWIDTH, -270, METRICS_KEY, "MenuWidth" );
3881 static TWIPS_ENTRY( PADDEDBORDERWIDTH, 0, METRICS_KEY, "PaddedBorderWidth" );
3882 static TWIPS_ENTRY( SCROLLHEIGHT, -240, METRICS_KEY, "ScrollHeight" );
3883 static TWIPS_ENTRY( SCROLLWIDTH, -240, METRICS_KEY, "ScrollWidth" );
3884 static TWIPS_ENTRY( SMCAPTIONHEIGHT, -225, METRICS_KEY, "SmCaptionHeight" );
3885 static TWIPS_ENTRY( SMCAPTIONWIDTH, -225, METRICS_KEY, "SmCaptionWidth" );
3887 static YESNO_ENTRY( BEEP, TRUE, SOUND_KEY, "Beep" );
3889 static DWORD_ENTRY( ACTIVEWINDOWTRACKING, 0, MOUSE_KEY, "ActiveWindowTracking" );
3890 static DWORD_ENTRY( ACTIVEWNDTRKTIMEOUT, 0, DESKTOP_KEY, "ActiveWndTrackTimeout" );
3891 static DWORD_ENTRY( CARETWIDTH, 1, DESKTOP_KEY, "CaretWidth" );
3892 static DWORD_ENTRY( DPISCALINGVER, 0, DESKTOP_KEY, "DpiScalingVer" );
3893 static DWORD_ENTRY( FOCUSBORDERHEIGHT, 1, DESKTOP_KEY, "FocusBorderHeight" );
3894 static DWORD_ENTRY( FOCUSBORDERWIDTH, 1, DESKTOP_KEY, "FocusBorderWidth" );
3895 static DWORD_ENTRY( FONTSMOOTHINGCONTRAST, 0, DESKTOP_KEY, "FontSmoothingGamma" );
3896 static DWORD_ENTRY( FONTSMOOTHINGORIENTATION, FE_FONTSMOOTHINGORIENTATIONRGB, DESKTOP_KEY, "FontSmoothingOrientation" );
3897 static DWORD_ENTRY( FONTSMOOTHINGTYPE, FE_FONTSMOOTHINGSTANDARD, DESKTOP_KEY, "FontSmoothingType" );
3898 static DWORD_ENTRY( FOREGROUNDFLASHCOUNT, 3, DESKTOP_KEY, "ForegroundFlashCount" );
3899 static DWORD_ENTRY( FOREGROUNDLOCKTIMEOUT, 0, DESKTOP_KEY, "ForegroundLockTimeout" );
3900 static DWORD_ENTRY( LOGPIXELS, 0, DESKTOP_KEY, "LogPixels" );
3901 static DWORD_ENTRY( MOUSECLICKLOCKTIME, 1200, DESKTOP_KEY, "ClickLockTime" );
3902 static DWORD_ENTRY( AUDIODESC_LOCALE, 0, AUDIODESC_KEY, "Locale" );
3904 static WCHAR desk_pattern_path[MAX_PATH];
3905 static WCHAR desk_wallpaper_path[MAX_PATH];
3906 static PATH_ENTRY( DESKPATTERN, DESKTOP_KEY, "Pattern", desk_pattern_path );
3907 static PATH_ENTRY( DESKWALLPAPER, DESKTOP_KEY, "Wallpaper", desk_wallpaper_path );
3909 static BYTE user_prefs[8] = { 0x30, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, 0x00 };
3910 static BINARY_ENTRY( USERPREFERENCESMASK, user_prefs, DESKTOP_KEY, "UserPreferencesMask" );
3912 static FONT_ENTRY( CAPTIONLOGFONT, FW_BOLD, METRICS_KEY, "CaptionFont" );
3913 static FONT_ENTRY( ICONTITLELOGFONT, FW_NORMAL, METRICS_KEY, "IconFont" );
3914 static FONT_ENTRY( MENULOGFONT, FW_NORMAL, METRICS_KEY, "MenuFont" );
3915 static FONT_ENTRY( MESSAGELOGFONT, FW_NORMAL, METRICS_KEY, "MessageFont" );
3916 static FONT_ENTRY( SMCAPTIONLOGFONT, FW_NORMAL, METRICS_KEY, "SmCaptionFont" );
3917 static FONT_ENTRY( STATUSLOGFONT, FW_NORMAL, METRICS_KEY, "StatusFont" );
3919 static USERPREF_ENTRY( MENUANIMATION, 0, 0x02 );
3920 static USERPREF_ENTRY( COMBOBOXANIMATION, 0, 0x04 );
3921 static USERPREF_ENTRY( LISTBOXSMOOTHSCROLLING, 0, 0x08 );
3922 static USERPREF_ENTRY( GRADIENTCAPTIONS, 0, 0x10 );
3923 static USERPREF_ENTRY( KEYBOARDCUES, 0, 0x20 );
3924 static USERPREF_ENTRY( ACTIVEWNDTRKZORDER, 0, 0x40 );
3925 static USERPREF_ENTRY( HOTTRACKING, 0, 0x80 );
3926 static USERPREF_ENTRY( MENUFADE, 1, 0x02 );
3927 static USERPREF_ENTRY( SELECTIONFADE, 1, 0x04 );
3928 static USERPREF_ENTRY( TOOLTIPANIMATION, 1, 0x08 );
3929 static USERPREF_ENTRY( TOOLTIPFADE, 1, 0x10 );
3930 static USERPREF_ENTRY( CURSORSHADOW, 1, 0x20 );
3931 static USERPREF_ENTRY( MOUSESONAR, 1, 0x40 );
3932 static USERPREF_ENTRY( MOUSECLICKLOCK, 1, 0x80 );
3933 static USERPREF_ENTRY( MOUSEVANISH, 2, 0x01 );
3934 static USERPREF_ENTRY( FLATMENU, 2, 0x02 );
3935 static USERPREF_ENTRY( DROPSHADOW, 2, 0x04 );
3936 static USERPREF_ENTRY( UIEFFECTS, 3, 0x80 );
3937 static USERPREF_ENTRY( DISABLEOVERLAPPEDCONTENT, 4, 0x01 );
3938 static USERPREF_ENTRY( CLIENTAREAANIMATION, 4, 0x02 );
3939 static USERPREF_ENTRY( CLEARTYPE, 4, 0x10 );
3940 static USERPREF_ENTRY( SPEECHRECOGNITION, 4, 0x20 );
3942 /* System parameter indexes */
3943 enum spi_index
3945 SPI_SETWORKAREA_IDX,
3946 SPI_INDEX_COUNT
3949 /* indicators whether system parameter value is loaded */
3950 static char spi_loaded[SPI_INDEX_COUNT];
3952 static struct sysparam_rgb_entry system_colors[] =
3954 #define RGB_ENTRY(name,val,reg) { { get_rgb_entry, set_rgb_entry, init_rgb_entry, COLORS_KEY, reg }, (val) }
3955 RGB_ENTRY( COLOR_SCROLLBAR, RGB(212, 208, 200), "Scrollbar" ),
3956 RGB_ENTRY( COLOR_BACKGROUND, RGB(58, 110, 165), "Background" ),
3957 RGB_ENTRY( COLOR_ACTIVECAPTION, RGB(10, 36, 106), "ActiveTitle" ),
3958 RGB_ENTRY( COLOR_INACTIVECAPTION, RGB(128, 128, 128), "InactiveTitle" ),
3959 RGB_ENTRY( COLOR_MENU, RGB(212, 208, 200), "Menu" ),
3960 RGB_ENTRY( COLOR_WINDOW, RGB(255, 255, 255), "Window" ),
3961 RGB_ENTRY( COLOR_WINDOWFRAME, RGB(0, 0, 0), "WindowFrame" ),
3962 RGB_ENTRY( COLOR_MENUTEXT, RGB(0, 0, 0), "MenuText" ),
3963 RGB_ENTRY( COLOR_WINDOWTEXT, RGB(0, 0, 0), "WindowText" ),
3964 RGB_ENTRY( COLOR_CAPTIONTEXT, RGB(255, 255, 255), "TitleText" ),
3965 RGB_ENTRY( COLOR_ACTIVEBORDER, RGB(212, 208, 200), "ActiveBorder" ),
3966 RGB_ENTRY( COLOR_INACTIVEBORDER, RGB(212, 208, 200), "InactiveBorder" ),
3967 RGB_ENTRY( COLOR_APPWORKSPACE, RGB(128, 128, 128), "AppWorkSpace" ),
3968 RGB_ENTRY( COLOR_HIGHLIGHT, RGB(10, 36, 106), "Hilight" ),
3969 RGB_ENTRY( COLOR_HIGHLIGHTTEXT, RGB(255, 255, 255), "HilightText" ),
3970 RGB_ENTRY( COLOR_BTNFACE, RGB(212, 208, 200), "ButtonFace" ),
3971 RGB_ENTRY( COLOR_BTNSHADOW, RGB(128, 128, 128), "ButtonShadow" ),
3972 RGB_ENTRY( COLOR_GRAYTEXT, RGB(128, 128, 128), "GrayText" ),
3973 RGB_ENTRY( COLOR_BTNTEXT, RGB(0, 0, 0), "ButtonText" ),
3974 RGB_ENTRY( COLOR_INACTIVECAPTIONTEXT, RGB(212, 208, 200), "InactiveTitleText" ),
3975 RGB_ENTRY( COLOR_BTNHIGHLIGHT, RGB(255, 255, 255), "ButtonHilight" ),
3976 RGB_ENTRY( COLOR_3DDKSHADOW, RGB(64, 64, 64), "ButtonDkShadow" ),
3977 RGB_ENTRY( COLOR_3DLIGHT, RGB(212, 208, 200), "ButtonLight" ),
3978 RGB_ENTRY( COLOR_INFOTEXT, RGB(0, 0, 0), "InfoText" ),
3979 RGB_ENTRY( COLOR_INFOBK, RGB(255, 255, 225), "InfoWindow" ),
3980 RGB_ENTRY( COLOR_ALTERNATEBTNFACE, RGB(181, 181, 181), "ButtonAlternateFace" ),
3981 RGB_ENTRY( COLOR_HOTLIGHT, RGB(0, 0, 200), "HotTrackingColor" ),
3982 RGB_ENTRY( COLOR_GRADIENTACTIVECAPTION, RGB(166, 202, 240), "GradientActiveTitle" ),
3983 RGB_ENTRY( COLOR_GRADIENTINACTIVECAPTION, RGB(192, 192, 192), "GradientInactiveTitle" ),
3984 RGB_ENTRY( COLOR_MENUHILIGHT, RGB(10, 36, 106), "MenuHilight" ),
3985 RGB_ENTRY( COLOR_MENUBAR, RGB(212, 208, 200), "MenuBar" )
3986 #undef RGB_ENTRY
3989 /* entries that are initialized by default in the registry */
3990 static union sysparam_all_entry * const default_entries[] =
3992 (union sysparam_all_entry *)&entry_ACTIVEWINDOWTRACKING,
3993 (union sysparam_all_entry *)&entry_ACTIVEWNDTRKTIMEOUT,
3994 (union sysparam_all_entry *)&entry_BEEP,
3995 (union sysparam_all_entry *)&entry_BLOCKSENDINPUTRESETS,
3996 (union sysparam_all_entry *)&entry_BORDER,
3997 (union sysparam_all_entry *)&entry_CAPTIONHEIGHT,
3998 (union sysparam_all_entry *)&entry_CAPTIONWIDTH,
3999 (union sysparam_all_entry *)&entry_CARETWIDTH,
4000 (union sysparam_all_entry *)&entry_DESKWALLPAPER,
4001 (union sysparam_all_entry *)&entry_DOUBLECLICKTIME,
4002 (union sysparam_all_entry *)&entry_DOUBLECLKHEIGHT,
4003 (union sysparam_all_entry *)&entry_DOUBLECLKWIDTH,
4004 (union sysparam_all_entry *)&entry_DRAGFULLWINDOWS,
4005 (union sysparam_all_entry *)&entry_DRAGHEIGHT,
4006 (union sysparam_all_entry *)&entry_DRAGWIDTH,
4007 (union sysparam_all_entry *)&entry_FOCUSBORDERHEIGHT,
4008 (union sysparam_all_entry *)&entry_FOCUSBORDERWIDTH,
4009 (union sysparam_all_entry *)&entry_FONTSMOOTHING,
4010 (union sysparam_all_entry *)&entry_FONTSMOOTHINGCONTRAST,
4011 (union sysparam_all_entry *)&entry_FONTSMOOTHINGORIENTATION,
4012 (union sysparam_all_entry *)&entry_FONTSMOOTHINGTYPE,
4013 (union sysparam_all_entry *)&entry_FOREGROUNDFLASHCOUNT,
4014 (union sysparam_all_entry *)&entry_FOREGROUNDLOCKTIMEOUT,
4015 (union sysparam_all_entry *)&entry_ICONHORIZONTALSPACING,
4016 (union sysparam_all_entry *)&entry_ICONTITLEWRAP,
4017 (union sysparam_all_entry *)&entry_ICONVERTICALSPACING,
4018 (union sysparam_all_entry *)&entry_KEYBOARDDELAY,
4019 (union sysparam_all_entry *)&entry_KEYBOARDPREF,
4020 (union sysparam_all_entry *)&entry_KEYBOARDSPEED,
4021 (union sysparam_all_entry *)&entry_LOWPOWERACTIVE,
4022 (union sysparam_all_entry *)&entry_MENUHEIGHT,
4023 (union sysparam_all_entry *)&entry_MENUSHOWDELAY,
4024 (union sysparam_all_entry *)&entry_MENUWIDTH,
4025 (union sysparam_all_entry *)&entry_MOUSEACCELERATION,
4026 (union sysparam_all_entry *)&entry_MOUSEBUTTONSWAP,
4027 (union sysparam_all_entry *)&entry_MOUSECLICKLOCKTIME,
4028 (union sysparam_all_entry *)&entry_MOUSEHOVERHEIGHT,
4029 (union sysparam_all_entry *)&entry_MOUSEHOVERTIME,
4030 (union sysparam_all_entry *)&entry_MOUSEHOVERWIDTH,
4031 (union sysparam_all_entry *)&entry_MOUSESPEED,
4032 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD1,
4033 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD2,
4034 (union sysparam_all_entry *)&entry_PADDEDBORDERWIDTH,
4035 (union sysparam_all_entry *)&entry_SCREENREADER,
4036 (union sysparam_all_entry *)&entry_SCROLLHEIGHT,
4037 (union sysparam_all_entry *)&entry_SCROLLWIDTH,
4038 (union sysparam_all_entry *)&entry_SHOWSOUNDS,
4039 (union sysparam_all_entry *)&entry_SMCAPTIONHEIGHT,
4040 (union sysparam_all_entry *)&entry_SMCAPTIONWIDTH,
4041 (union sysparam_all_entry *)&entry_SNAPTODEFBUTTON,
4042 (union sysparam_all_entry *)&entry_USERPREFERENCESMASK,
4043 (union sysparam_all_entry *)&entry_WHEELSCROLLCHARS,
4044 (union sysparam_all_entry *)&entry_WHEELSCROLLLINES,
4045 (union sysparam_all_entry *)&entry_AUDIODESC_LOCALE,
4046 (union sysparam_all_entry *)&entry_AUDIODESC_ON,
4049 void sysparams_init(void)
4052 DWORD i, dispos, dpi_scaling;
4053 WCHAR layout[KL_NAMELENGTH];
4054 pthread_mutexattr_t attr;
4055 HKEY hkey;
4057 static const WCHAR software_wineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e'};
4058 static const WCHAR temporary_system_parametersW[] =
4059 {'T','e','m','p','o','r','a','r','y',' ','S','y','s','t','e','m',' ',
4060 'P','a','r','a','m','e','t','e','r','s'};
4061 static const WCHAR oneW[] = {'1',0};
4062 static const WCHAR kl_preloadW[] =
4063 {'K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','\\','P','r','e','l','o','a','d'};
4065 pthread_mutexattr_init( &attr );
4066 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
4067 pthread_mutex_init( &user_mutex, &attr );
4068 pthread_mutexattr_destroy( &attr );
4070 if ((hkey = reg_create_key( hkcu_key, kl_preloadW, sizeof(kl_preloadW), 0, NULL )))
4072 if (NtUserGetKeyboardLayoutName( layout ))
4073 set_reg_value( hkey, oneW, REG_SZ, (const BYTE *)layout,
4074 (lstrlenW(layout) + 1) * sizeof(WCHAR) );
4075 NtClose( hkey );
4078 /* this one must be non-volatile */
4079 if (!(hkey = reg_create_key( hkcu_key, software_wineW, sizeof(software_wineW), 0, NULL )))
4081 ERR("Can't create wine registry branch\n");
4082 return;
4085 /* @@ Wine registry key: HKCU\Software\Wine\Temporary System Parameters */
4086 if (!(volatile_base_key = reg_create_key( hkey, temporary_system_parametersW,
4087 sizeof(temporary_system_parametersW),
4088 REG_OPTION_VOLATILE, &dispos )))
4089 ERR("Can't create non-permanent wine registry branch\n");
4091 NtClose( hkey );
4093 config_key = reg_create_key( NULL, config_keyW, sizeof(config_keyW), 0, NULL );
4095 get_dword_entry( (union sysparam_all_entry *)&entry_LOGPIXELS, 0, &system_dpi, 0 );
4096 if (!system_dpi) /* check fallback key */
4098 static const WCHAR log_pixelsW[] = {'L','o','g','P','i','x','e','l','s',0};
4099 static const WCHAR software_fontsW[] =
4100 {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s'};
4102 if ((hkey = reg_open_key( config_key, software_fontsW, sizeof(software_fontsW) )))
4104 char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(DWORD)])];
4105 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
4107 if (query_reg_value( hkey, log_pixelsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
4108 system_dpi = *(const DWORD *)value->Data;
4109 NtClose( hkey );
4112 if (!system_dpi) system_dpi = USER_DEFAULT_SCREEN_DPI;
4114 /* FIXME: what do the DpiScalingVer flags mean? */
4115 get_dword_entry( (union sysparam_all_entry *)&entry_DPISCALINGVER, 0, &dpi_scaling, 0 );
4116 if (!dpi_scaling) NtUserSetProcessDpiAwarenessContext( NTUSER_DPI_PER_MONITOR_AWARE, 0 );
4118 if (volatile_base_key && dispos == REG_CREATED_NEW_KEY) /* first process, initialize entries */
4120 for (i = 0; i < ARRAY_SIZE( default_entries ); i++)
4121 default_entries[i]->hdr.init( default_entries[i] );
4125 static BOOL update_desktop_wallpaper(void)
4127 /* FIXME: move implementation from user32 */
4128 entry_DESKWALLPAPER.hdr.loaded = entry_DESKPATTERN.hdr.loaded = FALSE;
4129 return TRUE;
4132 /***********************************************************************
4133 * NtUserSystemParametersInfoForDpi (win32u.@)
4135 BOOL WINAPI NtUserSystemParametersInfoForDpi( UINT action, UINT val, PVOID ptr, UINT winini, UINT dpi )
4137 BOOL ret = FALSE;
4139 switch (action)
4141 case SPI_GETICONTITLELOGFONT:
4142 ret = get_entry_dpi( &entry_ICONTITLELOGFONT, val, ptr, dpi );
4143 break;
4144 case SPI_GETNONCLIENTMETRICS:
4146 NONCLIENTMETRICSW *ncm = ptr;
4148 if (!ncm) break;
4149 ret = get_entry_dpi( &entry_BORDER, 0, &ncm->iBorderWidth, dpi ) &&
4150 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ncm->iScrollWidth, dpi ) &&
4151 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ncm->iScrollHeight, dpi ) &&
4152 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ncm->iCaptionWidth, dpi ) &&
4153 get_entry_dpi( &entry_CAPTIONHEIGHT, 0, &ncm->iCaptionHeight, dpi ) &&
4154 get_entry_dpi( &entry_CAPTIONLOGFONT, 0, &ncm->lfCaptionFont, dpi ) &&
4155 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ncm->iSmCaptionWidth, dpi ) &&
4156 get_entry_dpi( &entry_SMCAPTIONHEIGHT, 0, &ncm->iSmCaptionHeight, dpi ) &&
4157 get_entry_dpi( &entry_SMCAPTIONLOGFONT, 0, &ncm->lfSmCaptionFont, dpi ) &&
4158 get_entry_dpi( &entry_MENUWIDTH, 0, &ncm->iMenuWidth, dpi ) &&
4159 get_entry_dpi( &entry_MENUHEIGHT, 0, &ncm->iMenuHeight, dpi ) &&
4160 get_entry_dpi( &entry_MENULOGFONT, 0, &ncm->lfMenuFont, dpi ) &&
4161 get_entry_dpi( &entry_STATUSLOGFONT, 0, &ncm->lfStatusFont, dpi ) &&
4162 get_entry_dpi( &entry_MESSAGELOGFONT, 0, &ncm->lfMessageFont, dpi );
4163 if (ret && ncm->cbSize == sizeof(NONCLIENTMETRICSW))
4164 ret = get_entry_dpi( &entry_PADDEDBORDERWIDTH, 0, &ncm->iPaddedBorderWidth, dpi );
4165 normalize_nonclientmetrics( ncm );
4166 break;
4168 case SPI_GETICONMETRICS:
4170 ICONMETRICSW *im = ptr;
4171 if (im && im->cbSize == sizeof(*im))
4172 ret = get_entry_dpi( &entry_ICONHORIZONTALSPACING, 0, &im->iHorzSpacing, dpi ) &&
4173 get_entry_dpi( &entry_ICONVERTICALSPACING, 0, &im->iVertSpacing, dpi ) &&
4174 get_entry_dpi( &entry_ICONTITLEWRAP, 0, &im->iTitleWrap, dpi ) &&
4175 get_entry_dpi( &entry_ICONTITLELOGFONT, 0, &im->lfFont, dpi );
4176 break;
4178 default:
4179 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4180 break;
4182 return ret;
4185 /***********************************************************************
4186 * NtUserSystemParametersInfo (win32u.@)
4188 * Each system parameter has flag which shows whether the parameter
4189 * is loaded or not. Parameters, stored directly in SysParametersInfo are
4190 * loaded from registry only when they are requested and the flag is
4191 * "false", after the loading the flag is set to "true". On interprocess
4192 * notification of the parameter change the corresponding parameter flag is
4193 * set to "false". The parameter value will be reloaded when it is requested
4194 * the next time.
4195 * Parameters, backed by or depend on GetSystemMetrics are processed
4196 * differently. These parameters are always loaded. They are reloaded right
4197 * away on interprocess change notification. We can't do lazy loading because
4198 * we don't want to complicate GetSystemMetrics.
4199 * Parameters backed by driver settings are read from corresponding setting.
4200 * On the parameter change request the setting is changed. Interprocess change
4201 * notifications are ignored.
4202 * When parameter value is updated the changed value is stored in permanent
4203 * registry branch if saving is requested. Otherwise it is stored
4204 * in temporary branch
4206 * Some SPI values can also be stored as Twips values in the registry,
4207 * don't forget the conversion!
4209 BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT winini )
4211 #define WINE_SPI_FIXME(x) \
4212 case x: \
4214 static BOOL warn = TRUE; \
4215 if (warn) \
4217 warn = FALSE; \
4218 FIXME( "Unimplemented action: %u (%s)\n", x, #x ); \
4221 RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE ); \
4222 ret = FALSE; \
4223 break
4224 #define WINE_SPI_WARN(x) \
4225 case x: \
4226 WARN( "Ignored action: %u (%s)\n", x, #x ); \
4227 ret = TRUE; \
4228 break
4230 BOOL ret = user_driver->pSystemParametersInfo( action, val, ptr, winini );
4231 unsigned spi_idx = 0;
4233 if (!ret) switch (action)
4235 case SPI_GETBEEP:
4236 ret = get_entry( &entry_BEEP, val, ptr );
4237 break;
4238 case SPI_SETBEEP:
4239 ret = set_entry( &entry_BEEP, val, ptr, winini );
4240 break;
4241 case SPI_GETMOUSE:
4242 ret = get_entry( &entry_MOUSETHRESHOLD1, val, (INT *)ptr ) &&
4243 get_entry( &entry_MOUSETHRESHOLD2, val, (INT *)ptr + 1 ) &&
4244 get_entry( &entry_MOUSEACCELERATION, val, (INT *)ptr + 2 );
4245 break;
4246 case SPI_SETMOUSE:
4247 ret = set_entry( &entry_MOUSETHRESHOLD1, ((INT *)ptr)[0], ptr, winini ) &&
4248 set_entry( &entry_MOUSETHRESHOLD2, ((INT *)ptr)[1], ptr, winini ) &&
4249 set_entry( &entry_MOUSEACCELERATION, ((INT *)ptr)[2], ptr, winini );
4250 break;
4251 case SPI_GETBORDER:
4252 ret = get_entry( &entry_BORDER, val, ptr );
4253 if (*(INT*)ptr < 1) *(INT*)ptr = 1;
4254 break;
4255 case SPI_SETBORDER:
4256 ret = set_entry( &entry_BORDER, val, ptr, winini );
4257 break;
4258 case SPI_GETKEYBOARDSPEED:
4259 ret = get_entry( &entry_KEYBOARDSPEED, val, ptr );
4260 break;
4261 case SPI_SETKEYBOARDSPEED:
4262 if (val > 31) val = 31;
4263 ret = set_entry( &entry_KEYBOARDSPEED, val, ptr, winini );
4264 break;
4266 WINE_SPI_WARN(SPI_LANGDRIVER); /* not implemented in Windows */
4268 case SPI_ICONHORIZONTALSPACING:
4269 if (ptr != NULL)
4270 ret = get_entry( &entry_ICONHORIZONTALSPACING, val, ptr );
4271 else
4273 int min_val = map_to_dpi( 32, get_system_dpi() );
4274 ret = set_entry( &entry_ICONHORIZONTALSPACING, max( min_val, val ), ptr, winini );
4276 break;
4277 case SPI_GETSCREENSAVETIMEOUT:
4278 ret = get_entry( &entry_SCREENSAVETIMEOUT, val, ptr );
4279 break;
4280 case SPI_SETSCREENSAVETIMEOUT:
4281 ret = set_entry( &entry_SCREENSAVETIMEOUT, val, ptr, winini );
4282 break;
4283 case SPI_GETSCREENSAVEACTIVE:
4284 ret = get_entry( &entry_SCREENSAVEACTIVE, val, ptr );
4285 break;
4286 case SPI_SETSCREENSAVEACTIVE:
4287 ret = set_entry( &entry_SCREENSAVEACTIVE, val, ptr, winini );
4288 break;
4289 case SPI_GETGRIDGRANULARITY:
4290 ret = get_entry( &entry_GRIDGRANULARITY, val, ptr );
4291 break;
4292 case SPI_SETGRIDGRANULARITY:
4293 ret = set_entry( &entry_GRIDGRANULARITY, val, ptr, winini );
4294 break;
4295 case SPI_SETDESKWALLPAPER:
4296 if (!ptr || set_entry( &entry_DESKWALLPAPER, val, ptr, winini ))
4297 ret = update_desktop_wallpaper();
4298 break;
4299 case SPI_SETDESKPATTERN:
4300 if (!ptr || set_entry( &entry_DESKPATTERN, val, ptr, winini ))
4301 ret = update_desktop_wallpaper();
4302 break;
4303 case SPI_GETKEYBOARDDELAY:
4304 ret = get_entry( &entry_KEYBOARDDELAY, val, ptr );
4305 break;
4306 case SPI_SETKEYBOARDDELAY:
4307 ret = set_entry( &entry_KEYBOARDDELAY, val, ptr, winini );
4308 break;
4309 case SPI_ICONVERTICALSPACING:
4310 if (ptr != NULL)
4311 ret = get_entry( &entry_ICONVERTICALSPACING, val, ptr );
4312 else
4314 int min_val = map_to_dpi( 32, get_system_dpi() );
4315 ret = set_entry( &entry_ICONVERTICALSPACING, max( min_val, val ), ptr, winini );
4317 break;
4318 case SPI_GETICONTITLEWRAP:
4319 ret = get_entry( &entry_ICONTITLEWRAP, val, ptr );
4320 break;
4321 case SPI_SETICONTITLEWRAP:
4322 ret = set_entry( &entry_ICONTITLEWRAP, val, ptr, winini );
4323 break;
4324 case SPI_GETMENUDROPALIGNMENT:
4325 ret = get_entry( &entry_MENUDROPALIGNMENT, val, ptr );
4326 break;
4327 case SPI_SETMENUDROPALIGNMENT:
4328 ret = set_entry( &entry_MENUDROPALIGNMENT, val, ptr, winini );
4329 break;
4330 case SPI_SETDOUBLECLKWIDTH:
4331 ret = set_entry( &entry_DOUBLECLKWIDTH, val, ptr, winini );
4332 break;
4333 case SPI_SETDOUBLECLKHEIGHT:
4334 ret = set_entry( &entry_DOUBLECLKHEIGHT, val, ptr, winini );
4335 break;
4336 case SPI_GETICONTITLELOGFONT:
4337 ret = get_entry( &entry_ICONTITLELOGFONT, val, ptr );
4338 break;
4339 case SPI_SETDOUBLECLICKTIME:
4340 ret = set_entry( &entry_DOUBLECLICKTIME, val, ptr, winini );
4341 break;
4342 case SPI_SETMOUSEBUTTONSWAP:
4343 ret = set_entry( &entry_MOUSEBUTTONSWAP, val, ptr, winini );
4344 break;
4345 case SPI_SETICONTITLELOGFONT:
4346 ret = set_entry( &entry_ICONTITLELOGFONT, val, ptr, winini );
4347 break;
4348 case SPI_GETFASTTASKSWITCH:
4349 if (!ptr) return FALSE;
4350 *(BOOL *)ptr = TRUE;
4351 ret = TRUE;
4352 break;
4353 case SPI_SETFASTTASKSWITCH:
4354 /* the action is disabled */
4355 ret = FALSE;
4356 break;
4357 case SPI_SETDRAGFULLWINDOWS:
4358 ret = set_entry( &entry_DRAGFULLWINDOWS, val, ptr, winini );
4359 break;
4360 case SPI_GETDRAGFULLWINDOWS:
4361 ret = get_entry( &entry_DRAGFULLWINDOWS, val, ptr );
4362 break;
4363 case SPI_GETNONCLIENTMETRICS:
4365 NONCLIENTMETRICSW *nm = ptr;
4366 int padded_border;
4368 if (!ptr) return FALSE;
4370 ret = get_entry( &entry_BORDER, 0, &nm->iBorderWidth ) &&
4371 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border ) &&
4372 get_entry( &entry_SCROLLWIDTH, 0, &nm->iScrollWidth ) &&
4373 get_entry( &entry_SCROLLHEIGHT, 0, &nm->iScrollHeight ) &&
4374 get_entry( &entry_CAPTIONWIDTH, 0, &nm->iCaptionWidth ) &&
4375 get_entry( &entry_CAPTIONHEIGHT, 0, &nm->iCaptionHeight ) &&
4376 get_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont ) &&
4377 get_entry( &entry_SMCAPTIONWIDTH, 0, &nm->iSmCaptionWidth ) &&
4378 get_entry( &entry_SMCAPTIONHEIGHT, 0, &nm->iSmCaptionHeight ) &&
4379 get_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont ) &&
4380 get_entry( &entry_MENUWIDTH, 0, &nm->iMenuWidth ) &&
4381 get_entry( &entry_MENUHEIGHT, 0, &nm->iMenuHeight ) &&
4382 get_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont ) &&
4383 get_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont ) &&
4384 get_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont );
4385 if (ret)
4387 nm->iBorderWidth += padded_border;
4388 if (nm->cbSize == sizeof(NONCLIENTMETRICSW)) nm->iPaddedBorderWidth = 0;
4390 normalize_nonclientmetrics( nm );
4391 break;
4393 case SPI_SETNONCLIENTMETRICS:
4395 LPNONCLIENTMETRICSW nm = ptr;
4396 int padded_border;
4398 if (nm && (nm->cbSize == sizeof(NONCLIENTMETRICSW) ||
4399 nm->cbSize == FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth)))
4401 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border );
4403 ret = set_entry( &entry_BORDER, nm->iBorderWidth - padded_border, NULL, winini ) &&
4404 set_entry( &entry_SCROLLWIDTH, nm->iScrollWidth, NULL, winini ) &&
4405 set_entry( &entry_SCROLLHEIGHT, nm->iScrollHeight, NULL, winini ) &&
4406 set_entry( &entry_CAPTIONWIDTH, nm->iCaptionWidth, NULL, winini ) &&
4407 set_entry( &entry_CAPTIONHEIGHT, nm->iCaptionHeight, NULL, winini ) &&
4408 set_entry( &entry_SMCAPTIONWIDTH, nm->iSmCaptionWidth, NULL, winini ) &&
4409 set_entry( &entry_SMCAPTIONHEIGHT, nm->iSmCaptionHeight, NULL, winini ) &&
4410 set_entry( &entry_MENUWIDTH, nm->iMenuWidth, NULL, winini ) &&
4411 set_entry( &entry_MENUHEIGHT, nm->iMenuHeight, NULL, winini ) &&
4412 set_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont, winini ) &&
4413 set_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont, winini ) &&
4414 set_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont, winini ) &&
4415 set_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont, winini ) &&
4416 set_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont, winini );
4418 break;
4420 case SPI_GETMINIMIZEDMETRICS:
4422 MINIMIZEDMETRICS *mm = ptr;
4423 if (mm && mm->cbSize == sizeof(*mm)) {
4424 ret = get_entry( &entry_MINWIDTH, 0, &mm->iWidth ) &&
4425 get_entry( &entry_MINHORZGAP, 0, &mm->iHorzGap ) &&
4426 get_entry( &entry_MINVERTGAP, 0, &mm->iVertGap ) &&
4427 get_entry( &entry_MINARRANGE, 0, &mm->iArrange );
4428 mm->iWidth = max( 0, mm->iWidth );
4429 mm->iHorzGap = max( 0, mm->iHorzGap );
4430 mm->iVertGap = max( 0, mm->iVertGap );
4431 mm->iArrange &= 0x0f;
4433 break;
4435 case SPI_SETMINIMIZEDMETRICS:
4437 MINIMIZEDMETRICS *mm = ptr;
4438 if (mm && mm->cbSize == sizeof(*mm))
4439 ret = set_entry( &entry_MINWIDTH, max( 0, mm->iWidth ), NULL, winini ) &&
4440 set_entry( &entry_MINHORZGAP, max( 0, mm->iHorzGap ), NULL, winini ) &&
4441 set_entry( &entry_MINVERTGAP, max( 0, mm->iVertGap ), NULL, winini ) &&
4442 set_entry( &entry_MINARRANGE, mm->iArrange & 0x0f, NULL, winini );
4443 break;
4445 case SPI_GETICONMETRICS:
4447 ICONMETRICSW *icon = ptr;
4448 if(icon && icon->cbSize == sizeof(*icon))
4450 ret = get_entry( &entry_ICONHORIZONTALSPACING, 0, &icon->iHorzSpacing ) &&
4451 get_entry( &entry_ICONVERTICALSPACING, 0, &icon->iVertSpacing ) &&
4452 get_entry( &entry_ICONTITLEWRAP, 0, &icon->iTitleWrap ) &&
4453 get_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont );
4455 break;
4457 case SPI_SETICONMETRICS:
4459 ICONMETRICSW *icon = ptr;
4460 if (icon && icon->cbSize == sizeof(*icon))
4461 ret = set_entry( &entry_ICONVERTICALSPACING, max(32,icon->iVertSpacing), NULL, winini ) &&
4462 set_entry( &entry_ICONHORIZONTALSPACING, max(32,icon->iHorzSpacing), NULL, winini ) &&
4463 set_entry( &entry_ICONTITLEWRAP, icon->iTitleWrap, NULL, winini ) &&
4464 set_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont, winini );
4465 break;
4467 case SPI_SETWORKAREA:
4469 if (!ptr) return FALSE;
4470 spi_idx = SPI_SETWORKAREA_IDX;
4471 work_area = *(RECT*)ptr;
4472 spi_loaded[spi_idx] = TRUE;
4473 ret = TRUE;
4474 break;
4476 case SPI_GETWORKAREA:
4478 if (!ptr) return FALSE;
4480 spi_idx = SPI_SETWORKAREA_IDX;
4481 if (!spi_loaded[spi_idx])
4483 struct monitor *monitor;
4485 if (!lock_display_devices()) return FALSE;
4487 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
4489 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
4490 work_area = monitor->rc_work;
4491 break;
4494 unlock_display_devices();
4495 spi_loaded[spi_idx] = TRUE;
4497 *(RECT *)ptr = map_dpi_rect( work_area, system_dpi, get_thread_dpi() );
4498 ret = TRUE;
4499 TRACE("work area %s\n", wine_dbgstr_rect( &work_area ));
4500 break;
4503 WINE_SPI_FIXME(SPI_SETPENWINDOWS);
4505 case SPI_GETFILTERKEYS:
4507 LPFILTERKEYS filter_keys = ptr;
4508 WARN("SPI_GETFILTERKEYS not fully implemented\n");
4509 if (filter_keys && filter_keys->cbSize == sizeof(FILTERKEYS))
4511 /* Indicate that no FilterKeys feature available */
4512 filter_keys->dwFlags = 0;
4513 filter_keys->iWaitMSec = 0;
4514 filter_keys->iDelayMSec = 0;
4515 filter_keys->iRepeatMSec = 0;
4516 filter_keys->iBounceMSec = 0;
4517 ret = TRUE;
4519 break;
4521 WINE_SPI_FIXME(SPI_SETFILTERKEYS);
4523 case SPI_GETTOGGLEKEYS:
4525 LPTOGGLEKEYS toggle_keys = ptr;
4526 WARN("SPI_GETTOGGLEKEYS not fully implemented\n");
4527 if (toggle_keys && toggle_keys->cbSize == sizeof(TOGGLEKEYS))
4529 /* Indicate that no ToggleKeys feature available */
4530 toggle_keys->dwFlags = 0;
4531 ret = TRUE;
4533 break;
4536 WINE_SPI_FIXME(SPI_SETTOGGLEKEYS);
4538 case SPI_GETMOUSEKEYS:
4540 MOUSEKEYS *mouse_keys = ptr;
4541 WARN("SPI_GETMOUSEKEYS not fully implemented\n");
4542 if (mouse_keys && mouse_keys->cbSize == sizeof(MOUSEKEYS))
4544 /* Indicate that no MouseKeys feature available */
4545 mouse_keys->dwFlags = 0;
4546 mouse_keys->iMaxSpeed = 360;
4547 mouse_keys->iTimeToMaxSpeed = 1000;
4548 mouse_keys->iCtrlSpeed = 0;
4549 mouse_keys->dwReserved1 = 0;
4550 mouse_keys->dwReserved2 = 0;
4551 ret = TRUE;
4553 break;
4556 WINE_SPI_FIXME(SPI_SETMOUSEKEYS);
4558 case SPI_GETSHOWSOUNDS:
4559 ret = get_entry( &entry_SHOWSOUNDS, val, ptr );
4560 break;
4561 case SPI_SETSHOWSOUNDS:
4562 ret = set_entry( &entry_SHOWSOUNDS, val, ptr, winini );
4563 break;
4564 case SPI_GETSTICKYKEYS:
4566 STICKYKEYS *sticky_keys = ptr;
4567 WARN("SPI_GETSTICKYKEYS not fully implemented\n");
4568 if (sticky_keys && sticky_keys->cbSize == sizeof(STICKYKEYS))
4570 /* Indicate that no StickyKeys feature available */
4571 sticky_keys->dwFlags = 0;
4572 ret = TRUE;
4574 break;
4577 WINE_SPI_FIXME(SPI_SETSTICKYKEYS);
4579 case SPI_GETACCESSTIMEOUT:
4581 ACCESSTIMEOUT *access_timeout = ptr;
4582 WARN("SPI_GETACCESSTIMEOUT not fully implemented\n");
4583 if (access_timeout && access_timeout->cbSize == sizeof(ACCESSTIMEOUT))
4585 /* Indicate that no accessibility features timeout is available */
4586 access_timeout->dwFlags = 0;
4587 access_timeout->iTimeOutMSec = 0;
4588 ret = TRUE;
4590 break;
4593 WINE_SPI_FIXME(SPI_SETACCESSTIMEOUT);
4595 case SPI_GETSERIALKEYS:
4597 LPSERIALKEYSW serial_keys = ptr;
4598 WARN("SPI_GETSERIALKEYS not fully implemented\n");
4599 if (serial_keys && serial_keys->cbSize == sizeof(SERIALKEYSW))
4601 /* Indicate that no SerialKeys feature available */
4602 serial_keys->dwFlags = 0;
4603 serial_keys->lpszActivePort = NULL;
4604 serial_keys->lpszPort = NULL;
4605 serial_keys->iBaudRate = 0;
4606 serial_keys->iPortState = 0;
4607 ret = TRUE;
4609 break;
4612 WINE_SPI_FIXME(SPI_SETSERIALKEYS);
4614 case SPI_GETSOUNDSENTRY:
4616 SOUNDSENTRYW *sound_sentry = ptr;
4617 WARN("SPI_GETSOUNDSENTRY not fully implemented\n");
4618 if (sound_sentry && sound_sentry->cbSize == sizeof(SOUNDSENTRYW))
4620 /* Indicate that no SoundSentry feature available */
4621 sound_sentry->dwFlags = 0;
4622 sound_sentry->iFSTextEffect = 0;
4623 sound_sentry->iFSTextEffectMSec = 0;
4624 sound_sentry->iFSTextEffectColorBits = 0;
4625 sound_sentry->iFSGrafEffect = 0;
4626 sound_sentry->iFSGrafEffectMSec = 0;
4627 sound_sentry->iFSGrafEffectColor = 0;
4628 sound_sentry->iWindowsEffect = 0;
4629 sound_sentry->iWindowsEffectMSec = 0;
4630 sound_sentry->lpszWindowsEffectDLL = 0;
4631 sound_sentry->iWindowsEffectOrdinal = 0;
4632 ret = TRUE;
4634 break;
4637 WINE_SPI_FIXME(SPI_SETSOUNDSENTRY);
4639 case SPI_GETHIGHCONTRAST:
4641 HIGHCONTRASTW *high_contrast = ptr;
4642 WARN("SPI_GETHIGHCONTRAST not fully implemented\n");
4643 if (high_contrast && high_contrast->cbSize == sizeof(HIGHCONTRASTW))
4645 /* Indicate that no high contrast feature available */
4646 high_contrast->dwFlags = 0;
4647 high_contrast->lpszDefaultScheme = NULL;
4648 ret = TRUE;
4650 break;
4653 WINE_SPI_FIXME(SPI_SETHIGHCONTRAST);
4655 case SPI_GETKEYBOARDPREF:
4656 ret = get_entry( &entry_KEYBOARDPREF, val, ptr );
4657 break;
4658 case SPI_SETKEYBOARDPREF:
4659 ret = set_entry( &entry_KEYBOARDPREF, val, ptr, winini );
4660 break;
4661 case SPI_GETSCREENREADER:
4662 ret = get_entry( &entry_SCREENREADER, val, ptr );
4663 break;
4664 case SPI_SETSCREENREADER:
4665 ret = set_entry( &entry_SCREENREADER, val, ptr, winini );
4666 break;
4668 case SPI_GETANIMATION:
4670 ANIMATIONINFO *anim_info = ptr;
4672 /* Tell it "disabled" */
4673 if (anim_info && anim_info->cbSize == sizeof(ANIMATIONINFO))
4675 /* Minimize and restore animation is disabled (nonzero == enabled) */
4676 anim_info->iMinAnimate = 0;
4677 ret = TRUE;
4679 break;
4682 WINE_SPI_WARN(SPI_SETANIMATION);
4684 case SPI_GETFONTSMOOTHING:
4685 ret = get_entry( &entry_FONTSMOOTHING, val, ptr );
4686 if (ret) *(UINT *)ptr = (*(UINT *)ptr != 0);
4687 break;
4688 case SPI_SETFONTSMOOTHING:
4689 val = val ? 2 : 0; /* Win NT4/2k/XP behavior */
4690 ret = set_entry( &entry_FONTSMOOTHING, val, ptr, winini );
4691 break;
4692 case SPI_SETDRAGWIDTH:
4693 ret = set_entry( &entry_DRAGWIDTH, val, ptr, winini );
4694 break;
4695 case SPI_SETDRAGHEIGHT:
4696 ret = set_entry( &entry_DRAGHEIGHT, val, ptr, winini );
4697 break;
4699 WINE_SPI_FIXME(SPI_SETHANDHELD);
4700 WINE_SPI_FIXME(SPI_GETLOWPOWERTIMEOUT);
4701 WINE_SPI_FIXME(SPI_GETPOWEROFFTIMEOUT);
4702 WINE_SPI_FIXME(SPI_SETLOWPOWERTIMEOUT);
4703 WINE_SPI_FIXME(SPI_SETPOWEROFFTIMEOUT);
4705 case SPI_GETLOWPOWERACTIVE:
4706 ret = get_entry( &entry_LOWPOWERACTIVE, val, ptr );
4707 break;
4708 case SPI_SETLOWPOWERACTIVE:
4709 ret = set_entry( &entry_LOWPOWERACTIVE, val, ptr, winini );
4710 break;
4711 case SPI_GETPOWEROFFACTIVE:
4712 ret = get_entry( &entry_POWEROFFACTIVE, val, ptr );
4713 break;
4714 case SPI_SETPOWEROFFACTIVE:
4715 ret = set_entry( &entry_POWEROFFACTIVE, val, ptr, winini );
4716 break;
4718 WINE_SPI_FIXME(SPI_SETCURSORS);
4719 WINE_SPI_FIXME(SPI_SETICONS);
4721 case SPI_GETDEFAULTINPUTLANG:
4722 ret = NtUserGetKeyboardLayout(0) != 0;
4723 break;
4725 WINE_SPI_FIXME(SPI_SETDEFAULTINPUTLANG);
4726 WINE_SPI_FIXME(SPI_SETLANGTOGGLE);
4728 case SPI_GETWINDOWSEXTENSION:
4729 WARN( "pretend no support for Win9x Plus! for now.\n" );
4730 ret = FALSE; /* yes, this is the result value */
4731 break;
4732 case SPI_SETMOUSETRAILS:
4733 ret = set_entry( &entry_MOUSETRAILS, val, ptr, winini );
4734 break;
4735 case SPI_GETMOUSETRAILS:
4736 ret = get_entry( &entry_MOUSETRAILS, val, ptr );
4737 break;
4738 case SPI_GETSNAPTODEFBUTTON:
4739 ret = get_entry( &entry_SNAPTODEFBUTTON, val, ptr );
4740 break;
4741 case SPI_SETSNAPTODEFBUTTON:
4742 ret = set_entry( &entry_SNAPTODEFBUTTON, val, ptr, winini );
4743 break;
4744 case SPI_SETSCREENSAVERRUNNING:
4745 ret = set_entry( &entry_SCREENSAVERRUNNING, val, ptr, winini );
4746 break;
4747 case SPI_GETMOUSEHOVERWIDTH:
4748 ret = get_entry( &entry_MOUSEHOVERWIDTH, val, ptr );
4749 break;
4750 case SPI_SETMOUSEHOVERWIDTH:
4751 ret = set_entry( &entry_MOUSEHOVERWIDTH, val, ptr, winini );
4752 break;
4753 case SPI_GETMOUSEHOVERHEIGHT:
4754 ret = get_entry( &entry_MOUSEHOVERHEIGHT, val, ptr );
4755 break;
4756 case SPI_SETMOUSEHOVERHEIGHT:
4757 ret = set_entry( &entry_MOUSEHOVERHEIGHT, val, ptr, winini );
4758 break;
4759 case SPI_GETMOUSEHOVERTIME:
4760 ret = get_entry( &entry_MOUSEHOVERTIME, val, ptr );
4761 break;
4762 case SPI_SETMOUSEHOVERTIME:
4763 ret = set_entry( &entry_MOUSEHOVERTIME, val, ptr, winini );
4764 break;
4765 case SPI_GETWHEELSCROLLLINES:
4766 ret = get_entry( &entry_WHEELSCROLLLINES, val, ptr );
4767 break;
4768 case SPI_SETWHEELSCROLLLINES:
4769 ret = set_entry( &entry_WHEELSCROLLLINES, val, ptr, winini );
4770 break;
4771 case SPI_GETMENUSHOWDELAY:
4772 ret = get_entry( &entry_MENUSHOWDELAY, val, ptr );
4773 break;
4774 case SPI_SETMENUSHOWDELAY:
4775 ret = set_entry( &entry_MENUSHOWDELAY, val, ptr, winini );
4776 break;
4777 case SPI_GETWHEELSCROLLCHARS:
4778 ret = get_entry( &entry_WHEELSCROLLCHARS, val, ptr );
4779 break;
4780 case SPI_SETWHEELSCROLLCHARS:
4781 ret = set_entry( &entry_WHEELSCROLLCHARS, val, ptr, winini );
4782 break;
4784 WINE_SPI_FIXME(SPI_GETSHOWIMEUI);
4785 WINE_SPI_FIXME(SPI_SETSHOWIMEUI);
4787 case SPI_GETMOUSESPEED:
4788 ret = get_entry( &entry_MOUSESPEED, val, ptr );
4789 break;
4790 case SPI_SETMOUSESPEED:
4791 ret = set_entry( &entry_MOUSESPEED, val, ptr, winini );
4792 break;
4793 case SPI_GETSCREENSAVERRUNNING:
4794 ret = get_entry( &entry_SCREENSAVERRUNNING, val, ptr );
4795 break;
4796 case SPI_GETDESKWALLPAPER:
4797 ret = get_entry( &entry_DESKWALLPAPER, val, ptr );
4798 break;
4799 case SPI_GETACTIVEWINDOWTRACKING:
4800 ret = get_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr );
4801 break;
4802 case SPI_SETACTIVEWINDOWTRACKING:
4803 ret = set_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr, winini );
4804 break;
4805 case SPI_GETMENUANIMATION:
4806 ret = get_entry( &entry_MENUANIMATION, val, ptr );
4807 break;
4808 case SPI_SETMENUANIMATION:
4809 ret = set_entry( &entry_MENUANIMATION, val, ptr, winini );
4810 break;
4811 case SPI_GETCOMBOBOXANIMATION:
4812 ret = get_entry( &entry_COMBOBOXANIMATION, val, ptr );
4813 break;
4814 case SPI_SETCOMBOBOXANIMATION:
4815 ret = set_entry( &entry_COMBOBOXANIMATION, val, ptr, winini );
4816 break;
4817 case SPI_GETLISTBOXSMOOTHSCROLLING:
4818 ret = get_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr );
4819 break;
4820 case SPI_SETLISTBOXSMOOTHSCROLLING:
4821 ret = set_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr, winini );
4822 break;
4823 case SPI_GETGRADIENTCAPTIONS:
4824 ret = get_entry( &entry_GRADIENTCAPTIONS, val, ptr );
4825 break;
4826 case SPI_SETGRADIENTCAPTIONS:
4827 ret = set_entry( &entry_GRADIENTCAPTIONS, val, ptr, winini );
4828 break;
4829 case SPI_GETKEYBOARDCUES:
4830 ret = get_entry( &entry_KEYBOARDCUES, val, ptr );
4831 break;
4832 case SPI_SETKEYBOARDCUES:
4833 ret = set_entry( &entry_KEYBOARDCUES, val, ptr, winini );
4834 break;
4835 case SPI_GETACTIVEWNDTRKZORDER:
4836 ret = get_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr );
4837 break;
4838 case SPI_SETACTIVEWNDTRKZORDER:
4839 ret = set_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr, winini );
4840 break;
4841 case SPI_GETHOTTRACKING:
4842 ret = get_entry( &entry_HOTTRACKING, val, ptr );
4843 break;
4844 case SPI_SETHOTTRACKING:
4845 ret = set_entry( &entry_HOTTRACKING, val, ptr, winini );
4846 break;
4847 case SPI_GETMENUFADE:
4848 ret = get_entry( &entry_MENUFADE, val, ptr );
4849 break;
4850 case SPI_SETMENUFADE:
4851 ret = set_entry( &entry_MENUFADE, val, ptr, winini );
4852 break;
4853 case SPI_GETSELECTIONFADE:
4854 ret = get_entry( &entry_SELECTIONFADE, val, ptr );
4855 break;
4856 case SPI_SETSELECTIONFADE:
4857 ret = set_entry( &entry_SELECTIONFADE, val, ptr, winini );
4858 break;
4859 case SPI_GETTOOLTIPANIMATION:
4860 ret = get_entry( &entry_TOOLTIPANIMATION, val, ptr );
4861 break;
4862 case SPI_SETTOOLTIPANIMATION:
4863 ret = set_entry( &entry_TOOLTIPANIMATION, val, ptr, winini );
4864 break;
4865 case SPI_GETTOOLTIPFADE:
4866 ret = get_entry( &entry_TOOLTIPFADE, val, ptr );
4867 break;
4868 case SPI_SETTOOLTIPFADE:
4869 ret = set_entry( &entry_TOOLTIPFADE, val, ptr, winini );
4870 break;
4871 case SPI_GETCURSORSHADOW:
4872 ret = get_entry( &entry_CURSORSHADOW, val, ptr );
4873 break;
4874 case SPI_SETCURSORSHADOW:
4875 ret = set_entry( &entry_CURSORSHADOW, val, ptr, winini );
4876 break;
4877 case SPI_GETMOUSESONAR:
4878 ret = get_entry( &entry_MOUSESONAR, val, ptr );
4879 break;
4880 case SPI_SETMOUSESONAR:
4881 ret = set_entry( &entry_MOUSESONAR, val, ptr, winini );
4882 break;
4883 case SPI_GETMOUSECLICKLOCK:
4884 ret = get_entry( &entry_MOUSECLICKLOCK, val, ptr );
4885 break;
4886 case SPI_SETMOUSECLICKLOCK:
4887 ret = set_entry( &entry_MOUSECLICKLOCK, val, ptr, winini );
4888 break;
4889 case SPI_GETMOUSEVANISH:
4890 ret = get_entry( &entry_MOUSEVANISH, val, ptr );
4891 break;
4892 case SPI_SETMOUSEVANISH:
4893 ret = set_entry( &entry_MOUSEVANISH, val, ptr, winini );
4894 break;
4895 case SPI_GETFLATMENU:
4896 ret = get_entry( &entry_FLATMENU, val, ptr );
4897 break;
4898 case SPI_SETFLATMENU:
4899 ret = set_entry( &entry_FLATMENU, val, ptr, winini );
4900 break;
4901 case SPI_GETDROPSHADOW:
4902 ret = get_entry( &entry_DROPSHADOW, val, ptr );
4903 break;
4904 case SPI_SETDROPSHADOW:
4905 ret = set_entry( &entry_DROPSHADOW, val, ptr, winini );
4906 break;
4907 case SPI_GETBLOCKSENDINPUTRESETS:
4908 ret = get_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr );
4909 break;
4910 case SPI_SETBLOCKSENDINPUTRESETS:
4911 ret = set_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr, winini );
4912 break;
4913 case SPI_GETUIEFFECTS:
4914 ret = get_entry( &entry_UIEFFECTS, val, ptr );
4915 break;
4916 case SPI_SETUIEFFECTS:
4917 /* FIXME: this probably should mask other UI effect values when unset */
4918 ret = set_entry( &entry_UIEFFECTS, val, ptr, winini );
4919 break;
4920 case SPI_GETDISABLEOVERLAPPEDCONTENT:
4921 ret = get_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr );
4922 break;
4923 case SPI_SETDISABLEOVERLAPPEDCONTENT:
4924 ret = set_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr, winini );
4925 break;
4926 case SPI_GETCLIENTAREAANIMATION:
4927 ret = get_entry( &entry_CLIENTAREAANIMATION, val, ptr );
4928 break;
4929 case SPI_SETCLIENTAREAANIMATION:
4930 ret = set_entry( &entry_CLIENTAREAANIMATION, val, ptr, winini );
4931 break;
4932 case SPI_GETCLEARTYPE:
4933 ret = get_entry( &entry_CLEARTYPE, val, ptr );
4934 break;
4935 case SPI_SETCLEARTYPE:
4936 ret = set_entry( &entry_CLEARTYPE, val, ptr, winini );
4937 break;
4938 case SPI_GETSPEECHRECOGNITION:
4939 ret = get_entry( &entry_SPEECHRECOGNITION, val, ptr );
4940 break;
4941 case SPI_SETSPEECHRECOGNITION:
4942 ret = set_entry( &entry_SPEECHRECOGNITION, val, ptr, winini );
4943 break;
4944 case SPI_GETFOREGROUNDLOCKTIMEOUT:
4945 ret = get_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr );
4946 break;
4947 case SPI_SETFOREGROUNDLOCKTIMEOUT:
4948 /* FIXME: this should check that the calling thread
4949 * is able to change the foreground window */
4950 ret = set_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr, winini );
4951 break;
4952 case SPI_GETACTIVEWNDTRKTIMEOUT:
4953 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
4954 break;
4955 case SPI_SETACTIVEWNDTRKTIMEOUT:
4956 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
4957 break;
4958 case SPI_GETFOREGROUNDFLASHCOUNT:
4959 ret = get_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr );
4960 break;
4961 case SPI_SETFOREGROUNDFLASHCOUNT:
4962 ret = set_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr, winini );
4963 break;
4964 case SPI_GETCARETWIDTH:
4965 ret = get_entry( &entry_CARETWIDTH, val, ptr );
4966 break;
4967 case SPI_SETCARETWIDTH:
4968 ret = set_entry( &entry_CARETWIDTH, val, ptr, winini );
4969 break;
4970 case SPI_GETMOUSECLICKLOCKTIME:
4971 ret = get_entry( &entry_MOUSECLICKLOCKTIME, val, ptr );
4972 break;
4973 case SPI_SETMOUSECLICKLOCKTIME:
4974 ret = set_entry( &entry_MOUSECLICKLOCKTIME, val, ptr, winini );
4975 break;
4976 case SPI_GETFONTSMOOTHINGTYPE:
4977 ret = get_entry( &entry_FONTSMOOTHINGTYPE, val, ptr );
4978 break;
4979 case SPI_SETFONTSMOOTHINGTYPE:
4980 ret = set_entry( &entry_FONTSMOOTHINGTYPE, val, ptr, winini );
4981 break;
4982 case SPI_GETFONTSMOOTHINGCONTRAST:
4983 ret = get_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr );
4984 break;
4985 case SPI_SETFONTSMOOTHINGCONTRAST:
4986 ret = set_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr, winini );
4987 break;
4988 case SPI_GETFOCUSBORDERWIDTH:
4989 ret = get_entry( &entry_FOCUSBORDERWIDTH, val, ptr );
4990 break;
4991 case SPI_GETFOCUSBORDERHEIGHT:
4992 ret = get_entry( &entry_FOCUSBORDERHEIGHT, val, ptr );
4993 break;
4994 case SPI_SETFOCUSBORDERWIDTH:
4995 ret = set_entry( &entry_FOCUSBORDERWIDTH, val, ptr, winini );
4996 break;
4997 case SPI_SETFOCUSBORDERHEIGHT:
4998 ret = set_entry( &entry_FOCUSBORDERHEIGHT, val, ptr, winini );
4999 break;
5000 case SPI_GETFONTSMOOTHINGORIENTATION:
5001 ret = get_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr );
5002 break;
5003 case SPI_SETFONTSMOOTHINGORIENTATION:
5004 ret = set_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr, winini );
5005 break;
5006 case SPI_GETAUDIODESCRIPTION:
5008 AUDIODESCRIPTION *audio = ptr;
5009 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
5011 ret = get_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled ) &&
5012 get_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale );
5014 break;
5016 case SPI_SETAUDIODESCRIPTION:
5018 AUDIODESCRIPTION *audio = ptr;
5019 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
5021 ret = set_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled, winini) &&
5022 set_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale, winini );
5024 break;
5026 default:
5027 FIXME( "Unknown action: %u\n", action );
5028 RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE );
5029 ret = FALSE;
5030 break;
5033 if (ret && (winini & SPIF_UPDATEINIFILE))
5035 static const WCHAR emptyW[1];
5036 if (winini & (SPIF_SENDWININICHANGE | SPIF_SENDCHANGE))
5037 send_message_timeout( HWND_BROADCAST, WM_SETTINGCHANGE, action, (LPARAM) emptyW,
5038 SMTO_ABORTIFHUNG, 2000, FALSE );
5040 TRACE( "(%u, %u, %p, %u) ret %d\n", action, val, ptr, winini, ret );
5041 return ret;
5043 #undef WINE_SPI_FIXME
5044 #undef WINE_SPI_WARN
5047 int get_system_metrics( int index )
5049 NONCLIENTMETRICSW ncm;
5050 MINIMIZEDMETRICS mm;
5051 ICONMETRICSW im;
5052 RECT rect;
5053 UINT ret;
5054 HDC hdc;
5056 /* some metrics are dynamic */
5057 switch (index)
5059 case SM_CXVSCROLL:
5060 case SM_CYHSCROLL:
5061 get_entry( &entry_SCROLLWIDTH, 0, &ret );
5062 return max( ret, 8 );
5063 case SM_CYCAPTION:
5064 ncm.cbSize = sizeof(ncm);
5065 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5066 return ncm.iCaptionHeight + 1;
5067 case SM_CXBORDER:
5068 case SM_CYBORDER:
5069 /* SM_C{X,Y}BORDER always returns 1 regardless of 'BorderWidth' value in registry */
5070 return 1;
5071 case SM_CXDLGFRAME:
5072 case SM_CYDLGFRAME:
5073 return 3;
5074 case SM_CYVTHUMB:
5075 case SM_CXHTHUMB:
5076 case SM_CYVSCROLL:
5077 case SM_CXHSCROLL:
5078 get_entry( &entry_SCROLLHEIGHT, 0, &ret );
5079 return max( ret, 8 );
5080 case SM_CXICON:
5081 case SM_CYICON:
5082 return map_to_dpi( 32, get_system_dpi() );
5083 case SM_CXCURSOR:
5084 case SM_CYCURSOR:
5085 ret = map_to_dpi( 32, get_system_dpi() );
5086 if (ret >= 64) return 64;
5087 if (ret >= 48) return 48;
5088 return 32;
5089 case SM_CYMENU:
5090 ncm.cbSize = sizeof(ncm);
5091 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5092 return ncm.iMenuHeight + 1;
5093 case SM_CXFULLSCREEN:
5094 /* see the remark for SM_CXMAXIMIZED, at least this formulation is correct */
5095 return get_system_metrics( SM_CXMAXIMIZED ) - 2 * get_system_metrics( SM_CXFRAME );
5096 case SM_CYFULLSCREEN:
5097 /* see the remark for SM_CYMAXIMIZED, at least this formulation is
5098 * correct */
5099 return get_system_metrics( SM_CYMAXIMIZED ) - get_system_metrics( SM_CYMIN );
5100 case SM_CYKANJIWINDOW:
5101 return 0;
5102 case SM_MOUSEPRESENT:
5103 return 1;
5104 case SM_DEBUG:
5105 return 0;
5106 case SM_SWAPBUTTON:
5107 get_entry( &entry_MOUSEBUTTONSWAP, 0, &ret );
5108 return ret;
5109 case SM_RESERVED1:
5110 case SM_RESERVED2:
5111 case SM_RESERVED3:
5112 case SM_RESERVED4:
5113 return 0;
5114 case SM_CXMIN:
5115 ncm.cbSize = sizeof(ncm);
5116 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5117 hdc = get_display_dc();
5118 get_text_metr_size( hdc, &ncm.lfCaptionFont, NULL, &ret );
5119 release_display_dc( hdc );
5120 return 3 * ncm.iCaptionWidth + ncm.iCaptionHeight + 4 * ret +
5121 2 * get_system_metrics( SM_CXFRAME ) + 4;
5122 case SM_CYMIN:
5123 return get_system_metrics( SM_CYCAPTION ) + 2 * get_system_metrics( SM_CYFRAME );
5124 case SM_CXSIZE:
5125 get_entry( &entry_CAPTIONWIDTH, 0, &ret );
5126 return max( ret, 8 );
5127 case SM_CYSIZE:
5128 ncm.cbSize = sizeof(ncm);
5129 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5130 return ncm.iCaptionHeight;
5131 case SM_CXFRAME:
5132 get_entry( &entry_BORDER, 0, &ret );
5133 ret = max( ret, 1 );
5134 return get_system_metrics( SM_CXDLGFRAME ) + ret;
5135 case SM_CYFRAME:
5136 get_entry( &entry_BORDER, 0, &ret );
5137 ret = max( ret, 1 );
5138 return get_system_metrics( SM_CYDLGFRAME ) + ret;
5139 case SM_CXMINTRACK:
5140 return get_system_metrics( SM_CXMIN );
5141 case SM_CYMINTRACK:
5142 return get_system_metrics( SM_CYMIN );
5143 case SM_CXDOUBLECLK:
5144 get_entry( &entry_DOUBLECLKWIDTH, 0, &ret );
5145 return ret;
5146 case SM_CYDOUBLECLK:
5147 get_entry( &entry_DOUBLECLKHEIGHT, 0, &ret );
5148 return ret;
5149 case SM_CXICONSPACING:
5150 im.cbSize = sizeof(im);
5151 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
5152 return im.iHorzSpacing;
5153 case SM_CYICONSPACING:
5154 im.cbSize = sizeof(im);
5155 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
5156 return im.iVertSpacing;
5157 case SM_MENUDROPALIGNMENT:
5158 NtUserSystemParametersInfo( SPI_GETMENUDROPALIGNMENT, 0, &ret, 0 );
5159 return ret;
5160 case SM_PENWINDOWS:
5161 return 0;
5162 case SM_DBCSENABLED:
5163 return ansi_cp.MaximumCharacterSize > 1;
5164 case SM_CMOUSEBUTTONS:
5165 return 3;
5166 case SM_SECURE:
5167 return 0;
5168 case SM_CXEDGE:
5169 return get_system_metrics( SM_CXBORDER ) + 1;
5170 case SM_CYEDGE:
5171 return get_system_metrics( SM_CYBORDER ) + 1;
5172 case SM_CXMINSPACING:
5173 mm.cbSize = sizeof(mm);
5174 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5175 return get_system_metrics( SM_CXMINIMIZED ) + mm.iHorzGap;
5176 case SM_CYMINSPACING:
5177 mm.cbSize = sizeof(mm);
5178 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5179 return get_system_metrics( SM_CYMINIMIZED ) + mm.iVertGap;
5180 case SM_CXSMICON:
5181 case SM_CYSMICON:
5182 return map_to_dpi( 16, get_system_dpi() ) & ~1;
5183 case SM_CYSMCAPTION:
5184 ncm.cbSize = sizeof(ncm);
5185 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5186 return ncm.iSmCaptionHeight + 1;
5187 case SM_CXSMSIZE:
5188 get_entry( &entry_SMCAPTIONWIDTH, 0, &ret );
5189 return ret;
5190 case SM_CYSMSIZE:
5191 ncm.cbSize = sizeof(ncm);
5192 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5193 return ncm.iSmCaptionHeight;
5194 case SM_CXMENUSIZE:
5195 get_entry( &entry_MENUWIDTH, 0, &ret );
5196 return ret;
5197 case SM_CYMENUSIZE:
5198 ncm.cbSize = sizeof(ncm);
5199 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5200 return ncm.iMenuHeight;
5201 case SM_ARRANGE:
5202 mm.cbSize = sizeof(mm);
5203 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5204 return mm.iArrange;
5205 case SM_CXMINIMIZED:
5206 mm.cbSize = sizeof(mm);
5207 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5208 return mm.iWidth + 6;
5209 case SM_CYMINIMIZED:
5210 ncm.cbSize = sizeof(ncm);
5211 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5212 return ncm.iCaptionHeight + 6;
5213 case SM_CXMAXTRACK:
5214 return get_system_metrics( SM_CXVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CXFRAME );
5215 case SM_CYMAXTRACK:
5216 return get_system_metrics( SM_CYVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CYFRAME );
5217 case SM_CXMAXIMIZED:
5218 /* FIXME: subtract the width of any vertical application toolbars*/
5219 return get_system_metrics( SM_CXSCREEN ) + 2 * get_system_metrics( SM_CXFRAME );
5220 case SM_CYMAXIMIZED:
5221 /* FIXME: subtract the width of any horizontal application toolbars*/
5222 return get_system_metrics( SM_CYSCREEN ) + 2 * get_system_metrics( SM_CYCAPTION );
5223 case SM_NETWORK:
5224 return 3; /* FIXME */
5225 case SM_CLEANBOOT:
5226 return 0; /* 0 = ok, 1 = failsafe, 2 = failsafe + network */
5227 case SM_CXDRAG:
5228 get_entry( &entry_DRAGWIDTH, 0, &ret );
5229 return ret;
5230 case SM_CYDRAG:
5231 get_entry( &entry_DRAGHEIGHT, 0, &ret );
5232 return ret;
5233 case SM_SHOWSOUNDS:
5234 get_entry( &entry_SHOWSOUNDS, 0, &ret );
5235 return ret;
5236 case SM_CXMENUCHECK:
5237 case SM_CYMENUCHECK:
5239 TEXTMETRICW tm;
5240 ncm.cbSize = sizeof(ncm);
5241 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5242 hdc = get_display_dc();
5243 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL );
5244 release_display_dc( hdc );
5245 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading + 1) / 2) * 2 - 1;
5247 case SM_SLOWMACHINE:
5248 return 0; /* Never true */
5249 case SM_MIDEASTENABLED:
5250 return 0; /* FIXME */
5251 case SM_MOUSEWHEELPRESENT:
5252 return 1;
5253 case SM_CXSCREEN:
5254 rect = get_primary_monitor_rect( get_thread_dpi() );
5255 return rect.right - rect.left;
5256 case SM_CYSCREEN:
5257 rect = get_primary_monitor_rect( get_thread_dpi() );
5258 return rect.bottom - rect.top;
5259 case SM_XVIRTUALSCREEN:
5260 rect = get_virtual_screen_rect( get_thread_dpi() );
5261 return rect.left;
5262 case SM_YVIRTUALSCREEN:
5263 rect = get_virtual_screen_rect( get_thread_dpi() );
5264 return rect.top;
5265 case SM_CXVIRTUALSCREEN:
5266 rect = get_virtual_screen_rect( get_thread_dpi() );
5267 return rect.right - rect.left;
5268 case SM_CYVIRTUALSCREEN:
5269 rect = get_virtual_screen_rect( get_thread_dpi() );
5270 return rect.bottom - rect.top;
5271 case SM_CMONITORS:
5272 if (!lock_display_devices()) return FALSE;
5273 ret = active_monitor_count();
5274 unlock_display_devices();
5275 return ret;
5276 case SM_SAMEDISPLAYFORMAT:
5277 return 1;
5278 case SM_IMMENABLED:
5279 return 0; /* FIXME */
5280 case SM_CXFOCUSBORDER:
5281 case SM_CYFOCUSBORDER:
5282 return 1;
5283 case SM_TABLETPC:
5284 case SM_MEDIACENTER:
5285 return 0;
5286 case SM_CMETRICS:
5287 return SM_CMETRICS;
5288 default:
5289 return 0;
5293 static int get_system_metrics_for_dpi( int index, unsigned int dpi )
5295 NONCLIENTMETRICSW ncm;
5296 ICONMETRICSW im;
5297 UINT ret;
5298 HDC hdc;
5300 /* some metrics are dynamic */
5301 switch (index)
5303 case SM_CXVSCROLL:
5304 case SM_CYHSCROLL:
5305 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ret, dpi );
5306 return max( ret, 8 );
5307 case SM_CYCAPTION:
5308 ncm.cbSize = sizeof(ncm);
5309 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5310 return ncm.iCaptionHeight + 1;
5311 case SM_CYVTHUMB:
5312 case SM_CXHTHUMB:
5313 case SM_CYVSCROLL:
5314 case SM_CXHSCROLL:
5315 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ret, dpi );
5316 return max( ret, 8 );
5317 case SM_CXICON:
5318 case SM_CYICON:
5319 return map_to_dpi( 32, dpi );
5320 case SM_CXCURSOR:
5321 case SM_CYCURSOR:
5322 ret = map_to_dpi( 32, dpi );
5323 if (ret >= 64) return 64;
5324 if (ret >= 48) return 48;
5325 return 32;
5326 case SM_CYMENU:
5327 ncm.cbSize = sizeof(ncm);
5328 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5329 return ncm.iMenuHeight + 1;
5330 case SM_CXSIZE:
5331 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ret, dpi );
5332 return max( ret, 8 );
5333 case SM_CYSIZE:
5334 ncm.cbSize = sizeof(ncm);
5335 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5336 return ncm.iCaptionHeight;
5337 case SM_CXFRAME:
5338 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
5339 ret = max( ret, 1 );
5340 return get_system_metrics_for_dpi( SM_CXDLGFRAME, dpi ) + ret;
5341 case SM_CYFRAME:
5342 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
5343 ret = max( ret, 1 );
5344 return get_system_metrics_for_dpi( SM_CYDLGFRAME, dpi ) + ret;
5345 case SM_CXICONSPACING:
5346 im.cbSize = sizeof(im);
5347 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
5348 return im.iHorzSpacing;
5349 case SM_CYICONSPACING:
5350 im.cbSize = sizeof(im);
5351 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
5352 return im.iVertSpacing;
5353 case SM_CXSMICON:
5354 case SM_CYSMICON:
5355 return map_to_dpi( 16, dpi ) & ~1;
5356 case SM_CYSMCAPTION:
5357 ncm.cbSize = sizeof(ncm);
5358 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5359 return ncm.iSmCaptionHeight + 1;
5360 case SM_CXSMSIZE:
5361 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ret, dpi );
5362 return ret;
5363 case SM_CYSMSIZE:
5364 ncm.cbSize = sizeof(ncm);
5365 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5366 return ncm.iSmCaptionHeight;
5367 case SM_CXMENUSIZE:
5368 get_entry_dpi( &entry_MENUWIDTH, 0, &ret, dpi );
5369 return ret;
5370 case SM_CYMENUSIZE:
5371 ncm.cbSize = sizeof(ncm);
5372 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5373 return ncm.iMenuHeight;
5374 case SM_CXMENUCHECK:
5375 case SM_CYMENUCHECK:
5377 TEXTMETRICW tm;
5378 ncm.cbSize = sizeof(ncm);
5379 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5380 hdc = get_display_dc();
5381 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL);
5382 release_display_dc( hdc );
5383 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading - 1) | 1);
5385 default:
5386 return get_system_metrics( index );
5390 COLORREF get_sys_color( int index )
5392 COLORREF ret = 0;
5394 if (index >= 0 && index < ARRAY_SIZE( system_colors ))
5395 get_entry( &system_colors[index], 0, &ret );
5396 return ret;
5399 HBRUSH get_55aa_brush(void)
5401 static const WORD pattern[] = { 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa };
5402 static HBRUSH brush_55aa;
5404 if (!brush_55aa)
5406 HBITMAP bitmap = NtGdiCreateBitmap( 8, 8, 1, 1, pattern );
5407 HBRUSH brush = NtGdiCreatePatternBrushInternal( bitmap, FALSE, FALSE );
5408 NtGdiDeleteObjectApp( bitmap );
5409 make_gdi_object_system( brush, TRUE );
5410 if (InterlockedCompareExchangePointer( (void **)&brush_55aa, brush, 0 ))
5412 make_gdi_object_system( brush, FALSE );
5413 NtGdiDeleteObjectApp( brush );
5416 return brush_55aa;
5419 HBRUSH get_sys_color_brush( unsigned int index )
5421 if (index == COLOR_55AA_BRUSH) return get_55aa_brush();
5422 if (index >= ARRAY_SIZE( system_colors )) return 0;
5424 if (!system_colors[index].brush)
5426 HBRUSH brush = NtGdiCreateSolidBrush( get_sys_color( index ), NULL );
5427 make_gdi_object_system( brush, TRUE );
5428 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].brush, brush, 0 ))
5430 make_gdi_object_system( brush, FALSE );
5431 NtGdiDeleteObjectApp( brush );
5434 return system_colors[index].brush;
5437 HPEN get_sys_color_pen( unsigned int index )
5439 if (index >= ARRAY_SIZE( system_colors )) return 0;
5441 if (!system_colors[index].pen)
5443 HPEN pen = NtGdiCreatePen( PS_SOLID, 1, get_sys_color( index ), NULL );
5444 make_gdi_object_system( pen, TRUE );
5445 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].pen, pen, 0 ))
5447 make_gdi_object_system( pen, FALSE );
5448 NtGdiDeleteObjectApp( pen );
5451 return system_colors[index].pen;
5454 /**********************************************************************
5455 * NtUserGetDoubleClickTime (win32u.@)
5457 UINT WINAPI NtUserGetDoubleClickTime(void)
5459 UINT time = 0;
5461 get_entry( &entry_DOUBLECLICKTIME, 0, &time );
5462 if (!time) time = 500;
5463 return time;
5466 /*************************************************************************
5467 * NtUserSetSysColors (win32u.@)
5469 BOOL WINAPI NtUserSetSysColors( INT count, const INT *colors, const COLORREF *values )
5471 int i;
5473 if (IS_INTRESOURCE(colors)) return FALSE; /* stupid app passes a color instead of an array */
5475 for (i = 0; i < count; i++)
5476 if (colors[i] >= 0 && colors[i] <= ARRAY_SIZE( system_colors ))
5477 set_entry( &system_colors[colors[i]], values[i], 0, 0 );
5479 /* Send WM_SYSCOLORCHANGE message to all windows */
5480 send_message_timeout( HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0,
5481 SMTO_ABORTIFHUNG, 2000, FALSE );
5482 /* Repaint affected portions of all visible windows */
5483 NtUserRedrawWindow( 0, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN );
5484 return TRUE;
5488 static LONG dpi_awareness;
5490 /***********************************************************************
5491 * NtUserSetProcessDpiAwarenessContext (win32u.@)
5493 BOOL WINAPI NtUserSetProcessDpiAwarenessContext( ULONG awareness, ULONG unknown )
5495 switch (awareness)
5497 case NTUSER_DPI_UNAWARE:
5498 case NTUSER_DPI_SYSTEM_AWARE:
5499 case NTUSER_DPI_PER_MONITOR_AWARE:
5500 case NTUSER_DPI_PER_MONITOR_AWARE_V2:
5501 case NTUSER_DPI_PER_UNAWARE_GDISCALED:
5502 break;
5503 default:
5504 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
5505 return FALSE;
5508 return !InterlockedCompareExchange( &dpi_awareness, awareness, 0 );
5511 /***********************************************************************
5512 * NtUserGetProcessDpiAwarenessContext (win32u.@)
5514 ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process )
5516 DPI_AWARENESS val;
5518 if (process && process != GetCurrentProcess())
5520 WARN( "not supported on other process %p\n", process );
5521 return NTUSER_DPI_UNAWARE;
5524 val = ReadNoFence( &dpi_awareness );
5525 if (!val) return NTUSER_DPI_UNAWARE;
5526 return val;
5529 BOOL message_beep( UINT i )
5531 BOOL active = TRUE;
5532 NtUserSystemParametersInfo( SPI_GETBEEP, 0, &active, FALSE );
5533 if (active) user_driver->pBeep();
5534 return TRUE;
5537 static DWORD exiting_thread_id;
5539 /**********************************************************************
5540 * is_exiting_thread
5542 BOOL is_exiting_thread( DWORD tid )
5544 return tid == exiting_thread_id;
5547 static void thread_detach(void)
5549 struct user_thread_info *thread_info = get_user_thread_info();
5551 user_driver->pThreadDetach();
5553 free( thread_info->key_state );
5554 thread_info->key_state = 0;
5555 free( thread_info->rawinput );
5557 destroy_thread_windows();
5558 cleanup_imm_thread();
5559 NtClose( thread_info->server_queue );
5561 exiting_thread_id = 0;
5564 /***********************************************************************
5565 * NtUserCallNoParam (win32u.@)
5567 ULONG_PTR WINAPI NtUserCallNoParam( ULONG code )
5569 switch(code)
5571 case NtUserCallNoParam_DestroyCaret:
5572 return destroy_caret();
5574 case NtUserCallNoParam_GetDesktopWindow:
5575 return HandleToUlong( get_desktop_window() );
5577 case NtUserCallNoParam_GetDialogBaseUnits:
5578 return get_dialog_base_units();
5580 case NtUserCallNoParam_GetInputState:
5581 return get_input_state();
5583 case NtUserCallNoParam_GetProcessDefaultLayout:
5584 return process_layout;
5586 case NtUserCallNoParam_GetProgmanWindow:
5587 return HandleToUlong( get_progman_window() );
5589 case NtUserCallNoParam_GetShellWindow:
5590 return HandleToUlong( get_shell_window() );
5592 case NtUserCallNoParam_GetTaskmanWindow:
5593 return HandleToUlong( get_taskman_window() );
5595 case NtUserCallNoParam_ReleaseCapture:
5596 return release_capture();
5598 /* temporary exports */
5599 case NtUserExitingThread:
5600 exiting_thread_id = GetCurrentThreadId();
5601 return 0;
5603 case NtUserThreadDetach:
5604 thread_detach();
5605 return 0;
5607 default:
5608 FIXME( "invalid code %u\n", (int)code );
5609 return 0;
5613 /***********************************************************************
5614 * NtUserCallOneParam (win32u.@)
5616 ULONG_PTR WINAPI NtUserCallOneParam( ULONG_PTR arg, ULONG code )
5618 switch(code)
5620 case NtUserCallOneParam_BeginDeferWindowPos:
5621 return HandleToUlong( begin_defer_window_pos( arg ));
5623 case NtUserCallOneParam_CreateCursorIcon:
5624 return HandleToUlong( alloc_cursoricon_handle( arg ));
5626 case NtUserCallOneParam_CreateMenu:
5627 return HandleToUlong( create_menu( arg ) );
5629 case NtUserCallOneParam_EnableDC:
5630 return set_dce_flags( UlongToHandle(arg), DCHF_ENABLEDC );
5632 case NtUserCallOneParam_EnableThunkLock:
5633 enable_thunk_lock = arg;
5634 return 0;
5636 case NtUserCallOneParam_EnumClipboardFormats:
5637 return enum_clipboard_formats( arg );
5639 case NtUserCallOneParam_GetClipCursor:
5640 return get_clip_cursor( (RECT *)arg );
5642 case NtUserCallOneParam_GetCursorPos:
5643 return get_cursor_pos( (POINT *)arg );
5645 case NtUserCallOneParam_GetIconParam:
5646 return get_icon_param( UlongToHandle(arg) );
5648 case NtUserCallOneParam_GetMenuItemCount:
5649 return get_menu_item_count( UlongToHandle(arg) );
5651 case NtUserCallOneParam_GetSysColor:
5652 return get_sys_color( arg );
5654 case NtUserCallOneParam_IsWindowRectFullScreen:
5655 return is_window_rect_full_screen( (const RECT *)arg );
5657 case NtUserCallOneParam_RealizePalette:
5658 return realize_palette( UlongToHandle(arg) );
5660 case NtUserCallOneParam_GetPrimaryMonitorRect:
5661 *(RECT *)arg = get_primary_monitor_rect( 0 );
5662 return 1;
5664 case NtUserCallOneParam_GetSysColorBrush:
5665 return HandleToUlong( get_sys_color_brush(arg) );
5667 case NtUserCallOneParam_GetSysColorPen:
5668 return HandleToUlong( get_sys_color_pen(arg) );
5670 case NtUserCallOneParam_GetSystemMetrics:
5671 return get_system_metrics( arg );
5673 case NtUserCallOneParam_GetVirtualScreenRect:
5674 *(RECT *)arg = get_virtual_screen_rect( 0 );
5675 return 1;
5677 case NtUserCallOneParam_MessageBeep:
5678 return message_beep( arg );
5680 case NtUserCallOneParam_ReplyMessage:
5681 return reply_message_result( arg );
5683 case NtUserCallOneParam_SetCaretBlinkTime:
5684 return set_caret_blink_time( arg );
5686 case NtUserCallOneParam_SetProcessDefaultLayout:
5687 process_layout = arg;
5688 return TRUE;
5690 /* temporary exports */
5691 case NtUserGetDeskPattern:
5692 return get_entry( &entry_DESKPATTERN, 256, (WCHAR *)arg );
5694 default:
5695 FIXME( "invalid code %u\n", (int)code );
5696 return 0;
5700 /***********************************************************************
5701 * NtUserCallTwoParam (win32u.@)
5703 ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code )
5705 switch(code)
5707 case NtUserCallTwoParam_GetDialogProc:
5708 return (ULONG_PTR)get_dialog_proc( (DLGPROC)arg1, arg2 );
5710 case NtUserCallTwoParam_GetMenuInfo:
5711 return get_menu_info( UlongToHandle(arg1), (MENUINFO *)arg2 );
5713 case NtUserCallTwoParam_GetMonitorInfo:
5714 return get_monitor_info( UlongToHandle(arg1), (MONITORINFO *)arg2 );
5716 case NtUserCallTwoParam_GetSystemMetricsForDpi:
5717 return get_system_metrics_for_dpi( arg1, arg2 );
5719 case NtUserCallTwoParam_MonitorFromRect:
5720 return HandleToUlong( monitor_from_rect( (const RECT *)arg1, arg2, get_thread_dpi() ));
5722 case NtUserCallTwoParam_SetCaretPos:
5723 return set_caret_pos( arg1, arg2 );
5725 case NtUserCallTwoParam_SetIconParam:
5726 return set_icon_param( UlongToHandle(arg1), arg2 );
5728 case NtUserCallTwoParam_UnhookWindowsHook:
5729 return unhook_windows_hook( arg1, (HOOKPROC)arg2 );
5731 /* temporary exports */
5732 case NtUserAllocWinProc:
5733 return (UINT_PTR)alloc_winproc( (WNDPROC)arg1, arg2 );
5735 default:
5736 FIXME( "invalid code %u\n", (int)code );
5737 return 0;
5741 /***********************************************************************
5742 * NtUserDisplayConfigGetDeviceInfo (win32u.@)
5744 NTSTATUS WINAPI NtUserDisplayConfigGetDeviceInfo( DISPLAYCONFIG_DEVICE_INFO_HEADER *packet )
5746 NTSTATUS ret = STATUS_UNSUCCESSFUL;
5748 TRACE( "packet %p.\n", packet );
5750 if (!packet || packet->size < sizeof(*packet))
5751 return STATUS_UNSUCCESSFUL;
5753 switch (packet->type)
5755 case DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME:
5757 DISPLAYCONFIG_SOURCE_DEVICE_NAME *source_name = (DISPLAYCONFIG_SOURCE_DEVICE_NAME *)packet;
5758 struct adapter *adapter;
5760 TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME.\n" );
5762 if (packet->size < sizeof(*source_name))
5763 return STATUS_INVALID_PARAMETER;
5765 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
5767 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
5769 if (source_name->header.id != adapter->id) continue;
5770 if (memcmp( &source_name->header.adapterId, &adapter->gpu_luid, sizeof(adapter->gpu_luid) )) continue;
5772 lstrcpyW( source_name->viewGdiDeviceName, adapter->dev.device_name );
5773 ret = STATUS_SUCCESS;
5774 break;
5777 unlock_display_devices();
5778 return ret;
5780 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME:
5782 DISPLAYCONFIG_TARGET_DEVICE_NAME *target_name = (DISPLAYCONFIG_TARGET_DEVICE_NAME *)packet;
5783 char buffer[ARRAY_SIZE(target_name->monitorFriendlyDeviceName)];
5784 struct monitor *monitor;
5786 TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME.\n" );
5788 if (packet->size < sizeof(*target_name))
5789 return STATUS_INVALID_PARAMETER;
5791 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
5793 memset( &target_name->flags, 0, sizeof(*target_name) - offsetof(DISPLAYCONFIG_TARGET_DEVICE_NAME, flags) );
5795 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
5797 if (target_name->header.id != monitor->output_id) continue;
5798 if (memcmp( &target_name->header.adapterId, &monitor->adapter->gpu_luid,
5799 sizeof(monitor->adapter->gpu_luid) ))
5800 continue;
5802 target_name->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL;
5803 snprintf( buffer, ARRAY_SIZE(buffer), "Display%u", monitor->output_id + 1 );
5804 asciiz_to_unicode( target_name->monitorFriendlyDeviceName, buffer );
5805 lstrcpyW( target_name->monitorDevicePath, monitor->dev.interface_name );
5806 if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
5808 target_name->edidManufactureId = monitor->edid_info.manufacturer;
5809 target_name->edidProductCodeId = monitor->edid_info.product_code;
5810 target_name->flags.edidIdsValid = 1;
5812 if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_NAME)
5814 wcscpy( target_name->monitorFriendlyDeviceName, monitor->edid_info.monitor_name );
5815 target_name->flags.friendlyNameFromEdid = 1;
5817 ret = STATUS_SUCCESS;
5818 break;
5821 unlock_display_devices();
5822 return ret;
5824 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE:
5826 DISPLAYCONFIG_TARGET_PREFERRED_MODE *preferred_mode = (DISPLAYCONFIG_TARGET_PREFERRED_MODE *)packet;
5828 FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE stub.\n" );
5830 if (packet->size < sizeof(*preferred_mode))
5831 return STATUS_INVALID_PARAMETER;
5833 return STATUS_NOT_SUPPORTED;
5835 case DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME:
5837 DISPLAYCONFIG_ADAPTER_NAME *adapter_name = (DISPLAYCONFIG_ADAPTER_NAME *)packet;
5839 FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME stub.\n" );
5841 if (packet->size < sizeof(*adapter_name))
5842 return STATUS_INVALID_PARAMETER;
5844 return STATUS_NOT_SUPPORTED;
5846 case DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE:
5847 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE:
5848 case DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION:
5849 case DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION:
5850 case DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO:
5851 case DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE:
5852 case DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL:
5853 default:
5854 FIXME( "Unimplemented packet type %u.\n", packet->type );
5855 return STATUS_INVALID_PARAMETER;