win32u: Don't report cloned monitors in EnumDisplayMonitors().
[wine.git] / dlls / win32u / sysparams.c
bloba5a69a84bd91204c66fc0a7a1ec133168ef9e530
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 devpropkey_device_ispresentW[] =
98 'P','r','o','p','e','r','t','i','e','s',
99 '\\','{','5','4','0','B','9','4','7','E','-','8','B','4','0','-','4','5','B','C',
100 '-','A','8','A','2','-','6','A','0','B','8','9','4','C','B','D','A','2','}',
101 '\\','0','0','0','5'
104 static const WCHAR devpropkey_monitor_gpu_luidW[] =
106 'P','r','o','p','e','r','t','i','e','s',
107 '\\','{','C','A','0','8','5','8','5','3','-','1','6','C','E','-','4','8','A','A',
108 '-','B','1','1','4','-','D','E','9','C','7','2','3','3','4','2','2','3','}',
109 '\\','0','0','0','1'
112 static const WCHAR devpropkey_monitor_output_idW[] =
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','2'
120 static const WCHAR wine_devpropkey_monitor_stateflagsW[] =
122 'P','r','o','p','e','r','t','i','e','s','\\',
123 '{','2','3','3','a','9','e','f','3','-','a','f','c','4','-','4','a','b','d',
124 '-','b','5','6','4','-','c','3','2','f','2','1','f','1','5','3','5','b','}',
125 '\\','0','0','0','2'
128 static const WCHAR wine_devpropkey_monitor_rcmonitorW[] =
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','3'
136 static const WCHAR wine_devpropkey_monitor_rcworkW[] =
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','4'
144 static const WCHAR wine_devpropkey_monitor_adapternameW[] =
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','5'
152 static const WCHAR device_instanceW[] = {'D','e','v','i','c','e','I','n','s','t','a','n','c','e',0};
153 static const WCHAR controlW[] = {'C','o','n','t','r','o','l'};
154 static const WCHAR device_parametersW[] =
155 {'D','e','v','i','c','e',' ','P','a','r','a','m','e','t','e','r','s'};
156 static const WCHAR linkedW[] = {'L','i','n','k','e','d',0};
157 static const WCHAR symbolic_link_valueW[] =
158 {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
159 static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0};
160 static const WCHAR gpu_idW[] = {'G','P','U','I','D',0};
161 static const WCHAR hardware_idW[] = {'H','a','r','d','w','a','r','e','I','D',0};
162 static const WCHAR device_descW[] = {'D','e','v','i','c','e','D','e','s','c',0};
163 static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0};
164 static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
165 static const WCHAR class_guidW[] = {'C','l','a','s','s','G','U','I','D',0};
166 static const WCHAR pciW[] = {'P','C','I'};
167 static const WCHAR classW[] = {'C','l','a','s','s',0};
168 static const WCHAR displayW[] = {'D','i','s','p','l','a','y',0};
169 static const WCHAR monitorW[] = {'M','o','n','i','t','o','r',0};
170 static const WCHAR yesW[] = {'Y','e','s',0};
171 static const WCHAR noW[] = {'N','o',0};
173 static const char guid_devclass_displayA[] = "{4D36E968-E325-11CE-BFC1-08002BE10318}";
174 static const WCHAR guid_devclass_displayW[] =
175 {'{','4','D','3','6','E','9','6','8','-','E','3','2','5','-','1','1','C','E','-',
176 'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0};
178 static const char guid_devclass_monitorA[] = "{4D36E96E-E325-11CE-BFC1-08002BE10318}";
179 static const WCHAR guid_devclass_monitorW[] =
180 {'{','4','D','3','6','E','9','6','E','-','E','3','2','5','-','1','1','C','E','-'
181 ,'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}'};
183 static const WCHAR guid_devinterface_display_adapterW[] =
184 {'{','5','B','4','5','2','0','1','D','-','F','2','F','2','-','4','F','3','B','-',
185 '8','5','B','B','-','3','0','F','F','1','F','9','5','3','5','9','9','}',0};
187 static const WCHAR guid_display_device_arrivalW[] =
188 {'{','1','C','A','0','5','1','8','0','-','A','6','9','9','-','4','5','0','A','-',
189 '9','A','0','C','-','D','E','4','F','B','E','3','D','D','D','8','9','}',0};
191 static const WCHAR guid_devinterface_monitorW[] =
192 {'{','E','6','F','0','7','B','5','F','-','E','E','9','7','-','4','A','9','0','-',
193 'B','0','7','6','-','3','3','F','5','7','B','F','4','E','A','A','7','}',0};
195 #define NULLDRV_DEFAULT_HMONITOR ((HMONITOR)(UINT_PTR)(0x10000 + 1))
197 /* Cached display device information */
198 struct display_device
200 WCHAR device_name[32]; /* DeviceName in DISPLAY_DEVICEW */
201 WCHAR device_string[128]; /* DeviceString in DISPLAY_DEVICEW */
202 DWORD state_flags; /* StateFlags in DISPLAY_DEVICEW */
203 WCHAR device_id[128]; /* DeviceID in DISPLAY_DEVICEW */
204 WCHAR interface_name[128]; /* DeviceID in DISPLAY_DEVICEW when EDD_GET_DEVICE_INTERFACE_NAME is set */
205 WCHAR device_key[128]; /* DeviceKey in DISPLAY_DEVICEW */
208 struct adapter
210 struct list entry;
211 struct display_device dev;
212 unsigned int id;
213 const WCHAR *config_key;
216 struct monitor
218 struct list entry;
219 struct display_device dev;
220 struct adapter *adapter;
221 HANDLE handle;
222 unsigned int id;
223 unsigned int flags;
224 RECT rc_monitor;
225 RECT rc_work;
226 BOOL is_clone;
229 static struct list adapters = LIST_INIT(adapters);
230 static struct list monitors = LIST_INIT(monitors);
231 static INT64 last_query_display_time;
232 static pthread_mutex_t display_lock = PTHREAD_MUTEX_INITIALIZER;
234 BOOL enable_thunk_lock = FALSE;
236 static struct monitor virtual_monitor =
238 .handle = NULLDRV_DEFAULT_HMONITOR,
239 .flags = MONITORINFOF_PRIMARY,
240 .rc_monitor.right = 1024,
241 .rc_monitor.bottom = 768,
242 .rc_work.right = 1024,
243 .rc_work.bottom = 768,
244 .dev.state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED,
247 /* the various registry keys that are used to store parameters */
248 enum parameter_key
250 COLORS_KEY,
251 DESKTOP_KEY,
252 KEYBOARD_KEY,
253 MOUSE_KEY,
254 METRICS_KEY,
255 SOUND_KEY,
256 VERSION_KEY,
257 SHOWSOUNDS_KEY,
258 KEYBOARDPREF_KEY,
259 SCREENREADER_KEY,
260 AUDIODESC_KEY,
261 NB_PARAM_KEYS
264 static const char *parameter_key_names[NB_PARAM_KEYS] =
266 "Control Panel\\Colors",
267 "Control Panel\\Desktop",
268 "Control Panel\\Keyboard",
269 "Control Panel\\Mouse",
270 "Control Panel\\Desktop\\WindowMetrics",
271 "Control Panel\\Sound",
272 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
273 "Control Panel\\Accessibility\\ShowSounds",
274 "Control Panel\\Accessibility\\Keyboard Preference",
275 "Control Panel\\Accessibility\\Blind Access",
276 "Control Panel\\Accessibility\\AudioDescription",
279 /* System parameters storage */
280 union sysparam_all_entry;
282 struct sysparam_entry
284 BOOL (*get)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi );
285 BOOL (*set)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags );
286 BOOL (*init)( union sysparam_all_entry *entry );
287 enum parameter_key base_key;
288 const char *regval;
289 enum parameter_key mirror_key;
290 const char *mirror;
291 BOOL loaded;
294 struct sysparam_uint_entry
296 struct sysparam_entry hdr;
297 UINT val;
300 struct sysparam_bool_entry
302 struct sysparam_entry hdr;
303 BOOL val;
306 struct sysparam_dword_entry
308 struct sysparam_entry hdr;
309 DWORD val;
312 struct sysparam_rgb_entry
314 struct sysparam_entry hdr;
315 COLORREF val;
316 HBRUSH brush;
317 HPEN pen;
320 struct sysparam_binary_entry
322 struct sysparam_entry hdr;
323 void *ptr;
324 size_t size;
327 struct sysparam_path_entry
329 struct sysparam_entry hdr;
330 WCHAR path[MAX_PATH];
333 struct sysparam_font_entry
335 struct sysparam_entry hdr;
336 UINT weight;
337 LOGFONTW val;
338 WCHAR fullname[LF_FACESIZE];
341 struct sysparam_pref_entry
343 struct sysparam_entry hdr;
344 struct sysparam_binary_entry *parent;
345 UINT offset;
346 UINT mask;
349 union sysparam_all_entry
351 struct sysparam_entry hdr;
352 struct sysparam_uint_entry uint;
353 struct sysparam_bool_entry bool;
354 struct sysparam_dword_entry dword;
355 struct sysparam_rgb_entry rgb;
356 struct sysparam_binary_entry bin;
357 struct sysparam_path_entry path;
358 struct sysparam_font_entry font;
359 struct sysparam_pref_entry pref;
362 static UINT system_dpi;
363 static RECT work_area;
364 DWORD process_layout = ~0u;
366 static HDC display_dc;
367 static pthread_mutex_t display_dc_lock = PTHREAD_MUTEX_INITIALIZER;
369 static pthread_mutex_t user_mutex;
370 static unsigned int user_lock_thread, user_lock_rec;
372 void user_lock(void)
374 pthread_mutex_lock( &user_mutex );
375 if (!user_lock_rec++) user_lock_thread = GetCurrentThreadId();
378 void user_unlock(void)
380 if (!--user_lock_rec) user_lock_thread = 0;
381 pthread_mutex_unlock( &user_mutex );
384 void user_check_not_lock(void)
386 if (user_lock_thread == GetCurrentThreadId())
388 ERR( "BUG: holding USER lock\n" );
389 assert( 0 );
393 static HANDLE get_display_device_init_mutex( void )
395 static const WCHAR display_device_initW[] =
396 {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s',
397 '\\','d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t'};
398 UNICODE_STRING name = { sizeof(display_device_initW), sizeof(display_device_initW),
399 (WCHAR *)display_device_initW };
400 OBJECT_ATTRIBUTES attr;
401 HANDLE mutex;
403 InitializeObjectAttributes( &attr, &name, OBJ_OPENIF, NULL, NULL );
404 if (NtCreateMutant( &mutex, MUTEX_ALL_ACCESS, &attr, FALSE ) < 0) return 0;
405 NtWaitForSingleObject( mutex, FALSE, NULL );
406 return mutex;
409 static void release_display_device_init_mutex( HANDLE mutex )
411 NtReleaseMutant( mutex, NULL );
412 NtClose( mutex );
415 static BOOL read_display_adapter_settings( unsigned int index, struct adapter *info )
417 char buffer[4096];
418 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
419 WCHAR *value_str = (WCHAR *)value->Data;
420 HKEY hkey;
421 DWORD size;
423 if (!enum_key && !(enum_key = reg_open_key( NULL, enum_keyW, sizeof(enum_keyW) )))
424 return FALSE;
426 /* Find adapter */
427 sprintf( buffer, "\\Device\\Video%d", index );
428 size = query_reg_ascii_value( video_key, buffer, value, sizeof(buffer) );
429 if (!size || value->Type != REG_SZ ||
430 value->DataLength <= sizeof("\\Registry\\Machine\\") * sizeof(WCHAR))
431 return FALSE;
433 /* DeviceKey */
434 memcpy( info->dev.device_key, value_str, value->DataLength );
435 info->config_key = info->dev.device_key + sizeof("\\Registry\\Machine\\") - 1;
437 if (!(hkey = reg_open_key( NULL, value_str, value->DataLength - sizeof(WCHAR) )))
438 return FALSE;
440 /* DeviceString */
441 if (query_reg_value( hkey, driver_descW, value, sizeof(buffer) ) && value->Type == REG_SZ)
442 memcpy( info->dev.device_string, value_str, value->DataLength );
443 NtClose( hkey );
445 /* DeviceName */
446 sprintf( buffer, "\\\\.\\DISPLAY%d", index + 1 );
447 asciiz_to_unicode( info->dev.device_name, buffer );
449 if (!(hkey = reg_open_key( config_key, info->config_key,
450 lstrlenW( info->config_key ) * sizeof(WCHAR) )))
451 return FALSE;
453 /* StateFlags */
454 if (query_reg_value( hkey, state_flagsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
455 info->dev.state_flags = *(const DWORD *)value->Data;
457 /* Interface name */
458 info->dev.interface_name[0] = 0;
460 /* DeviceID */
461 size = query_reg_value( hkey, gpu_idW, value, sizeof(buffer) );
462 NtClose( hkey );
463 if (!size || value->Type != REG_SZ) return FALSE;
465 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
466 return FALSE;
468 size = query_reg_value( hkey, hardware_idW, value, sizeof(buffer) );
469 NtClose( hkey );
470 if (!size || (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
471 return FALSE;
473 lstrcpyW( info->dev.device_id, value_str );
474 return TRUE;
477 static unsigned int query_reg_subkey_value( HKEY hkey, const WCHAR *name, unsigned int name_size,
478 KEY_VALUE_PARTIAL_INFORMATION *value, unsigned int size )
480 HKEY subkey;
482 if (!(subkey = reg_open_key( hkey, name, name_size ))) return 0;
483 size = query_reg_value( subkey, NULL, value, size );
484 NtClose( subkey );
485 return size;
488 static BOOL read_monitor_settings( struct adapter *adapter, DWORD index, struct monitor *monitor )
490 char buffer[4096];
491 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
492 WCHAR *device_name, *value_str = (WCHAR *)value->Data, *ptr;
493 HKEY hkey;
494 DWORD size, len;
496 monitor->flags = adapter->id ? 0 : MONITORINFOF_PRIMARY;
498 /* DeviceName */
499 sprintf( buffer, "\\\\.\\DISPLAY%d\\Monitor%d", adapter->id + 1, index );
500 asciiz_to_unicode( monitor->dev.device_name, buffer );
502 if (!(hkey = reg_open_key( config_key, adapter->config_key,
503 lstrlenW( adapter->config_key ) * sizeof(WCHAR) )))
504 return FALSE;
506 /* Interface name */
507 sprintf( buffer, "MonitorID%u", index );
508 size = query_reg_ascii_value( hkey, buffer, value, sizeof(buffer) );
509 NtClose( hkey );
510 if (!size || value->Type != REG_SZ) return FALSE;
511 len = asciiz_to_unicode( monitor->dev.interface_name, "\\\\\?\\" ) / sizeof(WCHAR) - 1;
512 memcpy( monitor->dev.interface_name + len, value_str, value->DataLength - sizeof(WCHAR) );
513 len += value->DataLength / sizeof(WCHAR) - 1;
514 monitor->dev.interface_name[len++] = '#';
515 memcpy( monitor->dev.interface_name + len, guid_devinterface_monitorW,
516 sizeof(guid_devinterface_monitorW) );
518 /* Replace '\\' with '#' after prefix */
519 for (ptr = monitor->dev.interface_name + ARRAYSIZE("\\\\\?\\") - 1; *ptr; ptr++)
520 if (*ptr == '\\') *ptr = '#';
522 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
523 return FALSE;
525 /* StateFlags, WINE_DEVPROPKEY_MONITOR_STATEFLAGS */
526 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_stateflagsW,
527 sizeof(wine_devpropkey_monitor_stateflagsW),
528 value, sizeof(buffer) );
529 if (size != sizeof(monitor->dev.state_flags))
531 NtClose( hkey );
532 return FALSE;
534 monitor->dev.state_flags = *(const DWORD *)value->Data;
536 /* rc_monitor, WINE_DEVPROPKEY_MONITOR_RCMONITOR */
537 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcmonitorW,
538 sizeof(wine_devpropkey_monitor_rcmonitorW),
539 value, sizeof(buffer) );
540 if (size != sizeof(monitor->rc_monitor))
542 NtClose( hkey );
543 return FALSE;
545 monitor->rc_monitor = *(const RECT *)value->Data;
547 /* rc_work, WINE_DEVPROPKEY_MONITOR_RCWORK */
548 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcworkW,
549 sizeof(wine_devpropkey_monitor_rcworkW),
550 value, sizeof(buffer) );
551 if (size != sizeof(monitor->rc_work))
553 NtClose( hkey );
554 return FALSE;
556 monitor->rc_work = *(const RECT *)value->Data;
558 /* DeviceString */
559 if (!query_reg_value( hkey, device_descW, value, sizeof(buffer) ) || value->Type != REG_SZ)
561 NtClose( hkey );
562 return FALSE;
564 memcpy( monitor->dev.device_string, value->Data, value->DataLength );
566 /* DeviceKey */
567 if (!query_reg_value( hkey, driverW, value, sizeof(buffer) ) || value->Type != REG_SZ)
569 NtClose( hkey );
570 return FALSE;
572 size = asciiz_to_unicode( monitor->dev.device_key,
573 "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\" );
574 device_name = &monitor->dev.device_key[size / sizeof(WCHAR) - 1];
575 memcpy( device_name, value_str, value->DataLength );
577 /* DeviceID */
578 if (!query_reg_value( hkey, hardware_idW, value, sizeof(buffer) ) ||
579 (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
581 NtClose( hkey );
582 return FALSE;
584 size = lstrlenW( value_str );
585 memcpy( monitor->dev.device_id, value_str, size * sizeof(WCHAR) );
586 monitor->dev.device_id[size++] = '\\';
587 lstrcpyW( monitor->dev.device_id + size, device_name );
589 NtClose( hkey );
590 return TRUE;
593 static void reg_empty_key( HKEY root, const char *key_name )
595 char buffer[4096];
596 KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer;
597 KEY_VALUE_FULL_INFORMATION *value = (KEY_VALUE_FULL_INFORMATION *)buffer;
598 WCHAR bufferW[512];
599 DWORD size;
600 HKEY hkey;
602 if (key_name)
603 hkey = reg_open_key( root, bufferW, asciiz_to_unicode( bufferW, key_name ) - sizeof(WCHAR) );
604 else
605 hkey = root;
607 while (!NtEnumerateKey( hkey, 0, KeyNodeInformation, key, sizeof(buffer), &size ))
608 reg_delete_tree( hkey, key->Name, key->NameLength );
610 while (!NtEnumerateValueKey( hkey, 0, KeyValueFullInformation, value, sizeof(buffer), &size ))
612 UNICODE_STRING name = { value->NameLength, value->NameLength, value->Name };
613 NtDeleteValueKey( hkey, &name );
616 if (hkey != root) NtClose( hkey );
619 static void prepare_devices(void)
621 char buffer[4096];
622 KEY_NODE_INFORMATION *key = (void *)buffer;
623 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
624 WCHAR *value_str = (WCHAR *)value->Data;
625 WCHAR bufferW[128];
626 unsigned i = 0;
627 DWORD size;
628 HKEY hkey, subkey, device_key, prop_key;
630 if (!enum_key) enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL );
631 if (!control_key) control_key = reg_create_key( NULL, control_keyW, sizeof(control_keyW), 0, NULL );
632 if (!video_key) video_key = reg_create_key( NULL, devicemap_video_keyW, sizeof(devicemap_video_keyW),
633 REG_OPTION_VOLATILE, NULL );
635 /* delete monitors */
636 reg_empty_key( enum_key, "DISPLAY\\DEFAULT_MONITOR" );
637 sprintf( buffer, "Class\\%s", guid_devclass_monitorA );
638 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
639 0, NULL );
640 reg_empty_key( hkey, NULL );
641 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
642 NtClose( hkey );
644 /* delete adapters */
645 reg_empty_key( video_key, NULL );
647 /* clean GPUs */
648 sprintf( buffer, "Class\\%s", guid_devclass_displayA );
649 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
650 0, NULL );
651 reg_empty_key( hkey, NULL );
652 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
653 NtClose( hkey );
655 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
657 /* To preserve GPU GUIDs, mark them as not present and delete them in cleanup_devices if needed. */
658 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
660 unsigned int j = 0;
662 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
664 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
666 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
667 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
668 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
670 NtClose( device_key );
671 continue;
674 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
675 if (size == sizeof(guid_devclass_displayW) &&
676 !wcscmp( (const WCHAR *)value->Data, guid_devclass_displayW ) &&
677 (prop_key = reg_create_key( device_key, devpropkey_device_ispresentW,
678 sizeof(devpropkey_device_ispresentW), 0, NULL )))
680 BOOL present = FALSE;
681 set_reg_value( prop_key, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
682 &present, sizeof(present) );
683 NtClose( prop_key );
686 NtClose( device_key );
689 NtClose( subkey );
692 NtClose( hkey );
695 static void cleanup_devices(void)
697 char buffer[4096];
698 KEY_NODE_INFORMATION *key = (void *)buffer;
699 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
700 WCHAR bufferW[512], *value_str = (WCHAR *)value->Data;
701 unsigned i = 0;
702 DWORD size;
703 HKEY hkey, subkey, device_key, prop_key;
705 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
707 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
709 unsigned int j = 0;
711 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
713 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
715 BOOL present = FALSE;
717 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
718 memcpy( bufferW, key->Name, key->NameLength );
719 bufferW[key->NameLength / sizeof(WCHAR)] = 0;
721 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
722 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
724 NtClose( device_key );
725 continue;
728 if ((prop_key = reg_open_key( device_key, devpropkey_device_ispresentW,
729 sizeof(devpropkey_device_ispresentW) )))
731 if (query_reg_value( prop_key, NULL, value, sizeof(buffer) ) == sizeof(BOOL))
732 present = *(const BOOL *)value->Data;
733 NtClose( prop_key );
736 NtClose( device_key );
738 if (!present && reg_delete_tree( subkey, bufferW, lstrlenW( bufferW ) * sizeof(WCHAR) ))
739 j = 0;
742 NtClose( subkey );
745 NtClose( hkey );
748 /* see UuidCreate */
749 static void uuid_create( GUID *uuid )
751 char buf[4096];
752 NtQuerySystemInformation( SystemInterruptInformation, buf, sizeof(buf), NULL );
753 memcpy( uuid, buf, sizeof(*uuid) );
754 uuid->Data3 &= 0x0fff;
755 uuid->Data3 |= (4 << 12);
756 uuid->Data4[0] &= 0x3f;
757 uuid->Data4[0] |= 0x80;
760 #define TICKSPERSEC 10000000
761 #define SECSPERDAY 86400
762 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
763 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
765 static unsigned int format_date( WCHAR *bufferW, LONGLONG time )
767 int cleaps, years, yearday, months, days;
768 unsigned int day, month, year;
769 char buffer[32];
771 days = time / TICKSPERSEC / SECSPERDAY;
773 /* compute year, month and day of month, see RtlTimeToTimeFields */
774 cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
775 days += 28188 + cleaps;
776 years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
777 yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
778 months = (64 * yearday) / 1959;
779 if (months < 14)
781 month = months - 1;
782 year = years + 1524;
784 else
786 month = months - 13;
787 year = years + 1525;
789 day = yearday - (1959 * months) / 64 ;
791 sprintf( buffer, "%u-%u-%u", month, day, year );
792 return asciiz_to_unicode( bufferW, buffer );
795 struct device_manager_ctx
797 unsigned int gpu_count;
798 unsigned int adapter_count;
799 unsigned int video_count;
800 unsigned int monitor_count;
801 unsigned int output_count;
802 HANDLE mutex;
803 WCHAR gpuid[128];
804 WCHAR gpu_guid[64];
805 LUID gpu_luid;
806 HKEY adapter_key;
807 BOOL virtual_monitor;
810 static void link_device( const WCHAR *instance, const WCHAR *class )
812 unsigned int instance_len = lstrlenW( instance ), len;
813 unsigned int class_len = lstrlenW( class );
814 WCHAR buffer[MAX_PATH], *ptr;
815 HKEY hkey, subkey;
817 static const WCHAR symbolic_linkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k',0};
818 static const WCHAR hashW[] = {'#'};
820 len = asciiz_to_unicode( buffer, "DeviceClasses\\" ) / sizeof(WCHAR) - 1;
821 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
822 len += class_len;
823 len += asciiz_to_unicode( buffer + len, "\\##?#" ) / sizeof(WCHAR) - 1;
824 memcpy( buffer + len, instance, instance_len * sizeof(WCHAR) );
825 for (ptr = buffer + len; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
826 len += instance_len;
827 buffer[len++] = '#';
828 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
829 len += class_len;
830 hkey = reg_create_key( control_key, buffer, len * sizeof(WCHAR), 0, NULL );
832 set_reg_value( hkey, device_instanceW, REG_SZ, instance, (instance_len + 1) * sizeof(WCHAR) );
834 subkey = reg_create_key( hkey, hashW, sizeof(hashW), REG_OPTION_VOLATILE, NULL );
835 NtClose( hkey );
836 hkey = subkey;
838 len = asciiz_to_unicode( buffer, "\\\\?\\" ) / sizeof(WCHAR) - 1;
839 memcpy( buffer + len, instance, (instance_len + 1) * sizeof(WCHAR) );
840 len += instance_len;
841 memcpy( buffer + len, class, (class_len + 1) * sizeof(WCHAR) );
842 len += class_len + 1;
843 for (ptr = buffer + 4; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
844 set_reg_value( hkey, symbolic_linkW, REG_SZ, buffer, len * sizeof(WCHAR) );
846 if ((subkey = reg_create_key( hkey, controlW, sizeof(controlW), REG_OPTION_VOLATILE, NULL )))
848 const DWORD linked = 1;
849 set_reg_value( subkey, linkedW, REG_DWORD, &linked, sizeof(linked) );
850 NtClose( subkey );
854 static void add_gpu( const struct gdi_gpu *gpu, void *param )
856 struct device_manager_ctx *ctx = param;
857 const WCHAR *desc;
858 char buffer[4096];
859 WCHAR bufferW[512];
860 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
861 unsigned int gpu_index, size;
862 HKEY hkey, subkey;
863 LARGE_INTEGER ft;
865 static const BOOL present = TRUE;
866 static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0};
867 static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0};
868 static const WCHAR driver_date_dataW[] =
869 {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
870 static const WCHAR adapter_stringW[] =
871 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n',
872 '.','A','d','a','p','t','e','r','S','t','r','i','n','g',0};
873 static const WCHAR bios_stringW[] =
874 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
875 'B','i','o','s','S','t','r','i','n','g',0};
876 static const WCHAR chip_typeW[] =
877 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
878 'C','h','i','p','T','y','p','e',0};
879 static const WCHAR dac_typeW[] =
880 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
881 'D','a','c','T','y','p','e',0};
882 static const WCHAR ramdacW[] =
883 {'I','n','t','e','r','g','r','a','t','e','d',' ','R','A','M','D','A','C',0};
884 static const WCHAR driver_dateW[] = {'D','r','i','v','e','r','D','a','t','e',0};
886 TRACE( "%s %04X %04X %08X %02X\n", debugstr_w(gpu->name),
887 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
889 gpu_index = ctx->gpu_count++;
890 ctx->adapter_count = 0;
891 ctx->monitor_count = 0;
893 if (!enum_key && !(enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL )))
894 return;
896 if (!ctx->mutex)
898 ctx->mutex = get_display_device_init_mutex();
899 pthread_mutex_lock( &display_lock );
900 prepare_devices();
903 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X\\%08X",
904 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index );
905 size = asciiz_to_unicode( ctx->gpuid, buffer );
906 if (!(hkey = reg_create_key( enum_key, ctx->gpuid, size - sizeof(WCHAR), 0, NULL ))) return;
908 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
909 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_displayW,
910 sizeof(guid_devclass_displayW) );
911 sprintf( buffer, "%s\\%04X", guid_devclass_displayA, gpu_index );
912 set_reg_value( hkey, driverW, REG_SZ, bufferW, asciiz_to_unicode( bufferW, buffer ));
914 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X",
915 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
916 size = asciiz_to_unicode( bufferW, buffer );
917 bufferW[size / sizeof(WCHAR)] = 0; /* for REG_MULTI_SZ */
918 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, size + sizeof(WCHAR) );
920 desc = gpu->name;
921 if (!desc[0]) desc = wine_adapterW;
922 set_reg_value( hkey, device_descW, REG_SZ, desc, (lstrlenW( desc ) + 1) * sizeof(WCHAR) );
924 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
926 if (!query_reg_value( subkey, video_idW, value, sizeof(buffer) ))
928 GUID guid;
929 uuid_create( &guid );
930 sprintf( buffer, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
931 (unsigned int)guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
932 guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] );
933 size = asciiz_to_unicode( ctx->gpu_guid, buffer );
934 TRACE( "created guid %s\n", debugstr_w(ctx->gpu_guid) );
935 set_reg_value( subkey, video_idW, REG_SZ, ctx->gpu_guid, size );
937 else
939 memcpy( ctx->gpu_guid, value->Data, value->DataLength );
940 TRACE( "got guid %s\n", debugstr_w(ctx->gpu_guid) );
942 NtClose( subkey );
945 if ((subkey = reg_create_key( hkey, devpropkey_gpu_vulkan_uuidW,
946 sizeof(devpropkey_gpu_vulkan_uuidW), 0, NULL )))
948 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_GUID,
949 &gpu->vulkan_uuid, sizeof(gpu->vulkan_uuid) );
950 NtClose( subkey );
953 if ((subkey = reg_create_key( hkey, devpropkey_device_ispresentW,
954 sizeof(devpropkey_device_ispresentW), 0, NULL )))
956 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
957 &present, sizeof(present) );
958 NtClose( subkey );
961 if ((subkey = reg_create_key( hkey, devpropkey_gpu_luidW, sizeof(devpropkey_gpu_luidW), 0, NULL )))
963 if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) != sizeof(LUID))
965 NtAllocateLocallyUniqueId( &ctx->gpu_luid );
966 TRACE("allocated luid %08x%08x\n", ctx->gpu_luid.HighPart, ctx->gpu_luid.LowPart );
967 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT64,
968 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
970 else
972 memcpy( &ctx->gpu_luid, value->Data, sizeof(ctx->gpu_luid) );
973 TRACE("got luid %08x%08x\n", ctx->gpu_luid.HighPart, ctx->gpu_luid.LowPart );
975 NtClose( subkey );
978 NtClose( hkey );
980 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_displayA, gpu_index );
981 hkey = reg_create_key( control_key, bufferW,
982 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
984 NtQuerySystemTime( &ft );
985 set_reg_value( hkey, driver_dateW, REG_SZ, bufferW, format_date( bufferW, ft.QuadPart ));
987 set_reg_value( hkey, driver_date_dataW, REG_BINARY, &ft, sizeof(ft) );
989 size = (lstrlenW( desc ) + 1) * sizeof(WCHAR);
990 set_reg_value( hkey, driver_descW, REG_SZ, desc, size );
991 set_reg_value( hkey, adapter_stringW, REG_BINARY, desc, size );
992 set_reg_value( hkey, bios_stringW, REG_BINARY, desc, size );
993 set_reg_value( hkey, chip_typeW, REG_BINARY, desc, size );
994 set_reg_value( hkey, dac_typeW, REG_BINARY, ramdacW, sizeof(ramdacW) );
996 NtClose( hkey );
998 link_device( ctx->gpuid, guid_devinterface_display_adapterW );
999 link_device( ctx->gpuid, guid_display_device_arrivalW );
1002 static void add_adapter( const struct gdi_adapter *adapter, void *param )
1004 struct device_manager_ctx *ctx = param;
1005 unsigned int adapter_index, video_index, len;
1006 char name[64], buffer[MAX_PATH];
1007 WCHAR nameW[64], bufferW[MAX_PATH];
1008 HKEY hkey;
1010 TRACE( "\n" );
1012 if (!ctx->gpu_count)
1014 static const struct gdi_gpu default_gpu;
1015 TRACE( "adding default fake GPU\n" );
1016 add_gpu( &default_gpu, ctx );
1019 if (ctx->adapter_key)
1021 NtClose( ctx->adapter_key );
1022 ctx->adapter_key = NULL;
1025 adapter_index = ctx->adapter_count++;
1026 video_index = ctx->video_count++;
1027 ctx->monitor_count = 0;
1029 len = asciiz_to_unicode( bufferW, "\\Registry\\Machine\\System\\CurrentControlSet\\"
1030 "Control\\Video\\" ) / sizeof(WCHAR) - 1;
1031 lstrcpyW( bufferW + len, ctx->gpu_guid );
1032 len += lstrlenW( bufferW + len );
1033 sprintf( buffer, "\\%04x", adapter_index );
1034 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1035 hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1036 REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, NULL );
1037 if (!hkey) hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1038 REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK, NULL );
1040 sprintf( name, "\\Device\\Video%u", video_index );
1041 asciiz_to_unicode( nameW, name );
1042 set_reg_value( video_key, nameW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1044 if (hkey)
1046 sprintf( buffer, "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\"
1047 "%s\\%04X", guid_devclass_displayA, ctx->gpu_count - 1 );
1048 len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR);
1049 set_reg_value( hkey, symbolic_link_valueW, REG_LINK, bufferW, len );
1050 NtClose( hkey );
1052 else ERR( "failed to create link key\n" );
1054 /* Following information is Wine specific, it doesn't really exist on Windows. */
1055 len = asciiz_to_unicode( bufferW, "System\\CurrentControlSet\\Control\\Video\\" )
1056 / sizeof(WCHAR) - 1;
1057 lstrcpyW( bufferW + len, ctx->gpu_guid );
1058 len += lstrlenW( bufferW + len );
1059 sprintf( buffer, "\\%04x", adapter_index );
1060 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1061 ctx->adapter_key = reg_create_key( config_key, bufferW, len * sizeof(WCHAR),
1062 REG_OPTION_VOLATILE, NULL );
1064 set_reg_value( ctx->adapter_key, gpu_idW, REG_SZ, ctx->gpuid,
1065 (lstrlenW( ctx->gpuid ) + 1) * sizeof(WCHAR) );
1066 set_reg_value( ctx->adapter_key, state_flagsW, REG_DWORD, &adapter->state_flags,
1067 sizeof(adapter->state_flags) );
1070 static void add_monitor( const struct gdi_monitor *monitor, void *param )
1072 struct device_manager_ctx *ctx = param;
1073 char buffer[MAX_PATH], instance[64];
1074 unsigned int monitor_index, output_index;
1075 WCHAR bufferW[MAX_PATH];
1076 HKEY hkey, subkey;
1078 static const WCHAR default_monitorW[] =
1079 {'M','O','N','I','T','O','R','\\','D','e','f','a','u','l','t','_','M','o','n','i','t','o','r',0,0};
1081 if (!monitor)
1083 ctx->virtual_monitor = TRUE;
1084 return;
1087 TRACE( "%s %s %s\n", debugstr_w(monitor->name), wine_dbgstr_rect(&monitor->rc_monitor),
1088 wine_dbgstr_rect(&monitor->rc_work) );
1090 if (!ctx->adapter_count)
1092 static const struct gdi_adapter default_adapter =
1094 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE |
1095 DISPLAY_DEVICE_VGA_COMPATIBLE,
1097 TRACE( "adding default fake adapter\n" );
1098 add_adapter( &default_adapter, ctx );
1101 monitor_index = ctx->monitor_count++;
1102 output_index = ctx->output_count++;
1104 sprintf( buffer, "MonitorID%u", monitor_index );
1105 sprintf( instance, "DISPLAY\\Default_Monitor\\%04X&%04X", ctx->video_count - 1, monitor_index );
1106 set_reg_ascii_value( ctx->adapter_key, buffer, instance );
1108 hkey = reg_create_key( enum_key, bufferW, asciiz_to_unicode( bufferW, instance ) - sizeof(WCHAR),
1109 0, NULL );
1110 if (!hkey) return;
1112 link_device( bufferW, guid_devinterface_monitorW );
1114 lstrcpyW( bufferW, monitor->name );
1115 if (!bufferW[0]) asciiz_to_unicode( bufferW, "Generic Non-PnP Monitor" );
1116 set_reg_value( hkey, device_descW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1118 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
1119 sprintf( buffer, "%s\\%04X", guid_devclass_monitorA, output_index );
1120 set_reg_ascii_value( hkey, "Driver", buffer );
1121 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_monitorW, sizeof(guid_devclass_monitorW) );
1122 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, default_monitorW, sizeof(default_monitorW) );
1124 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
1126 static const WCHAR bad_edidW[] = {'B','A','D','_','E','D','I','D',0};
1127 static const WCHAR edidW[] = {'E','D','I','D',0};
1129 if (monitor->edid_len)
1130 set_reg_value( subkey, edidW, REG_BINARY, monitor->edid, monitor->edid_len );
1131 else
1132 set_reg_value( subkey, bad_edidW, REG_BINARY, NULL, 0 );
1133 NtClose( subkey );
1136 /* StateFlags */
1137 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_stateflagsW,
1138 sizeof(wine_devpropkey_monitor_stateflagsW), 0, NULL )))
1140 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32, &monitor->state_flags,
1141 sizeof(monitor->state_flags) );
1142 NtClose( subkey );
1145 /* WINE_DEVPROPKEY_MONITOR_RCMONITOR */
1146 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcmonitorW,
1147 sizeof(wine_devpropkey_monitor_rcmonitorW), 0, NULL )))
1149 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_monitor,
1150 sizeof(monitor->rc_monitor) );
1151 NtClose( subkey );
1154 /* WINE_DEVPROPKEY_MONITOR_RCWORK */
1155 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcworkW,
1156 sizeof(wine_devpropkey_monitor_rcworkW), 0, NULL )))
1158 TRACE( "rc_work %s\n", wine_dbgstr_rect(&monitor->rc_work) );
1159 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_work,
1160 sizeof(monitor->rc_work) );
1161 NtClose( subkey );
1164 /* WINE_DEVPROPKEY_MONITOR_ADAPTERNAME */
1165 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_adapternameW,
1166 sizeof(wine_devpropkey_monitor_adapternameW), 0, NULL )))
1168 sprintf( buffer, "\\\\.\\DISPLAY%u", ctx->video_count );
1169 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
1170 asciiz_to_unicode( bufferW, buffer ));
1171 NtClose( subkey );
1174 /* DEVPROPKEY_MONITOR_GPU_LUID */
1175 if ((subkey = reg_create_key( hkey, devpropkey_monitor_gpu_luidW,
1176 sizeof(devpropkey_monitor_gpu_luidW), 0, NULL )))
1178 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_INT64,
1179 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
1180 NtClose( subkey );
1183 /* DEVPROPKEY_MONITOR_OUTPUT_ID */
1184 if ((subkey = reg_create_key( hkey, devpropkey_monitor_output_idW,
1185 sizeof(devpropkey_monitor_output_idW), 0, NULL )))
1187 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1188 &output_index, sizeof(output_index) );
1189 NtClose( subkey );
1192 NtClose( hkey );
1194 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_monitorA, output_index );
1195 hkey = reg_create_key( control_key, bufferW,
1196 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
1197 if (hkey) NtClose( hkey );
1200 static const struct gdi_device_manager device_manager =
1202 add_gpu,
1203 add_adapter,
1204 add_monitor,
1207 static void release_display_manager_ctx( struct device_manager_ctx *ctx )
1209 if (ctx->mutex)
1211 pthread_mutex_unlock( &display_lock );
1212 release_display_device_init_mutex( ctx->mutex );
1214 if (ctx->adapter_key)
1216 NtClose( ctx->adapter_key );
1217 last_query_display_time = 0;
1219 if (ctx->gpu_count) cleanup_devices();
1222 static void clear_display_devices(void)
1224 struct adapter *adapter;
1225 struct monitor *monitor;
1227 if (list_head( &monitors ) == &virtual_monitor.entry)
1229 list_init( &monitors );
1230 return;
1233 while (!list_empty( &monitors ))
1235 monitor = LIST_ENTRY( list_head( &monitors ), struct monitor, entry );
1236 list_remove( &monitor->entry );
1237 free( monitor );
1240 while (!list_empty( &adapters ))
1242 adapter = LIST_ENTRY( list_head( &adapters ), struct adapter, entry );
1243 list_remove( &adapter->entry );
1244 free( adapter );
1248 static BOOL update_display_cache_from_registry(void)
1250 DWORD adapter_id, monitor_id, monitor_count = 0, size;
1251 KEY_BASIC_INFORMATION key;
1252 struct adapter *adapter;
1253 struct monitor *monitor, *monitor2;
1254 HANDLE mutex = NULL;
1255 NTSTATUS status;
1256 BOOL ret;
1258 /* If user driver did initialize the registry, then exit */
1259 if (!video_key && !(video_key = reg_open_key( NULL, devicemap_video_keyW,
1260 sizeof(devicemap_video_keyW) )))
1261 return FALSE;
1263 status = NtQueryKey( video_key, KeyBasicInformation, &key,
1264 offsetof(KEY_BASIC_INFORMATION, Name), &size );
1265 if (status && status != STATUS_BUFFER_OVERFLOW)
1266 return FALSE;
1268 if (key.LastWriteTime.QuadPart <= last_query_display_time) return TRUE;
1270 mutex = get_display_device_init_mutex();
1271 pthread_mutex_lock( &display_lock );
1273 clear_display_devices();
1275 for (adapter_id = 0;; adapter_id++)
1277 if (!(adapter = calloc( 1, sizeof(*adapter) ))) break;
1278 adapter->id = adapter_id;
1280 if (!read_display_adapter_settings( adapter_id, adapter ))
1282 free( adapter );
1283 break;
1286 list_add_tail( &adapters, &adapter->entry );
1287 for (monitor_id = 0;; monitor_id++)
1289 if (!(monitor = calloc( 1, sizeof(*monitor) ))) break;
1290 monitor->id = monitor_id;
1291 monitor->adapter = adapter;
1293 if (!read_monitor_settings( adapter, monitor_id, monitor ))
1295 free( monitor );
1296 break;
1299 LIST_FOR_EACH_ENTRY(monitor2, &monitors, struct monitor, entry)
1301 if (EqualRect(&monitor2->rc_monitor, &monitor->rc_monitor))
1303 monitor->is_clone = TRUE;
1304 break;
1308 monitor->handle = UlongToHandle( ++monitor_count );
1309 list_add_tail( &monitors, &monitor->entry );
1313 if ((ret = !list_empty( &adapters ) && !list_empty( &monitors )))
1314 last_query_display_time = key.LastWriteTime.QuadPart;
1315 pthread_mutex_unlock( &display_lock );
1316 release_display_device_init_mutex( mutex );
1317 return ret;
1320 static BOOL update_display_cache(void)
1322 struct device_manager_ctx ctx = { 0 };
1324 user_driver->pUpdateDisplayDevices( &device_manager, FALSE, &ctx );
1325 release_display_manager_ctx( &ctx );
1326 if (ctx.virtual_monitor)
1328 clear_display_devices();
1329 list_add_tail( &monitors, &virtual_monitor.entry );
1330 return TRUE;
1333 if (update_display_cache_from_registry()) return TRUE;
1334 if (ctx.gpu_count)
1336 ERR( "driver reported devices, but we failed to read them\n" );
1337 return FALSE;
1340 user_driver->pUpdateDisplayDevices( &device_manager, TRUE, &ctx );
1341 release_display_manager_ctx( &ctx );
1343 if (!update_display_cache_from_registry())
1345 ERR( "failed to read display config\n" );
1346 return FALSE;
1348 return TRUE;
1351 static BOOL lock_display_devices(void)
1353 if (!update_display_cache()) return FALSE;
1354 pthread_mutex_lock( &display_lock );
1355 return TRUE;
1358 static void unlock_display_devices(void)
1360 pthread_mutex_unlock( &display_lock );
1363 static HDC get_display_dc(void)
1365 pthread_mutex_lock( &display_dc_lock );
1366 if (!display_dc)
1368 HDC dc;
1370 pthread_mutex_unlock( &display_dc_lock );
1371 dc = NtGdiOpenDCW( NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL );
1372 pthread_mutex_lock( &display_dc_lock );
1373 if (display_dc)
1374 NtGdiDeleteObjectApp( dc );
1375 else
1376 display_dc = dc;
1378 return display_dc;
1381 static void release_display_dc( HDC hdc )
1383 pthread_mutex_unlock( &display_dc_lock );
1386 /**********************************************************************
1387 * get_monitor_dpi
1389 UINT get_monitor_dpi( HMONITOR monitor )
1391 /* FIXME: use the monitor DPI instead */
1392 return system_dpi;
1395 /**********************************************************************
1396 * get_win_monitor_dpi
1398 UINT get_win_monitor_dpi( HWND hwnd )
1400 /* FIXME: use the monitor DPI instead */
1401 return system_dpi;
1404 /**********************************************************************
1405 * get_thread_dpi_awareness
1407 DPI_AWARENESS get_thread_dpi_awareness(void)
1409 struct user_thread_info *info = get_user_thread_info();
1410 ULONG_PTR context = info->dpi_awareness;
1412 if (!context) context = NtUserGetProcessDpiAwarenessContext( NULL );
1414 switch (context)
1416 case 0x10:
1417 case 0x11:
1418 case 0x12:
1419 case 0x80000010:
1420 case 0x80000011:
1421 case 0x80000012:
1422 return context & 3;
1423 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
1424 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
1425 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
1426 return ~context;
1427 default:
1428 return DPI_AWARENESS_INVALID;
1432 /**********************************************************************
1433 * get_thread_dpi
1435 UINT get_thread_dpi(void)
1437 switch (get_thread_dpi_awareness())
1439 case DPI_AWARENESS_UNAWARE: return USER_DEFAULT_SCREEN_DPI;
1440 case DPI_AWARENESS_SYSTEM_AWARE: return system_dpi;
1441 default: return 0; /* no scaling */
1445 /* see GetDpiForSystem */
1446 UINT get_system_dpi(void)
1448 if (get_thread_dpi_awareness() == DPI_AWARENESS_UNAWARE) return USER_DEFAULT_SCREEN_DPI;
1449 return system_dpi;
1452 /* see GetAwarenessFromDpiAwarenessContext */
1453 static DPI_AWARENESS get_awareness_from_dpi_awareness_context( DPI_AWARENESS_CONTEXT context )
1455 switch ((ULONG_PTR)context)
1457 case 0x10:
1458 case 0x11:
1459 case 0x12:
1460 case 0x80000010:
1461 case 0x80000011:
1462 case 0x80000012:
1463 return (ULONG_PTR)context & 3;
1464 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
1465 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
1466 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
1467 return ~(ULONG_PTR)context;
1468 default:
1469 return DPI_AWARENESS_INVALID;
1473 /**********************************************************************
1474 * SetThreadDpiAwarenessContext (win32u.so)
1476 DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT context )
1478 struct user_thread_info *info = get_user_thread_info();
1479 DPI_AWARENESS prev, val = get_awareness_from_dpi_awareness_context( context );
1481 if (val == DPI_AWARENESS_INVALID)
1483 SetLastError( ERROR_INVALID_PARAMETER );
1484 return 0;
1486 if (!(prev = info->dpi_awareness))
1488 prev = NtUserGetProcessDpiAwarenessContext( GetCurrentProcess() ) & 3;
1489 prev |= 0x80000010; /* restore to process default */
1491 if (((ULONG_PTR)context & ~(ULONG_PTR)0x13) == 0x80000000) info->dpi_awareness = 0;
1492 else info->dpi_awareness = val | 0x10;
1493 return ULongToHandle( prev );
1496 /**********************************************************************
1497 * map_dpi_rect
1499 RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to )
1501 if (dpi_from && dpi_to && dpi_from != dpi_to)
1503 rect.left = muldiv( rect.left, dpi_to, dpi_from );
1504 rect.top = muldiv( rect.top, dpi_to, dpi_from );
1505 rect.right = muldiv( rect.right, dpi_to, dpi_from );
1506 rect.bottom = muldiv( rect.bottom, dpi_to, dpi_from );
1508 return rect;
1511 /**********************************************************************
1512 * map_dpi_point
1514 POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to )
1516 if (dpi_from && dpi_to && dpi_from != dpi_to)
1518 pt.x = muldiv( pt.x, dpi_to, dpi_from );
1519 pt.y = muldiv( pt.y, dpi_to, dpi_from );
1521 return pt;
1524 /**********************************************************************
1525 * point_phys_to_win_dpi
1527 POINT point_phys_to_win_dpi( HWND hwnd, POINT pt )
1529 return map_dpi_point( pt, get_win_monitor_dpi( hwnd ), get_dpi_for_window( hwnd ));
1532 /**********************************************************************
1533 * point_thread_to_win_dpi
1535 POINT point_thread_to_win_dpi( HWND hwnd, POINT pt )
1537 UINT dpi = get_thread_dpi();
1538 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
1539 return map_dpi_point( pt, dpi, get_dpi_for_window( hwnd ));
1542 /**********************************************************************
1543 * rect_thread_to_win_dpi
1545 RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect )
1547 UINT dpi = get_thread_dpi();
1548 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
1549 return map_dpi_rect( rect, dpi, get_dpi_for_window( hwnd ) );
1552 /* map value from system dpi to standard 96 dpi for storing in the registry */
1553 static int map_from_system_dpi( int val )
1555 return muldiv( val, USER_DEFAULT_SCREEN_DPI, get_system_dpi() );
1558 /* map value from 96 dpi to system or custom dpi */
1559 static int map_to_dpi( int val, UINT dpi )
1561 if (!dpi) dpi = get_system_dpi();
1562 return muldiv( val, dpi, USER_DEFAULT_SCREEN_DPI );
1565 RECT get_virtual_screen_rect( UINT dpi )
1567 struct monitor *monitor;
1568 RECT rect = {0};
1570 if (!lock_display_devices()) return rect;
1572 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
1574 union_rect( &rect, &rect, &monitor->rc_monitor );
1577 unlock_display_devices();
1579 if (dpi) rect = map_dpi_rect( rect, system_dpi, dpi );
1580 return rect;
1583 static BOOL is_window_rect_full_screen( const RECT *rect )
1585 struct monitor *monitor;
1586 BOOL ret = FALSE;
1588 if (!lock_display_devices()) return FALSE;
1590 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
1592 if (rect->left <= monitor->rc_monitor.left && rect->right >= monitor->rc_monitor.right &&
1593 rect->top <= monitor->rc_monitor.top && rect->bottom >= monitor->rc_monitor.bottom)
1595 ret = TRUE;
1596 break;
1600 unlock_display_devices();
1601 return ret;
1604 RECT get_display_rect( const WCHAR *display )
1606 struct monitor *monitor;
1607 RECT rect = {0};
1609 if (!lock_display_devices()) return rect;
1611 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
1613 if (!monitor->adapter || wcsicmp( monitor->adapter->dev.device_name, display )) continue;
1614 rect = monitor->rc_monitor;
1615 break;
1618 unlock_display_devices();
1619 return map_dpi_rect( rect, system_dpi, get_thread_dpi() );
1622 RECT get_primary_monitor_rect( UINT dpi )
1624 struct monitor *monitor;
1625 RECT rect = {0};
1627 if (!lock_display_devices()) return rect;
1629 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
1631 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
1632 rect = monitor->rc_monitor;
1633 break;
1636 unlock_display_devices();
1637 return map_dpi_rect( rect, system_dpi, dpi );
1640 /**********************************************************************
1641 * NtUserGetDisplayConfigBufferSizes (win32u.@)
1643 LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_info,
1644 UINT32 *num_mode_info )
1646 struct monitor *monitor;
1647 UINT32 count = 0;
1649 TRACE( "(0x%x %p %p)\n", flags, num_path_info, num_mode_info );
1651 if (!num_path_info || !num_mode_info)
1652 return ERROR_INVALID_PARAMETER;
1654 *num_path_info = 0;
1656 switch (flags)
1658 case QDC_ALL_PATHS:
1659 case QDC_ONLY_ACTIVE_PATHS:
1660 case QDC_DATABASE_CURRENT:
1661 break;
1662 default:
1663 return ERROR_INVALID_PARAMETER;
1666 /* FIXME: semi-stub */
1667 if (flags != QDC_ONLY_ACTIVE_PATHS)
1668 FIXME( "only returning active paths\n" );
1670 if (lock_display_devices())
1672 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
1674 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
1675 continue;
1676 count++;
1678 unlock_display_devices();
1681 *num_path_info = count;
1682 *num_mode_info = count * 2;
1683 TRACE( "returning %u paths %u modes\n", *num_path_info, *num_mode_info );
1684 return ERROR_SUCCESS;
1687 static struct adapter *find_adapter( UNICODE_STRING *name )
1689 struct adapter *adapter;
1691 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
1693 if (!name || !name->Length) return adapter; /* use primary adapter */
1694 if (!wcsnicmp( name->Buffer, adapter->dev.device_name, name->Length / sizeof(WCHAR) ) &&
1695 !adapter->dev.device_name[name->Length / sizeof(WCHAR)])
1696 return adapter;
1698 return NULL;
1701 /***********************************************************************
1702 * NtUserEnumDisplayDevices (win32u.@)
1704 NTSTATUS WINAPI NtUserEnumDisplayDevices( UNICODE_STRING *device, DWORD index,
1705 DISPLAY_DEVICEW *info, DWORD flags )
1707 struct display_device *found = NULL;
1708 struct adapter *adapter;
1709 struct monitor *monitor;
1711 TRACE( "%s %u %p %#x\n", debugstr_us( device ), index, info, flags );
1713 if (!info || !info->cb) return STATUS_UNSUCCESSFUL;
1715 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
1717 if (!device || !device->Length)
1719 /* Enumerate adapters */
1720 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
1722 if (index == adapter->id)
1724 found = &adapter->dev;
1725 break;
1729 else if ((adapter = find_adapter( device )))
1731 /* Enumerate monitors */
1732 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
1734 if (monitor->adapter == adapter && index == monitor->id)
1736 found = &monitor->dev;
1737 break;
1742 if (found)
1744 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceName) + sizeof(info->DeviceName))
1745 lstrcpyW( info->DeviceName, found->device_name );
1746 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceString) + sizeof(info->DeviceString))
1747 lstrcpyW( info->DeviceString, found->device_string );
1748 if (info->cb >= offsetof(DISPLAY_DEVICEW, StateFlags) + sizeof(info->StateFlags))
1749 info->StateFlags = found->state_flags;
1750 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID))
1751 lstrcpyW( info->DeviceID, (flags & EDD_GET_DEVICE_INTERFACE_NAME)
1752 ? found->interface_name : found->device_id );
1753 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey))
1754 lstrcpyW( info->DeviceKey, found->device_key );
1756 unlock_display_devices();
1757 return found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
1760 #define _X_FIELD(prefix, bits) \
1761 if ((fields) & prefix##_##bits) \
1763 p += sprintf( p, "%s%s", first ? "" : ",", #bits ); \
1764 first = FALSE; \
1767 static const char *_CDS_flags( DWORD fields )
1769 BOOL first = TRUE;
1770 CHAR buf[128];
1771 CHAR *p = buf;
1773 _X_FIELD(CDS, UPDATEREGISTRY)
1774 _X_FIELD(CDS, TEST)
1775 _X_FIELD(CDS, FULLSCREEN)
1776 _X_FIELD(CDS, GLOBAL)
1777 _X_FIELD(CDS, SET_PRIMARY)
1778 _X_FIELD(CDS, VIDEOPARAMETERS)
1779 _X_FIELD(CDS, ENABLE_UNSAFE_MODES)
1780 _X_FIELD(CDS, DISABLE_UNSAFE_MODES)
1781 _X_FIELD(CDS, RESET)
1782 _X_FIELD(CDS, RESET_EX)
1783 _X_FIELD(CDS, NORESET)
1785 *p = 0;
1786 return wine_dbg_sprintf( "%s", buf );
1789 static const char *_DM_fields( DWORD fields )
1791 BOOL first = TRUE;
1792 CHAR buf[128];
1793 CHAR *p = buf;
1795 _X_FIELD(DM, BITSPERPEL)
1796 _X_FIELD(DM, PELSWIDTH)
1797 _X_FIELD(DM, PELSHEIGHT)
1798 _X_FIELD(DM, DISPLAYFLAGS)
1799 _X_FIELD(DM, DISPLAYFREQUENCY)
1800 _X_FIELD(DM, POSITION)
1801 _X_FIELD(DM, DISPLAYORIENTATION)
1803 *p = 0;
1804 return wine_dbg_sprintf( "%s", buf );
1807 #undef _X_FIELD
1809 static void trace_devmode( const DEVMODEW *devmode )
1811 TRACE( "dmFields=%s ", _DM_fields(devmode->dmFields) );
1812 if (devmode->dmFields & DM_BITSPERPEL)
1813 TRACE( "dmBitsPerPel=%u ", devmode->dmBitsPerPel );
1814 if (devmode->dmFields & DM_PELSWIDTH)
1815 TRACE( "dmPelsWidth=%u ", devmode->dmPelsWidth );
1816 if (devmode->dmFields & DM_PELSHEIGHT)
1817 TRACE( "dmPelsHeight=%u ", devmode->dmPelsHeight );
1818 if (devmode->dmFields & DM_DISPLAYFREQUENCY)
1819 TRACE( "dmDisplayFrequency=%u ", devmode->dmDisplayFrequency );
1820 if (devmode->dmFields & DM_POSITION)
1821 TRACE( "dmPosition=(%d,%d) ", devmode->dmPosition.x, devmode->dmPosition.y );
1822 if (devmode->dmFields & DM_DISPLAYFLAGS)
1823 TRACE( "dmDisplayFlags=%#x ", devmode->dmDisplayFlags );
1824 if (devmode->dmFields & DM_DISPLAYORIENTATION)
1825 TRACE( "dmDisplayOrientation=%u ", devmode->dmDisplayOrientation );
1826 TRACE("\n");
1829 static BOOL is_detached_mode( const DEVMODEW *mode )
1831 return mode->dmFields & DM_POSITION &&
1832 mode->dmFields & DM_PELSWIDTH &&
1833 mode->dmFields & DM_PELSHEIGHT &&
1834 mode->dmPelsWidth == 0 &&
1835 mode->dmPelsHeight == 0;
1838 /***********************************************************************
1839 * NtUserChangeDisplaySettingsExW (win32u.@)
1841 LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devmode, HWND hwnd,
1842 DWORD flags, void *lparam )
1844 WCHAR device_name[CCHDEVICENAME];
1845 struct adapter *adapter;
1846 BOOL def_mode = TRUE;
1847 DEVMODEW dm;
1848 LONG ret;
1850 TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, flags, lparam );
1851 TRACE( "flags=%s\n", _CDS_flags(flags) );
1853 if ((!devname || !devname->Length) && !devmode)
1855 ret = user_driver->pChangeDisplaySettingsEx( NULL, NULL, hwnd, flags, lparam );
1856 if (ret != DISP_CHANGE_SUCCESSFUL)
1857 ERR( "Restoring all displays to their registry settings returned %d.\n", ret );
1858 return ret;
1861 if (!lock_display_devices()) return DISP_CHANGE_FAILED;
1862 if ((adapter = find_adapter( devname ))) lstrcpyW( device_name, adapter->dev.device_name );
1863 unlock_display_devices();
1864 if (!adapter)
1866 WARN( "Invalid device name %s.\n", debugstr_us(devname) );
1867 return DISP_CHANGE_BADPARAM;
1870 if (devmode)
1872 trace_devmode( devmode );
1874 if (devmode->dmSize < FIELD_OFFSET(DEVMODEW, dmICMMethod))
1875 return DISP_CHANGE_BADMODE;
1877 if (is_detached_mode(devmode) ||
1878 ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel) ||
1879 ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth) ||
1880 ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight) ||
1881 ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency))
1882 def_mode = FALSE;
1885 if (def_mode)
1887 memset( &dm, 0, sizeof(dm) );
1888 dm.dmSize = sizeof(dm);
1889 if (!NtUserEnumDisplaySettings( devname, ENUM_REGISTRY_SETTINGS, &dm, 0 ))
1891 ERR( "Default mode not found!\n" );
1892 return DISP_CHANGE_BADMODE;
1895 TRACE( "Return to original display mode\n" );
1896 devmode = &dm;
1899 if ((devmode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
1901 WARN( "devmode doesn't specify the resolution: %#x\n", devmode->dmFields );
1902 return DISP_CHANGE_BADMODE;
1905 if (!is_detached_mode(devmode) && (!devmode->dmPelsWidth || !devmode->dmPelsHeight))
1907 memset(&dm, 0, sizeof(dm));
1908 dm.dmSize = sizeof(dm);
1909 if (!NtUserEnumDisplaySettings( devname, ENUM_CURRENT_SETTINGS, &dm, 0 ))
1911 ERR( "Current mode not found!\n" );
1912 return DISP_CHANGE_BADMODE;
1915 if (!devmode->dmPelsWidth)
1916 devmode->dmPelsWidth = dm.dmPelsWidth;
1917 if (!devmode->dmPelsHeight)
1918 devmode->dmPelsHeight = dm.dmPelsHeight;
1921 ret = user_driver->pChangeDisplaySettingsEx( device_name, devmode, hwnd, flags, lparam );
1922 if (ret != DISP_CHANGE_SUCCESSFUL)
1923 ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret );
1924 return ret;
1927 /***********************************************************************
1928 * NtUserEnumDisplaySettings (win32u.@)
1930 BOOL WINAPI NtUserEnumDisplaySettings( UNICODE_STRING *device, DWORD mode,
1931 DEVMODEW *dev_mode, DWORD flags )
1933 WCHAR device_name[CCHDEVICENAME];
1934 struct adapter *adapter;
1935 BOOL ret;
1937 TRACE( "%s %#x %p %#x\n", debugstr_us(device), mode, dev_mode, flags );
1939 if (!lock_display_devices()) return FALSE;
1940 if ((adapter = find_adapter( device ))) lstrcpyW( device_name, adapter->dev.device_name );
1941 unlock_display_devices();
1942 if (!adapter)
1944 WARN( "Invalid device name %s.\n", debugstr_us(device) );
1945 return FALSE;
1948 ret = user_driver->pEnumDisplaySettingsEx( device_name, mode, dev_mode, flags );
1949 if (ret)
1950 TRACE( "device:%s mode index:%#x position:(%d,%d) resolution:%ux%u frequency:%uHz "
1951 "depth:%ubits orientation:%#x.\n", debugstr_w(device_name), mode,
1952 dev_mode->dmPosition.x, dev_mode->dmPosition.y, dev_mode->dmPelsWidth,
1953 dev_mode->dmPelsHeight, dev_mode->dmDisplayFrequency, dev_mode->dmBitsPerPel,
1954 dev_mode->dmDisplayOrientation );
1955 else
1956 WARN( "Failed to query %s display settings.\n", wine_dbgstr_w(device_name) );
1957 return ret;
1960 struct monitor_enum_info
1962 HANDLE handle;
1963 RECT rect;
1966 static unsigned int active_monitor_count(void)
1968 struct monitor *monitor;
1969 unsigned int count = 0;
1971 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
1973 if ((monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) count++;
1975 return count;
1978 /***********************************************************************
1979 * NtUserEnumDisplayMonitors (win32u.@)
1981 BOOL WINAPI NtUserEnumDisplayMonitors( HDC hdc, RECT *rect, MONITORENUMPROC proc, LPARAM lparam )
1983 struct monitor_enum_info enum_buf[8], *enum_info = enum_buf;
1984 struct enum_display_monitor_params params;
1985 struct monitor *monitor;
1986 unsigned int count = 0, i;
1987 POINT origin;
1988 RECT limit;
1989 BOOL ret = TRUE;
1991 if (hdc)
1993 DC *dc;
1994 if (!(dc = get_dc_ptr( hdc ))) return FALSE;
1995 origin.x = dc->attr->vis_rect.left;
1996 origin.y = dc->attr->vis_rect.top;
1997 release_dc_ptr( dc );
1998 if (NtGdiGetAppClipBox( hdc, &limit ) == ERROR) return FALSE;
2000 else
2002 origin.x = origin.y = 0;
2003 limit.left = limit.top = INT_MIN;
2004 limit.right = limit.bottom = INT_MAX;
2006 if (rect && !intersect_rect( &limit, &limit, rect )) return TRUE;
2008 if (!lock_display_devices()) return FALSE;
2010 count = list_count( &monitors );
2011 if (!count || (count > ARRAYSIZE(enum_buf) &&
2012 !(enum_info = malloc( count * sizeof(*enum_info) ))))
2014 unlock_display_devices();
2015 return FALSE;
2018 count = 0;
2019 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2021 RECT monrect;
2023 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) continue;
2025 monrect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
2026 get_thread_dpi() );
2027 OffsetRect( &monrect, -origin.x, -origin.y );
2028 if (!intersect_rect( &monrect, &monrect, &limit )) continue;
2029 if (monitor->is_clone) continue;
2031 enum_info[count].handle = monitor->handle;
2032 enum_info[count].rect = monrect;
2033 count++;
2036 unlock_display_devices();
2038 params.proc = proc;
2039 params.hdc = hdc;
2040 params.lparam = lparam;
2041 for (i = 0; i < count; i++)
2043 params.monitor = enum_info[i].handle;
2044 params.rect = enum_info[i].rect;
2045 if (!user32_call( NtUserCallEnumDisplayMonitor, &params, sizeof(params) ))
2047 ret = FALSE;
2048 break;
2051 if (enum_info != enum_buf) free( enum_info );
2052 return ret;
2055 BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info )
2057 struct monitor *monitor;
2058 UINT dpi_from, dpi_to;
2060 if (info->cbSize != sizeof(MONITORINFOEXW) && info->cbSize != sizeof(MONITORINFO)) return FALSE;
2062 if (!lock_display_devices()) return FALSE;
2064 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2066 if (monitor->handle != handle) continue;
2067 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) break;
2069 /* FIXME: map dpi */
2070 info->rcMonitor = monitor->rc_monitor;
2071 info->rcWork = monitor->rc_work;
2072 info->dwFlags = monitor->flags;
2073 if (info->cbSize >= sizeof(MONITORINFOEXW))
2075 if (monitor->adapter)
2076 lstrcpyW( ((MONITORINFOEXW *)info)->szDevice, monitor->adapter->dev.device_name );
2077 else
2078 asciiz_to_unicode( ((MONITORINFOEXW *)info)->szDevice, "WinDisc" );
2080 unlock_display_devices();
2082 if ((dpi_to = get_thread_dpi()))
2084 dpi_from = get_monitor_dpi( handle );
2085 info->rcMonitor = map_dpi_rect( info->rcMonitor, dpi_from, dpi_to );
2086 info->rcWork = map_dpi_rect( info->rcWork, dpi_from, dpi_to );
2088 TRACE( "flags %04x, monitor %s, work %s\n", info->dwFlags,
2089 wine_dbgstr_rect(&info->rcMonitor), wine_dbgstr_rect(&info->rcWork));
2090 return TRUE;
2093 unlock_display_devices();
2094 WARN( "invalid handle %p\n", handle );
2095 SetLastError( ERROR_INVALID_MONITOR_HANDLE );
2096 return FALSE;
2099 HMONITOR monitor_from_rect( const RECT *rect, DWORD flags, UINT dpi )
2101 HMONITOR primary = 0, nearest = 0, ret = 0;
2102 UINT max_area = 0, min_distance = ~0u;
2103 struct monitor *monitor;
2104 RECT r;
2106 r = map_dpi_rect( *rect, dpi, system_dpi );
2107 if (IsRectEmpty( &r ))
2109 r.right = r.left + 1;
2110 r.bottom = r.top + 1;
2113 if (!lock_display_devices()) return 0;
2115 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2117 RECT intersect;
2118 RECT monitor_rect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
2119 system_dpi );
2121 if (intersect_rect( &intersect, &monitor_rect, &r ))
2123 /* check for larger intersecting area */
2124 UINT area = (intersect.right - intersect.left) * (intersect.bottom - intersect.top);
2125 if (area > max_area)
2127 max_area = area;
2128 ret = monitor->handle;
2131 else if (!max_area) /* if not intersecting, check for min distance */
2133 UINT distance;
2134 UINT x, y;
2136 if (r.right <= monitor_rect.left) x = monitor_rect.left - r.right;
2137 else if (monitor_rect.right <= r.left) x = r.left - monitor_rect.right;
2138 else x = 0;
2139 if (r.bottom <= monitor_rect.top) y = monitor_rect.top - r.bottom;
2140 else if (monitor_rect.bottom <= r.top) y = r.top - monitor_rect.bottom;
2141 else y = 0;
2142 distance = x * x + y * y;
2143 if (distance < min_distance)
2145 min_distance = distance;
2146 nearest = monitor->handle;
2150 if (monitor->flags & MONITORINFOF_PRIMARY) primary = monitor->handle;
2153 unlock_display_devices();
2155 if (!ret)
2157 if (flags & MONITOR_DEFAULTTOPRIMARY) ret = primary;
2158 else if (flags & MONITOR_DEFAULTTONEAREST) ret = nearest;
2161 TRACE( "%s flags %x returning %p\n", wine_dbgstr_rect(rect), flags, ret );
2162 return ret;
2165 HMONITOR monitor_from_point( POINT pt, DWORD flags, UINT dpi )
2167 RECT rect;
2168 SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 );
2169 return monitor_from_rect( &rect, flags, dpi );
2172 /* see MonitorFromWindow */
2173 HMONITOR monitor_from_window( HWND hwnd, DWORD flags, UINT dpi )
2175 RECT rect;
2176 WINDOWPLACEMENT wp;
2178 TRACE( "(%p, 0x%08x)\n", hwnd, flags );
2180 wp.length = sizeof(wp);
2181 if (is_iconic( hwnd ) && NtUserGetWindowPlacement( hwnd, &wp ))
2182 return monitor_from_rect( &wp.rcNormalPosition, flags, dpi );
2184 if (get_window_rect( hwnd, &rect, dpi ))
2185 return monitor_from_rect( &rect, flags, dpi );
2187 if (!(flags & (MONITOR_DEFAULTTOPRIMARY|MONITOR_DEFAULTTONEAREST))) return 0;
2188 /* retrieve the primary */
2189 SetRect( &rect, 0, 0, 1, 1 );
2190 return monitor_from_rect( &rect, flags, dpi );
2193 /***********************************************************************
2194 * NtUserGetSystemDpiForProcess (win32u.@)
2196 ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process )
2198 if (process && process != GetCurrentProcess())
2200 FIXME( "not supported on other process %p\n", process );
2201 return 0;
2204 return system_dpi;
2207 /***********************************************************************
2208 * NtUserGetDpiForMonitor (win32u.@)
2210 BOOL WINAPI NtUserGetDpiForMonitor( HMONITOR monitor, UINT type, UINT *x, UINT *y )
2212 if (type > 2)
2214 SetLastError( ERROR_BAD_ARGUMENTS );
2215 return FALSE;
2217 if (!x || !y)
2219 SetLastError( ERROR_INVALID_ADDRESS );
2220 return FALSE;
2222 switch (get_thread_dpi_awareness())
2224 case DPI_AWARENESS_UNAWARE: *x = *y = USER_DEFAULT_SCREEN_DPI; break;
2225 case DPI_AWARENESS_SYSTEM_AWARE: *x = *y = system_dpi; break;
2226 default: *x = *y = get_monitor_dpi( monitor ); break;
2228 return TRUE;
2231 /* retrieve the cached base keys for a given entry */
2232 static BOOL get_base_keys( enum parameter_key index, HKEY *base_key, HKEY *volatile_key )
2234 static HKEY base_keys[NB_PARAM_KEYS];
2235 static HKEY volatile_keys[NB_PARAM_KEYS];
2236 WCHAR bufferW[128];
2237 HKEY key;
2239 if (!base_keys[index] && base_key)
2241 if (!(key = reg_create_key( hkcu_key, bufferW,
2242 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
2243 0, NULL )))
2244 return FALSE;
2245 if (InterlockedCompareExchangePointer( (void **)&base_keys[index], key, 0 ))
2246 NtClose( key );
2248 if (!volatile_keys[index] && volatile_key)
2250 if (!(key = reg_create_key( volatile_base_key, bufferW,
2251 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
2252 REG_OPTION_VOLATILE, NULL )))
2253 return FALSE;
2254 if (InterlockedCompareExchangePointer( (void **)&volatile_keys[index], key, 0 ))
2255 NtClose( key );
2257 if (base_key) *base_key = base_keys[index];
2258 if (volatile_key) *volatile_key = volatile_keys[index];
2259 return TRUE;
2262 /* load a value to a registry entry */
2263 static DWORD load_entry( struct sysparam_entry *entry, void *data, DWORD size )
2265 char buffer[4096];
2266 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
2267 DWORD count;
2268 HKEY base_key, volatile_key;
2270 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
2272 if (!(count = query_reg_ascii_value( volatile_key, entry->regval, value, sizeof(buffer) )))
2273 count = query_reg_ascii_value( base_key, entry->regval, value, sizeof(buffer) );
2274 if (count > size)
2276 count = size;
2277 /* make sure strings are null-terminated */
2278 if (value->Type == REG_SZ) ((WCHAR *)value->Data)[count / sizeof(WCHAR) - 1] = 0;
2280 if (count) memcpy( data, value->Data, count );
2281 entry->loaded = TRUE;
2282 return count;
2285 /* save a value to a registry entry */
2286 static BOOL save_entry( const struct sysparam_entry *entry, const void *data, DWORD size,
2287 DWORD type, UINT flags )
2289 HKEY base_key, volatile_key;
2290 WCHAR nameW[64];
2292 asciiz_to_unicode( nameW, entry->regval );
2293 if (flags & SPIF_UPDATEINIFILE)
2295 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
2296 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
2297 reg_delete_value( volatile_key, nameW );
2299 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
2301 asciiz_to_unicode( nameW, entry->mirror );
2302 set_reg_value( base_key, nameW, type, data, size );
2305 else
2307 if (!get_base_keys( entry->base_key, NULL, &volatile_key )) return FALSE;
2308 if (!set_reg_value( volatile_key, nameW, type, data, size )) return FALSE;
2310 return TRUE;
2313 /* save a string value to a registry entry */
2314 static BOOL save_entry_string( const struct sysparam_entry *entry, const WCHAR *str, UINT flags )
2316 return save_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ, flags );
2319 /* initialize an entry in the registry if missing */
2320 static BOOL init_entry( struct sysparam_entry *entry, const void *data, DWORD size, DWORD type )
2322 KEY_VALUE_PARTIAL_INFORMATION value;
2323 UNICODE_STRING name;
2324 WCHAR nameW[64];
2325 HKEY base_key;
2326 DWORD count;
2327 NTSTATUS status;
2329 if (!get_base_keys( entry->base_key, &base_key, NULL )) return FALSE;
2331 name.Buffer = nameW;
2332 name.MaximumLength = asciiz_to_unicode( nameW, entry->regval );
2333 name.Length = name.MaximumLength - sizeof(WCHAR);
2334 status = NtQueryValueKey( base_key, &name, KeyValuePartialInformation,
2335 &value, sizeof(value), &count );
2336 if (!status || status == STATUS_BUFFER_OVERFLOW) return TRUE;
2338 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
2339 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
2341 asciiz_to_unicode( nameW, entry->mirror );
2342 set_reg_value( base_key, nameW, type, data, size );
2344 entry->loaded = TRUE;
2345 return TRUE;
2348 /* initialize a string value in the registry if missing */
2349 static BOOL init_entry_string( struct sysparam_entry *entry, const WCHAR *str )
2351 return init_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ );
2354 /* set an int parameter in the registry */
2355 static BOOL set_int_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2357 WCHAR bufW[32];
2358 char buf[32];
2360 sprintf( buf, "%d", int_param );
2361 asciiz_to_unicode( bufW, buf );
2362 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
2363 entry->uint.val = int_param;
2364 entry->hdr.loaded = TRUE;
2365 return TRUE;
2368 /* initialize an int parameter */
2369 static BOOL init_int_entry( union sysparam_all_entry *entry )
2371 WCHAR bufW[32];
2372 char buf[32];
2374 sprintf( buf, "%d", entry->uint.val );
2375 asciiz_to_unicode( bufW, buf );
2376 return init_entry_string( &entry->hdr, bufW );
2379 /* load a uint parameter from the registry */
2380 static BOOL get_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2382 if (!ptr_param) return FALSE;
2384 if (!entry->hdr.loaded)
2386 WCHAR buf[32];
2387 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
2389 *(UINT *)ptr_param = entry->uint.val;
2390 return TRUE;
2393 /* set a uint parameter in the registry */
2394 static BOOL set_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2396 WCHAR bufW[32];
2397 char buf[32];
2399 sprintf( buf, "%u", int_param );
2400 asciiz_to_unicode( bufW, buf );
2401 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
2402 entry->uint.val = int_param;
2403 entry->hdr.loaded = TRUE;
2404 return TRUE;
2407 /* initialize a uint parameter */
2408 static BOOL init_uint_entry( union sysparam_all_entry *entry )
2410 WCHAR bufW[32];
2411 char buf[32];
2413 sprintf( buf, "%u", entry->uint.val );
2414 asciiz_to_unicode( bufW, buf );
2415 return init_entry_string( &entry->hdr, bufW );
2418 /* load a twips parameter from the registry */
2419 static BOOL get_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2421 int val;
2423 if (!ptr_param) return FALSE;
2425 if (!entry->hdr.loaded)
2427 WCHAR buf[32];
2428 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
2431 /* Dimensions are quoted as being "twips" values if negative and pixels if positive.
2432 * One inch is 1440 twips.
2433 * See for example
2434 * Technical Reference to the Windows 2000 Registry ->
2435 * HKEY_CURRENT_USER -> Control Panel -> Desktop -> WindowMetrics
2437 val = entry->uint.val;
2438 if (val < 0)
2439 val = muldiv( -val, dpi, 1440 );
2440 else
2441 val = map_to_dpi( val, dpi );
2443 *(int *)ptr_param = val;
2444 return TRUE;
2447 /* set a twips parameter in the registry */
2448 static BOOL set_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2450 int val = int_param;
2451 if (val > 0) val = map_from_system_dpi( val );
2452 return set_int_entry( entry, val, ptr_param, flags );
2455 /* load a bool parameter from the registry */
2456 static BOOL get_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2458 if (!ptr_param) return FALSE;
2460 if (!entry->hdr.loaded)
2462 WCHAR buf[32];
2463 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = wcstol( buf, NULL, 10 ) != 0;
2465 *(UINT *)ptr_param = entry->bool.val;
2466 return TRUE;
2469 /* set a bool parameter in the registry */
2470 static BOOL set_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2472 WCHAR buf[] = { int_param ? '1' : '0', 0 };
2474 if (!save_entry_string( &entry->hdr, buf, flags )) return FALSE;
2475 entry->bool.val = int_param != 0;
2476 entry->hdr.loaded = TRUE;
2477 return TRUE;
2480 /* initialize a bool parameter */
2481 static BOOL init_bool_entry( union sysparam_all_entry *entry )
2483 WCHAR buf[] = { entry->bool.val ? '1' : '0', 0 };
2485 return init_entry_string( &entry->hdr, buf );
2488 /* load a bool parameter using Yes/No strings from the registry */
2489 static BOOL get_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2491 if (!ptr_param) return FALSE;
2493 if (!entry->hdr.loaded)
2495 WCHAR buf[32];
2496 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = !wcsicmp( yesW, buf );
2498 *(UINT *)ptr_param = entry->bool.val;
2499 return TRUE;
2502 /* set a bool parameter using Yes/No strings from the registry */
2503 static BOOL set_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2505 const WCHAR *str = int_param ? yesW : noW;
2507 if (!save_entry_string( &entry->hdr, str, flags )) return FALSE;
2508 entry->bool.val = int_param != 0;
2509 entry->hdr.loaded = TRUE;
2510 return TRUE;
2513 /* initialize a bool parameter using Yes/No strings */
2514 static BOOL init_yesno_entry( union sysparam_all_entry *entry )
2516 return init_entry_string( &entry->hdr, entry->bool.val ? yesW : noW );
2519 /* load a dword (binary) parameter from the registry */
2520 static BOOL get_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2522 if (!ptr_param) return FALSE;
2524 if (!entry->hdr.loaded)
2526 DWORD val;
2527 if (load_entry( &entry->hdr, &val, sizeof(val) ) == sizeof(DWORD)) entry->dword.val = val;
2529 *(DWORD *)ptr_param = entry->dword.val;
2530 return TRUE;
2533 /* set a dword (binary) parameter in the registry */
2534 static BOOL set_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2536 DWORD val = PtrToUlong( ptr_param );
2538 if (!save_entry( &entry->hdr, &val, sizeof(val), REG_DWORD, flags )) return FALSE;
2539 entry->dword.val = val;
2540 entry->hdr.loaded = TRUE;
2541 return TRUE;
2544 /* initialize a dword parameter */
2545 static BOOL init_dword_entry( union sysparam_all_entry *entry )
2547 return init_entry( &entry->hdr, &entry->dword.val, sizeof(entry->dword.val), REG_DWORD );
2550 /* load an RGB parameter from the registry */
2551 static BOOL get_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2553 if (!ptr_param) return FALSE;
2555 if (!entry->hdr.loaded)
2557 WCHAR buf[32];
2559 if (load_entry( &entry->hdr, buf, sizeof(buf) ))
2561 DWORD r, g, b;
2562 WCHAR *end, *str = buf;
2564 r = wcstoul( str, &end, 10 );
2565 if (end == str || !*end) goto done;
2566 str = end + 1;
2567 g = wcstoul( str, &end, 10 );
2568 if (end == str || !*end) goto done;
2569 str = end + 1;
2570 b = wcstoul( str, &end, 10 );
2571 if (end == str) goto done;
2572 if (r > 255 || g > 255 || b > 255) goto done;
2573 entry->rgb.val = RGB( r, g, b );
2576 done:
2577 *(COLORREF *)ptr_param = entry->rgb.val;
2578 return TRUE;
2581 /* set an RGB parameter in the registry */
2582 static BOOL set_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2584 WCHAR bufW[32];
2585 char buf[32];
2586 HBRUSH brush;
2587 HPEN pen;
2589 sprintf( buf, "%u %u %u", GetRValue(int_param), GetGValue(int_param), GetBValue(int_param) );
2590 asciiz_to_unicode( bufW, buf );
2591 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
2592 entry->rgb.val = int_param;
2593 entry->hdr.loaded = TRUE;
2594 if ((brush = InterlockedExchangePointer( (void **)&entry->rgb.brush, 0 )))
2596 make_gdi_object_system( brush, FALSE );
2597 NtGdiDeleteObjectApp( brush );
2599 if ((pen = InterlockedExchangePointer( (void **)&entry->rgb.pen, 0 )))
2601 make_gdi_object_system( pen, FALSE );
2602 NtGdiDeleteObjectApp( pen );
2604 return TRUE;
2607 /* initialize an RGB parameter */
2608 static BOOL init_rgb_entry( union sysparam_all_entry *entry )
2610 WCHAR bufW[32];
2611 char buf[32];
2613 sprintf( buf, "%u %u %u", GetRValue(entry->rgb.val), GetGValue(entry->rgb.val),
2614 GetBValue(entry->rgb.val) );
2615 asciiz_to_unicode( bufW, buf );
2616 return init_entry_string( &entry->hdr, bufW );
2619 /* get a path parameter in the registry */
2620 static BOOL get_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2622 if (!ptr_param) return FALSE;
2624 if (!entry->hdr.loaded)
2626 WCHAR buffer[MAX_PATH];
2628 if (load_entry( &entry->hdr, buffer, sizeof(buffer) ))
2629 lstrcpynW( entry->path.path, buffer, MAX_PATH );
2631 lstrcpynW( ptr_param, entry->path.path, int_param );
2632 return TRUE;
2635 /* set a path parameter in the registry */
2636 static BOOL set_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2638 WCHAR buffer[MAX_PATH];
2639 BOOL ret;
2641 lstrcpynW( buffer, ptr_param, MAX_PATH );
2642 ret = save_entry_string( &entry->hdr, buffer, flags );
2643 if (ret)
2645 lstrcpyW( entry->path.path, buffer );
2646 entry->hdr.loaded = TRUE;
2648 return ret;
2651 /* initialize a path parameter */
2652 static BOOL init_path_entry( union sysparam_all_entry *entry )
2654 return init_entry_string( &entry->hdr, entry->path.path );
2657 /* get a binary parameter in the registry */
2658 static BOOL get_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2660 if (!ptr_param) return FALSE;
2662 if (!entry->hdr.loaded)
2664 void *buffer = malloc( entry->bin.size );
2665 DWORD len = load_entry( &entry->hdr, buffer, entry->bin.size );
2667 if (len)
2669 memcpy( entry->bin.ptr, buffer, entry->bin.size );
2670 memset( (char *)entry->bin.ptr + len, 0, entry->bin.size - len );
2672 free( buffer );
2674 memcpy( ptr_param, entry->bin.ptr, min( int_param, entry->bin.size ) );
2675 return TRUE;
2678 /* set a binary parameter in the registry */
2679 static BOOL set_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2681 BOOL ret;
2682 void *buffer = malloc( entry->bin.size );
2684 memcpy( buffer, entry->bin.ptr, entry->bin.size );
2685 memcpy( buffer, ptr_param, min( int_param, entry->bin.size ));
2686 ret = save_entry( &entry->hdr, buffer, entry->bin.size, REG_BINARY, flags );
2687 if (ret)
2689 memcpy( entry->bin.ptr, buffer, entry->bin.size );
2690 entry->hdr.loaded = TRUE;
2692 free( buffer );
2693 return ret;
2696 /* initialize a binary parameter */
2697 static BOOL init_binary_entry( union sysparam_all_entry *entry )
2699 return init_entry( &entry->hdr, entry->bin.ptr, entry->bin.size, REG_BINARY );
2702 static void logfont16to32( const LOGFONT16 *font16, LPLOGFONTW font32 )
2704 font32->lfHeight = font16->lfHeight;
2705 font32->lfWidth = font16->lfWidth;
2706 font32->lfEscapement = font16->lfEscapement;
2707 font32->lfOrientation = font16->lfOrientation;
2708 font32->lfWeight = font16->lfWeight;
2709 font32->lfItalic = font16->lfItalic;
2710 font32->lfUnderline = font16->lfUnderline;
2711 font32->lfStrikeOut = font16->lfStrikeOut;
2712 font32->lfCharSet = font16->lfCharSet;
2713 font32->lfOutPrecision = font16->lfOutPrecision;
2714 font32->lfClipPrecision = font16->lfClipPrecision;
2715 font32->lfQuality = font16->lfQuality;
2716 font32->lfPitchAndFamily = font16->lfPitchAndFamily;
2717 win32u_mbtowc( &ansi_cp, font32->lfFaceName, LF_FACESIZE, font16->lfFaceName,
2718 strlen( font16->lfFaceName ));
2719 font32->lfFaceName[LF_FACESIZE-1] = 0;
2722 static void get_real_fontname( LOGFONTW *lf, WCHAR fullname[LF_FACESIZE] )
2724 struct font_enum_entry enum_entry;
2725 ULONG count = sizeof(enum_entry);
2726 HDC hdc;
2728 hdc = get_display_dc();
2729 NtGdiEnumFonts( hdc, 0, 0, lstrlenW( lf->lfFaceName ), lf->lfFaceName, lf->lfCharSet,
2730 &count, &enum_entry );
2731 release_display_dc( hdc );
2733 if (count)
2734 lstrcpyW( fullname, enum_entry.lf.elfFullName );
2735 else
2736 lstrcpyW( fullname, lf->lfFaceName );
2739 LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, LONG *height )
2741 SIZE sz;
2742 static const WCHAR abcdW[] =
2743 {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
2744 'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H',
2745 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
2747 if (metric && !NtGdiGetTextMetricsW( hdc, metric, 0 )) return 0;
2749 if (!NtGdiGetTextExtentExW( hdc, abcdW, ARRAYSIZE(abcdW), 0, NULL, NULL, &sz, 0 ))
2750 return 0;
2752 if (height) *height = sz.cy;
2753 return (sz.cx / 26 + 1) / 2;
2756 /* get text metrics and/or "average" char width of the specified logfont
2757 * for the specified dc */
2758 static void get_text_metr_size( HDC hdc, LOGFONTW *lf, TEXTMETRICW *metric, UINT *psz )
2760 HFONT hfont, hfontsav;
2761 TEXTMETRICW tm;
2762 UINT ret;
2763 if (!metric) metric = &tm;
2764 hfont = NtGdiHfontCreate( lf, sizeof(*lf), 0, 0, NULL );
2765 if (!hfont || !(hfontsav = NtGdiSelectFont( hdc, hfont )))
2767 metric->tmHeight = -1;
2768 if (psz) *psz = 10;
2769 if (hfont) NtGdiDeleteObjectApp( hfont );
2770 return;
2772 ret = get_char_dimensions( hdc, metric, NULL );
2773 if (psz) *psz = ret ? ret : 10;
2774 NtGdiSelectFont( hdc, hfontsav );
2775 NtGdiDeleteObjectApp( hfont );
2778 DWORD get_dialog_base_units(void)
2780 static LONG cx, cy;
2782 if (!cx)
2784 HDC hdc;
2786 if ((hdc = NtUserGetDCEx( 0, 0, DCX_CACHE | DCX_WINDOW )))
2788 cx = get_char_dimensions( hdc, NULL, &cy );
2789 NtUserReleaseDC( 0, hdc );
2791 TRACE( "base units = %d,%d\n", cx, cy );
2794 return MAKELONG( muldiv( cx, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ),
2795 muldiv( cy, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ));
2798 /* adjust some of the raw values found in the registry */
2799 static void normalize_nonclientmetrics( NONCLIENTMETRICSW *pncm)
2801 TEXTMETRICW tm;
2802 HDC hdc = get_display_dc();
2804 if( pncm->iBorderWidth < 1) pncm->iBorderWidth = 1;
2805 if( pncm->iCaptionWidth < 8) pncm->iCaptionWidth = 8;
2806 if( pncm->iScrollWidth < 8) pncm->iScrollWidth = 8;
2807 if( pncm->iScrollHeight < 8) pncm->iScrollHeight = 8;
2809 /* adjust some heights to the corresponding font */
2810 get_text_metr_size( hdc, &pncm->lfMenuFont, &tm, NULL);
2811 pncm->iMenuHeight = max( pncm->iMenuHeight, 2 + tm.tmHeight + tm.tmExternalLeading );
2812 get_text_metr_size( hdc, &pncm->lfCaptionFont, &tm, NULL);
2813 pncm->iCaptionHeight = max( pncm->iCaptionHeight, 2 + tm.tmHeight);
2814 get_text_metr_size( hdc, &pncm->lfSmCaptionFont, &tm, NULL);
2815 pncm->iSmCaptionHeight = max( pncm->iSmCaptionHeight, 2 + tm.tmHeight);
2816 release_display_dc( hdc );
2819 /* load a font (binary) parameter from the registry */
2820 static BOOL get_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2822 LOGFONTW font;
2824 if (!ptr_param) return FALSE;
2826 if (!entry->hdr.loaded)
2828 switch (load_entry( &entry->hdr, &font, sizeof(font) ))
2830 case sizeof(font):
2831 if (font.lfHeight > 0) /* positive height value means points ( inch/72 ) */
2832 font.lfHeight = -muldiv( font.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
2833 entry->font.val = font;
2834 break;
2835 case sizeof(LOGFONT16): /* win9x-winME format */
2836 logfont16to32( (LOGFONT16 *)&font, &entry->font.val );
2837 if (entry->font.val.lfHeight > 0)
2838 entry->font.val.lfHeight = -muldiv( entry->font.val.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
2839 break;
2840 default:
2841 WARN( "Unknown format in key %s value %s\n",
2842 debugstr_a( parameter_key_names[entry->hdr.base_key] ),
2843 debugstr_a( entry->hdr.regval ));
2844 /* fall through */
2845 case 0: /* use the default GUI font */
2846 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(font), &font );
2847 font.lfHeight = map_from_system_dpi( font.lfHeight );
2848 font.lfWeight = entry->font.weight;
2849 entry->font.val = font;
2850 break;
2852 get_real_fontname( &entry->font.val, entry->font.fullname );
2853 entry->hdr.loaded = TRUE;
2855 font = entry->font.val;
2856 font.lfHeight = map_to_dpi( font.lfHeight, dpi );
2857 lstrcpyW( font.lfFaceName, entry->font.fullname );
2858 *(LOGFONTW *)ptr_param = font;
2859 return TRUE;
2862 /* set a font (binary) parameter in the registry */
2863 static BOOL set_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2865 LOGFONTW font;
2866 WCHAR *ptr;
2868 memcpy( &font, ptr_param, sizeof(font) );
2869 /* zero pad the end of lfFaceName so we don't save uninitialised data */
2870 for (ptr = font.lfFaceName; ptr < font.lfFaceName + LF_FACESIZE && *ptr; ptr++);
2871 if (ptr < font.lfFaceName + LF_FACESIZE)
2872 memset( ptr, 0, (font.lfFaceName + LF_FACESIZE - ptr) * sizeof(WCHAR) );
2873 if (font.lfHeight < 0) font.lfHeight = map_from_system_dpi( font.lfHeight );
2875 if (!save_entry( &entry->hdr, &font, sizeof(font), REG_BINARY, flags )) return FALSE;
2876 entry->font.val = font;
2877 get_real_fontname( &entry->font.val, entry->font.fullname );
2878 entry->hdr.loaded = TRUE;
2879 return TRUE;
2882 /* initialize a font (binary) parameter */
2883 static BOOL init_font_entry( union sysparam_all_entry *entry )
2885 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(entry->font.val), &entry->font.val );
2886 entry->font.val.lfHeight = map_from_system_dpi( entry->font.val.lfHeight );
2887 entry->font.val.lfWeight = entry->font.weight;
2888 get_real_fontname( &entry->font.val, entry->font.fullname );
2889 return init_entry( &entry->hdr, &entry->font.val, sizeof(entry->font.val), REG_BINARY );
2892 /* get a user pref parameter in the registry */
2893 static BOOL get_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
2895 union sysparam_all_entry *parent_entry = (union sysparam_all_entry *)entry->pref.parent;
2896 BYTE prefs[8];
2898 if (!ptr_param) return FALSE;
2900 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, dpi )) return FALSE;
2901 *(BOOL *)ptr_param = (prefs[entry->pref.offset] & entry->pref.mask) != 0;
2902 return TRUE;
2905 /* set a user pref parameter in the registry */
2906 static BOOL set_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
2908 union sysparam_all_entry *parent_entry = (union sysparam_all_entry *)entry->pref.parent;
2909 BYTE prefs[8];
2911 parent_entry->hdr.loaded = FALSE; /* force loading it again */
2912 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, get_system_dpi() )) return FALSE;
2914 if (PtrToUlong( ptr_param )) prefs[entry->pref.offset] |= entry->pref.mask;
2915 else prefs[entry->pref.offset] &= ~entry->pref.mask;
2917 return parent_entry->hdr.set( parent_entry, sizeof(prefs), prefs, flags );
2920 static BOOL get_entry_dpi( void *ptr, UINT int_param, void *ptr_param, UINT dpi )
2922 union sysparam_all_entry *entry = ptr;
2923 return entry->hdr.get( entry, int_param, ptr_param, dpi );
2926 static BOOL get_entry( void *ptr, UINT int_param, void *ptr_param )
2928 return get_entry_dpi( ptr, int_param, ptr_param, get_system_dpi() );
2931 static BOOL set_entry( void *ptr, UINT int_param, void *ptr_param, UINT flags )
2933 union sysparam_all_entry *entry = ptr;
2934 return entry->hdr.set( entry, int_param, ptr_param, flags );
2937 #define UINT_ENTRY(name,val,base,reg) \
2938 struct sysparam_uint_entry entry_##name = { { get_uint_entry, set_uint_entry, init_uint_entry, \
2939 base, reg }, (val) }
2941 #define UINT_ENTRY_MIRROR(name,val,base,reg,mirror_base) \
2942 struct sysparam_uint_entry entry_##name = { { get_uint_entry, set_uint_entry, init_uint_entry, \
2943 base, reg, mirror_base, reg }, (val) }
2945 #define INT_ENTRY(name,val,base,reg) \
2946 struct sysparam_uint_entry entry_##name = { { get_uint_entry, set_int_entry, init_int_entry, \
2947 base, reg }, (val) }
2949 #define BOOL_ENTRY(name,val,base,reg) \
2950 struct sysparam_bool_entry entry_##name = { { get_bool_entry, set_bool_entry, init_bool_entry, \
2951 base, reg }, (val) }
2953 #define BOOL_ENTRY_MIRROR(name,val,base,reg,mirror_base) \
2954 struct sysparam_bool_entry entry_##name = { { get_bool_entry, set_bool_entry, init_bool_entry, \
2955 base, reg, mirror_base, reg }, (val) }
2957 #define TWIPS_ENTRY(name,val,base,reg) \
2958 struct sysparam_uint_entry entry_##name = { { get_twips_entry, set_twips_entry, init_int_entry, \
2959 base, reg }, (val) }
2961 #define YESNO_ENTRY(name,val,base,reg) \
2962 struct sysparam_bool_entry entry_##name = { { get_yesno_entry, set_yesno_entry, init_yesno_entry, \
2963 base, reg }, (val) }
2965 #define DWORD_ENTRY(name,val,base,reg) \
2966 struct sysparam_dword_entry entry_##name = { { get_dword_entry, set_dword_entry, init_dword_entry, \
2967 base, reg }, (val) }
2969 #define BINARY_ENTRY(name,data,base,reg) \
2970 struct sysparam_binary_entry entry_##name = { { get_binary_entry, set_binary_entry, init_binary_entry, \
2971 base, reg }, data, sizeof(data) }
2973 #define PATH_ENTRY(name,base,reg) \
2974 struct sysparam_path_entry entry_##name = { { get_path_entry, set_path_entry, init_path_entry, \
2975 base, reg } }
2977 #define FONT_ENTRY(name,weight,base,reg) \
2978 struct sysparam_font_entry entry_##name = { { get_font_entry, set_font_entry, init_font_entry, \
2979 base, reg }, (weight) }
2981 #define USERPREF_ENTRY(name,offset,mask) \
2982 struct sysparam_pref_entry entry_##name = { { get_userpref_entry, set_userpref_entry }, \
2983 &entry_USERPREFERENCESMASK, (offset), (mask) }
2985 static UINT_ENTRY( DRAGWIDTH, 4, DESKTOP_KEY, "DragWidth" );
2986 static UINT_ENTRY( DRAGHEIGHT, 4, DESKTOP_KEY, "DragHeight" );
2987 static UINT_ENTRY( DOUBLECLICKTIME, 500, MOUSE_KEY, "DoubleClickSpeed" );
2988 static UINT_ENTRY( FONTSMOOTHING, 2, DESKTOP_KEY, "FontSmoothing" );
2989 static UINT_ENTRY( GRIDGRANULARITY, 0, DESKTOP_KEY, "GridGranularity" );
2990 static UINT_ENTRY( KEYBOARDDELAY, 1, KEYBOARD_KEY, "KeyboardDelay" );
2991 static UINT_ENTRY( KEYBOARDSPEED, 31, KEYBOARD_KEY, "KeyboardSpeed" );
2992 static UINT_ENTRY( MENUSHOWDELAY, 400, DESKTOP_KEY, "MenuShowDelay" );
2993 static UINT_ENTRY( MINARRANGE, ARW_HIDE, METRICS_KEY, "MinArrange" );
2994 static UINT_ENTRY( MINHORZGAP, 0, METRICS_KEY, "MinHorzGap" );
2995 static UINT_ENTRY( MINVERTGAP, 0, METRICS_KEY, "MinVertGap" );
2996 static UINT_ENTRY( MINWIDTH, 154, METRICS_KEY, "MinWidth" );
2997 static UINT_ENTRY( MOUSEHOVERHEIGHT, 4, MOUSE_KEY, "MouseHoverHeight" );
2998 static UINT_ENTRY( MOUSEHOVERTIME, 400, MOUSE_KEY, "MouseHoverTime" );
2999 static UINT_ENTRY( MOUSEHOVERWIDTH, 4, MOUSE_KEY, "MouseHoverWidth" );
3000 static UINT_ENTRY( MOUSESPEED, 10, MOUSE_KEY, "MouseSensitivity" );
3001 static UINT_ENTRY( MOUSETRAILS, 0, MOUSE_KEY, "MouseTrails" );
3002 static UINT_ENTRY( SCREENSAVETIMEOUT, 300, DESKTOP_KEY, "ScreenSaveTimeOut" );
3003 static UINT_ENTRY( WHEELSCROLLCHARS, 3, DESKTOP_KEY, "WheelScrollChars" );
3004 static UINT_ENTRY( WHEELSCROLLLINES, 3, DESKTOP_KEY, "WheelScrollLines" );
3005 static UINT_ENTRY_MIRROR( DOUBLECLKHEIGHT, 4, MOUSE_KEY, "DoubleClickHeight", DESKTOP_KEY );
3006 static UINT_ENTRY_MIRROR( DOUBLECLKWIDTH, 4, MOUSE_KEY, "DoubleClickWidth", DESKTOP_KEY );
3007 static UINT_ENTRY_MIRROR( MENUDROPALIGNMENT, 0, DESKTOP_KEY, "MenuDropAlignment", VERSION_KEY );
3009 static INT_ENTRY( MOUSETHRESHOLD1, 6, MOUSE_KEY, "MouseThreshold1" );
3010 static INT_ENTRY( MOUSETHRESHOLD2, 10, MOUSE_KEY, "MouseThreshold2" );
3011 static INT_ENTRY( MOUSEACCELERATION, 1, MOUSE_KEY, "MouseSpeed" );
3013 static BOOL_ENTRY( BLOCKSENDINPUTRESETS, FALSE, DESKTOP_KEY, "BlockSendInputResets" );
3014 static BOOL_ENTRY( DRAGFULLWINDOWS, FALSE, DESKTOP_KEY, "DragFullWindows" );
3015 static BOOL_ENTRY( KEYBOARDPREF, TRUE, KEYBOARDPREF_KEY, "On" );
3016 static BOOL_ENTRY( LOWPOWERACTIVE, FALSE, DESKTOP_KEY, "LowPowerActive" );
3017 static BOOL_ENTRY( MOUSEBUTTONSWAP, FALSE, MOUSE_KEY, "SwapMouseButtons" );
3018 static BOOL_ENTRY( POWEROFFACTIVE, FALSE, DESKTOP_KEY, "PowerOffActive" );
3019 static BOOL_ENTRY( SCREENREADER, FALSE, SCREENREADER_KEY, "On" );
3020 static BOOL_ENTRY( SCREENSAVEACTIVE, TRUE, DESKTOP_KEY, "ScreenSaveActive" );
3021 static BOOL_ENTRY( SCREENSAVERRUNNING, FALSE, DESKTOP_KEY, "WINE_ScreenSaverRunning" ); /* FIXME - real value */
3022 static BOOL_ENTRY( SHOWSOUNDS, FALSE, SHOWSOUNDS_KEY, "On" );
3023 static BOOL_ENTRY( SNAPTODEFBUTTON, FALSE, MOUSE_KEY, "SnapToDefaultButton" );
3024 static BOOL_ENTRY_MIRROR( ICONTITLEWRAP, TRUE, DESKTOP_KEY, "IconTitleWrap", METRICS_KEY );
3025 static BOOL_ENTRY( AUDIODESC_ON, FALSE, AUDIODESC_KEY, "On" );
3027 static TWIPS_ENTRY( BORDER, -15, METRICS_KEY, "BorderWidth" );
3028 static TWIPS_ENTRY( CAPTIONHEIGHT, -270, METRICS_KEY, "CaptionHeight" );
3029 static TWIPS_ENTRY( CAPTIONWIDTH, -270, METRICS_KEY, "CaptionWidth" );
3030 static TWIPS_ENTRY( ICONHORIZONTALSPACING, -1125, METRICS_KEY, "IconSpacing" );
3031 static TWIPS_ENTRY( ICONVERTICALSPACING, -1125, METRICS_KEY, "IconVerticalSpacing" );
3032 static TWIPS_ENTRY( MENUHEIGHT, -270, METRICS_KEY, "MenuHeight" );
3033 static TWIPS_ENTRY( MENUWIDTH, -270, METRICS_KEY, "MenuWidth" );
3034 static TWIPS_ENTRY( PADDEDBORDERWIDTH, 0, METRICS_KEY, "PaddedBorderWidth" );
3035 static TWIPS_ENTRY( SCROLLHEIGHT, -240, METRICS_KEY, "ScrollHeight" );
3036 static TWIPS_ENTRY( SCROLLWIDTH, -240, METRICS_KEY, "ScrollWidth" );
3037 static TWIPS_ENTRY( SMCAPTIONHEIGHT, -225, METRICS_KEY, "SmCaptionHeight" );
3038 static TWIPS_ENTRY( SMCAPTIONWIDTH, -225, METRICS_KEY, "SmCaptionWidth" );
3040 static YESNO_ENTRY( BEEP, TRUE, SOUND_KEY, "Beep" );
3042 static DWORD_ENTRY( ACTIVEWINDOWTRACKING, 0, MOUSE_KEY, "ActiveWindowTracking" );
3043 static DWORD_ENTRY( ACTIVEWNDTRKTIMEOUT, 0, DESKTOP_KEY, "ActiveWndTrackTimeout" );
3044 static DWORD_ENTRY( CARETWIDTH, 1, DESKTOP_KEY, "CaretWidth" );
3045 static DWORD_ENTRY( DPISCALINGVER, 0, DESKTOP_KEY, "DpiScalingVer" );
3046 static DWORD_ENTRY( FOCUSBORDERHEIGHT, 1, DESKTOP_KEY, "FocusBorderHeight" );
3047 static DWORD_ENTRY( FOCUSBORDERWIDTH, 1, DESKTOP_KEY, "FocusBorderWidth" );
3048 static DWORD_ENTRY( FONTSMOOTHINGCONTRAST, 0, DESKTOP_KEY, "FontSmoothingGamma" );
3049 static DWORD_ENTRY( FONTSMOOTHINGORIENTATION, FE_FONTSMOOTHINGORIENTATIONRGB, DESKTOP_KEY, "FontSmoothingOrientation" );
3050 static DWORD_ENTRY( FONTSMOOTHINGTYPE, FE_FONTSMOOTHINGSTANDARD, DESKTOP_KEY, "FontSmoothingType" );
3051 static DWORD_ENTRY( FOREGROUNDFLASHCOUNT, 3, DESKTOP_KEY, "ForegroundFlashCount" );
3052 static DWORD_ENTRY( FOREGROUNDLOCKTIMEOUT, 0, DESKTOP_KEY, "ForegroundLockTimeout" );
3053 static DWORD_ENTRY( LOGPIXELS, 0, DESKTOP_KEY, "LogPixels" );
3054 static DWORD_ENTRY( MOUSECLICKLOCKTIME, 1200, DESKTOP_KEY, "ClickLockTime" );
3055 static DWORD_ENTRY( AUDIODESC_LOCALE, 0, AUDIODESC_KEY, "Locale" );
3057 static PATH_ENTRY( DESKPATTERN, DESKTOP_KEY, "Pattern" );
3058 static PATH_ENTRY( DESKWALLPAPER, DESKTOP_KEY, "Wallpaper" );
3060 static BYTE user_prefs[8] = { 0x30, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, 0x00 };
3061 static BINARY_ENTRY( USERPREFERENCESMASK, user_prefs, DESKTOP_KEY, "UserPreferencesMask" );
3063 static FONT_ENTRY( CAPTIONLOGFONT, FW_BOLD, METRICS_KEY, "CaptionFont" );
3064 static FONT_ENTRY( ICONTITLELOGFONT, FW_NORMAL, METRICS_KEY, "IconFont" );
3065 static FONT_ENTRY( MENULOGFONT, FW_NORMAL, METRICS_KEY, "MenuFont" );
3066 static FONT_ENTRY( MESSAGELOGFONT, FW_NORMAL, METRICS_KEY, "MessageFont" );
3067 static FONT_ENTRY( SMCAPTIONLOGFONT, FW_NORMAL, METRICS_KEY, "SmCaptionFont" );
3068 static FONT_ENTRY( STATUSLOGFONT, FW_NORMAL, METRICS_KEY, "StatusFont" );
3070 static USERPREF_ENTRY( MENUANIMATION, 0, 0x02 );
3071 static USERPREF_ENTRY( COMBOBOXANIMATION, 0, 0x04 );
3072 static USERPREF_ENTRY( LISTBOXSMOOTHSCROLLING, 0, 0x08 );
3073 static USERPREF_ENTRY( GRADIENTCAPTIONS, 0, 0x10 );
3074 static USERPREF_ENTRY( KEYBOARDCUES, 0, 0x20 );
3075 static USERPREF_ENTRY( ACTIVEWNDTRKZORDER, 0, 0x40 );
3076 static USERPREF_ENTRY( HOTTRACKING, 0, 0x80 );
3077 static USERPREF_ENTRY( MENUFADE, 1, 0x02 );
3078 static USERPREF_ENTRY( SELECTIONFADE, 1, 0x04 );
3079 static USERPREF_ENTRY( TOOLTIPANIMATION, 1, 0x08 );
3080 static USERPREF_ENTRY( TOOLTIPFADE, 1, 0x10 );
3081 static USERPREF_ENTRY( CURSORSHADOW, 1, 0x20 );
3082 static USERPREF_ENTRY( MOUSESONAR, 1, 0x40 );
3083 static USERPREF_ENTRY( MOUSECLICKLOCK, 1, 0x80 );
3084 static USERPREF_ENTRY( MOUSEVANISH, 2, 0x01 );
3085 static USERPREF_ENTRY( FLATMENU, 2, 0x02 );
3086 static USERPREF_ENTRY( DROPSHADOW, 2, 0x04 );
3087 static USERPREF_ENTRY( UIEFFECTS, 3, 0x80 );
3088 static USERPREF_ENTRY( DISABLEOVERLAPPEDCONTENT, 4, 0x01 );
3089 static USERPREF_ENTRY( CLIENTAREAANIMATION, 4, 0x02 );
3090 static USERPREF_ENTRY( CLEARTYPE, 4, 0x10 );
3091 static USERPREF_ENTRY( SPEECHRECOGNITION, 4, 0x20 );
3093 /* System parameter indexes */
3094 enum spi_index
3096 SPI_SETWORKAREA_IDX,
3097 SPI_INDEX_COUNT
3100 /* indicators whether system parameter value is loaded */
3101 static char spi_loaded[SPI_INDEX_COUNT];
3103 static struct sysparam_rgb_entry system_colors[] =
3105 #define RGB_ENTRY(name,val,reg) { { get_rgb_entry, set_rgb_entry, init_rgb_entry, COLORS_KEY, reg }, (val) }
3106 RGB_ENTRY( COLOR_SCROLLBAR, RGB(212, 208, 200), "Scrollbar" ),
3107 RGB_ENTRY( COLOR_BACKGROUND, RGB(58, 110, 165), "Background" ),
3108 RGB_ENTRY( COLOR_ACTIVECAPTION, RGB(10, 36, 106), "ActiveTitle" ),
3109 RGB_ENTRY( COLOR_INACTIVECAPTION, RGB(128, 128, 128), "InactiveTitle" ),
3110 RGB_ENTRY( COLOR_MENU, RGB(212, 208, 200), "Menu" ),
3111 RGB_ENTRY( COLOR_WINDOW, RGB(255, 255, 255), "Window" ),
3112 RGB_ENTRY( COLOR_WINDOWFRAME, RGB(0, 0, 0), "WindowFrame" ),
3113 RGB_ENTRY( COLOR_MENUTEXT, RGB(0, 0, 0), "MenuText" ),
3114 RGB_ENTRY( COLOR_WINDOWTEXT, RGB(0, 0, 0), "WindowText" ),
3115 RGB_ENTRY( COLOR_CAPTIONTEXT, RGB(255, 255, 255), "TitleText" ),
3116 RGB_ENTRY( COLOR_ACTIVEBORDER, RGB(212, 208, 200), "ActiveBorder" ),
3117 RGB_ENTRY( COLOR_INACTIVEBORDER, RGB(212, 208, 200), "InactiveBorder" ),
3118 RGB_ENTRY( COLOR_APPWORKSPACE, RGB(128, 128, 128), "AppWorkSpace" ),
3119 RGB_ENTRY( COLOR_HIGHLIGHT, RGB(10, 36, 106), "Hilight" ),
3120 RGB_ENTRY( COLOR_HIGHLIGHTTEXT, RGB(255, 255, 255), "HilightText" ),
3121 RGB_ENTRY( COLOR_BTNFACE, RGB(212, 208, 200), "ButtonFace" ),
3122 RGB_ENTRY( COLOR_BTNSHADOW, RGB(128, 128, 128), "ButtonShadow" ),
3123 RGB_ENTRY( COLOR_GRAYTEXT, RGB(128, 128, 128), "GrayText" ),
3124 RGB_ENTRY( COLOR_BTNTEXT, RGB(0, 0, 0), "ButtonText" ),
3125 RGB_ENTRY( COLOR_INACTIVECAPTIONTEXT, RGB(212, 208, 200), "InactiveTitleText" ),
3126 RGB_ENTRY( COLOR_BTNHIGHLIGHT, RGB(255, 255, 255), "ButtonHilight" ),
3127 RGB_ENTRY( COLOR_3DDKSHADOW, RGB(64, 64, 64), "ButtonDkShadow" ),
3128 RGB_ENTRY( COLOR_3DLIGHT, RGB(212, 208, 200), "ButtonLight" ),
3129 RGB_ENTRY( COLOR_INFOTEXT, RGB(0, 0, 0), "InfoText" ),
3130 RGB_ENTRY( COLOR_INFOBK, RGB(255, 255, 225), "InfoWindow" ),
3131 RGB_ENTRY( COLOR_ALTERNATEBTNFACE, RGB(181, 181, 181), "ButtonAlternateFace" ),
3132 RGB_ENTRY( COLOR_HOTLIGHT, RGB(0, 0, 200), "HotTrackingColor" ),
3133 RGB_ENTRY( COLOR_GRADIENTACTIVECAPTION, RGB(166, 202, 240), "GradientActiveTitle" ),
3134 RGB_ENTRY( COLOR_GRADIENTINACTIVECAPTION, RGB(192, 192, 192), "GradientInactiveTitle" ),
3135 RGB_ENTRY( COLOR_MENUHILIGHT, RGB(10, 36, 106), "MenuHilight" ),
3136 RGB_ENTRY( COLOR_MENUBAR, RGB(212, 208, 200), "MenuBar" )
3137 #undef RGB_ENTRY
3140 /* entries that are initialized by default in the registry */
3141 static union sysparam_all_entry * const default_entries[] =
3143 (union sysparam_all_entry *)&entry_ACTIVEWINDOWTRACKING,
3144 (union sysparam_all_entry *)&entry_ACTIVEWNDTRKTIMEOUT,
3145 (union sysparam_all_entry *)&entry_BEEP,
3146 (union sysparam_all_entry *)&entry_BLOCKSENDINPUTRESETS,
3147 (union sysparam_all_entry *)&entry_BORDER,
3148 (union sysparam_all_entry *)&entry_CAPTIONHEIGHT,
3149 (union sysparam_all_entry *)&entry_CAPTIONWIDTH,
3150 (union sysparam_all_entry *)&entry_CARETWIDTH,
3151 (union sysparam_all_entry *)&entry_DESKWALLPAPER,
3152 (union sysparam_all_entry *)&entry_DOUBLECLICKTIME,
3153 (union sysparam_all_entry *)&entry_DOUBLECLKHEIGHT,
3154 (union sysparam_all_entry *)&entry_DOUBLECLKWIDTH,
3155 (union sysparam_all_entry *)&entry_DRAGFULLWINDOWS,
3156 (union sysparam_all_entry *)&entry_DRAGHEIGHT,
3157 (union sysparam_all_entry *)&entry_DRAGWIDTH,
3158 (union sysparam_all_entry *)&entry_FOCUSBORDERHEIGHT,
3159 (union sysparam_all_entry *)&entry_FOCUSBORDERWIDTH,
3160 (union sysparam_all_entry *)&entry_FONTSMOOTHING,
3161 (union sysparam_all_entry *)&entry_FONTSMOOTHINGCONTRAST,
3162 (union sysparam_all_entry *)&entry_FONTSMOOTHINGORIENTATION,
3163 (union sysparam_all_entry *)&entry_FONTSMOOTHINGTYPE,
3164 (union sysparam_all_entry *)&entry_FOREGROUNDFLASHCOUNT,
3165 (union sysparam_all_entry *)&entry_FOREGROUNDLOCKTIMEOUT,
3166 (union sysparam_all_entry *)&entry_ICONHORIZONTALSPACING,
3167 (union sysparam_all_entry *)&entry_ICONTITLEWRAP,
3168 (union sysparam_all_entry *)&entry_ICONVERTICALSPACING,
3169 (union sysparam_all_entry *)&entry_KEYBOARDDELAY,
3170 (union sysparam_all_entry *)&entry_KEYBOARDPREF,
3171 (union sysparam_all_entry *)&entry_KEYBOARDSPEED,
3172 (union sysparam_all_entry *)&entry_LOWPOWERACTIVE,
3173 (union sysparam_all_entry *)&entry_MENUHEIGHT,
3174 (union sysparam_all_entry *)&entry_MENUSHOWDELAY,
3175 (union sysparam_all_entry *)&entry_MENUWIDTH,
3176 (union sysparam_all_entry *)&entry_MOUSEACCELERATION,
3177 (union sysparam_all_entry *)&entry_MOUSEBUTTONSWAP,
3178 (union sysparam_all_entry *)&entry_MOUSECLICKLOCKTIME,
3179 (union sysparam_all_entry *)&entry_MOUSEHOVERHEIGHT,
3180 (union sysparam_all_entry *)&entry_MOUSEHOVERTIME,
3181 (union sysparam_all_entry *)&entry_MOUSEHOVERWIDTH,
3182 (union sysparam_all_entry *)&entry_MOUSESPEED,
3183 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD1,
3184 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD2,
3185 (union sysparam_all_entry *)&entry_PADDEDBORDERWIDTH,
3186 (union sysparam_all_entry *)&entry_SCREENREADER,
3187 (union sysparam_all_entry *)&entry_SCROLLHEIGHT,
3188 (union sysparam_all_entry *)&entry_SCROLLWIDTH,
3189 (union sysparam_all_entry *)&entry_SHOWSOUNDS,
3190 (union sysparam_all_entry *)&entry_SMCAPTIONHEIGHT,
3191 (union sysparam_all_entry *)&entry_SMCAPTIONWIDTH,
3192 (union sysparam_all_entry *)&entry_SNAPTODEFBUTTON,
3193 (union sysparam_all_entry *)&entry_USERPREFERENCESMASK,
3194 (union sysparam_all_entry *)&entry_WHEELSCROLLCHARS,
3195 (union sysparam_all_entry *)&entry_WHEELSCROLLLINES,
3196 (union sysparam_all_entry *)&entry_AUDIODESC_LOCALE,
3197 (union sysparam_all_entry *)&entry_AUDIODESC_ON,
3200 void sysparams_init(void)
3203 DWORD i, dispos, dpi_scaling;
3204 WCHAR layout[KL_NAMELENGTH];
3205 pthread_mutexattr_t attr;
3206 HKEY hkey;
3208 static const WCHAR software_wineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e'};
3209 static const WCHAR temporary_system_parametersW[] =
3210 {'T','e','m','p','o','r','a','r','y',' ','S','y','s','t','e','m',' ',
3211 'P','a','r','a','m','e','t','e','r','s'};
3212 static const WCHAR oneW[] = {'1',0};
3213 static const WCHAR kl_preloadW[] =
3214 {'K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','\\','P','r','e','l','o','a','d'};
3216 pthread_mutexattr_init( &attr );
3217 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
3218 pthread_mutex_init( &user_mutex, &attr );
3219 pthread_mutexattr_destroy( &attr );
3221 if ((hkey = reg_create_key( hkcu_key, kl_preloadW, sizeof(kl_preloadW), 0, NULL )))
3223 if (NtUserGetKeyboardLayoutName( layout ))
3224 set_reg_value( hkey, oneW, REG_SZ, (const BYTE *)layout,
3225 (lstrlenW(layout) + 1) * sizeof(WCHAR) );
3226 NtClose( hkey );
3229 /* this one must be non-volatile */
3230 if (!(hkey = reg_create_key( hkcu_key, software_wineW, sizeof(software_wineW), 0, NULL )))
3232 ERR("Can't create wine registry branch\n");
3233 return;
3236 /* @@ Wine registry key: HKCU\Software\Wine\Temporary System Parameters */
3237 if (!(volatile_base_key = reg_create_key( hkey, temporary_system_parametersW,
3238 sizeof(temporary_system_parametersW),
3239 REG_OPTION_VOLATILE, &dispos )))
3240 ERR("Can't create non-permanent wine registry branch\n");
3242 NtClose( hkey );
3244 config_key = reg_create_key( NULL, config_keyW, sizeof(config_keyW), 0, NULL );
3246 get_dword_entry( (union sysparam_all_entry *)&entry_LOGPIXELS, 0, &system_dpi, 0 );
3247 if (!system_dpi) /* check fallback key */
3249 static const WCHAR log_pixelsW[] = {'L','o','g','P','i','x','e','l','s',0};
3250 static const WCHAR software_fontsW[] =
3251 {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s'};
3253 if ((hkey = reg_open_key( config_key, software_fontsW, sizeof(software_fontsW) )))
3255 char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(DWORD)])];
3256 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
3258 if (query_reg_value( hkey, log_pixelsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
3259 system_dpi = *(const DWORD *)value->Data;
3260 NtClose( hkey );
3263 if (!system_dpi) system_dpi = USER_DEFAULT_SCREEN_DPI;
3265 /* FIXME: what do the DpiScalingVer flags mean? */
3266 get_dword_entry( (union sysparam_all_entry *)&entry_DPISCALINGVER, 0, &dpi_scaling, 0 );
3267 if (!dpi_scaling) NtUserSetProcessDpiAwarenessContext( NTUSER_DPI_PER_MONITOR_AWARE, 0 );
3269 if (volatile_base_key && dispos == REG_CREATED_NEW_KEY) /* first process, initialize entries */
3271 for (i = 0; i < ARRAY_SIZE( default_entries ); i++)
3272 default_entries[i]->hdr.init( default_entries[i] );
3276 static BOOL update_desktop_wallpaper(void)
3278 /* FIXME: move implementation from user32 */
3279 entry_DESKWALLPAPER.hdr.loaded = entry_DESKPATTERN.hdr.loaded = FALSE;
3280 return TRUE;
3283 /***********************************************************************
3284 * NtUserSystemParametersInfoForDpi (win32u.@)
3286 BOOL WINAPI NtUserSystemParametersInfoForDpi( UINT action, UINT val, PVOID ptr, UINT winini, UINT dpi )
3288 BOOL ret = FALSE;
3290 switch (action)
3292 case SPI_GETICONTITLELOGFONT:
3293 ret = get_entry_dpi( &entry_ICONTITLELOGFONT, val, ptr, dpi );
3294 break;
3295 case SPI_GETNONCLIENTMETRICS:
3297 NONCLIENTMETRICSW *ncm = ptr;
3299 if (!ncm) break;
3300 ret = get_entry_dpi( &entry_BORDER, 0, &ncm->iBorderWidth, dpi ) &&
3301 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ncm->iScrollWidth, dpi ) &&
3302 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ncm->iScrollHeight, dpi ) &&
3303 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ncm->iCaptionWidth, dpi ) &&
3304 get_entry_dpi( &entry_CAPTIONHEIGHT, 0, &ncm->iCaptionHeight, dpi ) &&
3305 get_entry_dpi( &entry_CAPTIONLOGFONT, 0, &ncm->lfCaptionFont, dpi ) &&
3306 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ncm->iSmCaptionWidth, dpi ) &&
3307 get_entry_dpi( &entry_SMCAPTIONHEIGHT, 0, &ncm->iSmCaptionHeight, dpi ) &&
3308 get_entry_dpi( &entry_SMCAPTIONLOGFONT, 0, &ncm->lfSmCaptionFont, dpi ) &&
3309 get_entry_dpi( &entry_MENUWIDTH, 0, &ncm->iMenuWidth, dpi ) &&
3310 get_entry_dpi( &entry_MENUHEIGHT, 0, &ncm->iMenuHeight, dpi ) &&
3311 get_entry_dpi( &entry_MENULOGFONT, 0, &ncm->lfMenuFont, dpi ) &&
3312 get_entry_dpi( &entry_STATUSLOGFONT, 0, &ncm->lfStatusFont, dpi ) &&
3313 get_entry_dpi( &entry_MESSAGELOGFONT, 0, &ncm->lfMessageFont, dpi );
3314 if (ret && ncm->cbSize == sizeof(NONCLIENTMETRICSW))
3315 ret = get_entry_dpi( &entry_PADDEDBORDERWIDTH, 0, &ncm->iPaddedBorderWidth, dpi );
3316 normalize_nonclientmetrics( ncm );
3317 break;
3319 case SPI_GETICONMETRICS:
3321 ICONMETRICSW *im = ptr;
3322 if (im && im->cbSize == sizeof(*im))
3323 ret = get_entry_dpi( &entry_ICONHORIZONTALSPACING, 0, &im->iHorzSpacing, dpi ) &&
3324 get_entry_dpi( &entry_ICONVERTICALSPACING, 0, &im->iVertSpacing, dpi ) &&
3325 get_entry_dpi( &entry_ICONTITLEWRAP, 0, &im->iTitleWrap, dpi ) &&
3326 get_entry_dpi( &entry_ICONTITLELOGFONT, 0, &im->lfFont, dpi );
3327 break;
3329 default:
3330 SetLastError( ERROR_INVALID_PARAMETER );
3331 break;
3333 return ret;
3336 /***********************************************************************
3337 * NtUserSystemParametersInfo (win32u.@)
3339 * Each system parameter has flag which shows whether the parameter
3340 * is loaded or not. Parameters, stored directly in SysParametersInfo are
3341 * loaded from registry only when they are requested and the flag is
3342 * "false", after the loading the flag is set to "true". On interprocess
3343 * notification of the parameter change the corresponding parameter flag is
3344 * set to "false". The parameter value will be reloaded when it is requested
3345 * the next time.
3346 * Parameters, backed by or depend on GetSystemMetrics are processed
3347 * differently. These parameters are always loaded. They are reloaded right
3348 * away on interprocess change notification. We can't do lazy loading because
3349 * we don't want to complicate GetSystemMetrics.
3350 * Parameters backed by driver settings are read from corresponding setting.
3351 * On the parameter change request the setting is changed. Interprocess change
3352 * notifications are ignored.
3353 * When parameter value is updated the changed value is stored in permanent
3354 * registry branch if saving is requested. Otherwise it is stored
3355 * in temporary branch
3357 * Some SPI values can also be stored as Twips values in the registry,
3358 * don't forget the conversion!
3360 BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT winini )
3362 #define WINE_SPI_FIXME(x) \
3363 case x: \
3365 static BOOL warn = TRUE; \
3366 if (warn) \
3368 warn = FALSE; \
3369 FIXME( "Unimplemented action: %u (%s)\n", x, #x ); \
3372 SetLastError( ERROR_INVALID_SPI_VALUE ); \
3373 ret = FALSE; \
3374 break
3375 #define WINE_SPI_WARN(x) \
3376 case x: \
3377 WARN( "Ignored action: %u (%s)\n", x, #x ); \
3378 ret = TRUE; \
3379 break
3381 BOOL ret = user_driver->pSystemParametersInfo( action, val, ptr, winini );
3382 unsigned spi_idx = 0;
3384 if (!ret) switch (action)
3386 case SPI_GETBEEP:
3387 ret = get_entry( &entry_BEEP, val, ptr );
3388 break;
3389 case SPI_SETBEEP:
3390 ret = set_entry( &entry_BEEP, val, ptr, winini );
3391 break;
3392 case SPI_GETMOUSE:
3393 ret = get_entry( &entry_MOUSETHRESHOLD1, val, (INT *)ptr ) &&
3394 get_entry( &entry_MOUSETHRESHOLD2, val, (INT *)ptr + 1 ) &&
3395 get_entry( &entry_MOUSEACCELERATION, val, (INT *)ptr + 2 );
3396 break;
3397 case SPI_SETMOUSE:
3398 ret = set_entry( &entry_MOUSETHRESHOLD1, ((INT *)ptr)[0], ptr, winini ) &&
3399 set_entry( &entry_MOUSETHRESHOLD2, ((INT *)ptr)[1], ptr, winini ) &&
3400 set_entry( &entry_MOUSEACCELERATION, ((INT *)ptr)[2], ptr, winini );
3401 break;
3402 case SPI_GETBORDER:
3403 ret = get_entry( &entry_BORDER, val, ptr );
3404 if (*(INT*)ptr < 1) *(INT*)ptr = 1;
3405 break;
3406 case SPI_SETBORDER:
3407 ret = set_entry( &entry_BORDER, val, ptr, winini );
3408 break;
3409 case SPI_GETKEYBOARDSPEED:
3410 ret = get_entry( &entry_KEYBOARDSPEED, val, ptr );
3411 break;
3412 case SPI_SETKEYBOARDSPEED:
3413 if (val > 31) val = 31;
3414 ret = set_entry( &entry_KEYBOARDSPEED, val, ptr, winini );
3415 break;
3417 WINE_SPI_WARN(SPI_LANGDRIVER); /* not implemented in Windows */
3419 case SPI_ICONHORIZONTALSPACING:
3420 if (ptr != NULL)
3421 ret = get_entry( &entry_ICONHORIZONTALSPACING, val, ptr );
3422 else
3424 int min_val = map_to_dpi( 32, get_system_dpi() );
3425 ret = set_entry( &entry_ICONHORIZONTALSPACING, max( min_val, val ), ptr, winini );
3427 break;
3428 case SPI_GETSCREENSAVETIMEOUT:
3429 ret = get_entry( &entry_SCREENSAVETIMEOUT, val, ptr );
3430 break;
3431 case SPI_SETSCREENSAVETIMEOUT:
3432 ret = set_entry( &entry_SCREENSAVETIMEOUT, val, ptr, winini );
3433 break;
3434 case SPI_GETSCREENSAVEACTIVE:
3435 ret = get_entry( &entry_SCREENSAVEACTIVE, val, ptr );
3436 break;
3437 case SPI_SETSCREENSAVEACTIVE:
3438 ret = set_entry( &entry_SCREENSAVEACTIVE, val, ptr, winini );
3439 break;
3440 case SPI_GETGRIDGRANULARITY:
3441 ret = get_entry( &entry_GRIDGRANULARITY, val, ptr );
3442 break;
3443 case SPI_SETGRIDGRANULARITY:
3444 ret = set_entry( &entry_GRIDGRANULARITY, val, ptr, winini );
3445 break;
3446 case SPI_SETDESKWALLPAPER:
3447 if (!ptr || set_entry( &entry_DESKWALLPAPER, val, ptr, winini ))
3448 ret = update_desktop_wallpaper();
3449 break;
3450 case SPI_SETDESKPATTERN:
3451 if (!ptr || set_entry( &entry_DESKPATTERN, val, ptr, winini ))
3452 ret = update_desktop_wallpaper();
3453 break;
3454 case SPI_GETKEYBOARDDELAY:
3455 ret = get_entry( &entry_KEYBOARDDELAY, val, ptr );
3456 break;
3457 case SPI_SETKEYBOARDDELAY:
3458 ret = set_entry( &entry_KEYBOARDDELAY, val, ptr, winini );
3459 break;
3460 case SPI_ICONVERTICALSPACING:
3461 if (ptr != NULL)
3462 ret = get_entry( &entry_ICONVERTICALSPACING, val, ptr );
3463 else
3465 int min_val = map_to_dpi( 32, get_system_dpi() );
3466 ret = set_entry( &entry_ICONVERTICALSPACING, max( min_val, val ), ptr, winini );
3468 break;
3469 case SPI_GETICONTITLEWRAP:
3470 ret = get_entry( &entry_ICONTITLEWRAP, val, ptr );
3471 break;
3472 case SPI_SETICONTITLEWRAP:
3473 ret = set_entry( &entry_ICONTITLEWRAP, val, ptr, winini );
3474 break;
3475 case SPI_GETMENUDROPALIGNMENT:
3476 ret = get_entry( &entry_MENUDROPALIGNMENT, val, ptr );
3477 break;
3478 case SPI_SETMENUDROPALIGNMENT:
3479 ret = set_entry( &entry_MENUDROPALIGNMENT, val, ptr, winini );
3480 break;
3481 case SPI_SETDOUBLECLKWIDTH:
3482 ret = set_entry( &entry_DOUBLECLKWIDTH, val, ptr, winini );
3483 break;
3484 case SPI_SETDOUBLECLKHEIGHT:
3485 ret = set_entry( &entry_DOUBLECLKHEIGHT, val, ptr, winini );
3486 break;
3487 case SPI_GETICONTITLELOGFONT:
3488 ret = get_entry( &entry_ICONTITLELOGFONT, val, ptr );
3489 break;
3490 case SPI_SETDOUBLECLICKTIME:
3491 ret = set_entry( &entry_DOUBLECLICKTIME, val, ptr, winini );
3492 break;
3493 case SPI_SETMOUSEBUTTONSWAP:
3494 ret = set_entry( &entry_MOUSEBUTTONSWAP, val, ptr, winini );
3495 break;
3496 case SPI_SETICONTITLELOGFONT:
3497 ret = set_entry( &entry_ICONTITLELOGFONT, val, ptr, winini );
3498 break;
3499 case SPI_GETFASTTASKSWITCH:
3500 if (!ptr) return FALSE;
3501 *(BOOL *)ptr = TRUE;
3502 ret = TRUE;
3503 break;
3504 case SPI_SETFASTTASKSWITCH:
3505 /* the action is disabled */
3506 ret = FALSE;
3507 break;
3508 case SPI_SETDRAGFULLWINDOWS:
3509 ret = set_entry( &entry_DRAGFULLWINDOWS, val, ptr, winini );
3510 break;
3511 case SPI_GETDRAGFULLWINDOWS:
3512 ret = get_entry( &entry_DRAGFULLWINDOWS, val, ptr );
3513 break;
3514 case SPI_GETNONCLIENTMETRICS:
3516 NONCLIENTMETRICSW *nm = ptr;
3517 int padded_border;
3519 if (!ptr) return FALSE;
3521 ret = get_entry( &entry_BORDER, 0, &nm->iBorderWidth ) &&
3522 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border ) &&
3523 get_entry( &entry_SCROLLWIDTH, 0, &nm->iScrollWidth ) &&
3524 get_entry( &entry_SCROLLHEIGHT, 0, &nm->iScrollHeight ) &&
3525 get_entry( &entry_CAPTIONWIDTH, 0, &nm->iCaptionWidth ) &&
3526 get_entry( &entry_CAPTIONHEIGHT, 0, &nm->iCaptionHeight ) &&
3527 get_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont ) &&
3528 get_entry( &entry_SMCAPTIONWIDTH, 0, &nm->iSmCaptionWidth ) &&
3529 get_entry( &entry_SMCAPTIONHEIGHT, 0, &nm->iSmCaptionHeight ) &&
3530 get_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont ) &&
3531 get_entry( &entry_MENUWIDTH, 0, &nm->iMenuWidth ) &&
3532 get_entry( &entry_MENUHEIGHT, 0, &nm->iMenuHeight ) &&
3533 get_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont ) &&
3534 get_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont ) &&
3535 get_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont );
3536 if (ret)
3538 nm->iBorderWidth += padded_border;
3539 if (nm->cbSize == sizeof(NONCLIENTMETRICSW)) nm->iPaddedBorderWidth = 0;
3541 normalize_nonclientmetrics( nm );
3542 break;
3544 case SPI_SETNONCLIENTMETRICS:
3546 LPNONCLIENTMETRICSW nm = ptr;
3547 int padded_border;
3549 if (nm && (nm->cbSize == sizeof(NONCLIENTMETRICSW) ||
3550 nm->cbSize == FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth)))
3552 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border );
3554 ret = set_entry( &entry_BORDER, nm->iBorderWidth - padded_border, NULL, winini ) &&
3555 set_entry( &entry_SCROLLWIDTH, nm->iScrollWidth, NULL, winini ) &&
3556 set_entry( &entry_SCROLLHEIGHT, nm->iScrollHeight, NULL, winini ) &&
3557 set_entry( &entry_CAPTIONWIDTH, nm->iCaptionWidth, NULL, winini ) &&
3558 set_entry( &entry_CAPTIONHEIGHT, nm->iCaptionHeight, NULL, winini ) &&
3559 set_entry( &entry_SMCAPTIONWIDTH, nm->iSmCaptionWidth, NULL, winini ) &&
3560 set_entry( &entry_SMCAPTIONHEIGHT, nm->iSmCaptionHeight, NULL, winini ) &&
3561 set_entry( &entry_MENUWIDTH, nm->iMenuWidth, NULL, winini ) &&
3562 set_entry( &entry_MENUHEIGHT, nm->iMenuHeight, NULL, winini ) &&
3563 set_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont, winini ) &&
3564 set_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont, winini ) &&
3565 set_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont, winini ) &&
3566 set_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont, winini ) &&
3567 set_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont, winini );
3569 break;
3571 case SPI_GETMINIMIZEDMETRICS:
3573 MINIMIZEDMETRICS *mm = ptr;
3574 if (mm && mm->cbSize == sizeof(*mm)) {
3575 ret = get_entry( &entry_MINWIDTH, 0, &mm->iWidth ) &&
3576 get_entry( &entry_MINHORZGAP, 0, &mm->iHorzGap ) &&
3577 get_entry( &entry_MINVERTGAP, 0, &mm->iVertGap ) &&
3578 get_entry( &entry_MINARRANGE, 0, &mm->iArrange );
3579 mm->iWidth = max( 0, mm->iWidth );
3580 mm->iHorzGap = max( 0, mm->iHorzGap );
3581 mm->iVertGap = max( 0, mm->iVertGap );
3582 mm->iArrange &= 0x0f;
3584 break;
3586 case SPI_SETMINIMIZEDMETRICS:
3588 MINIMIZEDMETRICS *mm = ptr;
3589 if (mm && mm->cbSize == sizeof(*mm))
3590 ret = set_entry( &entry_MINWIDTH, max( 0, mm->iWidth ), NULL, winini ) &&
3591 set_entry( &entry_MINHORZGAP, max( 0, mm->iHorzGap ), NULL, winini ) &&
3592 set_entry( &entry_MINVERTGAP, max( 0, mm->iVertGap ), NULL, winini ) &&
3593 set_entry( &entry_MINARRANGE, mm->iArrange & 0x0f, NULL, winini );
3594 break;
3596 case SPI_GETICONMETRICS:
3598 ICONMETRICSW *icon = ptr;
3599 if(icon && icon->cbSize == sizeof(*icon))
3601 ret = get_entry( &entry_ICONHORIZONTALSPACING, 0, &icon->iHorzSpacing ) &&
3602 get_entry( &entry_ICONVERTICALSPACING, 0, &icon->iVertSpacing ) &&
3603 get_entry( &entry_ICONTITLEWRAP, 0, &icon->iTitleWrap ) &&
3604 get_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont );
3606 break;
3608 case SPI_SETICONMETRICS:
3610 ICONMETRICSW *icon = ptr;
3611 if (icon && icon->cbSize == sizeof(*icon))
3612 ret = set_entry( &entry_ICONVERTICALSPACING, max(32,icon->iVertSpacing), NULL, winini ) &&
3613 set_entry( &entry_ICONHORIZONTALSPACING, max(32,icon->iHorzSpacing), NULL, winini ) &&
3614 set_entry( &entry_ICONTITLEWRAP, icon->iTitleWrap, NULL, winini ) &&
3615 set_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont, winini );
3616 break;
3618 case SPI_SETWORKAREA:
3620 if (!ptr) return FALSE;
3621 spi_idx = SPI_SETWORKAREA_IDX;
3622 work_area = *(RECT*)ptr;
3623 spi_loaded[spi_idx] = TRUE;
3624 ret = TRUE;
3625 break;
3627 case SPI_GETWORKAREA:
3629 if (!ptr) return FALSE;
3631 spi_idx = SPI_SETWORKAREA_IDX;
3632 if (!spi_loaded[spi_idx])
3634 struct monitor *monitor;
3636 if (!lock_display_devices()) return FALSE;
3638 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
3640 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
3641 work_area = monitor->rc_work;
3642 break;
3645 unlock_display_devices();
3646 spi_loaded[spi_idx] = TRUE;
3648 *(RECT *)ptr = map_dpi_rect( work_area, system_dpi, get_thread_dpi() );
3649 ret = TRUE;
3650 TRACE("work area %s\n", wine_dbgstr_rect( &work_area ));
3651 break;
3654 WINE_SPI_FIXME(SPI_SETPENWINDOWS);
3656 case SPI_GETFILTERKEYS:
3658 LPFILTERKEYS filter_keys = ptr;
3659 WARN("SPI_GETFILTERKEYS not fully implemented\n");
3660 if (filter_keys && filter_keys->cbSize == sizeof(FILTERKEYS))
3662 /* Indicate that no FilterKeys feature available */
3663 filter_keys->dwFlags = 0;
3664 filter_keys->iWaitMSec = 0;
3665 filter_keys->iDelayMSec = 0;
3666 filter_keys->iRepeatMSec = 0;
3667 filter_keys->iBounceMSec = 0;
3668 ret = TRUE;
3670 break;
3672 WINE_SPI_FIXME(SPI_SETFILTERKEYS);
3674 case SPI_GETTOGGLEKEYS:
3676 LPTOGGLEKEYS toggle_keys = ptr;
3677 WARN("SPI_GETTOGGLEKEYS not fully implemented\n");
3678 if (toggle_keys && toggle_keys->cbSize == sizeof(TOGGLEKEYS))
3680 /* Indicate that no ToggleKeys feature available */
3681 toggle_keys->dwFlags = 0;
3682 ret = TRUE;
3684 break;
3687 WINE_SPI_FIXME(SPI_SETTOGGLEKEYS);
3689 case SPI_GETMOUSEKEYS:
3691 MOUSEKEYS *mouse_keys = ptr;
3692 WARN("SPI_GETMOUSEKEYS not fully implemented\n");
3693 if (mouse_keys && mouse_keys->cbSize == sizeof(MOUSEKEYS))
3695 /* Indicate that no MouseKeys feature available */
3696 mouse_keys->dwFlags = 0;
3697 mouse_keys->iMaxSpeed = 360;
3698 mouse_keys->iTimeToMaxSpeed = 1000;
3699 mouse_keys->iCtrlSpeed = 0;
3700 mouse_keys->dwReserved1 = 0;
3701 mouse_keys->dwReserved2 = 0;
3702 ret = TRUE;
3704 break;
3707 WINE_SPI_FIXME(SPI_SETMOUSEKEYS);
3709 case SPI_GETSHOWSOUNDS:
3710 ret = get_entry( &entry_SHOWSOUNDS, val, ptr );
3711 break;
3712 case SPI_SETSHOWSOUNDS:
3713 ret = set_entry( &entry_SHOWSOUNDS, val, ptr, winini );
3714 break;
3715 case SPI_GETSTICKYKEYS:
3717 STICKYKEYS *sticky_keys = ptr;
3718 WARN("SPI_GETSTICKYKEYS not fully implemented\n");
3719 if (sticky_keys && sticky_keys->cbSize == sizeof(STICKYKEYS))
3721 /* Indicate that no StickyKeys feature available */
3722 sticky_keys->dwFlags = 0;
3723 ret = TRUE;
3725 break;
3728 WINE_SPI_FIXME(SPI_SETSTICKYKEYS);
3730 case SPI_GETACCESSTIMEOUT:
3732 ACCESSTIMEOUT *access_timeout = ptr;
3733 WARN("SPI_GETACCESSTIMEOUT not fully implemented\n");
3734 if (access_timeout && access_timeout->cbSize == sizeof(ACCESSTIMEOUT))
3736 /* Indicate that no accessibility features timeout is available */
3737 access_timeout->dwFlags = 0;
3738 access_timeout->iTimeOutMSec = 0;
3739 ret = TRUE;
3741 break;
3744 WINE_SPI_FIXME(SPI_SETACCESSTIMEOUT);
3746 case SPI_GETSERIALKEYS:
3748 LPSERIALKEYSW serial_keys = ptr;
3749 WARN("SPI_GETSERIALKEYS not fully implemented\n");
3750 if (serial_keys && serial_keys->cbSize == sizeof(SERIALKEYSW))
3752 /* Indicate that no SerialKeys feature available */
3753 serial_keys->dwFlags = 0;
3754 serial_keys->lpszActivePort = NULL;
3755 serial_keys->lpszPort = NULL;
3756 serial_keys->iBaudRate = 0;
3757 serial_keys->iPortState = 0;
3758 ret = TRUE;
3760 break;
3763 WINE_SPI_FIXME(SPI_SETSERIALKEYS);
3765 case SPI_GETSOUNDSENTRY:
3767 SOUNDSENTRYW *sound_sentry = ptr;
3768 WARN("SPI_GETSOUNDSENTRY not fully implemented\n");
3769 if (sound_sentry && sound_sentry->cbSize == sizeof(SOUNDSENTRYW))
3771 /* Indicate that no SoundSentry feature available */
3772 sound_sentry->dwFlags = 0;
3773 sound_sentry->iFSTextEffect = 0;
3774 sound_sentry->iFSTextEffectMSec = 0;
3775 sound_sentry->iFSTextEffectColorBits = 0;
3776 sound_sentry->iFSGrafEffect = 0;
3777 sound_sentry->iFSGrafEffectMSec = 0;
3778 sound_sentry->iFSGrafEffectColor = 0;
3779 sound_sentry->iWindowsEffect = 0;
3780 sound_sentry->iWindowsEffectMSec = 0;
3781 sound_sentry->lpszWindowsEffectDLL = 0;
3782 sound_sentry->iWindowsEffectOrdinal = 0;
3783 ret = TRUE;
3785 break;
3788 WINE_SPI_FIXME(SPI_SETSOUNDSENTRY);
3790 case SPI_GETHIGHCONTRAST:
3792 HIGHCONTRASTW *high_contrast = ptr;
3793 WARN("SPI_GETHIGHCONTRAST not fully implemented\n");
3794 if (high_contrast && high_contrast->cbSize == sizeof(HIGHCONTRASTW))
3796 /* Indicate that no high contrast feature available */
3797 high_contrast->dwFlags = 0;
3798 high_contrast->lpszDefaultScheme = NULL;
3799 ret = TRUE;
3801 break;
3804 WINE_SPI_FIXME(SPI_SETHIGHCONTRAST);
3806 case SPI_GETKEYBOARDPREF:
3807 ret = get_entry( &entry_KEYBOARDPREF, val, ptr );
3808 break;
3809 case SPI_SETKEYBOARDPREF:
3810 ret = set_entry( &entry_KEYBOARDPREF, val, ptr, winini );
3811 break;
3812 case SPI_GETSCREENREADER:
3813 ret = get_entry( &entry_SCREENREADER, val, ptr );
3814 break;
3815 case SPI_SETSCREENREADER:
3816 ret = set_entry( &entry_SCREENREADER, val, ptr, winini );
3817 break;
3819 case SPI_GETANIMATION:
3821 ANIMATIONINFO *anim_info = ptr;
3823 /* Tell it "disabled" */
3824 if (anim_info && anim_info->cbSize == sizeof(ANIMATIONINFO))
3826 /* Minimize and restore animation is disabled (nonzero == enabled) */
3827 anim_info->iMinAnimate = 0;
3828 ret = TRUE;
3830 break;
3833 WINE_SPI_WARN(SPI_SETANIMATION);
3835 case SPI_GETFONTSMOOTHING:
3836 ret = get_entry( &entry_FONTSMOOTHING, val, ptr );
3837 if (ret) *(UINT *)ptr = (*(UINT *)ptr != 0);
3838 break;
3839 case SPI_SETFONTSMOOTHING:
3840 val = val ? 2 : 0; /* Win NT4/2k/XP behavior */
3841 ret = set_entry( &entry_FONTSMOOTHING, val, ptr, winini );
3842 break;
3843 case SPI_SETDRAGWIDTH:
3844 ret = set_entry( &entry_DRAGWIDTH, val, ptr, winini );
3845 break;
3846 case SPI_SETDRAGHEIGHT:
3847 ret = set_entry( &entry_DRAGHEIGHT, val, ptr, winini );
3848 break;
3850 WINE_SPI_FIXME(SPI_SETHANDHELD);
3851 WINE_SPI_FIXME(SPI_GETLOWPOWERTIMEOUT);
3852 WINE_SPI_FIXME(SPI_GETPOWEROFFTIMEOUT);
3853 WINE_SPI_FIXME(SPI_SETLOWPOWERTIMEOUT);
3854 WINE_SPI_FIXME(SPI_SETPOWEROFFTIMEOUT);
3856 case SPI_GETLOWPOWERACTIVE:
3857 ret = get_entry( &entry_LOWPOWERACTIVE, val, ptr );
3858 break;
3859 case SPI_SETLOWPOWERACTIVE:
3860 ret = set_entry( &entry_LOWPOWERACTIVE, val, ptr, winini );
3861 break;
3862 case SPI_GETPOWEROFFACTIVE:
3863 ret = get_entry( &entry_POWEROFFACTIVE, val, ptr );
3864 break;
3865 case SPI_SETPOWEROFFACTIVE:
3866 ret = set_entry( &entry_POWEROFFACTIVE, val, ptr, winini );
3867 break;
3869 WINE_SPI_FIXME(SPI_SETCURSORS);
3870 WINE_SPI_FIXME(SPI_SETICONS);
3872 case SPI_GETDEFAULTINPUTLANG:
3873 ret = NtUserGetKeyboardLayout(0) != 0;
3874 break;
3876 WINE_SPI_FIXME(SPI_SETDEFAULTINPUTLANG);
3877 WINE_SPI_FIXME(SPI_SETLANGTOGGLE);
3879 case SPI_GETWINDOWSEXTENSION:
3880 WARN( "pretend no support for Win9x Plus! for now.\n" );
3881 ret = FALSE; /* yes, this is the result value */
3882 break;
3883 case SPI_SETMOUSETRAILS:
3884 ret = set_entry( &entry_MOUSETRAILS, val, ptr, winini );
3885 break;
3886 case SPI_GETMOUSETRAILS:
3887 ret = get_entry( &entry_MOUSETRAILS, val, ptr );
3888 break;
3889 case SPI_GETSNAPTODEFBUTTON:
3890 ret = get_entry( &entry_SNAPTODEFBUTTON, val, ptr );
3891 break;
3892 case SPI_SETSNAPTODEFBUTTON:
3893 ret = set_entry( &entry_SNAPTODEFBUTTON, val, ptr, winini );
3894 break;
3895 case SPI_SETSCREENSAVERRUNNING:
3896 ret = set_entry( &entry_SCREENSAVERRUNNING, val, ptr, winini );
3897 break;
3898 case SPI_GETMOUSEHOVERWIDTH:
3899 ret = get_entry( &entry_MOUSEHOVERWIDTH, val, ptr );
3900 break;
3901 case SPI_SETMOUSEHOVERWIDTH:
3902 ret = set_entry( &entry_MOUSEHOVERWIDTH, val, ptr, winini );
3903 break;
3904 case SPI_GETMOUSEHOVERHEIGHT:
3905 ret = get_entry( &entry_MOUSEHOVERHEIGHT, val, ptr );
3906 break;
3907 case SPI_SETMOUSEHOVERHEIGHT:
3908 ret = set_entry( &entry_MOUSEHOVERHEIGHT, val, ptr, winini );
3909 break;
3910 case SPI_GETMOUSEHOVERTIME:
3911 ret = get_entry( &entry_MOUSEHOVERTIME, val, ptr );
3912 break;
3913 case SPI_SETMOUSEHOVERTIME:
3914 ret = set_entry( &entry_MOUSEHOVERTIME, val, ptr, winini );
3915 break;
3916 case SPI_GETWHEELSCROLLLINES:
3917 ret = get_entry( &entry_WHEELSCROLLLINES, val, ptr );
3918 break;
3919 case SPI_SETWHEELSCROLLLINES:
3920 ret = set_entry( &entry_WHEELSCROLLLINES, val, ptr, winini );
3921 break;
3922 case SPI_GETMENUSHOWDELAY:
3923 ret = get_entry( &entry_MENUSHOWDELAY, val, ptr );
3924 break;
3925 case SPI_SETMENUSHOWDELAY:
3926 ret = set_entry( &entry_MENUSHOWDELAY, val, ptr, winini );
3927 break;
3928 case SPI_GETWHEELSCROLLCHARS:
3929 ret = get_entry( &entry_WHEELSCROLLCHARS, val, ptr );
3930 break;
3931 case SPI_SETWHEELSCROLLCHARS:
3932 ret = set_entry( &entry_WHEELSCROLLCHARS, val, ptr, winini );
3933 break;
3935 WINE_SPI_FIXME(SPI_GETSHOWIMEUI);
3936 WINE_SPI_FIXME(SPI_SETSHOWIMEUI);
3938 case SPI_GETMOUSESPEED:
3939 ret = get_entry( &entry_MOUSESPEED, val, ptr );
3940 break;
3941 case SPI_SETMOUSESPEED:
3942 ret = set_entry( &entry_MOUSESPEED, val, ptr, winini );
3943 break;
3944 case SPI_GETSCREENSAVERRUNNING:
3945 ret = get_entry( &entry_SCREENSAVERRUNNING, val, ptr );
3946 break;
3947 case SPI_GETDESKWALLPAPER:
3948 ret = get_entry( &entry_DESKWALLPAPER, val, ptr );
3949 break;
3950 case SPI_GETACTIVEWINDOWTRACKING:
3951 ret = get_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr );
3952 break;
3953 case SPI_SETACTIVEWINDOWTRACKING:
3954 ret = set_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr, winini );
3955 break;
3956 case SPI_GETMENUANIMATION:
3957 ret = get_entry( &entry_MENUANIMATION, val, ptr );
3958 break;
3959 case SPI_SETMENUANIMATION:
3960 ret = set_entry( &entry_MENUANIMATION, val, ptr, winini );
3961 break;
3962 case SPI_GETCOMBOBOXANIMATION:
3963 ret = get_entry( &entry_COMBOBOXANIMATION, val, ptr );
3964 break;
3965 case SPI_SETCOMBOBOXANIMATION:
3966 ret = set_entry( &entry_COMBOBOXANIMATION, val, ptr, winini );
3967 break;
3968 case SPI_GETLISTBOXSMOOTHSCROLLING:
3969 ret = get_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr );
3970 break;
3971 case SPI_SETLISTBOXSMOOTHSCROLLING:
3972 ret = set_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr, winini );
3973 break;
3974 case SPI_GETGRADIENTCAPTIONS:
3975 ret = get_entry( &entry_GRADIENTCAPTIONS, val, ptr );
3976 break;
3977 case SPI_SETGRADIENTCAPTIONS:
3978 ret = set_entry( &entry_GRADIENTCAPTIONS, val, ptr, winini );
3979 break;
3980 case SPI_GETKEYBOARDCUES:
3981 ret = get_entry( &entry_KEYBOARDCUES, val, ptr );
3982 break;
3983 case SPI_SETKEYBOARDCUES:
3984 ret = set_entry( &entry_KEYBOARDCUES, val, ptr, winini );
3985 break;
3986 case SPI_GETACTIVEWNDTRKZORDER:
3987 ret = get_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr );
3988 break;
3989 case SPI_SETACTIVEWNDTRKZORDER:
3990 ret = set_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr, winini );
3991 break;
3992 case SPI_GETHOTTRACKING:
3993 ret = get_entry( &entry_HOTTRACKING, val, ptr );
3994 break;
3995 case SPI_SETHOTTRACKING:
3996 ret = set_entry( &entry_HOTTRACKING, val, ptr, winini );
3997 break;
3998 case SPI_GETMENUFADE:
3999 ret = get_entry( &entry_MENUFADE, val, ptr );
4000 break;
4001 case SPI_SETMENUFADE:
4002 ret = set_entry( &entry_MENUFADE, val, ptr, winini );
4003 break;
4004 case SPI_GETSELECTIONFADE:
4005 ret = get_entry( &entry_SELECTIONFADE, val, ptr );
4006 break;
4007 case SPI_SETSELECTIONFADE:
4008 ret = set_entry( &entry_SELECTIONFADE, val, ptr, winini );
4009 break;
4010 case SPI_GETTOOLTIPANIMATION:
4011 ret = get_entry( &entry_TOOLTIPANIMATION, val, ptr );
4012 break;
4013 case SPI_SETTOOLTIPANIMATION:
4014 ret = set_entry( &entry_TOOLTIPANIMATION, val, ptr, winini );
4015 break;
4016 case SPI_GETTOOLTIPFADE:
4017 ret = get_entry( &entry_TOOLTIPFADE, val, ptr );
4018 break;
4019 case SPI_SETTOOLTIPFADE:
4020 ret = set_entry( &entry_TOOLTIPFADE, val, ptr, winini );
4021 break;
4022 case SPI_GETCURSORSHADOW:
4023 ret = get_entry( &entry_CURSORSHADOW, val, ptr );
4024 break;
4025 case SPI_SETCURSORSHADOW:
4026 ret = set_entry( &entry_CURSORSHADOW, val, ptr, winini );
4027 break;
4028 case SPI_GETMOUSESONAR:
4029 ret = get_entry( &entry_MOUSESONAR, val, ptr );
4030 break;
4031 case SPI_SETMOUSESONAR:
4032 ret = set_entry( &entry_MOUSESONAR, val, ptr, winini );
4033 break;
4034 case SPI_GETMOUSECLICKLOCK:
4035 ret = get_entry( &entry_MOUSECLICKLOCK, val, ptr );
4036 break;
4037 case SPI_SETMOUSECLICKLOCK:
4038 ret = set_entry( &entry_MOUSECLICKLOCK, val, ptr, winini );
4039 break;
4040 case SPI_GETMOUSEVANISH:
4041 ret = get_entry( &entry_MOUSEVANISH, val, ptr );
4042 break;
4043 case SPI_SETMOUSEVANISH:
4044 ret = set_entry( &entry_MOUSEVANISH, val, ptr, winini );
4045 break;
4046 case SPI_GETFLATMENU:
4047 ret = get_entry( &entry_FLATMENU, val, ptr );
4048 break;
4049 case SPI_SETFLATMENU:
4050 ret = set_entry( &entry_FLATMENU, val, ptr, winini );
4051 break;
4052 case SPI_GETDROPSHADOW:
4053 ret = get_entry( &entry_DROPSHADOW, val, ptr );
4054 break;
4055 case SPI_SETDROPSHADOW:
4056 ret = set_entry( &entry_DROPSHADOW, val, ptr, winini );
4057 break;
4058 case SPI_GETBLOCKSENDINPUTRESETS:
4059 ret = get_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr );
4060 break;
4061 case SPI_SETBLOCKSENDINPUTRESETS:
4062 ret = set_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr, winini );
4063 break;
4064 case SPI_GETUIEFFECTS:
4065 ret = get_entry( &entry_UIEFFECTS, val, ptr );
4066 break;
4067 case SPI_SETUIEFFECTS:
4068 /* FIXME: this probably should mask other UI effect values when unset */
4069 ret = set_entry( &entry_UIEFFECTS, val, ptr, winini );
4070 break;
4071 case SPI_GETDISABLEOVERLAPPEDCONTENT:
4072 ret = get_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr );
4073 break;
4074 case SPI_SETDISABLEOVERLAPPEDCONTENT:
4075 ret = set_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr, winini );
4076 break;
4077 case SPI_GETCLIENTAREAANIMATION:
4078 ret = get_entry( &entry_CLIENTAREAANIMATION, val, ptr );
4079 break;
4080 case SPI_SETCLIENTAREAANIMATION:
4081 ret = set_entry( &entry_CLIENTAREAANIMATION, val, ptr, winini );
4082 break;
4083 case SPI_GETCLEARTYPE:
4084 ret = get_entry( &entry_CLEARTYPE, val, ptr );
4085 break;
4086 case SPI_SETCLEARTYPE:
4087 ret = set_entry( &entry_CLEARTYPE, val, ptr, winini );
4088 break;
4089 case SPI_GETSPEECHRECOGNITION:
4090 ret = get_entry( &entry_SPEECHRECOGNITION, val, ptr );
4091 break;
4092 case SPI_SETSPEECHRECOGNITION:
4093 ret = set_entry( &entry_SPEECHRECOGNITION, val, ptr, winini );
4094 break;
4095 case SPI_GETFOREGROUNDLOCKTIMEOUT:
4096 ret = get_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr );
4097 break;
4098 case SPI_SETFOREGROUNDLOCKTIMEOUT:
4099 /* FIXME: this should check that the calling thread
4100 * is able to change the foreground window */
4101 ret = set_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr, winini );
4102 break;
4103 case SPI_GETACTIVEWNDTRKTIMEOUT:
4104 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
4105 break;
4106 case SPI_SETACTIVEWNDTRKTIMEOUT:
4107 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
4108 break;
4109 case SPI_GETFOREGROUNDFLASHCOUNT:
4110 ret = get_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr );
4111 break;
4112 case SPI_SETFOREGROUNDFLASHCOUNT:
4113 ret = set_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr, winini );
4114 break;
4115 case SPI_GETCARETWIDTH:
4116 ret = get_entry( &entry_CARETWIDTH, val, ptr );
4117 break;
4118 case SPI_SETCARETWIDTH:
4119 ret = set_entry( &entry_CARETWIDTH, val, ptr, winini );
4120 break;
4121 case SPI_GETMOUSECLICKLOCKTIME:
4122 ret = get_entry( &entry_MOUSECLICKLOCKTIME, val, ptr );
4123 break;
4124 case SPI_SETMOUSECLICKLOCKTIME:
4125 ret = set_entry( &entry_MOUSECLICKLOCKTIME, val, ptr, winini );
4126 break;
4127 case SPI_GETFONTSMOOTHINGTYPE:
4128 ret = get_entry( &entry_FONTSMOOTHINGTYPE, val, ptr );
4129 break;
4130 case SPI_SETFONTSMOOTHINGTYPE:
4131 ret = set_entry( &entry_FONTSMOOTHINGTYPE, val, ptr, winini );
4132 break;
4133 case SPI_GETFONTSMOOTHINGCONTRAST:
4134 ret = get_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr );
4135 break;
4136 case SPI_SETFONTSMOOTHINGCONTRAST:
4137 ret = set_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr, winini );
4138 break;
4139 case SPI_GETFOCUSBORDERWIDTH:
4140 ret = get_entry( &entry_FOCUSBORDERWIDTH, val, ptr );
4141 break;
4142 case SPI_GETFOCUSBORDERHEIGHT:
4143 ret = get_entry( &entry_FOCUSBORDERHEIGHT, val, ptr );
4144 break;
4145 case SPI_SETFOCUSBORDERWIDTH:
4146 ret = set_entry( &entry_FOCUSBORDERWIDTH, val, ptr, winini );
4147 break;
4148 case SPI_SETFOCUSBORDERHEIGHT:
4149 ret = set_entry( &entry_FOCUSBORDERHEIGHT, val, ptr, winini );
4150 break;
4151 case SPI_GETFONTSMOOTHINGORIENTATION:
4152 ret = get_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr );
4153 break;
4154 case SPI_SETFONTSMOOTHINGORIENTATION:
4155 ret = set_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr, winini );
4156 break;
4157 case SPI_GETAUDIODESCRIPTION:
4159 AUDIODESCRIPTION *audio = ptr;
4160 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
4162 ret = get_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled ) &&
4163 get_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale );
4165 break;
4167 case SPI_SETAUDIODESCRIPTION:
4169 AUDIODESCRIPTION *audio = ptr;
4170 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
4172 ret = set_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled, winini) &&
4173 set_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale, winini );
4175 break;
4177 default:
4178 FIXME( "Unknown action: %u\n", action );
4179 SetLastError( ERROR_INVALID_SPI_VALUE );
4180 ret = FALSE;
4181 break;
4184 if (ret && (winini & SPIF_UPDATEINIFILE))
4186 static const WCHAR emptyW[1];
4187 if (winini & (SPIF_SENDWININICHANGE | SPIF_SENDCHANGE))
4188 send_message_timeout( HWND_BROADCAST, WM_SETTINGCHANGE, action, (LPARAM) emptyW,
4189 SMTO_ABORTIFHUNG, 2000, NULL, FALSE );
4191 TRACE( "(%u, %u, %p, %u) ret %d\n", action, val, ptr, winini, ret );
4192 return ret;
4194 #undef WINE_SPI_FIXME
4195 #undef WINE_SPI_WARN
4198 int get_system_metrics( int index )
4200 NONCLIENTMETRICSW ncm;
4201 MINIMIZEDMETRICS mm;
4202 ICONMETRICSW im;
4203 RECT rect;
4204 UINT ret;
4205 HDC hdc;
4207 /* some metrics are dynamic */
4208 switch (index)
4210 case SM_CXVSCROLL:
4211 case SM_CYHSCROLL:
4212 get_entry( &entry_SCROLLWIDTH, 0, &ret );
4213 return max( ret, 8 );
4214 case SM_CYCAPTION:
4215 ncm.cbSize = sizeof(ncm);
4216 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
4217 return ncm.iCaptionHeight + 1;
4218 case SM_CXBORDER:
4219 case SM_CYBORDER:
4220 /* SM_C{X,Y}BORDER always returns 1 regardless of 'BorderWidth' value in registry */
4221 return 1;
4222 case SM_CXDLGFRAME:
4223 case SM_CYDLGFRAME:
4224 return 3;
4225 case SM_CYVTHUMB:
4226 case SM_CXHTHUMB:
4227 case SM_CYVSCROLL:
4228 case SM_CXHSCROLL:
4229 get_entry( &entry_SCROLLHEIGHT, 0, &ret );
4230 return max( ret, 8 );
4231 case SM_CXICON:
4232 case SM_CYICON:
4233 return map_to_dpi( 32, get_system_dpi() );
4234 case SM_CXCURSOR:
4235 case SM_CYCURSOR:
4236 ret = map_to_dpi( 32, get_system_dpi() );
4237 if (ret >= 64) return 64;
4238 if (ret >= 48) return 48;
4239 return 32;
4240 case SM_CYMENU:
4241 ncm.cbSize = sizeof(ncm);
4242 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
4243 return ncm.iMenuHeight + 1;
4244 case SM_CXFULLSCREEN:
4245 /* see the remark for SM_CXMAXIMIZED, at least this formulation is correct */
4246 return get_system_metrics( SM_CXMAXIMIZED ) - 2 * get_system_metrics( SM_CXFRAME );
4247 case SM_CYFULLSCREEN:
4248 /* see the remark for SM_CYMAXIMIZED, at least this formulation is
4249 * correct */
4250 return get_system_metrics( SM_CYMAXIMIZED ) - get_system_metrics( SM_CYMIN );
4251 case SM_CYKANJIWINDOW:
4252 return 0;
4253 case SM_MOUSEPRESENT:
4254 return 1;
4255 case SM_DEBUG:
4256 return 0;
4257 case SM_SWAPBUTTON:
4258 get_entry( &entry_MOUSEBUTTONSWAP, 0, &ret );
4259 return ret;
4260 case SM_RESERVED1:
4261 case SM_RESERVED2:
4262 case SM_RESERVED3:
4263 case SM_RESERVED4:
4264 return 0;
4265 case SM_CXMIN:
4266 ncm.cbSize = sizeof(ncm);
4267 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
4268 hdc = get_display_dc();
4269 get_text_metr_size( hdc, &ncm.lfCaptionFont, NULL, &ret );
4270 release_display_dc( hdc );
4271 return 3 * ncm.iCaptionWidth + ncm.iCaptionHeight + 4 * ret +
4272 2 * get_system_metrics( SM_CXFRAME ) + 4;
4273 case SM_CYMIN:
4274 return get_system_metrics( SM_CYCAPTION ) + 2 * get_system_metrics( SM_CYFRAME );
4275 case SM_CXSIZE:
4276 get_entry( &entry_CAPTIONWIDTH, 0, &ret );
4277 return max( ret, 8 );
4278 case SM_CYSIZE:
4279 ncm.cbSize = sizeof(ncm);
4280 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
4281 return ncm.iCaptionHeight;
4282 case SM_CXFRAME:
4283 get_entry( &entry_BORDER, 0, &ret );
4284 ret = max( ret, 1 );
4285 return get_system_metrics( SM_CXDLGFRAME ) + ret;
4286 case SM_CYFRAME:
4287 get_entry( &entry_BORDER, 0, &ret );
4288 ret = max( ret, 1 );
4289 return get_system_metrics( SM_CYDLGFRAME ) + ret;
4290 case SM_CXMINTRACK:
4291 return get_system_metrics( SM_CXMIN );
4292 case SM_CYMINTRACK:
4293 return get_system_metrics( SM_CYMIN );
4294 case SM_CXDOUBLECLK:
4295 get_entry( &entry_DOUBLECLKWIDTH, 0, &ret );
4296 return ret;
4297 case SM_CYDOUBLECLK:
4298 get_entry( &entry_DOUBLECLKHEIGHT, 0, &ret );
4299 return ret;
4300 case SM_CXICONSPACING:
4301 im.cbSize = sizeof(im);
4302 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
4303 return im.iHorzSpacing;
4304 case SM_CYICONSPACING:
4305 im.cbSize = sizeof(im);
4306 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
4307 return im.iVertSpacing;
4308 case SM_MENUDROPALIGNMENT:
4309 NtUserSystemParametersInfo( SPI_GETMENUDROPALIGNMENT, 0, &ret, 0 );
4310 return ret;
4311 case SM_PENWINDOWS:
4312 return 0;
4313 case SM_DBCSENABLED:
4314 return ansi_cp.MaximumCharacterSize > 1;
4315 case SM_CMOUSEBUTTONS:
4316 return 3;
4317 case SM_SECURE:
4318 return 0;
4319 case SM_CXEDGE:
4320 return get_system_metrics( SM_CXBORDER ) + 1;
4321 case SM_CYEDGE:
4322 return get_system_metrics( SM_CYBORDER ) + 1;
4323 case SM_CXMINSPACING:
4324 mm.cbSize = sizeof(mm);
4325 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
4326 return get_system_metrics( SM_CXMINIMIZED ) + mm.iHorzGap;
4327 case SM_CYMINSPACING:
4328 mm.cbSize = sizeof(mm);
4329 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
4330 return get_system_metrics( SM_CYMINIMIZED ) + mm.iVertGap;
4331 case SM_CXSMICON:
4332 case SM_CYSMICON:
4333 return map_to_dpi( 16, get_system_dpi() ) & ~1;
4334 case SM_CYSMCAPTION:
4335 ncm.cbSize = sizeof(ncm);
4336 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
4337 return ncm.iSmCaptionHeight + 1;
4338 case SM_CXSMSIZE:
4339 get_entry( &entry_SMCAPTIONWIDTH, 0, &ret );
4340 return ret;
4341 case SM_CYSMSIZE:
4342 ncm.cbSize = sizeof(ncm);
4343 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
4344 return ncm.iSmCaptionHeight;
4345 case SM_CXMENUSIZE:
4346 get_entry( &entry_MENUWIDTH, 0, &ret );
4347 return ret;
4348 case SM_CYMENUSIZE:
4349 ncm.cbSize = sizeof(ncm);
4350 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
4351 return ncm.iMenuHeight;
4352 case SM_ARRANGE:
4353 mm.cbSize = sizeof(mm);
4354 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
4355 return mm.iArrange;
4356 case SM_CXMINIMIZED:
4357 mm.cbSize = sizeof(mm);
4358 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
4359 return mm.iWidth + 6;
4360 case SM_CYMINIMIZED:
4361 ncm.cbSize = sizeof(ncm);
4362 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
4363 return ncm.iCaptionHeight + 6;
4364 case SM_CXMAXTRACK:
4365 return get_system_metrics( SM_CXVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CXFRAME );
4366 case SM_CYMAXTRACK:
4367 return get_system_metrics( SM_CYVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CYFRAME );
4368 case SM_CXMAXIMIZED:
4369 /* FIXME: subtract the width of any vertical application toolbars*/
4370 return get_system_metrics( SM_CXSCREEN ) + 2 * get_system_metrics( SM_CXFRAME );
4371 case SM_CYMAXIMIZED:
4372 /* FIXME: subtract the width of any horizontal application toolbars*/
4373 return get_system_metrics( SM_CYSCREEN ) + 2 * get_system_metrics( SM_CYCAPTION );
4374 case SM_NETWORK:
4375 return 3; /* FIXME */
4376 case SM_CLEANBOOT:
4377 return 0; /* 0 = ok, 1 = failsafe, 2 = failsafe + network */
4378 case SM_CXDRAG:
4379 get_entry( &entry_DRAGWIDTH, 0, &ret );
4380 return ret;
4381 case SM_CYDRAG:
4382 get_entry( &entry_DRAGHEIGHT, 0, &ret );
4383 return ret;
4384 case SM_SHOWSOUNDS:
4385 get_entry( &entry_SHOWSOUNDS, 0, &ret );
4386 return ret;
4387 case SM_CXMENUCHECK:
4388 case SM_CYMENUCHECK:
4390 TEXTMETRICW tm;
4391 ncm.cbSize = sizeof(ncm);
4392 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
4393 hdc = get_display_dc();
4394 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL );
4395 release_display_dc( hdc );
4396 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading + 1) / 2) * 2 - 1;
4398 case SM_SLOWMACHINE:
4399 return 0; /* Never true */
4400 case SM_MIDEASTENABLED:
4401 return 0; /* FIXME */
4402 case SM_MOUSEWHEELPRESENT:
4403 return 1;
4404 case SM_CXSCREEN:
4405 rect = get_primary_monitor_rect( get_thread_dpi() );
4406 return rect.right - rect.left;
4407 case SM_CYSCREEN:
4408 rect = get_primary_monitor_rect( get_thread_dpi() );
4409 return rect.bottom - rect.top;
4410 case SM_XVIRTUALSCREEN:
4411 rect = get_virtual_screen_rect( get_thread_dpi() );
4412 return rect.left;
4413 case SM_YVIRTUALSCREEN:
4414 rect = get_virtual_screen_rect( get_thread_dpi() );
4415 return rect.top;
4416 case SM_CXVIRTUALSCREEN:
4417 rect = get_virtual_screen_rect( get_thread_dpi() );
4418 return rect.right - rect.left;
4419 case SM_CYVIRTUALSCREEN:
4420 rect = get_virtual_screen_rect( get_thread_dpi() );
4421 return rect.bottom - rect.top;
4422 case SM_CMONITORS:
4423 if (!lock_display_devices()) return FALSE;
4424 ret = active_monitor_count();
4425 unlock_display_devices();
4426 return ret;
4427 case SM_SAMEDISPLAYFORMAT:
4428 return 1;
4429 case SM_IMMENABLED:
4430 return 0; /* FIXME */
4431 case SM_CXFOCUSBORDER:
4432 case SM_CYFOCUSBORDER:
4433 return 1;
4434 case SM_TABLETPC:
4435 case SM_MEDIACENTER:
4436 return 0;
4437 case SM_CMETRICS:
4438 return SM_CMETRICS;
4439 default:
4440 return 0;
4444 static int get_system_metrics_for_dpi( int index, unsigned int dpi )
4446 NONCLIENTMETRICSW ncm;
4447 ICONMETRICSW im;
4448 UINT ret;
4449 HDC hdc;
4451 /* some metrics are dynamic */
4452 switch (index)
4454 case SM_CXVSCROLL:
4455 case SM_CYHSCROLL:
4456 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ret, dpi );
4457 return max( ret, 8 );
4458 case SM_CYCAPTION:
4459 ncm.cbSize = sizeof(ncm);
4460 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
4461 return ncm.iCaptionHeight + 1;
4462 case SM_CYVTHUMB:
4463 case SM_CXHTHUMB:
4464 case SM_CYVSCROLL:
4465 case SM_CXHSCROLL:
4466 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ret, dpi );
4467 return max( ret, 8 );
4468 case SM_CXICON:
4469 case SM_CYICON:
4470 return map_to_dpi( 32, dpi );
4471 case SM_CXCURSOR:
4472 case SM_CYCURSOR:
4473 ret = map_to_dpi( 32, dpi );
4474 if (ret >= 64) return 64;
4475 if (ret >= 48) return 48;
4476 return 32;
4477 case SM_CYMENU:
4478 ncm.cbSize = sizeof(ncm);
4479 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
4480 return ncm.iMenuHeight + 1;
4481 case SM_CXSIZE:
4482 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ret, dpi );
4483 return max( ret, 8 );
4484 case SM_CYSIZE:
4485 ncm.cbSize = sizeof(ncm);
4486 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
4487 return ncm.iCaptionHeight;
4488 case SM_CXFRAME:
4489 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
4490 ret = max( ret, 1 );
4491 return get_system_metrics_for_dpi( SM_CXDLGFRAME, dpi ) + ret;
4492 case SM_CYFRAME:
4493 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
4494 ret = max( ret, 1 );
4495 return get_system_metrics_for_dpi( SM_CYDLGFRAME, dpi ) + ret;
4496 case SM_CXICONSPACING:
4497 im.cbSize = sizeof(im);
4498 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
4499 return im.iHorzSpacing;
4500 case SM_CYICONSPACING:
4501 im.cbSize = sizeof(im);
4502 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
4503 return im.iVertSpacing;
4504 case SM_CXSMICON:
4505 case SM_CYSMICON:
4506 return map_to_dpi( 16, dpi ) & ~1;
4507 case SM_CYSMCAPTION:
4508 ncm.cbSize = sizeof(ncm);
4509 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
4510 return ncm.iSmCaptionHeight + 1;
4511 case SM_CXSMSIZE:
4512 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ret, dpi );
4513 return ret;
4514 case SM_CYSMSIZE:
4515 ncm.cbSize = sizeof(ncm);
4516 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
4517 return ncm.iSmCaptionHeight;
4518 case SM_CXMENUSIZE:
4519 get_entry_dpi( &entry_MENUWIDTH, 0, &ret, dpi );
4520 return ret;
4521 case SM_CYMENUSIZE:
4522 ncm.cbSize = sizeof(ncm);
4523 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
4524 return ncm.iMenuHeight;
4525 case SM_CXMENUCHECK:
4526 case SM_CYMENUCHECK:
4528 TEXTMETRICW tm;
4529 ncm.cbSize = sizeof(ncm);
4530 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
4531 hdc = get_display_dc();
4532 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL);
4533 release_display_dc( hdc );
4534 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading - 1) | 1);
4536 default:
4537 return get_system_metrics( index );
4541 COLORREF get_sys_color( int index )
4543 COLORREF ret = 0;
4545 if (index >= 0 && index < ARRAY_SIZE( system_colors ))
4546 get_entry( &system_colors[index], 0, &ret );
4547 return ret;
4550 HBRUSH get_55aa_brush(void)
4552 static const WORD pattern[] = { 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa };
4553 static HBRUSH brush_55aa;
4555 if (!brush_55aa)
4557 HBITMAP bitmap = NtGdiCreateBitmap( 8, 8, 1, 1, pattern );
4558 HBRUSH brush = NtGdiCreatePatternBrushInternal( bitmap, FALSE, FALSE );
4559 NtGdiDeleteObjectApp( bitmap );
4560 make_gdi_object_system( brush, TRUE );
4561 if (InterlockedCompareExchangePointer( (void **)&brush_55aa, brush, 0 ))
4563 make_gdi_object_system( brush, FALSE );
4564 NtGdiDeleteObjectApp( brush );
4567 return brush_55aa;
4570 HBRUSH get_sys_color_brush( unsigned int index )
4572 if (index == COLOR_55AA_BRUSH) return get_55aa_brush();
4573 if (index >= ARRAY_SIZE( system_colors )) return 0;
4575 if (!system_colors[index].brush)
4577 HBRUSH brush = NtGdiCreateSolidBrush( get_sys_color( index ), NULL );
4578 make_gdi_object_system( brush, TRUE );
4579 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].brush, brush, 0 ))
4581 make_gdi_object_system( brush, FALSE );
4582 NtGdiDeleteObjectApp( brush );
4585 return system_colors[index].brush;
4588 HPEN get_sys_color_pen( unsigned int index )
4590 if (index >= ARRAY_SIZE( system_colors )) return 0;
4592 if (!system_colors[index].pen)
4594 HPEN pen = NtGdiCreatePen( PS_SOLID, 1, get_sys_color( index ), NULL );
4595 make_gdi_object_system( pen, TRUE );
4596 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].pen, pen, 0 ))
4598 make_gdi_object_system( pen, FALSE );
4599 NtGdiDeleteObjectApp( pen );
4602 return system_colors[index].pen;
4605 /**********************************************************************
4606 * NtUserGetDoubleClickTime (win32u.@)
4608 UINT WINAPI NtUserGetDoubleClickTime(void)
4610 UINT time = 0;
4612 get_entry( &entry_DOUBLECLICKTIME, 0, &time );
4613 if (!time) time = 500;
4614 return time;
4617 /*************************************************************************
4618 * NtUserSetSysColors (win32u.@)
4620 BOOL WINAPI NtUserSetSysColors( INT count, const INT *colors, const COLORREF *values )
4622 int i;
4624 if (IS_INTRESOURCE(colors)) return FALSE; /* stupid app passes a color instead of an array */
4626 for (i = 0; i < count; i++)
4627 if (colors[i] >= 0 && colors[i] <= ARRAY_SIZE( system_colors ))
4628 set_entry( &system_colors[colors[i]], values[i], 0, 0 );
4630 /* Send WM_SYSCOLORCHANGE message to all windows */
4631 send_message_timeout( HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0,
4632 SMTO_ABORTIFHUNG, 2000, NULL, FALSE );
4633 /* Repaint affected portions of all visible windows */
4634 NtUserRedrawWindow( 0, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN );
4635 return TRUE;
4639 static DPI_AWARENESS dpi_awareness;
4641 /***********************************************************************
4642 * NtUserSetProcessDpiAwarenessContext (win32u.@)
4644 BOOL WINAPI NtUserSetProcessDpiAwarenessContext( ULONG awareness, ULONG unknown )
4646 switch (awareness)
4648 case NTUSER_DPI_UNAWARE:
4649 case NTUSER_DPI_SYSTEM_AWARE:
4650 case NTUSER_DPI_PER_MONITOR_AWARE:
4651 case NTUSER_DPI_PER_MONITOR_AWARE_V2:
4652 case NTUSER_DPI_PER_UNAWARE_GDISCALED:
4653 break;
4654 default:
4655 SetLastError( ERROR_INVALID_PARAMETER );
4656 return FALSE;
4659 return !InterlockedCompareExchange( &dpi_awareness, awareness, 0 );
4662 /***********************************************************************
4663 * NtUserGetProcessDpiAwarenessContext (win32u.@)
4665 ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process )
4667 if (process && process != GetCurrentProcess())
4669 WARN( "not supported on other process %p\n", process );
4670 return NTUSER_DPI_UNAWARE;
4673 if (!dpi_awareness) return NTUSER_DPI_UNAWARE;
4674 return dpi_awareness;
4677 BOOL message_beep( UINT i )
4679 BOOL active = TRUE;
4680 NtUserSystemParametersInfo( SPI_GETBEEP, 0, &active, FALSE );
4681 if (active) user_driver->pBeep();
4682 return TRUE;
4685 static DWORD exiting_thread_id;
4687 /**********************************************************************
4688 * is_exiting_thread
4690 BOOL is_exiting_thread( DWORD tid )
4692 return tid == exiting_thread_id;
4695 static void thread_detach(void)
4697 struct user_thread_info *thread_info = get_user_thread_info();
4699 user_driver->pThreadDetach();
4701 free( thread_info->key_state );
4702 thread_info->key_state = 0;
4703 free( thread_info->rawinput );
4705 destroy_thread_windows();
4706 NtClose( thread_info->server_queue );
4708 exiting_thread_id = 0;
4711 /***********************************************************************
4712 * NtUserCallNoParam (win32u.@)
4714 ULONG_PTR WINAPI NtUserCallNoParam( ULONG code )
4716 switch(code)
4718 case NtUserCallNoParam_DestroyCaret:
4719 return destroy_caret();
4721 case NtUserCallNoParam_GetDesktopWindow:
4722 return HandleToUlong( get_desktop_window() );
4724 case NtUserCallNoParam_GetDialogBaseUnits:
4725 return get_dialog_base_units();
4727 case NtUserCallNoParam_GetInputState:
4728 return get_input_state();
4730 case NtUserCallNoParam_GetProcessDefaultLayout:
4731 return process_layout;
4733 case NtUserCallNoParam_ReleaseCapture:
4734 return release_capture();
4736 /* temporary exports */
4737 case NtUserExitingThread:
4738 exiting_thread_id = GetCurrentThreadId();
4739 return 0;
4741 case NtUserThreadDetach:
4742 thread_detach();
4743 return 0;
4745 default:
4746 FIXME( "invalid code %u\n", code );
4747 return 0;
4751 /***********************************************************************
4752 * NtUserCallOneParam (win32u.@)
4754 ULONG_PTR WINAPI NtUserCallOneParam( ULONG_PTR arg, ULONG code )
4756 switch(code)
4758 case NtUserCallOneParam_BeginDeferWindowPos:
4759 return HandleToUlong( begin_defer_window_pos( arg ));
4761 case NtUserCallOneParam_CreateCursorIcon:
4762 return HandleToUlong( alloc_cursoricon_handle( arg ));
4764 case NtUserCallOneParam_CreateMenu:
4765 return HandleToUlong( create_menu( arg ) );
4767 case NtUserCallOneParam_DispatchMessageA:
4768 return dispatch_message( (const MSG *)arg, TRUE );
4770 case NtUserCallOneParam_EnableDC:
4771 return set_dce_flags( UlongToHandle(arg), DCHF_ENABLEDC );
4773 case NtUserCallOneParam_EnableThunkLock:
4774 enable_thunk_lock = arg;
4775 return 0;
4777 case NtUserCallOneParam_EnumClipboardFormats:
4778 return enum_clipboard_formats( arg );
4780 case NtUserCallOneParam_GetClipCursor:
4781 return get_clip_cursor( (RECT *)arg );
4783 case NtUserCallOneParam_GetCursorPos:
4784 return get_cursor_pos( (POINT *)arg );
4786 case NtUserCallOneParam_GetIconParam:
4787 return get_icon_param( UlongToHandle(arg) );
4789 case NtUserCallOneParam_GetMenuItemCount:
4790 return get_menu_item_count( UlongToHandle(arg) );
4792 case NtUserCallOneParam_GetSysColor:
4793 return get_sys_color( arg );
4795 case NtUserCallOneParam_IsWindowRectFullScreen:
4796 return is_window_rect_full_screen( (const RECT *)arg );
4798 case NtUserCallOneParam_RealizePalette:
4799 return realize_palette( UlongToHandle(arg) );
4801 case NtUserCallOneParam_GetPrimaryMonitorRect:
4802 *(RECT *)arg = get_primary_monitor_rect( 0 );
4803 return 1;
4805 case NtUserCallOneParam_GetSysColorBrush:
4806 return HandleToUlong( get_sys_color_brush(arg) );
4808 case NtUserCallOneParam_GetSysColorPen:
4809 return HandleToUlong( get_sys_color_pen(arg) );
4811 case NtUserCallOneParam_GetSystemMetrics:
4812 return get_system_metrics( arg );
4814 case NtUserCallOneParam_GetVirtualScreenRect:
4815 *(RECT *)arg = get_virtual_screen_rect( 0 );
4816 return 1;
4818 case NtUserCallOneParam_MessageBeep:
4819 return message_beep( arg );
4821 case NtUserCallOneParam_SetCaretBlinkTime:
4822 return set_caret_blink_time( arg );
4824 case NtUserCallOneParam_SetProcessDefaultLayout:
4825 process_layout = arg;
4826 return TRUE;
4828 /* temporary exports */
4829 case NtUserGetDeskPattern:
4830 return get_entry( &entry_DESKPATTERN, 256, (WCHAR *)arg );
4832 case NtUserGetWinProcPtr:
4833 return (UINT_PTR)get_winproc_ptr( UlongToHandle(arg) );
4835 case NtUserLock:
4836 switch( arg )
4838 case 0: user_lock(); return 0;
4839 case 1: user_unlock(); return 0;
4840 default: user_check_not_lock(); return 0;
4843 case NtUserSetCallbacks:
4844 return (UINT_PTR)InterlockedExchangePointer( (void **)&user_callbacks, (void *)arg );
4846 case NtUserSpyGetVKeyName:
4847 return (UINT_PTR)debugstr_vkey_name( arg );
4849 default:
4850 FIXME( "invalid code %u\n", code );
4851 return 0;
4855 /***********************************************************************
4856 * NtUserCallTwoParam (win32u.@)
4858 ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code )
4860 switch(code)
4862 case NtUserCallTwoParam_GetMenuInfo:
4863 return get_menu_info( UlongToHandle(arg1), (MENUINFO *)arg2 );
4865 case NtUserCallTwoParam_GetMonitorInfo:
4866 return get_monitor_info( UlongToHandle(arg1), (MONITORINFO *)arg2 );
4868 case NtUserCallTwoParam_GetSystemMetricsForDpi:
4869 return get_system_metrics_for_dpi( arg1, arg2 );
4871 case NtUserCallTwoParam_MonitorFromRect:
4872 return HandleToUlong( monitor_from_rect( (const RECT *)arg1, arg2, get_thread_dpi() ));
4874 case NtUserCallTwoParam_ReplyMessage:
4875 return reply_message_result( arg1, (MSG *)arg2 );
4877 case NtUserCallTwoParam_SetCaretPos:
4878 return set_caret_pos( arg1, arg2 );
4880 case NtUserCallTwoParam_SetIconParam:
4881 return set_icon_param( UlongToHandle(arg1), arg2 );
4883 case NtUserCallTwoParam_UnhookWindowsHook:
4884 return unhook_windows_hook( arg1, (HOOKPROC)arg2 );
4886 /* temporary exports */
4887 case NtUserAllocWinProc:
4888 return (UINT_PTR)alloc_winproc( (WNDPROC)arg1, arg2 );
4890 case NtUserGetHandlePtr:
4891 return (UINT_PTR)get_user_handle_ptr( UlongToHandle(arg1), arg2 );
4893 default:
4894 FIXME( "invalid code %u\n", code );
4895 return 0;