dmime/tests: Check more notification / dirty messages fields.
[wine.git] / dlls / win32u / sysparams.c
blob5506abac7c661137d3980fcc2c674df9b50fdeda
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 "cfgmgr32.h"
36 #include "d3dkmdt.h"
37 #include "wine/wingdi16.h"
38 #include "wine/server.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(system);
43 static HKEY video_key, enum_key, control_key, config_key, volatile_base_key;
45 static const WCHAR devicemap_video_keyW[] =
47 '\\','R','e','g','i','s','t','r','y',
48 '\\','M','a','c','h','i','n','e',
49 '\\','H','A','R','D','W','A','R','E',
50 '\\','D','E','V','I','C','E','M','A','P',
51 '\\','V','I','D','E','O'
54 static const WCHAR enum_keyW[] =
56 '\\','R','e','g','i','s','t','r','y',
57 '\\','M','a','c','h','i','n','e',
58 '\\','S','y','s','t','e','m',
59 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
60 '\\','E','n','u','m'
63 static const WCHAR control_keyW[] =
65 '\\','R','e','g','i','s','t','r','y',
66 '\\','M','a','c','h','i','n','e',
67 '\\','S','y','s','t','e','m',
68 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
69 '\\','C','o','n','t','r','o','l'
72 static const WCHAR config_keyW[] =
74 '\\','R','e','g','i','s','t','r','y',
75 '\\','M','a','c','h','i','n','e',
76 '\\','S','y','s','t','e','m',
77 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
78 '\\','H','a','r','d','w','a','r','e',' ','P','r','o','f','i','l','e','s',
79 '\\','C','u','r','r','e','n','t'
82 static const WCHAR devpropkey_gpu_vulkan_uuidW[] =
84 'P','r','o','p','e','r','t','i','e','s',
85 '\\','{','2','3','3','A','9','E','F','3','-','A','F','C','4','-','4','A','B','D',
86 '-','B','5','6','4','-','C','3','2','F','2','1','F','1','5','3','5','C','}',
87 '\\','0','0','0','2'
90 static const WCHAR devpropkey_gpu_luidW[] =
92 'P','r','o','p','e','r','t','i','e','s',
93 '\\','{','6','0','B','1','9','3','C','B','-','5','2','7','6','-','4','D','0','F',
94 '-','9','6','F','C','-','F','1','7','3','A','B','A','D','3','E','C','6','}',
95 '\\','0','0','0','2'
98 static const WCHAR devpkey_device_matching_device_id[] =
100 'P','r','o','p','e','r','t','i','e','s',
101 '\\','{','A','8','B','8','6','5','D','D','-','2','E','3','D','-','4','0','9','4',
102 '-','A','D','9','7','-','E','5','9','3','A','7','0','C','7','5','D','6','}',
103 '\\','0','0','0','8'
106 static const WCHAR devpkey_device_bus_number[] =
108 'P','r','o','p','e','r','t','i','e','s',
109 '\\','{','A','4','5','C','2','5','4','E','-','D','F','1','C','-','4','E','F','D',
110 '-','8','0','2','0','-','6','7','D','1','4','6','A','8','5','0','E','0','}',
111 '\\','0','0','1','7'
114 static const WCHAR devpkey_device_removal_policy[] =
116 'P','r','o','p','e','r','t','i','e','s',
117 '\\','{','A','4','5','C','2','5','4','E','-','D','F','1','C','-','4','E','F','D',
118 '-','8','0','2','0','-','6','7','D','1','4','6','A','8','5','0','E','0','}',
119 '\\','0','0','2','1'
122 static const WCHAR devpropkey_device_ispresentW[] =
124 'P','r','o','p','e','r','t','i','e','s',
125 '\\','{','5','4','0','B','9','4','7','E','-','8','B','4','0','-','4','5','B','C',
126 '-','A','8','A','2','-','6','A','0','B','8','9','4','C','B','D','A','2','}',
127 '\\','0','0','0','5'
130 static const WCHAR devpropkey_monitor_gpu_luidW[] =
132 'P','r','o','p','e','r','t','i','e','s',
133 '\\','{','C','A','0','8','5','8','5','3','-','1','6','C','E','-','4','8','A','A',
134 '-','B','1','1','4','-','D','E','9','C','7','2','3','3','4','2','2','3','}',
135 '\\','0','0','0','1'
138 static const WCHAR devpropkey_monitor_output_idW[] =
140 'P','r','o','p','e','r','t','i','e','s',
141 '\\','{','C','A','0','8','5','8','5','3','-','1','6','C','E','-','4','8','A','A',
142 '-','B','1','1','4','-','D','E','9','C','7','2','3','3','4','2','2','3','}',
143 '\\','0','0','0','2'
146 static const WCHAR wine_devpropkey_monitor_stateflagsW[] =
148 'P','r','o','p','e','r','t','i','e','s','\\',
149 '{','2','3','3','a','9','e','f','3','-','a','f','c','4','-','4','a','b','d',
150 '-','b','5','6','4','-','c','3','2','f','2','1','f','1','5','3','5','b','}',
151 '\\','0','0','0','2'
154 static const WCHAR wine_devpropkey_monitor_rcmonitorW[] =
156 'P','r','o','p','e','r','t','i','e','s','\\',
157 '{','2','3','3','a','9','e','f','3','-','a','f','c','4','-','4','a','b','d',
158 '-','b','5','6','4','-','c','3','2','f','2','1','f','1','5','3','5','b','}',
159 '\\','0','0','0','3'
162 static const WCHAR wine_devpropkey_monitor_rcworkW[] =
164 'P','r','o','p','e','r','t','i','e','s','\\',
165 '{','2','3','3','a','9','e','f','3','-','a','f','c','4','-','4','a','b','d',
166 '-','b','5','6','4','-','c','3','2','f','2','1','f','1','5','3','5','b','}',
167 '\\','0','0','0','4'
170 static const WCHAR wine_devpropkey_monitor_adapternameW[] =
172 'P','r','o','p','e','r','t','i','e','s','\\',
173 '{','2','3','3','a','9','e','f','3','-','a','f','c','4','-','4','a','b','d',
174 '-','b','5','6','4','-','c','3','2','f','2','1','f','1','5','3','5','b','}',
175 '\\','0','0','0','5'
178 static const WCHAR device_instanceW[] = {'D','e','v','i','c','e','I','n','s','t','a','n','c','e',0};
179 static const WCHAR controlW[] = {'C','o','n','t','r','o','l'};
180 static const WCHAR device_parametersW[] =
181 {'D','e','v','i','c','e',' ','P','a','r','a','m','e','t','e','r','s'};
182 static const WCHAR linkedW[] = {'L','i','n','k','e','d',0};
183 static const WCHAR symbolic_link_valueW[] =
184 {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
185 static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0};
186 static const WCHAR gpu_idW[] = {'G','P','U','I','D',0};
187 static const WCHAR hardware_idW[] = {'H','a','r','d','w','a','r','e','I','D',0};
188 static const WCHAR device_descW[] = {'D','e','v','i','c','e','D','e','s','c',0};
189 static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0};
190 static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
191 static const WCHAR class_guidW[] = {'C','l','a','s','s','G','U','I','D',0};
192 static const WCHAR pciW[] = {'P','C','I'};
193 static const WCHAR classW[] = {'C','l','a','s','s',0};
194 static const WCHAR displayW[] = {'D','i','s','p','l','a','y',0};
195 static const WCHAR monitorW[] = {'M','o','n','i','t','o','r',0};
196 static const WCHAR yesW[] = {'Y','e','s',0};
197 static const WCHAR noW[] = {'N','o',0};
198 static const WCHAR mode_countW[] = {'M','o','d','e','C','o','u','n','t',0};
199 static const WCHAR edidW[] = {'E','D','I','D',0};
201 static const char guid_devclass_displayA[] = "{4D36E968-E325-11CE-BFC1-08002BE10318}";
202 static const WCHAR guid_devclass_displayW[] =
203 {'{','4','D','3','6','E','9','6','8','-','E','3','2','5','-','1','1','C','E','-',
204 'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0};
206 static const char guid_devclass_monitorA[] = "{4D36E96E-E325-11CE-BFC1-08002BE10318}";
207 static const WCHAR guid_devclass_monitorW[] =
208 {'{','4','D','3','6','E','9','6','E','-','E','3','2','5','-','1','1','C','E','-',
209 'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0};
211 static const WCHAR guid_devinterface_display_adapterW[] =
212 {'{','5','B','4','5','2','0','1','D','-','F','2','F','2','-','4','F','3','B','-',
213 '8','5','B','B','-','3','0','F','F','1','F','9','5','3','5','9','9','}',0};
215 static const WCHAR guid_display_device_arrivalW[] =
216 {'{','1','C','A','0','5','1','8','0','-','A','6','9','9','-','4','5','0','A','-',
217 '9','A','0','C','-','D','E','4','F','B','E','3','D','D','D','8','9','}',0};
219 static const WCHAR guid_devinterface_monitorW[] =
220 {'{','E','6','F','0','7','B','5','F','-','E','E','9','7','-','4','A','9','0','-',
221 'B','0','7','6','-','3','3','F','5','7','B','F','4','E','A','A','7','}',0};
223 #define NEXT_DEVMODEW(mode) ((DEVMODEW *)((char *)((mode) + 1) + (mode)->dmDriverExtra))
225 /* Cached display device information */
226 struct display_device
228 WCHAR device_name[32]; /* DeviceName in DISPLAY_DEVICEW */
229 WCHAR device_string[128]; /* DeviceString in DISPLAY_DEVICEW */
230 DWORD state_flags; /* StateFlags in DISPLAY_DEVICEW */
231 WCHAR device_id[128]; /* DeviceID in DISPLAY_DEVICEW */
232 WCHAR interface_name[128]; /* DeviceID in DISPLAY_DEVICEW when EDD_GET_DEVICE_INTERFACE_NAME is set */
233 WCHAR device_key[128]; /* DeviceKey in DISPLAY_DEVICEW */
236 struct adapter
238 LONG refcount;
239 struct list entry;
240 struct display_device dev;
241 LUID gpu_luid;
242 unsigned int id;
243 const WCHAR *config_key;
244 unsigned int mode_count;
245 DEVMODEW *modes;
248 #define MONITOR_INFO_HAS_MONITOR_ID 0x00000001
249 #define MONITOR_INFO_HAS_MONITOR_NAME 0x00000002
250 #define MONITOR_INFO_HAS_PREFERRED_MODE 0x00000004
251 struct edid_monitor_info
253 unsigned int flags;
254 /* MONITOR_INFO_HAS_MONITOR_ID */
255 unsigned short manufacturer, product_code;
256 char monitor_id_string[8];
257 /* MONITOR_INFO_HAS_MONITOR_NAME */
258 WCHAR monitor_name[14];
259 /* MONITOR_INFO_HAS_PREFERRED_MODE */
260 unsigned int preferred_width, preferred_height;
263 struct monitor
265 struct list entry;
266 struct display_device dev;
267 struct adapter *adapter;
268 HANDLE handle;
269 unsigned int id;
270 unsigned int flags;
271 unsigned int output_id;
272 RECT rc_monitor;
273 RECT rc_work;
274 BOOL is_clone;
275 struct edid_monitor_info edid_info;
278 static struct list adapters = LIST_INIT(adapters);
279 static struct list monitors = LIST_INIT(monitors);
280 static INT64 last_query_display_time;
281 static pthread_mutex_t display_lock = PTHREAD_MUTEX_INITIALIZER;
283 BOOL enable_thunk_lock = FALSE;
285 #define VIRTUAL_HMONITOR ((HMONITOR)(UINT_PTR)(0x10000 + 1))
286 static struct monitor virtual_monitor =
288 .handle = VIRTUAL_HMONITOR,
289 .flags = MONITORINFOF_PRIMARY,
290 .rc_monitor.right = 1024,
291 .rc_monitor.bottom = 768,
292 .rc_work.right = 1024,
293 .rc_work.bottom = 768,
294 .dev.state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED,
297 /* the various registry keys that are used to store parameters */
298 enum parameter_key
300 COLORS_KEY,
301 DESKTOP_KEY,
302 KEYBOARD_KEY,
303 MOUSE_KEY,
304 METRICS_KEY,
305 SOUND_KEY,
306 VERSION_KEY,
307 SHOWSOUNDS_KEY,
308 KEYBOARDPREF_KEY,
309 SCREENREADER_KEY,
310 AUDIODESC_KEY,
311 NB_PARAM_KEYS
314 static const char *parameter_key_names[NB_PARAM_KEYS] =
316 "Control Panel\\Colors",
317 "Control Panel\\Desktop",
318 "Control Panel\\Keyboard",
319 "Control Panel\\Mouse",
320 "Control Panel\\Desktop\\WindowMetrics",
321 "Control Panel\\Sound",
322 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
323 "Control Panel\\Accessibility\\ShowSounds",
324 "Control Panel\\Accessibility\\Keyboard Preference",
325 "Control Panel\\Accessibility\\Blind Access",
326 "Control Panel\\Accessibility\\AudioDescription",
329 /* System parameters storage */
330 union sysparam_all_entry;
332 struct sysparam_entry
334 BOOL (*get)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi );
335 BOOL (*set)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags );
336 BOOL (*init)( union sysparam_all_entry *entry );
337 enum parameter_key base_key;
338 const char *regval;
339 enum parameter_key mirror_key;
340 const char *mirror;
341 BOOL loaded;
344 struct sysparam_uint_entry
346 struct sysparam_entry hdr;
347 UINT val;
350 struct sysparam_bool_entry
352 struct sysparam_entry hdr;
353 BOOL val;
356 struct sysparam_dword_entry
358 struct sysparam_entry hdr;
359 DWORD val;
362 struct sysparam_rgb_entry
364 struct sysparam_entry hdr;
365 COLORREF val;
366 HBRUSH brush;
367 HPEN pen;
370 struct sysparam_binary_entry
372 struct sysparam_entry hdr;
373 void *ptr;
374 size_t size;
377 struct sysparam_path_entry
379 struct sysparam_entry hdr;
380 WCHAR *path;
383 struct sysparam_font_entry
385 struct sysparam_entry hdr;
386 UINT weight;
387 LOGFONTW val;
388 WCHAR fullname[LF_FACESIZE];
391 struct sysparam_pref_entry
393 struct sysparam_entry hdr;
394 union sysparam_all_entry *parent;
395 UINT offset;
396 UINT mask;
399 union sysparam_all_entry
401 struct sysparam_entry hdr;
402 struct sysparam_uint_entry uint;
403 struct sysparam_bool_entry bool;
404 struct sysparam_dword_entry dword;
405 struct sysparam_rgb_entry rgb;
406 struct sysparam_binary_entry bin;
407 struct sysparam_path_entry path;
408 struct sysparam_font_entry font;
409 struct sysparam_pref_entry pref;
412 static UINT system_dpi;
413 static RECT work_area;
414 static DWORD process_layout = ~0u;
416 static HDC display_dc;
417 static pthread_mutex_t display_dc_lock = PTHREAD_MUTEX_INITIALIZER;
419 static pthread_mutex_t user_mutex;
420 static unsigned int user_lock_thread, user_lock_rec;
422 void user_lock(void)
424 pthread_mutex_lock( &user_mutex );
425 if (!user_lock_rec++) user_lock_thread = GetCurrentThreadId();
428 void user_unlock(void)
430 if (!--user_lock_rec) user_lock_thread = 0;
431 pthread_mutex_unlock( &user_mutex );
434 void user_check_not_lock(void)
436 if (user_lock_thread == GetCurrentThreadId())
438 ERR( "BUG: holding USER lock\n" );
439 assert( 0 );
443 static HANDLE get_display_device_init_mutex( void )
445 WCHAR bufferW[256];
446 UNICODE_STRING name = {.Buffer = bufferW};
447 OBJECT_ATTRIBUTES attr;
448 char buffer[256];
449 HANDLE mutex;
451 snprintf( buffer, ARRAY_SIZE(buffer), "\\Sessions\\%u\\BaseNamedObjects\\display_device_init",
452 (int)NtCurrentTeb()->Peb->SessionId );
453 name.MaximumLength = asciiz_to_unicode( bufferW, buffer );
454 name.Length = name.MaximumLength - sizeof(WCHAR);
456 InitializeObjectAttributes( &attr, &name, OBJ_OPENIF, NULL, NULL );
457 if (NtCreateMutant( &mutex, MUTEX_ALL_ACCESS, &attr, FALSE ) < 0) return 0;
458 NtWaitForSingleObject( mutex, FALSE, NULL );
459 return mutex;
462 static void release_display_device_init_mutex( HANDLE mutex )
464 NtReleaseMutant( mutex, NULL );
465 NtClose( mutex );
468 static struct adapter *adapter_acquire( struct adapter *adapter )
470 InterlockedIncrement( &adapter->refcount );
471 return adapter;
474 static void adapter_release( struct adapter *adapter )
476 if (!InterlockedDecrement( &adapter->refcount ))
478 free( adapter->modes );
479 free( adapter );
483 C_ASSERT(sizeof(DEVMODEW) - offsetof(DEVMODEW, dmFields) == 0x94);
485 static void get_monitor_info_from_edid( struct edid_monitor_info *info, const unsigned char *edid, unsigned int edid_len )
487 unsigned int i, j;
488 unsigned short w;
489 unsigned char d;
490 const char *s;
492 info->flags = 0;
493 if (!edid || edid_len < 128) return;
495 w = (edid[8] << 8) | edid[9]; /* Manufacturer ID, big endian. */
496 for (i = 0; i < 3; ++i)
498 d = w & 0x1f;
499 if (!d || d - 1 > 'Z' - 'A') return;
500 info->monitor_id_string[2 - i] = 'A' + d - 1;
501 w >>= 5;
503 if (w) return;
504 w = edid[10] | (edid[11] << 8); /* Product code, little endian. */
505 info->manufacturer = *(unsigned short *)(edid + 8);
506 info->product_code = w;
507 sprintf( info->monitor_id_string + 3, "%04X", w );
508 info->flags = MONITOR_INFO_HAS_MONITOR_ID;
509 TRACE( "Monitor id %s.\n", info->monitor_id_string );
511 for (i = 0; i < 4; ++i)
513 if (edid[54 + i * 18] || edid[54 + i * 18 + 1])
515 /* Detailed timing descriptor. */
516 if (info->flags & MONITOR_INFO_HAS_PREFERRED_MODE) continue;
517 info->preferred_width = edid[54 + i * 18 + 2] | ((UINT32)(edid[54 + i * 18 + 4] & 0xf0) << 4);
518 info->preferred_height = edid[54 + i * 18 + 5] | ((UINT32)(edid[54 + i * 18 + 7] & 0xf0) << 4);
519 if (info->preferred_width && info->preferred_height)
520 info->flags |= MONITOR_INFO_HAS_PREFERRED_MODE;
521 continue;
523 if (edid[54 + i * 18 + 3] != 0xfc) continue;
524 /* "Display name" ASCII descriptor. */
525 s = (const char *)&edid[54 + i * 18 + 5];
526 for (j = 0; s[j] && j < 13; ++j)
527 info->monitor_name[j] = s[j];
528 while (j && isspace(s[j - 1])) --j;
529 info->monitor_name[j] = 0;
530 info->flags |= MONITOR_INFO_HAS_MONITOR_NAME;
531 break;
535 static BOOL write_adapter_mode( HKEY adapter_key, UINT index, const DEVMODEW *mode )
537 WCHAR bufferW[MAX_PATH] = {0};
538 char buffer[MAX_PATH];
540 sprintf( buffer, "Modes\\%08X", index );
541 asciiz_to_unicode( bufferW, buffer );
542 return set_reg_value( adapter_key, bufferW, REG_BINARY, &mode->dmFields, sizeof(*mode) - offsetof(DEVMODEW, dmFields) );
545 static BOOL read_adapter_mode( HKEY adapter_key, UINT index, DEVMODEW *mode )
547 char value_buf[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(*mode)])];
548 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)value_buf;
549 WCHAR bufferW[MAX_PATH] = {0};
550 char buffer[MAX_PATH];
552 sprintf( buffer, "Modes\\%08X", index );
553 asciiz_to_unicode( bufferW, buffer );
554 if (!query_reg_value( adapter_key, bufferW, value, sizeof(value_buf) )) return FALSE;
556 memcpy( &mode->dmFields, value->Data, sizeof(*mode) - offsetof(DEVMODEW, dmFields) );
557 return TRUE;
560 static BOOL adapter_get_registry_settings( const struct adapter *adapter, DEVMODEW *mode )
562 BOOL ret = FALSE;
563 HANDLE mutex;
564 HKEY hkey;
566 mutex = get_display_device_init_mutex();
568 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
569 else if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
570 else
572 ret = read_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode );
573 NtClose( hkey );
576 release_display_device_init_mutex( mutex );
577 return ret;
580 static BOOL adapter_set_registry_settings( const struct adapter *adapter, const DEVMODEW *mode )
582 HANDLE mutex;
583 HKEY hkey;
584 BOOL ret;
586 mutex = get_display_device_init_mutex();
588 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
589 if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
590 else
592 ret = write_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode );
593 NtClose( hkey );
596 release_display_device_init_mutex( mutex );
597 return ret;
600 static BOOL adapter_get_current_settings( const struct adapter *adapter, DEVMODEW *mode )
602 BOOL is_primary = !!(adapter->dev.state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
603 HANDLE mutex;
604 HKEY hkey;
605 BOOL ret;
607 /* use the default implementation in virtual desktop mode */
608 if (is_virtual_desktop()) ret = FALSE;
609 else ret = user_driver->pGetCurrentDisplaySettings( adapter->dev.device_name, is_primary, mode );
611 if (ret) return TRUE;
613 /* default implementation: read current display settings from the registry. */
615 mutex = get_display_device_init_mutex();
617 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
618 else if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
619 else
621 ret = read_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
622 NtClose( hkey );
625 release_display_device_init_mutex( mutex );
626 return ret;
629 static BOOL adapter_set_current_settings( const struct adapter *adapter, const DEVMODEW *mode )
631 HANDLE mutex;
632 HKEY hkey;
633 BOOL ret;
635 mutex = get_display_device_init_mutex();
637 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
638 if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
639 else
641 ret = write_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
642 NtClose( hkey );
645 release_display_device_init_mutex( mutex );
646 return ret;
649 static int mode_compare(const void *p1, const void *p2)
651 BOOL a_interlaced, b_interlaced, a_stretched, b_stretched;
652 DWORD a_width, a_height, b_width, b_height;
653 const DEVMODEW *a = p1, *b = p2;
654 int ret;
656 /* Depth in descending order */
657 if ((ret = b->dmBitsPerPel - a->dmBitsPerPel)) return ret;
659 /* Use the width and height in landscape mode for comparison */
660 if (a->dmDisplayOrientation == DMDO_DEFAULT || a->dmDisplayOrientation == DMDO_180)
662 a_width = a->dmPelsWidth;
663 a_height = a->dmPelsHeight;
665 else
667 a_width = a->dmPelsHeight;
668 a_height = a->dmPelsWidth;
671 if (b->dmDisplayOrientation == DMDO_DEFAULT || b->dmDisplayOrientation == DMDO_180)
673 b_width = b->dmPelsWidth;
674 b_height = b->dmPelsHeight;
676 else
678 b_width = b->dmPelsHeight;
679 b_height = b->dmPelsWidth;
682 /* Width in ascending order */
683 if ((ret = a_width - b_width)) return ret;
685 /* Height in ascending order */
686 if ((ret = a_height - b_height)) return ret;
688 /* Frequency in descending order */
689 if ((ret = b->dmDisplayFrequency - a->dmDisplayFrequency)) return ret;
691 /* Orientation in ascending order */
692 if ((ret = a->dmDisplayOrientation - b->dmDisplayOrientation)) return ret;
694 if (!(a->dmFields & DM_DISPLAYFLAGS)) a_interlaced = FALSE;
695 else a_interlaced = !!(a->dmDisplayFlags & DM_INTERLACED);
696 if (!(b->dmFields & DM_DISPLAYFLAGS)) b_interlaced = FALSE;
697 else b_interlaced = !!(b->dmDisplayFlags & DM_INTERLACED);
699 /* Interlaced in ascending order */
700 if ((ret = a_interlaced - b_interlaced)) return ret;
702 if (!(a->dmFields & DM_DISPLAYFIXEDOUTPUT)) a_stretched = FALSE;
703 else a_stretched = a->dmDisplayFixedOutput == DMDFO_STRETCH;
704 if (!(b->dmFields & DM_DISPLAYFIXEDOUTPUT)) b_stretched = FALSE;
705 else b_stretched = b->dmDisplayFixedOutput == DMDFO_STRETCH;
707 /* Stretched in ascending order */
708 if ((ret = a_stretched - b_stretched)) return ret;
710 return 0;
713 static unsigned int query_reg_subkey_value( HKEY hkey, const WCHAR *name, unsigned int name_size,
714 KEY_VALUE_PARTIAL_INFORMATION *value, unsigned int size )
716 HKEY subkey;
718 if (!(subkey = reg_open_key( hkey, name, name_size ))) return 0;
719 size = query_reg_value( subkey, NULL, value, size );
720 NtClose( subkey );
721 return size;
724 static BOOL read_display_adapter_settings( unsigned int index, struct adapter *info )
726 char buffer[4096];
727 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
728 WCHAR *value_str = (WCHAR *)value->Data;
729 DEVMODEW *mode;
730 DWORD i, size;
731 HKEY hkey;
733 if (!enum_key && !(enum_key = reg_open_key( NULL, enum_keyW, sizeof(enum_keyW) )))
734 return FALSE;
736 /* Find adapter */
737 sprintf( buffer, "\\Device\\Video%d", index );
738 size = query_reg_ascii_value( video_key, buffer, value, sizeof(buffer) );
739 if (!size || value->Type != REG_SZ ||
740 value->DataLength <= sizeof("\\Registry\\Machine\\") * sizeof(WCHAR))
741 return FALSE;
743 /* DeviceKey */
744 memcpy( info->dev.device_key, value_str, value->DataLength );
745 info->config_key = info->dev.device_key + sizeof("\\Registry\\Machine\\") - 1;
747 if (!(hkey = reg_open_key( NULL, value_str, value->DataLength - sizeof(WCHAR) )))
748 return FALSE;
750 /* DeviceString */
751 if (query_reg_value( hkey, driver_descW, value, sizeof(buffer) ) && value->Type == REG_SZ)
752 memcpy( info->dev.device_string, value_str, value->DataLength );
753 NtClose( hkey );
755 /* DeviceName */
756 sprintf( buffer, "\\\\.\\DISPLAY%d", index + 1 );
757 asciiz_to_unicode( info->dev.device_name, buffer );
759 if (!(hkey = reg_open_key( config_key, info->config_key,
760 lstrlenW( info->config_key ) * sizeof(WCHAR) )))
761 return FALSE;
763 /* StateFlags */
764 if (query_reg_value( hkey, state_flagsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
765 info->dev.state_flags = *(const DWORD *)value->Data;
767 /* Interface name */
768 info->dev.interface_name[0] = 0;
770 /* ModeCount */
771 if (query_reg_value( hkey, mode_countW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
772 info->mode_count = *(const DWORD *)value->Data;
774 /* Modes, allocate an extra mode for easier iteration */
775 if ((info->modes = calloc( info->mode_count + 1, sizeof(DEVMODEW) )))
777 for (i = 0, mode = info->modes; i < info->mode_count; i++)
779 mode->dmSize = offsetof(DEVMODEW, dmICMMethod);
780 if (!read_adapter_mode( hkey, i, mode )) break;
781 mode = NEXT_DEVMODEW(mode);
783 info->mode_count = i;
785 qsort(info->modes, info->mode_count, sizeof(*info->modes) + info->modes->dmDriverExtra, mode_compare);
788 /* DeviceID */
789 size = query_reg_value( hkey, gpu_idW, value, sizeof(buffer) );
790 NtClose( hkey );
791 if (!size || value->Type != REG_SZ || !info->mode_count || !info->modes) return FALSE;
793 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
794 return FALSE;
796 size = query_reg_subkey_value( hkey, devpropkey_gpu_luidW, sizeof(devpropkey_gpu_luidW), value, sizeof(buffer) );
797 if (size != sizeof(info->gpu_luid))
799 NtClose( hkey );
800 return FALSE;
802 memcpy( &info->gpu_luid, value->Data, sizeof(info->gpu_luid) );
804 size = query_reg_value( hkey, hardware_idW, value, sizeof(buffer) );
805 NtClose( hkey );
806 if (!size || (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
807 return FALSE;
809 lstrcpyW( info->dev.device_id, value_str );
810 return TRUE;
813 static BOOL read_monitor_settings( struct adapter *adapter, UINT index, struct monitor *monitor )
815 char buffer[4096];
816 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
817 WCHAR *device_name, *value_str = (WCHAR *)value->Data, *ptr;
818 HKEY hkey, subkey;
819 DWORD size, len;
821 monitor->flags = adapter->id ? 0 : MONITORINFOF_PRIMARY;
823 /* DeviceName */
824 sprintf( buffer, "\\\\.\\DISPLAY%d\\Monitor%d", adapter->id + 1, index );
825 asciiz_to_unicode( monitor->dev.device_name, buffer );
827 if (!(hkey = reg_open_key( config_key, adapter->config_key,
828 lstrlenW( adapter->config_key ) * sizeof(WCHAR) )))
829 return FALSE;
831 /* Interface name */
832 sprintf( buffer, "MonitorID%u", index );
833 size = query_reg_ascii_value( hkey, buffer, value, sizeof(buffer) );
834 NtClose( hkey );
835 if (!size || value->Type != REG_SZ) return FALSE;
836 len = asciiz_to_unicode( monitor->dev.interface_name, "\\\\\?\\" ) / sizeof(WCHAR) - 1;
837 memcpy( monitor->dev.interface_name + len, value_str, value->DataLength - sizeof(WCHAR) );
838 len += value->DataLength / sizeof(WCHAR) - 1;
839 monitor->dev.interface_name[len++] = '#';
840 memcpy( monitor->dev.interface_name + len, guid_devinterface_monitorW,
841 sizeof(guid_devinterface_monitorW) );
843 /* Replace '\\' with '#' after prefix */
844 for (ptr = monitor->dev.interface_name + ARRAYSIZE("\\\\\?\\") - 1; *ptr; ptr++)
845 if (*ptr == '\\') *ptr = '#';
847 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
848 return FALSE;
850 /* StateFlags, WINE_DEVPROPKEY_MONITOR_STATEFLAGS */
851 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_stateflagsW,
852 sizeof(wine_devpropkey_monitor_stateflagsW),
853 value, sizeof(buffer) );
854 if (size != sizeof(monitor->dev.state_flags))
856 NtClose( hkey );
857 return FALSE;
859 monitor->dev.state_flags = *(const DWORD *)value->Data;
861 /* Output ID */
862 size = query_reg_subkey_value( hkey, devpropkey_monitor_output_idW,
863 sizeof(devpropkey_monitor_output_idW),
864 value, sizeof(buffer) );
865 if (size != sizeof(monitor->output_id))
867 NtClose( hkey );
868 return FALSE;
870 monitor->output_id = *(const unsigned int *)value->Data;
872 /* rc_monitor, WINE_DEVPROPKEY_MONITOR_RCMONITOR */
873 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcmonitorW,
874 sizeof(wine_devpropkey_monitor_rcmonitorW),
875 value, sizeof(buffer) );
876 if (size != sizeof(monitor->rc_monitor))
878 NtClose( hkey );
879 return FALSE;
881 monitor->rc_monitor = *(const RECT *)value->Data;
883 /* rc_work, WINE_DEVPROPKEY_MONITOR_RCWORK */
884 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcworkW,
885 sizeof(wine_devpropkey_monitor_rcworkW),
886 value, sizeof(buffer) );
887 if (size != sizeof(monitor->rc_work))
889 NtClose( hkey );
890 return FALSE;
892 monitor->rc_work = *(const RECT *)value->Data;
894 /* DeviceString */
895 if (!query_reg_value( hkey, device_descW, value, sizeof(buffer) ) || value->Type != REG_SZ)
897 NtClose( hkey );
898 return FALSE;
900 memcpy( monitor->dev.device_string, value->Data, value->DataLength );
902 /* DeviceKey */
903 if (!query_reg_value( hkey, driverW, value, sizeof(buffer) ) || value->Type != REG_SZ)
905 NtClose( hkey );
906 return FALSE;
908 size = asciiz_to_unicode( monitor->dev.device_key,
909 "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\" );
910 device_name = &monitor->dev.device_key[size / sizeof(WCHAR) - 1];
911 memcpy( device_name, value_str, value->DataLength );
913 /* DeviceID */
914 if (!query_reg_value( hkey, hardware_idW, value, sizeof(buffer) ) ||
915 (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
917 NtClose( hkey );
918 return FALSE;
920 size = lstrlenW( value_str );
921 memcpy( monitor->dev.device_id, value_str, size * sizeof(WCHAR) );
922 monitor->dev.device_id[size++] = '\\';
923 lstrcpyW( monitor->dev.device_id + size, device_name );
925 /* EDID */
926 if ((subkey = reg_open_key( hkey, device_parametersW, sizeof(device_parametersW) )))
928 if (query_reg_value( subkey, edidW, value, sizeof(buffer) ))
929 get_monitor_info_from_edid( &monitor->edid_info, value->Data, value->DataLength );
930 NtClose( subkey );
933 NtClose( hkey );
934 return TRUE;
937 static void reg_empty_key( HKEY root, const char *key_name )
939 char buffer[4096];
940 KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer;
941 KEY_VALUE_FULL_INFORMATION *value = (KEY_VALUE_FULL_INFORMATION *)buffer;
942 WCHAR bufferW[512];
943 DWORD size;
944 HKEY hkey;
946 if (key_name)
947 hkey = reg_open_key( root, bufferW, asciiz_to_unicode( bufferW, key_name ) - sizeof(WCHAR) );
948 else
949 hkey = root;
951 while (!NtEnumerateKey( hkey, 0, KeyNodeInformation, key, sizeof(buffer), &size ))
952 reg_delete_tree( hkey, key->Name, key->NameLength );
954 while (!NtEnumerateValueKey( hkey, 0, KeyValueFullInformation, value, sizeof(buffer), &size ))
956 UNICODE_STRING name = { value->NameLength, value->NameLength, value->Name };
957 NtDeleteValueKey( hkey, &name );
960 if (hkey != root) NtClose( hkey );
963 static void prepare_devices(void)
965 char buffer[4096];
966 KEY_NODE_INFORMATION *key = (void *)buffer;
967 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
968 WCHAR *value_str = (WCHAR *)value->Data;
969 WCHAR bufferW[128];
970 unsigned i = 0;
971 DWORD size;
972 HKEY hkey, subkey, device_key, prop_key;
974 if (!enum_key) enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL );
975 if (!control_key) control_key = reg_create_key( NULL, control_keyW, sizeof(control_keyW), 0, NULL );
976 if (!video_key) video_key = reg_create_key( NULL, devicemap_video_keyW, sizeof(devicemap_video_keyW),
977 REG_OPTION_VOLATILE, NULL );
979 /* delete monitors */
980 reg_empty_key( enum_key, "DISPLAY" );
981 sprintf( buffer, "Class\\%s", guid_devclass_monitorA );
982 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
983 0, NULL );
984 reg_empty_key( hkey, NULL );
985 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
986 NtClose( hkey );
988 /* delete adapters */
989 reg_empty_key( video_key, NULL );
991 /* clean GPUs */
992 sprintf( buffer, "Class\\%s", guid_devclass_displayA );
993 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
994 0, NULL );
995 reg_empty_key( hkey, NULL );
996 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
997 NtClose( hkey );
999 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
1001 /* To preserve GPU GUIDs, mark them as not present and delete them in cleanup_devices if needed. */
1002 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
1004 unsigned int j = 0;
1006 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
1008 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
1010 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
1011 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1012 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
1014 NtClose( device_key );
1015 continue;
1018 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1019 if (size == sizeof(guid_devclass_displayW) &&
1020 !wcscmp( (const WCHAR *)value->Data, guid_devclass_displayW ) &&
1021 (prop_key = reg_create_key( device_key, devpropkey_device_ispresentW,
1022 sizeof(devpropkey_device_ispresentW), 0, NULL )))
1024 BOOL present = FALSE;
1025 set_reg_value( prop_key, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
1026 &present, sizeof(present) );
1027 NtClose( prop_key );
1030 NtClose( device_key );
1033 NtClose( subkey );
1036 NtClose( hkey );
1039 static void cleanup_devices(void)
1041 char buffer[4096];
1042 KEY_NODE_INFORMATION *key = (void *)buffer;
1043 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1044 WCHAR bufferW[512], *value_str = (WCHAR *)value->Data;
1045 unsigned i = 0;
1046 DWORD size;
1047 HKEY hkey, subkey, device_key, prop_key;
1049 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
1051 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
1053 unsigned int j = 0;
1055 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
1057 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
1059 BOOL present = FALSE;
1061 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
1062 memcpy( bufferW, key->Name, key->NameLength );
1063 bufferW[key->NameLength / sizeof(WCHAR)] = 0;
1065 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1066 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
1068 NtClose( device_key );
1069 continue;
1072 if ((prop_key = reg_open_key( device_key, devpropkey_device_ispresentW,
1073 sizeof(devpropkey_device_ispresentW) )))
1075 if (query_reg_value( prop_key, NULL, value, sizeof(buffer) ) == sizeof(BOOL))
1076 present = *(const BOOL *)value->Data;
1077 NtClose( prop_key );
1080 NtClose( device_key );
1082 if (!present && reg_delete_tree( subkey, bufferW, lstrlenW( bufferW ) * sizeof(WCHAR) ))
1083 j = 0;
1086 NtClose( subkey );
1089 NtClose( hkey );
1092 /* see UuidCreate */
1093 static void uuid_create( GUID *uuid )
1095 char buf[4096];
1096 NtQuerySystemInformation( SystemInterruptInformation, buf, sizeof(buf), NULL );
1097 memcpy( uuid, buf, sizeof(*uuid) );
1098 uuid->Data3 &= 0x0fff;
1099 uuid->Data3 |= (4 << 12);
1100 uuid->Data4[0] &= 0x3f;
1101 uuid->Data4[0] |= 0x80;
1104 #define TICKSPERSEC 10000000
1105 #define SECSPERDAY 86400
1106 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
1107 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
1109 static unsigned int format_date( WCHAR *bufferW, LONGLONG time )
1111 int cleaps, years, yearday, months, days;
1112 unsigned int day, month, year;
1113 char buffer[32];
1115 days = time / TICKSPERSEC / SECSPERDAY;
1117 /* compute year, month and day of month, see RtlTimeToTimeFields */
1118 cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
1119 days += 28188 + cleaps;
1120 years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
1121 yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
1122 months = (64 * yearday) / 1959;
1123 if (months < 14)
1125 month = months - 1;
1126 year = years + 1524;
1128 else
1130 month = months - 13;
1131 year = years + 1525;
1133 day = yearday - (1959 * months) / 64 ;
1135 sprintf( buffer, "%u-%u-%u", month, day, year );
1136 return asciiz_to_unicode( bufferW, buffer );
1139 struct device_manager_ctx
1141 unsigned int gpu_count;
1142 unsigned int adapter_count;
1143 unsigned int video_count;
1144 unsigned int monitor_count;
1145 unsigned int output_count;
1146 unsigned int mode_count;
1147 HANDLE mutex;
1148 WCHAR gpuid[128];
1149 WCHAR gpu_guid[64];
1150 LUID gpu_luid;
1151 HKEY adapter_key;
1152 /* for the virtual desktop settings */
1153 BOOL is_primary;
1154 UINT primary_bpp;
1155 UINT primary_width;
1156 UINT primary_height;
1159 static void link_device( const WCHAR *instance, const WCHAR *class )
1161 unsigned int instance_len = lstrlenW( instance ), len;
1162 unsigned int class_len = lstrlenW( class );
1163 WCHAR buffer[MAX_PATH], *ptr;
1164 HKEY hkey, subkey;
1166 static const WCHAR symbolic_linkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k',0};
1167 static const WCHAR hashW[] = {'#'};
1169 len = asciiz_to_unicode( buffer, "DeviceClasses\\" ) / sizeof(WCHAR) - 1;
1170 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
1171 len += class_len;
1172 len += asciiz_to_unicode( buffer + len, "\\##?#" ) / sizeof(WCHAR) - 1;
1173 memcpy( buffer + len, instance, instance_len * sizeof(WCHAR) );
1174 for (ptr = buffer + len; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
1175 len += instance_len;
1176 buffer[len++] = '#';
1177 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
1178 len += class_len;
1179 hkey = reg_create_key( control_key, buffer, len * sizeof(WCHAR), 0, NULL );
1181 set_reg_value( hkey, device_instanceW, REG_SZ, instance, (instance_len + 1) * sizeof(WCHAR) );
1183 subkey = reg_create_key( hkey, hashW, sizeof(hashW), REG_OPTION_VOLATILE, NULL );
1184 NtClose( hkey );
1185 hkey = subkey;
1187 len = asciiz_to_unicode( buffer, "\\\\?\\" ) / sizeof(WCHAR) - 1;
1188 memcpy( buffer + len, instance, (instance_len + 1) * sizeof(WCHAR) );
1189 len += instance_len;
1190 memcpy( buffer + len, class, (class_len + 1) * sizeof(WCHAR) );
1191 len += class_len + 1;
1192 for (ptr = buffer + 4; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
1193 set_reg_value( hkey, symbolic_linkW, REG_SZ, buffer, len * sizeof(WCHAR) );
1195 if ((subkey = reg_create_key( hkey, controlW, sizeof(controlW), REG_OPTION_VOLATILE, NULL )))
1197 const DWORD linked = 1;
1198 set_reg_value( subkey, linkedW, REG_DWORD, &linked, sizeof(linked) );
1199 NtClose( subkey );
1203 static void add_gpu( const struct gdi_gpu *gpu, void *param )
1205 struct device_manager_ctx *ctx = param;
1206 const WCHAR *desc;
1207 char buffer[4096];
1208 WCHAR bufferW[512];
1209 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1210 unsigned int gpu_index, size;
1211 HKEY hkey, subkey;
1212 LARGE_INTEGER ft;
1214 static const BOOL present = TRUE;
1215 static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0};
1216 static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0};
1217 static const WCHAR driver_date_dataW[] =
1218 {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
1219 static const WCHAR adapter_stringW[] =
1220 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n',
1221 '.','A','d','a','p','t','e','r','S','t','r','i','n','g',0};
1222 static const WCHAR bios_stringW[] =
1223 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1224 'B','i','o','s','S','t','r','i','n','g',0};
1225 static const WCHAR chip_typeW[] =
1226 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1227 'C','h','i','p','T','y','p','e',0};
1228 static const WCHAR dac_typeW[] =
1229 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1230 'D','a','c','T','y','p','e',0};
1231 static const WCHAR ramdacW[] =
1232 {'I','n','t','e','r','g','r','a','t','e','d',' ','R','A','M','D','A','C',0};
1233 static const WCHAR driver_dateW[] = {'D','r','i','v','e','r','D','a','t','e',0};
1234 static const WCHAR driver_versionW[] =
1235 {'D','r','i','v','e','r','V','e','r','s','i','o','n',0};
1237 TRACE( "%s %04X %04X %08X %02X\n", debugstr_w(gpu->name),
1238 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
1240 gpu_index = ctx->gpu_count++;
1241 ctx->adapter_count = 0;
1242 ctx->monitor_count = 0;
1243 ctx->mode_count = 0;
1245 if (!enum_key && !(enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL )))
1246 return;
1248 if (!ctx->mutex)
1250 ctx->mutex = get_display_device_init_mutex();
1251 pthread_mutex_lock( &display_lock );
1252 prepare_devices();
1255 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X\\%08X",
1256 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index );
1257 size = asciiz_to_unicode( ctx->gpuid, buffer );
1258 if (!(hkey = reg_create_key( enum_key, ctx->gpuid, size - sizeof(WCHAR), 0, NULL ))) return;
1260 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
1261 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_displayW,
1262 sizeof(guid_devclass_displayW) );
1263 sprintf( buffer, "%s\\%04X", guid_devclass_displayA, gpu_index );
1264 set_reg_value( hkey, driverW, REG_SZ, bufferW, asciiz_to_unicode( bufferW, buffer ));
1266 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X",
1267 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
1268 size = asciiz_to_unicode( bufferW, buffer );
1269 bufferW[size / sizeof(WCHAR)] = 0; /* for REG_MULTI_SZ */
1270 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, size + sizeof(WCHAR) );
1272 if ((subkey = reg_create_key( hkey, devpkey_device_matching_device_id,
1273 sizeof(devpkey_device_matching_device_id), 0, NULL )))
1275 if (gpu->vendor_id && gpu->device_id)
1276 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW, size );
1277 else
1278 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
1279 asciiz_to_unicode( bufferW, "ROOT\\BasicRender" ));
1280 NtClose( subkey );
1283 if (gpu->vendor_id && gpu->device_id)
1285 if ((subkey = reg_create_key( hkey, devpkey_device_bus_number,
1286 sizeof(devpkey_device_bus_number), 0, NULL )))
1288 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1289 &gpu_index, sizeof(gpu_index) );
1290 NtClose( subkey );
1294 if ((subkey = reg_create_key( hkey, devpkey_device_removal_policy,
1295 sizeof(devpkey_device_removal_policy), 0, NULL )))
1297 unsigned int removal_policy = CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL;
1299 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1300 &removal_policy, sizeof(removal_policy) );
1301 NtClose( subkey );
1304 desc = gpu->name;
1305 if (!desc[0]) desc = wine_adapterW;
1306 set_reg_value( hkey, device_descW, REG_SZ, desc, (lstrlenW( desc ) + 1) * sizeof(WCHAR) );
1308 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
1310 if (!query_reg_value( subkey, video_idW, value, sizeof(buffer) ))
1312 GUID guid;
1313 uuid_create( &guid );
1314 sprintf( buffer, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
1315 (unsigned int)guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
1316 guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] );
1317 size = asciiz_to_unicode( ctx->gpu_guid, buffer );
1318 TRACE( "created guid %s\n", debugstr_w(ctx->gpu_guid) );
1319 set_reg_value( subkey, video_idW, REG_SZ, ctx->gpu_guid, size );
1321 else
1323 memcpy( ctx->gpu_guid, value->Data, value->DataLength );
1324 TRACE( "got guid %s\n", debugstr_w(ctx->gpu_guid) );
1326 NtClose( subkey );
1329 if ((subkey = reg_create_key( hkey, devpropkey_gpu_vulkan_uuidW,
1330 sizeof(devpropkey_gpu_vulkan_uuidW), 0, NULL )))
1332 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_GUID,
1333 &gpu->vulkan_uuid, sizeof(gpu->vulkan_uuid) );
1334 NtClose( subkey );
1337 if ((subkey = reg_create_key( hkey, devpropkey_device_ispresentW,
1338 sizeof(devpropkey_device_ispresentW), 0, NULL )))
1340 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
1341 &present, sizeof(present) );
1342 NtClose( subkey );
1345 if ((subkey = reg_create_key( hkey, devpropkey_gpu_luidW, sizeof(devpropkey_gpu_luidW), 0, NULL )))
1347 if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) != sizeof(LUID))
1349 NtAllocateLocallyUniqueId( &ctx->gpu_luid );
1350 TRACE("allocated luid %08x%08x\n", (int)ctx->gpu_luid.HighPart, (int)ctx->gpu_luid.LowPart );
1351 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT64,
1352 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
1354 else
1356 memcpy( &ctx->gpu_luid, value->Data, sizeof(ctx->gpu_luid) );
1357 TRACE("got luid %08x%08x\n", (int)ctx->gpu_luid.HighPart, (int)ctx->gpu_luid.LowPart );
1359 NtClose( subkey );
1362 NtClose( hkey );
1364 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_displayA, gpu_index );
1365 hkey = reg_create_key( control_key, bufferW,
1366 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
1368 NtQuerySystemTime( &ft );
1369 set_reg_value( hkey, driver_dateW, REG_SZ, bufferW, format_date( bufferW, ft.QuadPart ));
1371 set_reg_value( hkey, driver_date_dataW, REG_BINARY, &ft, sizeof(ft) );
1373 size = (lstrlenW( desc ) + 1) * sizeof(WCHAR);
1374 set_reg_value( hkey, driver_descW, REG_SZ, desc, size );
1375 set_reg_value( hkey, adapter_stringW, REG_BINARY, desc, size );
1376 set_reg_value( hkey, bios_stringW, REG_BINARY, desc, size );
1377 set_reg_value( hkey, chip_typeW, REG_BINARY, desc, size );
1378 set_reg_value( hkey, dac_typeW, REG_BINARY, ramdacW, sizeof(ramdacW) );
1380 if (gpu->vendor_id && gpu->device_id)
1382 /* The last seven digits are the driver number. */
1383 switch (gpu->vendor_id)
1385 /* Intel */
1386 case 0x8086:
1387 sprintf( buffer, "31.0.101.4576" );
1388 break;
1389 /* AMD */
1390 case 0x1002:
1391 sprintf( buffer, "31.0.14051.5006" );
1392 break;
1393 /* Nvidia */
1394 case 0x10de:
1395 sprintf( buffer, "31.0.15.3625" );
1396 break;
1397 /* Default value for any other vendor. */
1398 default:
1399 sprintf( buffer, "31.0.10.1000" );
1400 break;
1402 set_reg_value( hkey, driver_versionW, REG_SZ, bufferW, asciiz_to_unicode( bufferW, buffer ) );
1405 NtClose( hkey );
1407 link_device( ctx->gpuid, guid_devinterface_display_adapterW );
1408 link_device( ctx->gpuid, guid_display_device_arrivalW );
1411 static void add_adapter( const struct gdi_adapter *adapter, void *param )
1413 struct device_manager_ctx *ctx = param;
1414 unsigned int adapter_index, video_index, len;
1415 char name[64], buffer[MAX_PATH];
1416 WCHAR nameW[64], bufferW[MAX_PATH];
1417 HKEY hkey;
1419 TRACE( "\n" );
1421 if (ctx->adapter_key)
1423 NtClose( ctx->adapter_key );
1424 ctx->adapter_key = NULL;
1427 adapter_index = ctx->adapter_count++;
1428 video_index = ctx->video_count++;
1429 ctx->monitor_count = 0;
1430 ctx->mode_count = 0;
1432 len = asciiz_to_unicode( bufferW, "\\Registry\\Machine\\System\\CurrentControlSet\\"
1433 "Control\\Video\\" ) / sizeof(WCHAR) - 1;
1434 lstrcpyW( bufferW + len, ctx->gpu_guid );
1435 len += lstrlenW( bufferW + len );
1436 sprintf( buffer, "\\%04x", adapter_index );
1437 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1438 hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1439 REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, NULL );
1440 if (!hkey) hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1441 REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK, NULL );
1443 sprintf( name, "\\Device\\Video%u", video_index );
1444 asciiz_to_unicode( nameW, name );
1445 set_reg_value( video_key, nameW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1447 if (hkey)
1449 sprintf( buffer, "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\"
1450 "%s\\%04X", guid_devclass_displayA, ctx->gpu_count - 1 );
1451 len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR);
1452 set_reg_value( hkey, symbolic_link_valueW, REG_LINK, bufferW, len );
1453 NtClose( hkey );
1455 else ERR( "failed to create link key\n" );
1457 /* Following information is Wine specific, it doesn't really exist on Windows. */
1458 len = asciiz_to_unicode( bufferW, "System\\CurrentControlSet\\Control\\Video\\" )
1459 / sizeof(WCHAR) - 1;
1460 lstrcpyW( bufferW + len, ctx->gpu_guid );
1461 len += lstrlenW( bufferW + len );
1462 sprintf( buffer, "\\%04x", adapter_index );
1463 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1464 ctx->adapter_key = reg_create_key( config_key, bufferW, len * sizeof(WCHAR),
1465 REG_OPTION_VOLATILE, NULL );
1467 set_reg_value( ctx->adapter_key, gpu_idW, REG_SZ, ctx->gpuid,
1468 (lstrlenW( ctx->gpuid ) + 1) * sizeof(WCHAR) );
1469 set_reg_value( ctx->adapter_key, state_flagsW, REG_DWORD, &adapter->state_flags,
1470 sizeof(adapter->state_flags) );
1473 static void add_monitor( const struct gdi_monitor *monitor, void *param )
1475 struct device_manager_ctx *ctx = param;
1476 char buffer[MAX_PATH], instance[64];
1477 unsigned int monitor_index, output_index;
1478 struct edid_monitor_info monitor_info;
1479 char monitor_id_string[16];
1480 WCHAR bufferW[MAX_PATH];
1481 HKEY hkey, subkey;
1482 unsigned int len;
1484 monitor_index = ctx->monitor_count++;
1485 output_index = ctx->output_count++;
1487 TRACE( "%u %s %s\n", monitor_index, wine_dbgstr_rect(&monitor->rc_monitor), wine_dbgstr_rect(&monitor->rc_work) );
1489 get_monitor_info_from_edid( &monitor_info, monitor->edid, monitor->edid_len );
1490 if (monitor_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
1491 strcpy( monitor_id_string, monitor_info.monitor_id_string );
1492 else
1493 strcpy( monitor_id_string, "Default_Monitor" );
1495 sprintf( buffer, "MonitorID%u", monitor_index );
1496 sprintf( instance, "DISPLAY\\%s\\%04X&%04X", monitor_id_string, ctx->video_count - 1, monitor_index );
1497 set_reg_ascii_value( ctx->adapter_key, buffer, instance );
1499 hkey = reg_create_key( enum_key, bufferW, asciiz_to_unicode( bufferW, instance ) - sizeof(WCHAR),
1500 0, NULL );
1501 if (!hkey) return;
1503 link_device( bufferW, guid_devinterface_monitorW );
1505 asciiz_to_unicode( bufferW, "Generic Non-PnP Monitor" );
1506 set_reg_value( hkey, device_descW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1508 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
1509 sprintf( buffer, "%s\\%04X", guid_devclass_monitorA, output_index );
1510 set_reg_ascii_value( hkey, "Driver", buffer );
1511 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_monitorW, sizeof(guid_devclass_monitorW) );
1513 sprintf( buffer, "MONITOR\\%s", monitor_id_string );
1514 len = asciiz_to_unicode( bufferW, buffer );
1515 bufferW[len / sizeof(WCHAR)] = 0;
1516 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, len + sizeof(WCHAR) );
1518 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
1520 static const WCHAR bad_edidW[] = {'B','A','D','_','E','D','I','D',0};
1522 if (monitor->edid_len)
1523 set_reg_value( subkey, edidW, REG_BINARY, monitor->edid, monitor->edid_len );
1524 else
1525 set_reg_value( subkey, bad_edidW, REG_BINARY, NULL, 0 );
1526 NtClose( subkey );
1529 /* StateFlags */
1530 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_stateflagsW,
1531 sizeof(wine_devpropkey_monitor_stateflagsW), 0, NULL )))
1533 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32, &monitor->state_flags,
1534 sizeof(monitor->state_flags) );
1535 NtClose( subkey );
1538 /* WINE_DEVPROPKEY_MONITOR_RCMONITOR */
1539 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcmonitorW,
1540 sizeof(wine_devpropkey_monitor_rcmonitorW), 0, NULL )))
1542 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_monitor,
1543 sizeof(monitor->rc_monitor) );
1544 NtClose( subkey );
1547 /* WINE_DEVPROPKEY_MONITOR_RCWORK */
1548 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcworkW,
1549 sizeof(wine_devpropkey_monitor_rcworkW), 0, NULL )))
1551 TRACE( "rc_work %s\n", wine_dbgstr_rect(&monitor->rc_work) );
1552 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_work,
1553 sizeof(monitor->rc_work) );
1554 NtClose( subkey );
1557 /* WINE_DEVPROPKEY_MONITOR_ADAPTERNAME */
1558 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_adapternameW,
1559 sizeof(wine_devpropkey_monitor_adapternameW), 0, NULL )))
1561 sprintf( buffer, "\\\\.\\DISPLAY%u", ctx->video_count );
1562 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
1563 asciiz_to_unicode( bufferW, buffer ));
1564 NtClose( subkey );
1567 /* DEVPROPKEY_MONITOR_GPU_LUID */
1568 if ((subkey = reg_create_key( hkey, devpropkey_monitor_gpu_luidW,
1569 sizeof(devpropkey_monitor_gpu_luidW), 0, NULL )))
1571 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_INT64,
1572 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
1573 NtClose( subkey );
1576 /* DEVPROPKEY_MONITOR_OUTPUT_ID */
1577 if ((subkey = reg_create_key( hkey, devpropkey_monitor_output_idW,
1578 sizeof(devpropkey_monitor_output_idW), 0, NULL )))
1580 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1581 &output_index, sizeof(output_index) );
1582 NtClose( subkey );
1585 NtClose( hkey );
1587 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_monitorA, output_index );
1588 hkey = reg_create_key( control_key, bufferW,
1589 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
1590 if (hkey) NtClose( hkey );
1593 static void add_mode( const DEVMODEW *mode, BOOL current, void *param )
1595 struct device_manager_ctx *ctx = param;
1596 DEVMODEW nopos_mode;
1598 if (!ctx->adapter_count)
1600 static const struct gdi_adapter default_adapter =
1602 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1604 TRACE( "adding default fake adapter\n" );
1605 add_adapter( &default_adapter, ctx );
1608 nopos_mode = *mode;
1609 nopos_mode.dmPosition.x = 0;
1610 nopos_mode.dmPosition.y = 0;
1611 nopos_mode.dmFields &= ~DM_POSITION;
1613 if (write_adapter_mode( ctx->adapter_key, ctx->mode_count, &nopos_mode ))
1615 ctx->mode_count++;
1616 set_reg_value( ctx->adapter_key, mode_countW, REG_DWORD, &ctx->mode_count, sizeof(ctx->mode_count) );
1617 if (current)
1619 if (!read_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, &nopos_mode ))
1620 write_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, mode );
1621 write_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, mode );
1626 static const struct gdi_device_manager device_manager =
1628 add_gpu,
1629 add_adapter,
1630 add_monitor,
1631 add_mode,
1634 static void reset_display_manager_ctx( struct device_manager_ctx *ctx )
1636 HANDLE mutex = ctx->mutex;
1638 if (ctx->adapter_key)
1640 NtClose( ctx->adapter_key );
1641 last_query_display_time = 0;
1643 if (ctx->gpu_count) cleanup_devices();
1645 memset( ctx, 0, sizeof(*ctx) );
1646 if ((ctx->mutex = mutex)) prepare_devices();
1649 static void release_display_manager_ctx( struct device_manager_ctx *ctx )
1651 if (ctx->mutex)
1653 pthread_mutex_unlock( &display_lock );
1654 release_display_device_init_mutex( ctx->mutex );
1655 ctx->mutex = 0;
1657 reset_display_manager_ctx( ctx );
1660 static void clear_display_devices(void)
1662 struct adapter *adapter;
1663 struct monitor *monitor;
1665 if (list_head( &monitors ) == &virtual_monitor.entry)
1667 list_init( &monitors );
1668 return;
1671 while (!list_empty( &monitors ))
1673 monitor = LIST_ENTRY( list_head( &monitors ), struct monitor, entry );
1674 adapter_release( monitor->adapter );
1675 list_remove( &monitor->entry );
1676 free( monitor );
1679 while (!list_empty( &adapters ))
1681 adapter = LIST_ENTRY( list_head( &adapters ), struct adapter, entry );
1682 list_remove( &adapter->entry );
1683 adapter_release( adapter );
1687 static BOOL update_display_cache_from_registry(void)
1689 DWORD adapter_id, monitor_id, monitor_count = 0, size;
1690 KEY_BASIC_INFORMATION key;
1691 struct adapter *adapter;
1692 struct monitor *monitor, *monitor2;
1693 HANDLE mutex = NULL;
1694 NTSTATUS status;
1695 BOOL ret;
1697 /* If user driver did initialize the registry, then exit */
1698 if (!video_key && !(video_key = reg_open_key( NULL, devicemap_video_keyW,
1699 sizeof(devicemap_video_keyW) )))
1700 return FALSE;
1702 status = NtQueryKey( video_key, KeyBasicInformation, &key,
1703 offsetof(KEY_BASIC_INFORMATION, Name), &size );
1704 if (status && status != STATUS_BUFFER_OVERFLOW)
1705 return FALSE;
1707 if (key.LastWriteTime.QuadPart <= last_query_display_time) return TRUE;
1709 mutex = get_display_device_init_mutex();
1710 pthread_mutex_lock( &display_lock );
1712 clear_display_devices();
1714 for (adapter_id = 0;; adapter_id++)
1716 if (!(adapter = calloc( 1, sizeof(*adapter) ))) break;
1717 adapter->refcount = 1;
1718 adapter->id = adapter_id;
1720 if (!read_display_adapter_settings( adapter_id, adapter ))
1722 adapter_release( adapter );
1723 break;
1726 list_add_tail( &adapters, &adapter->entry );
1727 for (monitor_id = 0;; monitor_id++)
1729 if (!(monitor = calloc( 1, sizeof(*monitor) ))) break;
1730 if (!read_monitor_settings( adapter, monitor_id, monitor ))
1732 free( monitor );
1733 break;
1736 monitor->id = monitor_id;
1737 monitor->adapter = adapter_acquire( adapter );
1739 LIST_FOR_EACH_ENTRY(monitor2, &monitors, struct monitor, entry)
1741 if (EqualRect(&monitor2->rc_monitor, &monitor->rc_monitor))
1743 monitor->is_clone = TRUE;
1744 break;
1748 monitor->handle = UlongToHandle( ++monitor_count );
1749 list_add_tail( &monitors, &monitor->entry );
1753 if ((ret = !list_empty( &adapters ) && !list_empty( &monitors )))
1754 last_query_display_time = key.LastWriteTime.QuadPart;
1755 pthread_mutex_unlock( &display_lock );
1756 release_display_device_init_mutex( mutex );
1757 return ret;
1760 static BOOL is_same_devmode( const DEVMODEW *a, const DEVMODEW *b )
1762 return a->dmDisplayOrientation == b->dmDisplayOrientation &&
1763 a->dmBitsPerPel == b->dmBitsPerPel &&
1764 a->dmPelsWidth == b->dmPelsWidth &&
1765 a->dmPelsHeight == b->dmPelsHeight &&
1766 a->dmDisplayFrequency == b->dmDisplayFrequency;
1769 static BOOL default_update_display_devices( const struct gdi_device_manager *manager, BOOL force, struct device_manager_ctx *ctx )
1771 /* default implementation: expose an adapter and a monitor with a few standard modes,
1772 * and read / write current display settings from / to the registry.
1774 static const DEVMODEW modes[] =
1776 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1777 .dmBitsPerPel = 32, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
1778 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1779 .dmBitsPerPel = 32, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
1780 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1781 .dmBitsPerPel = 32, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
1782 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1783 .dmBitsPerPel = 16, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
1784 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1785 .dmBitsPerPel = 16, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
1786 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1787 .dmBitsPerPel = 16, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
1789 static const struct gdi_gpu gpu;
1790 static const struct gdi_adapter adapter =
1792 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1794 struct gdi_monitor monitor =
1796 .state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED,
1798 DEVMODEW mode = {{0}};
1799 UINT i;
1801 if (!force) return TRUE;
1803 manager->add_gpu( &gpu, ctx );
1804 manager->add_adapter( &adapter, ctx );
1806 if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, &mode ))
1808 mode = modes[2];
1809 mode.dmFields |= DM_POSITION;
1811 monitor.rc_monitor.right = mode.dmPelsWidth;
1812 monitor.rc_monitor.bottom = mode.dmPelsHeight;
1813 monitor.rc_work.right = mode.dmPelsWidth;
1814 monitor.rc_work.bottom = mode.dmPelsHeight;
1816 manager->add_monitor( &monitor, ctx );
1817 for (i = 0; i < ARRAY_SIZE(modes); ++i)
1819 if (is_same_devmode( modes + i, &mode )) manager->add_mode( &mode, TRUE, ctx );
1820 else manager->add_mode( modes + i, FALSE, ctx );
1823 return TRUE;
1826 static BOOL update_display_devices( const struct gdi_device_manager *manager, BOOL force, struct device_manager_ctx *ctx )
1828 if (user_driver->pUpdateDisplayDevices( manager, force, ctx )) return TRUE;
1829 return default_update_display_devices( manager, force, ctx );
1832 /* parse the desktop size specification */
1833 static BOOL parse_size( const WCHAR *size, unsigned int *width, unsigned int *height )
1835 WCHAR *end;
1837 *width = wcstoul( size, &end, 10 );
1838 if (end == size) return FALSE;
1839 if (*end != 'x') return FALSE;
1840 size = end + 1;
1841 *height = wcstoul( size, &end, 10 );
1842 return !*end;
1845 /* retrieve the default desktop size from the registry */
1846 static BOOL get_default_desktop_size( unsigned int *width, unsigned int *height )
1848 static const WCHAR defaultW[] = {'D','e','f','a','u','l','t',0};
1849 WCHAR buffer[4096];
1850 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1851 DWORD size;
1852 HKEY hkey;
1854 /* @@ Wine registry key: HKCU\Software\Wine\Explorer\Desktops */
1855 if (!(hkey = reg_open_hkcu_key( "Software\\Wine\\Explorer\\Desktops" ))) return FALSE;
1857 size = query_reg_value( hkey, defaultW, value, sizeof(buffer) );
1858 NtClose( hkey );
1859 if (!size || value->Type != REG_SZ) return FALSE;
1861 if (!parse_size( (const WCHAR *)value->Data, width, height )) return FALSE;
1862 return TRUE;
1865 static void desktop_add_gpu( const struct gdi_gpu *gpu, void *param )
1869 static void desktop_add_adapter( const struct gdi_adapter *adapter, void *param )
1871 struct device_manager_ctx *ctx = param;
1872 ctx->is_primary = !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
1875 static void desktop_add_monitor( const struct gdi_monitor *monitor, void *param )
1879 static void desktop_add_mode( const DEVMODEW *mode, BOOL current, void *param )
1881 struct device_manager_ctx *ctx = param;
1883 if (ctx->is_primary && current)
1885 ctx->primary_bpp = mode->dmBitsPerPel;
1886 ctx->primary_width = mode->dmPelsWidth;
1887 ctx->primary_height = mode->dmPelsHeight;
1891 static const struct gdi_device_manager desktop_device_manager =
1893 desktop_add_gpu,
1894 desktop_add_adapter,
1895 desktop_add_monitor,
1896 desktop_add_mode,
1899 static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ctx *ctx )
1901 static const struct gdi_gpu gpu;
1902 static const struct gdi_adapter adapter =
1904 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1906 struct gdi_monitor monitor =
1908 .state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED,
1910 static struct screen_size
1912 unsigned int width;
1913 unsigned int height;
1914 } screen_sizes[] = {
1915 /* 4:3 */
1916 { 320, 240},
1917 { 400, 300},
1918 { 512, 384},
1919 { 640, 480},
1920 { 768, 576},
1921 { 800, 600},
1922 {1024, 768},
1923 {1152, 864},
1924 {1280, 960},
1925 {1400, 1050},
1926 {1600, 1200},
1927 {2048, 1536},
1928 /* 5:4 */
1929 {1280, 1024},
1930 {2560, 2048},
1931 /* 16:9 */
1932 {1280, 720},
1933 {1366, 768},
1934 {1600, 900},
1935 {1920, 1080},
1936 {2560, 1440},
1937 {3840, 2160},
1938 /* 16:10 */
1939 { 320, 200},
1940 { 640, 400},
1941 {1280, 800},
1942 {1440, 900},
1943 {1680, 1050},
1944 {1920, 1200},
1945 {2560, 1600}
1948 struct device_manager_ctx desktop_ctx = {0};
1949 UINT screen_width, screen_height, max_width, max_height;
1950 unsigned int depths[] = {8, 16, 0};
1951 DEVMODEW current, mode =
1953 .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1954 .dmDisplayFrequency = 60,
1956 UINT i, j;
1958 if (!force) return TRUE;
1959 /* in virtual desktop mode, read the device list from the user driver but expose virtual devices */
1960 if (!update_display_devices( &desktop_device_manager, TRUE, &desktop_ctx )) return FALSE;
1962 max_width = desktop_ctx.primary_width;
1963 max_height = desktop_ctx.primary_height;
1964 depths[ARRAY_SIZE(depths) - 1] = desktop_ctx.primary_bpp;
1966 if (!get_default_desktop_size( &screen_width, &screen_height ))
1968 screen_width = max_width;
1969 screen_height = max_height;
1972 add_gpu( &gpu, ctx );
1973 add_adapter( &adapter, ctx );
1974 if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, &current ))
1976 current = mode;
1977 current.dmFields |= DM_POSITION;
1978 current.dmBitsPerPel = desktop_ctx.primary_bpp;
1979 current.dmPelsWidth = screen_width;
1980 current.dmPelsHeight = screen_height;
1983 monitor.rc_monitor.right = current.dmPelsWidth;
1984 monitor.rc_monitor.bottom = current.dmPelsHeight;
1985 monitor.rc_work.right = current.dmPelsWidth;
1986 monitor.rc_work.bottom = current.dmPelsHeight;
1987 add_monitor( &monitor, ctx );
1989 for (i = 0; i < ARRAY_SIZE(depths); ++i)
1991 mode.dmBitsPerPel = depths[i];
1993 for (j = 0; j < ARRAY_SIZE(screen_sizes); ++j)
1995 mode.dmPelsWidth = screen_sizes[j].width;
1996 mode.dmPelsHeight = screen_sizes[j].height;
1998 if (mode.dmPelsWidth > max_width || mode.dmPelsHeight > max_height) continue;
1999 if (mode.dmPelsWidth == max_width && mode.dmPelsHeight == max_height) continue;
2000 if (mode.dmPelsWidth == screen_width && mode.dmPelsHeight == screen_height) continue;
2002 if (is_same_devmode( &mode, &current )) add_mode( &current, TRUE, ctx );
2003 else add_mode( &mode, FALSE, ctx );
2006 mode.dmPelsWidth = screen_width;
2007 mode.dmPelsHeight = screen_height;
2008 if (is_same_devmode( &mode, &current )) add_mode( &current, TRUE, ctx );
2009 else add_mode( &mode, FALSE, ctx );
2011 if (max_width != screen_width || max_height != screen_height)
2013 mode.dmPelsWidth = max_width;
2014 mode.dmPelsHeight = max_height;
2015 if (is_same_devmode( &mode, &current )) add_mode( &current, TRUE, ctx );
2016 else add_mode( &mode, FALSE, ctx );
2020 return TRUE;
2023 BOOL update_display_cache( BOOL force )
2025 static const WCHAR wine_service_station_name[] =
2026 {'_','_','w','i','n','e','s','e','r','v','i','c','e','_','w','i','n','s','t','a','t','i','o','n',0};
2027 HWINSTA winstation = NtUserGetProcessWindowStation();
2028 struct device_manager_ctx ctx = {0};
2029 BOOL was_virtual_desktop, ret;
2030 WCHAR name[MAX_PATH];
2032 /* services do not have any adapters, only a virtual monitor */
2033 if (NtUserGetObjectInformation( winstation, UOI_NAME, name, sizeof(name), NULL )
2034 && !wcscmp( name, wine_service_station_name ))
2036 pthread_mutex_lock( &display_lock );
2037 clear_display_devices();
2038 list_add_tail( &monitors, &virtual_monitor.entry );
2039 pthread_mutex_unlock( &display_lock );
2040 return TRUE;
2043 if ((was_virtual_desktop = is_virtual_desktop())) ret = TRUE;
2044 else ret = update_display_devices( &device_manager, force, &ctx );
2046 /* as update_display_devices calls the user driver, it starts explorer and may change the virtual desktop state */
2047 if (ret && is_virtual_desktop())
2049 reset_display_manager_ctx( &ctx );
2050 ret = desktop_update_display_devices( force || !was_virtual_desktop, &ctx );
2053 release_display_manager_ctx( &ctx );
2054 if (!ret) WARN( "Failed to update display devices\n" );
2056 if (!update_display_cache_from_registry())
2058 if (force)
2060 ERR( "Failed to read display config.\n" );
2061 return FALSE;
2064 if (ctx.gpu_count)
2066 ERR( "Driver reported devices, but we failed to read them.\n" );
2067 return FALSE;
2070 return update_display_cache( TRUE );
2073 return TRUE;
2076 static BOOL lock_display_devices(void)
2078 if (!update_display_cache( FALSE )) return FALSE;
2079 pthread_mutex_lock( &display_lock );
2080 return TRUE;
2083 static void unlock_display_devices(void)
2085 pthread_mutex_unlock( &display_lock );
2088 static HDC get_display_dc(void)
2090 pthread_mutex_lock( &display_dc_lock );
2091 if (!display_dc)
2093 HDC dc;
2095 pthread_mutex_unlock( &display_dc_lock );
2096 dc = NtGdiOpenDCW( NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL );
2097 pthread_mutex_lock( &display_dc_lock );
2098 if (display_dc)
2099 NtGdiDeleteObjectApp( dc );
2100 else
2101 display_dc = dc;
2103 return display_dc;
2106 static void release_display_dc( HDC hdc )
2108 pthread_mutex_unlock( &display_dc_lock );
2111 /**********************************************************************
2112 * get_monitor_dpi
2114 UINT get_monitor_dpi( HMONITOR monitor )
2116 /* FIXME: use the monitor DPI instead */
2117 return system_dpi;
2120 /**********************************************************************
2121 * get_win_monitor_dpi
2123 UINT get_win_monitor_dpi( HWND hwnd )
2125 /* FIXME: use the monitor DPI instead */
2126 return system_dpi;
2129 /**********************************************************************
2130 * get_thread_dpi_awareness
2132 DPI_AWARENESS get_thread_dpi_awareness(void)
2134 struct ntuser_thread_info *info = NtUserGetThreadInfo();
2135 ULONG_PTR context = info->dpi_awareness;
2137 if (!context) context = NtUserGetProcessDpiAwarenessContext( NULL );
2139 switch (context)
2141 case 0x10:
2142 case 0x11:
2143 case 0x12:
2144 case 0x80000010:
2145 case 0x80000011:
2146 case 0x80000012:
2147 return context & 3;
2148 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
2149 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
2150 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
2151 return ~context;
2152 default:
2153 return DPI_AWARENESS_INVALID;
2157 DWORD get_process_layout(void)
2159 return process_layout == ~0u ? 0 : process_layout;
2162 /**********************************************************************
2163 * get_thread_dpi
2165 UINT get_thread_dpi(void)
2167 switch (get_thread_dpi_awareness())
2169 case DPI_AWARENESS_UNAWARE: return USER_DEFAULT_SCREEN_DPI;
2170 case DPI_AWARENESS_SYSTEM_AWARE: return system_dpi;
2171 default: return 0; /* no scaling */
2175 /* see GetDpiForSystem */
2176 UINT get_system_dpi(void)
2178 if (get_thread_dpi_awareness() == DPI_AWARENESS_UNAWARE) return USER_DEFAULT_SCREEN_DPI;
2179 return system_dpi;
2182 /* see GetAwarenessFromDpiAwarenessContext */
2183 static DPI_AWARENESS get_awareness_from_dpi_awareness_context( DPI_AWARENESS_CONTEXT context )
2185 switch ((ULONG_PTR)context)
2187 case 0x10:
2188 case 0x11:
2189 case 0x12:
2190 case 0x80000010:
2191 case 0x80000011:
2192 case 0x80000012:
2193 return (ULONG_PTR)context & 3;
2194 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
2195 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
2196 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
2197 return ~(ULONG_PTR)context;
2198 default:
2199 return DPI_AWARENESS_INVALID;
2203 /**********************************************************************
2204 * SetThreadDpiAwarenessContext (win32u.so)
2206 DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT context )
2208 struct ntuser_thread_info *info = NtUserGetThreadInfo();
2209 DPI_AWARENESS prev, val = get_awareness_from_dpi_awareness_context( context );
2211 if (val == DPI_AWARENESS_INVALID)
2213 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2214 return 0;
2216 if (!(prev = info->dpi_awareness))
2218 prev = NtUserGetProcessDpiAwarenessContext( GetCurrentProcess() ) & 3;
2219 prev |= 0x80000010; /* restore to process default */
2221 if (((ULONG_PTR)context & ~(ULONG_PTR)0x13) == 0x80000000) info->dpi_awareness = 0;
2222 else info->dpi_awareness = val | 0x10;
2223 return ULongToHandle( prev );
2226 /**********************************************************************
2227 * map_dpi_rect
2229 RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to )
2231 if (dpi_from && dpi_to && dpi_from != dpi_to)
2233 rect.left = muldiv( rect.left, dpi_to, dpi_from );
2234 rect.top = muldiv( rect.top, dpi_to, dpi_from );
2235 rect.right = muldiv( rect.right, dpi_to, dpi_from );
2236 rect.bottom = muldiv( rect.bottom, dpi_to, dpi_from );
2238 return rect;
2241 /**********************************************************************
2242 * map_dpi_point
2244 POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to )
2246 if (dpi_from && dpi_to && dpi_from != dpi_to)
2248 pt.x = muldiv( pt.x, dpi_to, dpi_from );
2249 pt.y = muldiv( pt.y, dpi_to, dpi_from );
2251 return pt;
2254 /**********************************************************************
2255 * point_win_to_phys_dpi
2257 static POINT point_win_to_phys_dpi( HWND hwnd, POINT pt )
2259 return map_dpi_point( pt, get_dpi_for_window( hwnd ), get_win_monitor_dpi( hwnd ) );
2262 /**********************************************************************
2263 * point_phys_to_win_dpi
2265 POINT point_phys_to_win_dpi( HWND hwnd, POINT pt )
2267 return map_dpi_point( pt, get_win_monitor_dpi( hwnd ), get_dpi_for_window( hwnd ));
2270 /**********************************************************************
2271 * point_thread_to_win_dpi
2273 POINT point_thread_to_win_dpi( HWND hwnd, POINT pt )
2275 UINT dpi = get_thread_dpi();
2276 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
2277 return map_dpi_point( pt, dpi, get_dpi_for_window( hwnd ));
2280 /**********************************************************************
2281 * rect_thread_to_win_dpi
2283 RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect )
2285 UINT dpi = get_thread_dpi();
2286 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
2287 return map_dpi_rect( rect, dpi, get_dpi_for_window( hwnd ) );
2290 /* map value from system dpi to standard 96 dpi for storing in the registry */
2291 static int map_from_system_dpi( int val )
2293 return muldiv( val, USER_DEFAULT_SCREEN_DPI, get_system_dpi() );
2296 /* map value from 96 dpi to system or custom dpi */
2297 static int map_to_dpi( int val, UINT dpi )
2299 if (!dpi) dpi = get_system_dpi();
2300 return muldiv( val, dpi, USER_DEFAULT_SCREEN_DPI );
2303 RECT get_virtual_screen_rect( UINT dpi )
2305 struct monitor *monitor;
2306 RECT rect = {0};
2308 if (!lock_display_devices()) return rect;
2310 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2312 union_rect( &rect, &rect, &monitor->rc_monitor );
2315 unlock_display_devices();
2317 if (dpi) rect = map_dpi_rect( rect, system_dpi, dpi );
2318 return rect;
2321 static BOOL is_window_rect_full_screen( const RECT *rect )
2323 struct monitor *monitor;
2324 BOOL ret = FALSE;
2326 if (!lock_display_devices()) return FALSE;
2328 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2330 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2331 continue;
2333 if (rect->left <= monitor->rc_monitor.left && rect->right >= monitor->rc_monitor.right &&
2334 rect->top <= monitor->rc_monitor.top && rect->bottom >= monitor->rc_monitor.bottom)
2336 ret = TRUE;
2337 break;
2341 unlock_display_devices();
2342 return ret;
2345 RECT get_display_rect( const WCHAR *display )
2347 struct monitor *monitor;
2348 RECT rect = {0};
2350 if (!lock_display_devices()) return rect;
2352 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2354 if (!monitor->adapter || wcsicmp( monitor->adapter->dev.device_name, display )) continue;
2355 rect = monitor->rc_monitor;
2356 break;
2359 unlock_display_devices();
2360 return map_dpi_rect( rect, system_dpi, get_thread_dpi() );
2363 RECT get_primary_monitor_rect( UINT dpi )
2365 struct monitor *monitor;
2366 RECT rect = {0};
2368 if (!lock_display_devices()) return rect;
2370 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2372 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
2373 rect = monitor->rc_monitor;
2374 break;
2377 unlock_display_devices();
2378 return map_dpi_rect( rect, system_dpi, dpi );
2381 /**********************************************************************
2382 * NtUserGetDisplayConfigBufferSizes (win32u.@)
2384 LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_info,
2385 UINT32 *num_mode_info )
2387 struct monitor *monitor;
2388 UINT32 count = 0;
2390 TRACE( "(0x%x %p %p)\n", flags, num_path_info, num_mode_info );
2392 if (!num_path_info || !num_mode_info)
2393 return ERROR_INVALID_PARAMETER;
2395 *num_path_info = 0;
2397 switch (flags)
2399 case QDC_ALL_PATHS:
2400 case QDC_ONLY_ACTIVE_PATHS:
2401 case QDC_DATABASE_CURRENT:
2402 break;
2403 default:
2404 return ERROR_INVALID_PARAMETER;
2407 /* FIXME: semi-stub */
2408 if (flags != QDC_ONLY_ACTIVE_PATHS)
2409 FIXME( "only returning active paths\n" );
2411 if (lock_display_devices())
2413 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2415 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2416 continue;
2417 count++;
2419 unlock_display_devices();
2422 *num_path_info = count;
2423 *num_mode_info = count * 2;
2424 TRACE( "returning %u paths %u modes\n", *num_path_info, *num_mode_info );
2425 return ERROR_SUCCESS;
2428 static DISPLAYCONFIG_ROTATION get_dc_rotation( const DEVMODEW *devmode )
2430 if (devmode->dmFields & DM_DISPLAYORIENTATION)
2431 return devmode->dmDisplayOrientation + 1;
2432 else
2433 return DISPLAYCONFIG_ROTATION_IDENTITY;
2436 static DISPLAYCONFIG_SCANLINE_ORDERING get_dc_scanline_ordering( const DEVMODEW *devmode )
2438 if (!(devmode->dmFields & DM_DISPLAYFLAGS))
2439 return DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
2440 else if (devmode->dmDisplayFlags & DM_INTERLACED)
2441 return DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED;
2442 else
2443 return DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
2446 static DISPLAYCONFIG_PIXELFORMAT get_dc_pixelformat( DWORD dmBitsPerPel )
2448 if ((dmBitsPerPel == 8) || (dmBitsPerPel == 16) ||
2449 (dmBitsPerPel == 24) || (dmBitsPerPel == 32))
2450 return dmBitsPerPel / 8;
2451 else
2452 return DISPLAYCONFIG_PIXELFORMAT_NONGDI;
2455 static void set_mode_target_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid, UINT32 target_id,
2456 UINT32 flags, const DEVMODEW *devmode )
2458 DISPLAYCONFIG_TARGET_MODE *mode = &info->targetMode;
2460 info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET;
2461 info->adapterId = *gpu_luid;
2462 info->id = target_id;
2464 /* FIXME: Populate pixelRate/hSyncFreq/totalSize with real data */
2465 mode->targetVideoSignalInfo.pixelRate = devmode->dmDisplayFrequency * devmode->dmPelsWidth * devmode->dmPelsHeight;
2466 mode->targetVideoSignalInfo.hSyncFreq.Numerator = devmode->dmDisplayFrequency * devmode->dmPelsWidth;
2467 mode->targetVideoSignalInfo.hSyncFreq.Denominator = 1;
2468 mode->targetVideoSignalInfo.vSyncFreq.Numerator = devmode->dmDisplayFrequency;
2469 mode->targetVideoSignalInfo.vSyncFreq.Denominator = 1;
2470 mode->targetVideoSignalInfo.activeSize.cx = devmode->dmPelsWidth;
2471 mode->targetVideoSignalInfo.activeSize.cy = devmode->dmPelsHeight;
2472 if (flags & QDC_DATABASE_CURRENT)
2474 mode->targetVideoSignalInfo.totalSize.cx = 0;
2475 mode->targetVideoSignalInfo.totalSize.cy = 0;
2477 else
2479 mode->targetVideoSignalInfo.totalSize.cx = devmode->dmPelsWidth;
2480 mode->targetVideoSignalInfo.totalSize.cy = devmode->dmPelsHeight;
2482 mode->targetVideoSignalInfo.videoStandard = D3DKMDT_VSS_OTHER;
2483 mode->targetVideoSignalInfo.scanLineOrdering = get_dc_scanline_ordering( devmode );
2486 static void set_path_target_info( DISPLAYCONFIG_PATH_TARGET_INFO *info, const LUID *gpu_luid,
2487 UINT32 target_id, UINT32 mode_index, const DEVMODEW *devmode )
2489 info->adapterId = *gpu_luid;
2490 info->id = target_id;
2491 info->modeInfoIdx = mode_index;
2492 info->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL;
2493 info->rotation = get_dc_rotation( devmode );
2494 info->scaling = DISPLAYCONFIG_SCALING_IDENTITY;
2495 info->refreshRate.Numerator = devmode->dmDisplayFrequency;
2496 info->refreshRate.Denominator = 1;
2497 info->scanLineOrdering = get_dc_scanline_ordering( devmode );
2498 info->targetAvailable = TRUE;
2499 info->statusFlags = DISPLAYCONFIG_TARGET_IN_USE;
2502 static void set_mode_source_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid,
2503 UINT32 source_id, const DEVMODEW *devmode )
2505 DISPLAYCONFIG_SOURCE_MODE *mode = &info->sourceMode;
2507 info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE;
2508 info->adapterId = *gpu_luid;
2509 info->id = source_id;
2511 mode->width = devmode->dmPelsWidth;
2512 mode->height = devmode->dmPelsHeight;
2513 mode->pixelFormat = get_dc_pixelformat( devmode->dmBitsPerPel );
2514 if (devmode->dmFields & DM_POSITION)
2516 mode->position = devmode->dmPosition;
2518 else
2520 mode->position.x = 0;
2521 mode->position.y = 0;
2525 static void set_path_source_info( DISPLAYCONFIG_PATH_SOURCE_INFO *info, const LUID *gpu_luid,
2526 UINT32 source_id, UINT32 mode_index )
2528 info->adapterId = *gpu_luid;
2529 info->id = source_id;
2530 info->modeInfoIdx = mode_index;
2531 info->statusFlags = DISPLAYCONFIG_SOURCE_IN_USE;
2534 static BOOL source_mode_exists( const DISPLAYCONFIG_MODE_INFO *modes, UINT32 modes_count,
2535 UINT32 source_id, UINT32 *found_mode_index )
2537 UINT32 i;
2539 for (i = 0; i < modes_count; i++)
2541 if (modes[i].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE &&
2542 modes[i].id == source_id)
2544 *found_mode_index = i;
2545 return TRUE;
2548 return FALSE;
2551 /***********************************************************************
2552 * NtUserQueryDisplayConfig (win32u.@)
2554 LONG WINAPI NtUserQueryDisplayConfig( UINT32 flags, UINT32 *paths_count, DISPLAYCONFIG_PATH_INFO *paths,
2555 UINT32 *modes_count, DISPLAYCONFIG_MODE_INFO *modes,
2556 DISPLAYCONFIG_TOPOLOGY_ID *topology_id )
2558 ULONG adapter_index;
2559 LONG ret;
2560 UINT32 output_id, source_mode_index, path_index = 0, mode_index = 0;
2561 const LUID *gpu_luid;
2562 DEVMODEW devmode;
2563 struct monitor *monitor;
2565 FIXME( "flags %#x, paths_count %p, paths %p, modes_count %p, modes %p, topology_id %p semi-stub\n",
2566 flags, paths_count, paths, modes_count, modes, topology_id );
2568 if (!paths_count || !modes_count)
2569 return ERROR_INVALID_PARAMETER;
2571 if (!*paths_count || !*modes_count)
2572 return ERROR_INVALID_PARAMETER;
2574 if (flags != QDC_ALL_PATHS &&
2575 flags != QDC_ONLY_ACTIVE_PATHS &&
2576 flags != QDC_DATABASE_CURRENT)
2577 return ERROR_INVALID_PARAMETER;
2579 if (((flags == QDC_DATABASE_CURRENT) && !topology_id) ||
2580 ((flags != QDC_DATABASE_CURRENT) && topology_id))
2581 return ERROR_INVALID_PARAMETER;
2583 if (flags != QDC_ONLY_ACTIVE_PATHS)
2584 FIXME( "only returning active paths\n" );
2586 if (topology_id)
2588 FIXME( "setting toplogyid to DISPLAYCONFIG_TOPOLOGY_INTERNAL\n" );
2589 *topology_id = DISPLAYCONFIG_TOPOLOGY_INTERNAL;
2592 if (!lock_display_devices())
2593 return ERROR_GEN_FAILURE;
2595 ret = ERROR_GEN_FAILURE;
2597 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2599 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2600 continue;
2602 if (!monitor->adapter)
2603 continue;
2605 adapter_index = monitor->adapter->id;
2606 gpu_luid = &monitor->adapter->gpu_luid;
2607 output_id = monitor->output_id;
2609 memset( &devmode, 0, sizeof(devmode) );
2610 devmode.dmSize = sizeof(devmode);
2611 if (!adapter_get_current_settings( monitor->adapter, &devmode ))
2613 goto done;
2616 if (path_index == *paths_count || mode_index == *modes_count)
2618 ret = ERROR_INSUFFICIENT_BUFFER;
2619 goto done;
2622 paths[path_index].flags = DISPLAYCONFIG_PATH_ACTIVE;
2623 set_mode_target_info( &modes[mode_index], gpu_luid, output_id, flags, &devmode );
2624 set_path_target_info( &paths[path_index].targetInfo, gpu_luid, output_id, mode_index, &devmode );
2626 mode_index++;
2627 if (mode_index == *modes_count)
2629 ret = ERROR_INSUFFICIENT_BUFFER;
2630 goto done;
2633 /* Multiple targets can be driven by the same source, ensure a mode
2634 * hasn't already been added for this source.
2636 if (!source_mode_exists( modes, mode_index, adapter_index, &source_mode_index ))
2638 set_mode_source_info( &modes[mode_index], gpu_luid, adapter_index, &devmode );
2639 source_mode_index = mode_index;
2640 mode_index++;
2642 set_path_source_info( &paths[path_index].sourceInfo, gpu_luid, adapter_index, source_mode_index );
2643 path_index++;
2646 *paths_count = path_index;
2647 *modes_count = mode_index;
2648 ret = ERROR_SUCCESS;
2650 done:
2651 unlock_display_devices();
2652 return ret;
2655 /* display_lock mutex must be held */
2656 static struct display_device *find_monitor_device( struct display_device *adapter, UINT index )
2658 struct monitor *monitor;
2660 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2661 if (&monitor->adapter->dev == adapter && index == monitor->id)
2662 return &monitor->dev;
2664 WARN( "Failed to find adapter %s monitor with id %u.\n", debugstr_w(adapter->device_name), index );
2665 return NULL;
2668 /* display_lock mutex must be held */
2669 static struct display_device *find_adapter_device_by_id( UINT index )
2671 struct adapter *adapter;
2673 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
2674 if (index == adapter->id) return &adapter->dev;
2676 WARN( "Failed to find adapter with id %u.\n", index );
2677 return NULL;
2680 /* display_lock mutex must be held */
2681 static struct display_device *find_adapter_device_by_name( UNICODE_STRING *name )
2683 SIZE_T len = name->Length / sizeof(WCHAR);
2684 struct adapter *adapter;
2686 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
2687 if (!wcsnicmp( name->Buffer, adapter->dev.device_name, len ) && !adapter->dev.device_name[len])
2688 return &adapter->dev;
2690 WARN( "Failed to find adapter with name %s.\n", debugstr_us(name) );
2691 return NULL;
2694 /* Find and acquire the adapter matching name, or primary adapter if name is NULL.
2695 * If not NULL, the returned adapter needs to be released with adapter_release.
2697 static struct adapter *find_adapter( UNICODE_STRING *name )
2699 struct display_device *device;
2700 struct adapter *adapter;
2702 if (!lock_display_devices()) return NULL;
2704 if (name && name->Length) device = find_adapter_device_by_name( name );
2705 else device = find_adapter_device_by_id( 0 ); /* use primary adapter */
2707 if (!device) adapter = NULL;
2708 else adapter = adapter_acquire( CONTAINING_RECORD( device, struct adapter, dev ) );
2710 unlock_display_devices();
2711 return adapter;
2714 /***********************************************************************
2715 * NtUserEnumDisplayDevices (win32u.@)
2717 NTSTATUS WINAPI NtUserEnumDisplayDevices( UNICODE_STRING *device, DWORD index,
2718 DISPLAY_DEVICEW *info, DWORD flags )
2720 struct display_device *found = NULL;
2722 TRACE( "%s %u %p %#x\n", debugstr_us( device ), (int)index, info, (int)flags );
2724 if (!info || !info->cb) return STATUS_UNSUCCESSFUL;
2726 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
2728 if (!device || !device->Length) found = find_adapter_device_by_id( index );
2729 else if ((found = find_adapter_device_by_name( device ))) found = find_monitor_device( found, index );
2731 if (found)
2733 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceName) + sizeof(info->DeviceName))
2734 lstrcpyW( info->DeviceName, found->device_name );
2735 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceString) + sizeof(info->DeviceString))
2736 lstrcpyW( info->DeviceString, found->device_string );
2737 if (info->cb >= offsetof(DISPLAY_DEVICEW, StateFlags) + sizeof(info->StateFlags))
2738 info->StateFlags = found->state_flags;
2739 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID))
2740 lstrcpyW( info->DeviceID, (flags & EDD_GET_DEVICE_INTERFACE_NAME)
2741 ? found->interface_name : found->device_id );
2742 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey))
2743 lstrcpyW( info->DeviceKey, found->device_key );
2745 unlock_display_devices();
2746 return found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
2749 #define _X_FIELD(prefix, bits) \
2750 if ((fields) & prefix##_##bits) \
2752 p += sprintf( p, "%s%s", first ? "" : ",", #bits ); \
2753 first = FALSE; \
2756 static const char *_CDS_flags( DWORD fields )
2758 BOOL first = TRUE;
2759 CHAR buf[128];
2760 CHAR *p = buf;
2762 _X_FIELD(CDS, UPDATEREGISTRY)
2763 _X_FIELD(CDS, TEST)
2764 _X_FIELD(CDS, FULLSCREEN)
2765 _X_FIELD(CDS, GLOBAL)
2766 _X_FIELD(CDS, SET_PRIMARY)
2767 _X_FIELD(CDS, VIDEOPARAMETERS)
2768 _X_FIELD(CDS, ENABLE_UNSAFE_MODES)
2769 _X_FIELD(CDS, DISABLE_UNSAFE_MODES)
2770 _X_FIELD(CDS, RESET)
2771 _X_FIELD(CDS, RESET_EX)
2772 _X_FIELD(CDS, NORESET)
2774 *p = 0;
2775 return wine_dbg_sprintf( "%s", buf );
2778 static const char *_DM_fields( DWORD fields )
2780 BOOL first = TRUE;
2781 CHAR buf[128];
2782 CHAR *p = buf;
2784 _X_FIELD(DM, BITSPERPEL)
2785 _X_FIELD(DM, PELSWIDTH)
2786 _X_FIELD(DM, PELSHEIGHT)
2787 _X_FIELD(DM, DISPLAYFLAGS)
2788 _X_FIELD(DM, DISPLAYFREQUENCY)
2789 _X_FIELD(DM, POSITION)
2790 _X_FIELD(DM, DISPLAYORIENTATION)
2792 *p = 0;
2793 return wine_dbg_sprintf( "%s", buf );
2796 #undef _X_FIELD
2798 static void trace_devmode( const DEVMODEW *devmode )
2800 TRACE( "dmFields=%s ", _DM_fields(devmode->dmFields) );
2801 if (devmode->dmFields & DM_BITSPERPEL)
2802 TRACE( "dmBitsPerPel=%u ", (int)devmode->dmBitsPerPel );
2803 if (devmode->dmFields & DM_PELSWIDTH)
2804 TRACE( "dmPelsWidth=%u ", (int)devmode->dmPelsWidth );
2805 if (devmode->dmFields & DM_PELSHEIGHT)
2806 TRACE( "dmPelsHeight=%u ", (int)devmode->dmPelsHeight );
2807 if (devmode->dmFields & DM_DISPLAYFREQUENCY)
2808 TRACE( "dmDisplayFrequency=%u ", (int)devmode->dmDisplayFrequency );
2809 if (devmode->dmFields & DM_POSITION)
2810 TRACE( "dmPosition=(%d,%d) ", (int)devmode->dmPosition.x, (int)devmode->dmPosition.y );
2811 if (devmode->dmFields & DM_DISPLAYFLAGS)
2812 TRACE( "dmDisplayFlags=%#x ", (int)devmode->dmDisplayFlags );
2813 if (devmode->dmFields & DM_DISPLAYORIENTATION)
2814 TRACE( "dmDisplayOrientation=%u ", (int)devmode->dmDisplayOrientation );
2815 TRACE("\n");
2818 static BOOL is_detached_mode( const DEVMODEW *mode )
2820 return mode->dmFields & DM_POSITION &&
2821 mode->dmFields & DM_PELSWIDTH &&
2822 mode->dmFields & DM_PELSHEIGHT &&
2823 mode->dmPelsWidth == 0 &&
2824 mode->dmPelsHeight == 0;
2827 static const DEVMODEW *find_display_mode( const DEVMODEW *modes, DEVMODEW *devmode )
2829 const DEVMODEW *mode;
2831 if (is_detached_mode( devmode )) return devmode;
2833 for (mode = modes; mode && mode->dmSize; mode = NEXT_DEVMODEW(mode))
2835 if ((mode->dmFields & DM_DISPLAYFLAGS) && (mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
2836 continue;
2837 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel && devmode->dmBitsPerPel != mode->dmBitsPerPel)
2838 continue;
2839 if ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth != mode->dmPelsWidth)
2840 continue;
2841 if ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight != mode->dmPelsHeight)
2842 continue;
2843 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != mode->dmDisplayFrequency
2844 && devmode->dmDisplayFrequency > 1 && mode->dmDisplayFrequency)
2845 continue;
2846 if ((devmode->dmFields & DM_DISPLAYORIENTATION) && devmode->dmDisplayOrientation != mode->dmDisplayOrientation)
2847 continue;
2848 if ((devmode->dmFields & DM_DISPLAYFLAGS) && (mode->dmFields & DM_DISPLAYFLAGS) &&
2849 (devmode->dmDisplayFlags & DM_INTERLACED) != (mode->dmDisplayFlags & DM_INTERLACED))
2850 continue;
2851 if ((devmode->dmFields & DM_DISPLAYFIXEDOUTPUT) && (mode->dmFields & DM_DISPLAYFIXEDOUTPUT) &&
2852 devmode->dmDisplayFixedOutput != mode->dmDisplayFixedOutput)
2853 continue;
2855 return mode;
2858 return NULL;
2861 static BOOL adapter_get_full_mode( const struct adapter *adapter, const DEVMODEW *devmode, DEVMODEW *full_mode )
2863 const DEVMODEW *adapter_mode;
2865 if (devmode)
2867 trace_devmode( devmode );
2869 if (devmode->dmSize < offsetof(DEVMODEW, dmICMMethod)) return FALSE;
2870 if (!is_detached_mode( devmode ) &&
2871 (!(devmode->dmFields & DM_BITSPERPEL) || !devmode->dmBitsPerPel) &&
2872 (!(devmode->dmFields & DM_PELSWIDTH) || !devmode->dmPelsWidth) &&
2873 (!(devmode->dmFields & DM_PELSHEIGHT) || !devmode->dmPelsHeight) &&
2874 (!(devmode->dmFields & DM_DISPLAYFREQUENCY) || !devmode->dmDisplayFrequency))
2875 devmode = NULL;
2878 if (devmode) memcpy( full_mode, devmode, devmode->dmSize );
2879 else
2881 if (!adapter_get_registry_settings( adapter, full_mode )) return FALSE;
2882 TRACE( "Return to original display mode\n" );
2885 if ((full_mode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
2887 WARN( "devmode doesn't specify the resolution: %#x\n", (int)full_mode->dmFields );
2888 return FALSE;
2891 if (!is_detached_mode( full_mode ) && (!full_mode->dmPelsWidth || !full_mode->dmPelsHeight || !(full_mode->dmFields & DM_POSITION)))
2893 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2895 if (!adapter_get_current_settings( adapter, &current_mode )) return FALSE;
2896 if (!full_mode->dmPelsWidth) full_mode->dmPelsWidth = current_mode.dmPelsWidth;
2897 if (!full_mode->dmPelsHeight) full_mode->dmPelsHeight = current_mode.dmPelsHeight;
2898 if (!(full_mode->dmFields & DM_POSITION))
2900 full_mode->dmPosition = current_mode.dmPosition;
2901 full_mode->dmFields |= DM_POSITION;
2905 if ((adapter_mode = find_display_mode( adapter->modes, full_mode )) && adapter_mode != full_mode)
2907 POINTL position = full_mode->dmPosition;
2908 *full_mode = *adapter_mode;
2909 full_mode->dmFields |= DM_POSITION;
2910 full_mode->dmPosition = position;
2913 return adapter_mode != NULL;
2916 static DEVMODEW *get_display_settings( const WCHAR *devname, const DEVMODEW *devmode )
2918 DEVMODEW *mode, *displays;
2919 struct adapter *adapter;
2920 BOOL ret;
2922 /* allocate an extra mode for easier iteration */
2923 if (!(displays = calloc( list_count( &adapters ) + 1, sizeof(DEVMODEW) ))) return NULL;
2924 mode = displays;
2926 LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
2928 mode->dmSize = sizeof(DEVMODEW);
2929 if (devmode && !wcsicmp( devname, adapter->dev.device_name ))
2930 memcpy( &mode->dmFields, &devmode->dmFields, devmode->dmSize - offsetof(DEVMODEW, dmFields) );
2931 else
2933 if (!devname) ret = adapter_get_registry_settings( adapter, mode );
2934 else ret = adapter_get_current_settings( adapter, mode );
2935 if (!ret)
2937 free( displays );
2938 return NULL;
2942 lstrcpyW( mode->dmDeviceName, adapter->dev.device_name );
2943 mode = NEXT_DEVMODEW(mode);
2946 return displays;
2949 static INT offset_length( POINT offset )
2951 return offset.x * offset.x + offset.y * offset.y;
2954 static void set_rect_from_devmode( RECT *rect, const DEVMODEW *mode )
2956 SetRect( rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth,
2957 mode->dmPosition.y + mode->dmPelsHeight );
2960 /* Check if a rect overlaps with placed display rects */
2961 static BOOL overlap_placed_displays( const RECT *rect, const DEVMODEW *displays )
2963 const DEVMODEW *mode;
2964 RECT intersect;
2966 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2968 set_rect_from_devmode( &intersect, mode );
2969 if ((mode->dmFields & DM_POSITION) && intersect_rect( &intersect, &intersect, rect )) return TRUE;
2972 return FALSE;
2975 /* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */
2976 static POINT get_placement_offset( const DEVMODEW *displays, const DEVMODEW *placing )
2978 POINT points[8], left_top, offset, min_offset = {0, 0};
2979 INT point_idx, point_count, vertex_idx;
2980 BOOL has_placed = FALSE, first = TRUE;
2981 RECT desired_rect, rect;
2982 const DEVMODEW *mode;
2983 INT width, height;
2985 set_rect_from_devmode( &desired_rect, placing );
2987 /* If the display to be placed is detached, no offset is needed to place it */
2988 if (IsRectEmpty( &desired_rect )) return min_offset;
2990 /* If there is no placed and attached display, place this display as it is */
2991 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2993 set_rect_from_devmode( &rect, mode );
2994 if ((mode->dmFields & DM_POSITION) && !IsRectEmpty( &rect ))
2996 has_placed = TRUE;
2997 break;
3001 if (!has_placed) return min_offset;
3003 /* Try to place this display with each of its four vertices at every vertex of the placed
3004 * displays and see which combination has the minimum offset length */
3005 width = desired_rect.right - desired_rect.left;
3006 height = desired_rect.bottom - desired_rect.top;
3008 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
3010 set_rect_from_devmode( &rect, mode );
3011 if (!(mode->dmFields & DM_POSITION) || IsRectEmpty( &rect )) continue;
3013 /* Get four vertices of the placed display rectangle */
3014 points[0].x = rect.left;
3015 points[0].y = rect.top;
3016 points[1].x = rect.left;
3017 points[1].y = rect.bottom;
3018 points[2].x = rect.right;
3019 points[2].y = rect.top;
3020 points[3].x = rect.right;
3021 points[3].y = rect.bottom;
3022 point_count = 4;
3024 /* Intersected points when moving the display to be placed horizontally */
3025 if (desired_rect.bottom >= rect.top && desired_rect.top <= rect.bottom)
3027 points[point_count].x = rect.left;
3028 points[point_count++].y = desired_rect.top;
3029 points[point_count].x = rect.right;
3030 points[point_count++].y = desired_rect.top;
3032 /* Intersected points when moving the display to be placed vertically */
3033 if (desired_rect.left <= rect.right && desired_rect.right >= rect.left)
3035 points[point_count].x = desired_rect.left;
3036 points[point_count++].y = rect.top;
3037 points[point_count].x = desired_rect.left;
3038 points[point_count++].y = rect.bottom;
3041 /* Try moving each vertex of the display rectangle to each points */
3042 for (point_idx = 0; point_idx < point_count; ++point_idx)
3044 for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx)
3046 switch (vertex_idx)
3048 /* Move the bottom right vertex to the point */
3049 case 0:
3050 left_top.x = points[point_idx].x - width;
3051 left_top.y = points[point_idx].y - height;
3052 break;
3053 /* Move the bottom left vertex to the point */
3054 case 1:
3055 left_top.x = points[point_idx].x;
3056 left_top.y = points[point_idx].y - height;
3057 break;
3058 /* Move the top right vertex to the point */
3059 case 2:
3060 left_top.x = points[point_idx].x - width;
3061 left_top.y = points[point_idx].y;
3062 break;
3063 /* Move the top left vertex to the point */
3064 case 3:
3065 left_top.x = points[point_idx].x;
3066 left_top.y = points[point_idx].y;
3067 break;
3070 offset.x = left_top.x - desired_rect.left;
3071 offset.y = left_top.y - desired_rect.top;
3072 rect = desired_rect;
3073 OffsetRect( &rect, offset.x, offset.y );
3074 if (!overlap_placed_displays( &rect, displays ))
3076 if (first)
3078 min_offset = offset;
3079 first = FALSE;
3080 continue;
3083 if (offset_length( offset ) < offset_length( min_offset )) min_offset = offset;
3089 return min_offset;
3092 static void place_all_displays( DEVMODEW *displays )
3094 POINT min_offset, offset;
3095 DEVMODEW *mode, *placing;
3097 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
3098 mode->dmFields &= ~DM_POSITION;
3100 /* Place all displays with no extra space between them and no overlapping */
3101 while (1)
3103 /* Place the unplaced display with the minimum offset length first */
3104 placing = NULL;
3105 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
3107 if (mode->dmFields & DM_POSITION) continue;
3109 offset = get_placement_offset( displays, mode );
3110 if (!placing || offset_length( offset ) < offset_length( min_offset ))
3112 min_offset = offset;
3113 placing = mode;
3117 /* If all displays are placed */
3118 if (!placing) break;
3120 placing->dmPosition.x += min_offset.x;
3121 placing->dmPosition.y += min_offset.y;
3122 placing->dmFields |= DM_POSITION;
3126 static BOOL all_detached_settings( const DEVMODEW *displays )
3128 const DEVMODEW *mode;
3130 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
3131 if (!is_detached_mode( mode )) return FALSE;
3133 return TRUE;
3136 static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmode,
3137 HWND hwnd, DWORD flags, void *lparam )
3139 WCHAR primary_name[CCHDEVICENAME];
3140 struct display_device *primary;
3141 DEVMODEW *mode, *displays;
3142 struct adapter *adapter;
3143 LONG ret;
3145 if (!lock_display_devices()) return DISP_CHANGE_FAILED;
3146 if (!(displays = get_display_settings( devname, devmode )))
3148 unlock_display_devices();
3149 return DISP_CHANGE_FAILED;
3152 if (all_detached_settings( displays ))
3154 unlock_display_devices();
3155 WARN( "Detaching all modes is not permitted.\n" );
3156 free( displays );
3157 return DISP_CHANGE_SUCCESSFUL;
3160 place_all_displays( displays );
3162 if (!(primary = find_adapter_device_by_id( 0 ))) primary_name[0] = 0;
3163 else wcscpy( primary_name, primary->device_name );
3165 /* use the default implementation in virtual desktop mode */
3166 if (is_virtual_desktop()) ret = E_NOTIMPL;
3167 else ret = user_driver->pChangeDisplaySettings( displays, primary_name, hwnd, flags, lparam );
3169 if (ret == E_NOTIMPL)
3171 /* default implementation: write current display settings to the registry. */
3172 mode = displays;
3173 LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
3175 if (!adapter_set_current_settings( adapter, mode ))
3176 WARN( "Failed to write adapter %s current mode.\n", debugstr_w(adapter->dev.device_name) );
3177 mode = NEXT_DEVMODEW(mode);
3179 ret = DISP_CHANGE_SUCCESSFUL;
3181 unlock_display_devices();
3183 free( displays );
3184 if (ret) return ret;
3186 if (!update_display_cache( TRUE ))
3187 WARN( "Failed to update display cache after mode change.\n" );
3189 if ((adapter = find_adapter( NULL )))
3191 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
3193 if (!adapter_get_current_settings( adapter, &current_mode )) WARN( "Failed to get primary adapter current display settings.\n" );
3194 adapter_release( adapter );
3196 NtUserClipCursor( NULL );
3197 send_notify_message( NtUserGetDesktopWindow(), WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
3198 MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), FALSE );
3199 send_message_timeout( HWND_BROADCAST, WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
3200 MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ),
3201 SMTO_ABORTIFHUNG, 2000, FALSE );
3202 /* post clip_fullscreen_window request to the foreground window */
3203 NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_CLIPCURSOR, SET_CURSOR_FSCLIP, 0 );
3206 return ret;
3209 /***********************************************************************
3210 * NtUserChangeDisplaySettings (win32u.@)
3212 LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devmode, HWND hwnd,
3213 DWORD flags, void *lparam )
3215 DEVMODEW full_mode = {.dmSize = sizeof(DEVMODEW)};
3216 int ret = DISP_CHANGE_SUCCESSFUL;
3217 struct adapter *adapter;
3219 TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, (int)flags, lparam );
3220 TRACE( "flags=%s\n", _CDS_flags(flags) );
3222 if ((!devname || !devname->Length) && !devmode) return apply_display_settings( NULL, NULL, hwnd, flags, lparam );
3224 if (!(adapter = find_adapter( devname ))) return DISP_CHANGE_BADPARAM;
3226 if (!adapter_get_full_mode( adapter, devmode, &full_mode )) ret = DISP_CHANGE_BADMODE;
3227 else if ((flags & CDS_UPDATEREGISTRY) && !adapter_set_registry_settings( adapter, &full_mode )) ret = DISP_CHANGE_NOTUPDATED;
3228 else if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL;
3229 else ret = apply_display_settings( adapter->dev.device_name, &full_mode, hwnd, flags, lparam );
3230 adapter_release( adapter );
3232 if (ret) ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret );
3233 return ret;
3236 static BOOL adapter_enum_display_settings( const struct adapter *adapter, UINT index, DEVMODEW *devmode, UINT flags )
3238 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
3239 const DEVMODEW *adapter_mode;
3241 if (!(flags & EDS_ROTATEDMODE) && !adapter_get_current_settings( adapter, &current_mode ))
3243 WARN( "Failed to query current display mode for EDS_ROTATEDMODE flag.\n" );
3244 return FALSE;
3247 for (adapter_mode = adapter->modes; adapter_mode->dmSize; adapter_mode = NEXT_DEVMODEW(adapter_mode))
3249 if (!(flags & EDS_ROTATEDMODE) && (adapter_mode->dmFields & DM_DISPLAYORIENTATION) &&
3250 adapter_mode->dmDisplayOrientation != current_mode.dmDisplayOrientation)
3251 continue;
3252 if (!(flags & EDS_RAWMODE) && (adapter_mode->dmFields & DM_DISPLAYFLAGS) &&
3253 (adapter_mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
3254 continue;
3255 if (!index--)
3257 memcpy( &devmode->dmFields, &adapter_mode->dmFields, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields) );
3258 devmode->dmDisplayFlags &= ~WINE_DM_UNSUPPORTED;
3259 return TRUE;
3263 WARN( "device %s, index %#x, flags %#x display mode not found.\n",
3264 debugstr_w( adapter->dev.device_name ), index, flags );
3265 RtlSetLastWin32Error( ERROR_NO_MORE_FILES );
3266 return FALSE;
3269 /***********************************************************************
3270 * NtUserEnumDisplaySettings (win32u.@)
3272 BOOL WINAPI NtUserEnumDisplaySettings( UNICODE_STRING *device, DWORD index, DEVMODEW *devmode, DWORD flags )
3274 static const WCHAR wine_display_driverW[] = {'W','i','n','e',' ','D','i','s','p','l','a','y',' ','D','r','i','v','e','r',0};
3275 struct adapter *adapter;
3276 BOOL ret;
3278 TRACE( "device %s, index %#x, devmode %p, flags %#x\n",
3279 debugstr_us(device), (int)index, devmode, (int)flags );
3281 if (!(adapter = find_adapter( device ))) return FALSE;
3283 lstrcpynW( devmode->dmDeviceName, wine_display_driverW, ARRAY_SIZE(devmode->dmDeviceName) );
3284 devmode->dmSpecVersion = DM_SPECVERSION;
3285 devmode->dmDriverVersion = DM_SPECVERSION;
3286 devmode->dmSize = offsetof(DEVMODEW, dmICMMethod);
3287 devmode->dmDriverExtra = 0;
3289 if (index == ENUM_REGISTRY_SETTINGS) ret = adapter_get_registry_settings( adapter, devmode );
3290 else if (index == ENUM_CURRENT_SETTINGS) ret = adapter_get_current_settings( adapter, devmode );
3291 else ret = adapter_enum_display_settings( adapter, index, devmode, flags );
3292 adapter_release( adapter );
3294 if (!ret) WARN( "Failed to query %s display settings.\n", debugstr_us(device) );
3295 else TRACE( "position %dx%d, resolution %ux%u, frequency %u, depth %u, orientation %#x.\n",
3296 (int)devmode->dmPosition.x, (int)devmode->dmPosition.y, (int)devmode->dmPelsWidth,
3297 (int)devmode->dmPelsHeight, (int)devmode->dmDisplayFrequency,
3298 (int)devmode->dmBitsPerPel, (int)devmode->dmDisplayOrientation );
3299 return ret;
3302 struct monitor_enum_info
3304 HANDLE handle;
3305 RECT rect;
3308 static unsigned int active_monitor_count(void)
3310 struct monitor *monitor;
3311 unsigned int count = 0;
3313 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
3315 if ((monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) count++;
3317 return count;
3320 INT get_display_depth( UNICODE_STRING *name )
3322 struct display_device *device;
3323 BOOL is_primary;
3324 INT depth;
3326 if (!lock_display_devices())
3327 return 32;
3329 if (name && name->Length)
3330 device = find_adapter_device_by_name( name );
3331 else
3332 device = find_adapter_device_by_id( 0 ); /* use primary adapter */
3334 if (!device)
3336 unlock_display_devices();
3337 return 32;
3340 is_primary = !!(device->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
3342 /* use the default implementation in virtual desktop mode */
3343 if (is_virtual_desktop()) depth = -1;
3344 else depth = user_driver->pGetDisplayDepth( device->device_name, is_primary );
3346 if (depth < 0)
3348 struct adapter *adapter = CONTAINING_RECORD( device, struct adapter, dev );
3349 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
3351 if (!adapter_get_current_settings( adapter, &current_mode )) depth = 32;
3352 else depth = current_mode.dmBitsPerPel;
3355 unlock_display_devices();
3356 return depth;
3359 /***********************************************************************
3360 * NtUserEnumDisplayMonitors (win32u.@)
3362 BOOL WINAPI NtUserEnumDisplayMonitors( HDC hdc, RECT *rect, MONITORENUMPROC proc, LPARAM lparam )
3364 struct monitor_enum_info enum_buf[8], *enum_info = enum_buf;
3365 struct enum_display_monitor_params params;
3366 struct monitor *monitor;
3367 unsigned int count = 0, i;
3368 POINT origin;
3369 RECT limit;
3370 BOOL ret = TRUE;
3372 if (hdc)
3374 DC *dc;
3375 if (!(dc = get_dc_ptr( hdc ))) return FALSE;
3376 origin.x = dc->attr->vis_rect.left;
3377 origin.y = dc->attr->vis_rect.top;
3378 release_dc_ptr( dc );
3379 if (NtGdiGetAppClipBox( hdc, &limit ) == ERROR) return FALSE;
3381 else
3383 origin.x = origin.y = 0;
3384 limit.left = limit.top = INT_MIN;
3385 limit.right = limit.bottom = INT_MAX;
3387 if (rect && !intersect_rect( &limit, &limit, rect )) return TRUE;
3389 if (!lock_display_devices()) return FALSE;
3391 count = list_count( &monitors );
3392 if (!count || (count > ARRAYSIZE(enum_buf) &&
3393 !(enum_info = malloc( count * sizeof(*enum_info) ))))
3395 unlock_display_devices();
3396 return FALSE;
3399 count = 0;
3400 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
3402 RECT monrect;
3404 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) continue;
3406 monrect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
3407 get_thread_dpi() );
3408 OffsetRect( &monrect, -origin.x, -origin.y );
3409 if (!intersect_rect( &monrect, &monrect, &limit )) continue;
3410 if (monitor->is_clone) continue;
3412 enum_info[count].handle = monitor->handle;
3413 enum_info[count].rect = monrect;
3414 count++;
3417 unlock_display_devices();
3419 params.proc = proc;
3420 params.hdc = hdc;
3421 params.lparam = lparam;
3422 for (i = 0; i < count; i++)
3424 void *ret_ptr;
3425 ULONG ret_len;
3426 params.monitor = enum_info[i].handle;
3427 params.rect = enum_info[i].rect;
3428 if (!KeUserModeCallback( NtUserCallEnumDisplayMonitor, &params, sizeof(params),
3429 &ret_ptr, &ret_len ))
3431 ret = FALSE;
3432 break;
3435 if (enum_info != enum_buf) free( enum_info );
3436 return ret;
3439 BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info )
3441 struct monitor *monitor;
3442 UINT dpi_from, dpi_to;
3444 if (info->cbSize != sizeof(MONITORINFOEXW) && info->cbSize != sizeof(MONITORINFO)) return FALSE;
3446 if (!lock_display_devices()) return FALSE;
3448 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
3450 if (monitor->handle != handle) continue;
3451 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) break;
3453 /* FIXME: map dpi */
3454 info->rcMonitor = monitor->rc_monitor;
3455 info->rcWork = monitor->rc_work;
3456 info->dwFlags = monitor->flags;
3457 if (info->cbSize >= sizeof(MONITORINFOEXW))
3459 if (monitor->adapter)
3460 lstrcpyW( ((MONITORINFOEXW *)info)->szDevice, monitor->adapter->dev.device_name );
3461 else
3462 asciiz_to_unicode( ((MONITORINFOEXW *)info)->szDevice, "WinDisc" );
3464 unlock_display_devices();
3466 if ((dpi_to = get_thread_dpi()))
3468 dpi_from = get_monitor_dpi( handle );
3469 info->rcMonitor = map_dpi_rect( info->rcMonitor, dpi_from, dpi_to );
3470 info->rcWork = map_dpi_rect( info->rcWork, dpi_from, dpi_to );
3472 TRACE( "flags %04x, monitor %s, work %s\n", (int)info->dwFlags,
3473 wine_dbgstr_rect(&info->rcMonitor), wine_dbgstr_rect(&info->rcWork));
3474 return TRUE;
3477 unlock_display_devices();
3478 WARN( "invalid handle %p\n", handle );
3479 RtlSetLastWin32Error( ERROR_INVALID_MONITOR_HANDLE );
3480 return FALSE;
3483 HMONITOR monitor_from_rect( const RECT *rect, UINT flags, UINT dpi )
3485 HMONITOR primary = 0, nearest = 0, ret = 0;
3486 UINT max_area = 0, min_distance = ~0u;
3487 struct monitor *monitor;
3488 RECT r;
3490 r = map_dpi_rect( *rect, dpi, system_dpi );
3491 if (IsRectEmpty( &r ))
3493 r.right = r.left + 1;
3494 r.bottom = r.top + 1;
3497 if (!lock_display_devices()) return 0;
3499 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
3501 RECT intersect;
3502 RECT monitor_rect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
3503 system_dpi );
3505 if (intersect_rect( &intersect, &monitor_rect, &r ))
3507 /* check for larger intersecting area */
3508 UINT area = (intersect.right - intersect.left) * (intersect.bottom - intersect.top);
3509 if (area > max_area)
3511 max_area = area;
3512 ret = monitor->handle;
3515 else if (!max_area) /* if not intersecting, check for min distance */
3517 UINT distance;
3518 UINT x, y;
3520 if (r.right <= monitor_rect.left) x = monitor_rect.left - r.right;
3521 else if (monitor_rect.right <= r.left) x = r.left - monitor_rect.right;
3522 else x = 0;
3523 if (r.bottom <= monitor_rect.top) y = monitor_rect.top - r.bottom;
3524 else if (monitor_rect.bottom <= r.top) y = r.top - monitor_rect.bottom;
3525 else y = 0;
3526 distance = x * x + y * y;
3527 if (distance < min_distance)
3529 min_distance = distance;
3530 nearest = monitor->handle;
3534 if (monitor->flags & MONITORINFOF_PRIMARY) primary = monitor->handle;
3537 unlock_display_devices();
3539 if (!ret)
3541 if (flags & MONITOR_DEFAULTTOPRIMARY) ret = primary;
3542 else if (flags & MONITOR_DEFAULTTONEAREST) ret = nearest;
3545 TRACE( "%s flags %x returning %p\n", wine_dbgstr_rect(rect), flags, ret );
3546 return ret;
3549 HMONITOR monitor_from_point( POINT pt, UINT flags, UINT dpi )
3551 RECT rect;
3552 SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 );
3553 return monitor_from_rect( &rect, flags, dpi );
3556 /* see MonitorFromWindow */
3557 HMONITOR monitor_from_window( HWND hwnd, UINT flags, UINT dpi )
3559 RECT rect;
3560 WINDOWPLACEMENT wp;
3562 TRACE( "(%p, 0x%08x)\n", hwnd, flags );
3564 wp.length = sizeof(wp);
3565 if (is_iconic( hwnd ) && NtUserGetWindowPlacement( hwnd, &wp ))
3566 return monitor_from_rect( &wp.rcNormalPosition, flags, dpi );
3568 if (get_window_rect( hwnd, &rect, dpi ))
3569 return monitor_from_rect( &rect, flags, dpi );
3571 if (!(flags & (MONITOR_DEFAULTTOPRIMARY|MONITOR_DEFAULTTONEAREST))) return 0;
3572 /* retrieve the primary */
3573 SetRect( &rect, 0, 0, 1, 1 );
3574 return monitor_from_rect( &rect, flags, dpi );
3577 /***********************************************************************
3578 * NtUserGetSystemDpiForProcess (win32u.@)
3580 ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process )
3582 if (process && process != GetCurrentProcess())
3584 FIXME( "not supported on other process %p\n", process );
3585 return 0;
3588 return system_dpi;
3591 /***********************************************************************
3592 * NtUserGetDpiForMonitor (win32u.@)
3594 BOOL WINAPI NtUserGetDpiForMonitor( HMONITOR monitor, UINT type, UINT *x, UINT *y )
3596 if (type > 2)
3598 RtlSetLastWin32Error( ERROR_BAD_ARGUMENTS );
3599 return FALSE;
3601 if (!x || !y)
3603 RtlSetLastWin32Error( ERROR_INVALID_ADDRESS );
3604 return FALSE;
3606 switch (get_thread_dpi_awareness())
3608 case DPI_AWARENESS_UNAWARE: *x = *y = USER_DEFAULT_SCREEN_DPI; break;
3609 case DPI_AWARENESS_SYSTEM_AWARE: *x = *y = system_dpi; break;
3610 default: *x = *y = get_monitor_dpi( monitor ); break;
3612 return TRUE;
3615 /**********************************************************************
3616 * LogicalToPhysicalPointForPerMonitorDPI (win32u.@)
3618 BOOL WINAPI NtUserLogicalToPerMonitorDPIPhysicalPoint( HWND hwnd, POINT *pt )
3620 RECT rect;
3622 if (!get_window_rect( hwnd, &rect, get_thread_dpi() )) return FALSE;
3623 if (pt->x < rect.left || pt->y < rect.top || pt->x > rect.right || pt->y > rect.bottom) return FALSE;
3624 *pt = point_win_to_phys_dpi( hwnd, *pt );
3625 return TRUE;
3628 /**********************************************************************
3629 * NtUserPerMonitorDPIPhysicalToLogicalPoint (win32u.@)
3631 BOOL WINAPI NtUserPerMonitorDPIPhysicalToLogicalPoint( HWND hwnd, POINT *pt )
3633 RECT rect;
3634 BOOL ret = FALSE;
3636 if (get_window_rect( hwnd, &rect, 0 ) &&
3637 pt->x >= rect.left && pt->y >= rect.top && pt->x <= rect.right && pt->y <= rect.bottom)
3639 *pt = point_phys_to_win_dpi( hwnd, *pt );
3640 ret = TRUE;
3642 return ret;
3645 /* retrieve the cached base keys for a given entry */
3646 static BOOL get_base_keys( enum parameter_key index, HKEY *base_key, HKEY *volatile_key )
3648 static HKEY base_keys[NB_PARAM_KEYS];
3649 static HKEY volatile_keys[NB_PARAM_KEYS];
3650 WCHAR bufferW[128];
3651 HKEY key;
3653 if (!base_keys[index] && base_key)
3655 if (!(key = reg_create_key( hkcu_key, bufferW,
3656 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
3657 0, NULL )))
3658 return FALSE;
3659 if (InterlockedCompareExchangePointer( (void **)&base_keys[index], key, 0 ))
3660 NtClose( key );
3662 if (!volatile_keys[index] && volatile_key)
3664 if (!(key = reg_create_key( volatile_base_key, bufferW,
3665 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
3666 REG_OPTION_VOLATILE, NULL )))
3667 return FALSE;
3668 if (InterlockedCompareExchangePointer( (void **)&volatile_keys[index], key, 0 ))
3669 NtClose( key );
3671 if (base_key) *base_key = base_keys[index];
3672 if (volatile_key) *volatile_key = volatile_keys[index];
3673 return TRUE;
3676 /* load a value to a registry entry */
3677 static DWORD load_entry( struct sysparam_entry *entry, void *data, DWORD size )
3679 char buffer[4096];
3680 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
3681 DWORD count;
3682 HKEY base_key, volatile_key;
3684 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
3686 if (!(count = query_reg_ascii_value( volatile_key, entry->regval, value, sizeof(buffer) )))
3687 count = query_reg_ascii_value( base_key, entry->regval, value, sizeof(buffer) );
3688 if (count > size)
3690 count = size;
3691 /* make sure strings are null-terminated */
3692 if (value->Type == REG_SZ) ((WCHAR *)value->Data)[count / sizeof(WCHAR) - 1] = 0;
3694 if (count) memcpy( data, value->Data, count );
3695 entry->loaded = TRUE;
3696 return count;
3699 /* save a value to a registry entry */
3700 static BOOL save_entry( const struct sysparam_entry *entry, const void *data, DWORD size,
3701 DWORD type, UINT flags )
3703 HKEY base_key, volatile_key;
3704 WCHAR nameW[64];
3706 asciiz_to_unicode( nameW, entry->regval );
3707 if (flags & SPIF_UPDATEINIFILE)
3709 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
3710 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
3711 reg_delete_value( volatile_key, nameW );
3713 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
3715 asciiz_to_unicode( nameW, entry->mirror );
3716 set_reg_value( base_key, nameW, type, data, size );
3719 else
3721 if (!get_base_keys( entry->base_key, NULL, &volatile_key )) return FALSE;
3722 if (!set_reg_value( volatile_key, nameW, type, data, size )) return FALSE;
3724 return TRUE;
3727 /* save a string value to a registry entry */
3728 static BOOL save_entry_string( const struct sysparam_entry *entry, const WCHAR *str, UINT flags )
3730 return save_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ, flags );
3733 /* initialize an entry in the registry if missing */
3734 static BOOL init_entry( struct sysparam_entry *entry, const void *data, DWORD size, DWORD type )
3736 KEY_VALUE_PARTIAL_INFORMATION value;
3737 UNICODE_STRING name;
3738 WCHAR nameW[64];
3739 HKEY base_key;
3740 DWORD count;
3741 NTSTATUS status;
3743 if (!get_base_keys( entry->base_key, &base_key, NULL )) return FALSE;
3745 name.Buffer = nameW;
3746 name.MaximumLength = asciiz_to_unicode( nameW, entry->regval );
3747 name.Length = name.MaximumLength - sizeof(WCHAR);
3748 status = NtQueryValueKey( base_key, &name, KeyValuePartialInformation,
3749 &value, sizeof(value), &count );
3750 if (!status || status == STATUS_BUFFER_OVERFLOW) return TRUE;
3752 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
3753 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
3755 asciiz_to_unicode( nameW, entry->mirror );
3756 set_reg_value( base_key, nameW, type, data, size );
3758 entry->loaded = TRUE;
3759 return TRUE;
3762 /* initialize a string value in the registry if missing */
3763 static BOOL init_entry_string( struct sysparam_entry *entry, const WCHAR *str )
3765 return init_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ );
3768 /* set an int parameter in the registry */
3769 static BOOL set_int_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3771 WCHAR bufW[32];
3772 char buf[32];
3774 sprintf( buf, "%d", int_param );
3775 asciiz_to_unicode( bufW, buf );
3776 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3777 entry->uint.val = int_param;
3778 entry->hdr.loaded = TRUE;
3779 return TRUE;
3782 /* initialize an int parameter */
3783 static BOOL init_int_entry( union sysparam_all_entry *entry )
3785 WCHAR bufW[32];
3786 char buf[32];
3788 sprintf( buf, "%d", entry->uint.val );
3789 asciiz_to_unicode( bufW, buf );
3790 return init_entry_string( &entry->hdr, bufW );
3793 /* load a uint parameter from the registry */
3794 static BOOL get_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3796 if (!ptr_param) return FALSE;
3798 if (!entry->hdr.loaded)
3800 WCHAR buf[32];
3801 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
3803 *(UINT *)ptr_param = entry->uint.val;
3804 return TRUE;
3807 /* set a uint parameter in the registry */
3808 static BOOL set_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3810 WCHAR bufW[32];
3811 char buf[32];
3813 sprintf( buf, "%u", int_param );
3814 asciiz_to_unicode( bufW, buf );
3815 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3816 entry->uint.val = int_param;
3817 entry->hdr.loaded = TRUE;
3818 return TRUE;
3821 /* initialize a uint parameter */
3822 static BOOL init_uint_entry( union sysparam_all_entry *entry )
3824 WCHAR bufW[32];
3825 char buf[32];
3827 sprintf( buf, "%u", entry->uint.val );
3828 asciiz_to_unicode( bufW, buf );
3829 return init_entry_string( &entry->hdr, bufW );
3832 /* load a twips parameter from the registry */
3833 static BOOL get_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3835 int val;
3837 if (!ptr_param) return FALSE;
3839 if (!entry->hdr.loaded)
3841 WCHAR buf[32];
3842 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
3845 /* Dimensions are quoted as being "twips" values if negative and pixels if positive.
3846 * One inch is 1440 twips.
3847 * See for example
3848 * Technical Reference to the Windows 2000 Registry ->
3849 * HKEY_CURRENT_USER -> Control Panel -> Desktop -> WindowMetrics
3851 val = entry->uint.val;
3852 if (val < 0)
3853 val = muldiv( -val, dpi, 1440 );
3854 else
3855 val = map_to_dpi( val, dpi );
3857 *(int *)ptr_param = val;
3858 return TRUE;
3861 /* set a twips parameter in the registry */
3862 static BOOL set_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3864 int val = int_param;
3865 if (val > 0) val = map_from_system_dpi( val );
3866 return set_int_entry( entry, val, ptr_param, flags );
3869 /* load a bool parameter from the registry */
3870 static BOOL get_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3872 if (!ptr_param) return FALSE;
3874 if (!entry->hdr.loaded)
3876 WCHAR buf[32];
3877 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = wcstol( buf, NULL, 10 ) != 0;
3879 *(UINT *)ptr_param = entry->bool.val;
3880 return TRUE;
3883 /* set a bool parameter in the registry */
3884 static BOOL set_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3886 WCHAR buf[] = { int_param ? '1' : '0', 0 };
3888 if (!save_entry_string( &entry->hdr, buf, flags )) return FALSE;
3889 entry->bool.val = int_param != 0;
3890 entry->hdr.loaded = TRUE;
3891 return TRUE;
3894 /* initialize a bool parameter */
3895 static BOOL init_bool_entry( union sysparam_all_entry *entry )
3897 WCHAR buf[] = { entry->bool.val ? '1' : '0', 0 };
3899 return init_entry_string( &entry->hdr, buf );
3902 /* load a bool parameter using Yes/No strings from the registry */
3903 static BOOL get_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3905 if (!ptr_param) return FALSE;
3907 if (!entry->hdr.loaded)
3909 WCHAR buf[32];
3910 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = !wcsicmp( yesW, buf );
3912 *(UINT *)ptr_param = entry->bool.val;
3913 return TRUE;
3916 /* set a bool parameter using Yes/No strings from the registry */
3917 static BOOL set_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3919 const WCHAR *str = int_param ? yesW : noW;
3921 if (!save_entry_string( &entry->hdr, str, flags )) return FALSE;
3922 entry->bool.val = int_param != 0;
3923 entry->hdr.loaded = TRUE;
3924 return TRUE;
3927 /* initialize a bool parameter using Yes/No strings */
3928 static BOOL init_yesno_entry( union sysparam_all_entry *entry )
3930 return init_entry_string( &entry->hdr, entry->bool.val ? yesW : noW );
3933 /* load a dword (binary) parameter from the registry */
3934 static BOOL get_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3936 if (!ptr_param) return FALSE;
3938 if (!entry->hdr.loaded)
3940 DWORD val;
3941 if (load_entry( &entry->hdr, &val, sizeof(val) ) == sizeof(DWORD)) entry->dword.val = val;
3943 *(DWORD *)ptr_param = entry->dword.val;
3944 return TRUE;
3947 /* set a dword (binary) parameter in the registry */
3948 static BOOL set_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3950 DWORD val = PtrToUlong( ptr_param );
3952 if (!save_entry( &entry->hdr, &val, sizeof(val), REG_DWORD, flags )) return FALSE;
3953 entry->dword.val = val;
3954 entry->hdr.loaded = TRUE;
3955 return TRUE;
3958 /* initialize a dword parameter */
3959 static BOOL init_dword_entry( union sysparam_all_entry *entry )
3961 return init_entry( &entry->hdr, &entry->dword.val, sizeof(entry->dword.val), REG_DWORD );
3964 /* load an RGB parameter from the registry */
3965 static BOOL get_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3967 if (!ptr_param) return FALSE;
3969 if (!entry->hdr.loaded)
3971 WCHAR buf[32];
3973 if (load_entry( &entry->hdr, buf, sizeof(buf) ))
3975 DWORD r, g, b;
3976 WCHAR *end, *str = buf;
3978 r = wcstoul( str, &end, 10 );
3979 if (end == str || !*end) goto done;
3980 str = end + 1;
3981 g = wcstoul( str, &end, 10 );
3982 if (end == str || !*end) goto done;
3983 str = end + 1;
3984 b = wcstoul( str, &end, 10 );
3985 if (end == str) goto done;
3986 if (r > 255 || g > 255 || b > 255) goto done;
3987 entry->rgb.val = RGB( r, g, b );
3990 done:
3991 *(COLORREF *)ptr_param = entry->rgb.val;
3992 return TRUE;
3995 /* set an RGB parameter in the registry */
3996 static BOOL set_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3998 WCHAR bufW[32];
3999 char buf[32];
4000 HBRUSH brush;
4001 HPEN pen;
4003 sprintf( buf, "%u %u %u", GetRValue(int_param), GetGValue(int_param), GetBValue(int_param) );
4004 asciiz_to_unicode( bufW, buf );
4005 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
4006 entry->rgb.val = int_param;
4007 entry->hdr.loaded = TRUE;
4008 if ((brush = InterlockedExchangePointer( (void **)&entry->rgb.brush, 0 )))
4010 make_gdi_object_system( brush, FALSE );
4011 NtGdiDeleteObjectApp( brush );
4013 if ((pen = InterlockedExchangePointer( (void **)&entry->rgb.pen, 0 )))
4015 make_gdi_object_system( pen, FALSE );
4016 NtGdiDeleteObjectApp( pen );
4018 return TRUE;
4021 /* initialize an RGB parameter */
4022 static BOOL init_rgb_entry( union sysparam_all_entry *entry )
4024 WCHAR bufW[32];
4025 char buf[32];
4027 sprintf( buf, "%u %u %u", GetRValue(entry->rgb.val), GetGValue(entry->rgb.val),
4028 GetBValue(entry->rgb.val) );
4029 asciiz_to_unicode( bufW, buf );
4030 return init_entry_string( &entry->hdr, bufW );
4033 /* get a path parameter in the registry */
4034 static BOOL get_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
4036 if (!ptr_param) return FALSE;
4038 if (!entry->hdr.loaded)
4040 WCHAR buffer[MAX_PATH];
4042 if (load_entry( &entry->hdr, buffer, sizeof(buffer) ))
4043 lstrcpynW( entry->path.path, buffer, MAX_PATH );
4045 lstrcpynW( ptr_param, entry->path.path, int_param );
4046 return TRUE;
4049 /* set a path parameter in the registry */
4050 static BOOL set_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
4052 WCHAR buffer[MAX_PATH];
4053 BOOL ret;
4055 lstrcpynW( buffer, ptr_param, MAX_PATH );
4056 ret = save_entry_string( &entry->hdr, buffer, flags );
4057 if (ret)
4059 lstrcpyW( entry->path.path, buffer );
4060 entry->hdr.loaded = TRUE;
4062 return ret;
4065 /* initialize a path parameter */
4066 static BOOL init_path_entry( union sysparam_all_entry *entry )
4068 return init_entry_string( &entry->hdr, entry->path.path );
4071 /* get a binary parameter in the registry */
4072 static BOOL get_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
4074 if (!ptr_param) return FALSE;
4076 if (!entry->hdr.loaded)
4078 void *buffer = malloc( entry->bin.size );
4079 DWORD len = load_entry( &entry->hdr, buffer, entry->bin.size );
4081 if (len)
4083 memcpy( entry->bin.ptr, buffer, entry->bin.size );
4084 memset( (char *)entry->bin.ptr + len, 0, entry->bin.size - len );
4086 free( buffer );
4088 memcpy( ptr_param, entry->bin.ptr, min( int_param, entry->bin.size ) );
4089 return TRUE;
4092 /* set a binary parameter in the registry */
4093 static BOOL set_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
4095 BOOL ret;
4096 void *buffer = malloc( entry->bin.size );
4098 memcpy( buffer, entry->bin.ptr, entry->bin.size );
4099 memcpy( buffer, ptr_param, min( int_param, entry->bin.size ));
4100 ret = save_entry( &entry->hdr, buffer, entry->bin.size, REG_BINARY, flags );
4101 if (ret)
4103 memcpy( entry->bin.ptr, buffer, entry->bin.size );
4104 entry->hdr.loaded = TRUE;
4106 free( buffer );
4107 return ret;
4110 /* initialize a binary parameter */
4111 static BOOL init_binary_entry( union sysparam_all_entry *entry )
4113 return init_entry( &entry->hdr, entry->bin.ptr, entry->bin.size, REG_BINARY );
4116 static void logfont16to32( const LOGFONT16 *font16, LPLOGFONTW font32 )
4118 font32->lfHeight = font16->lfHeight;
4119 font32->lfWidth = font16->lfWidth;
4120 font32->lfEscapement = font16->lfEscapement;
4121 font32->lfOrientation = font16->lfOrientation;
4122 font32->lfWeight = font16->lfWeight;
4123 font32->lfItalic = font16->lfItalic;
4124 font32->lfUnderline = font16->lfUnderline;
4125 font32->lfStrikeOut = font16->lfStrikeOut;
4126 font32->lfCharSet = font16->lfCharSet;
4127 font32->lfOutPrecision = font16->lfOutPrecision;
4128 font32->lfClipPrecision = font16->lfClipPrecision;
4129 font32->lfQuality = font16->lfQuality;
4130 font32->lfPitchAndFamily = font16->lfPitchAndFamily;
4131 win32u_mbtowc( &ansi_cp, font32->lfFaceName, LF_FACESIZE, font16->lfFaceName,
4132 strlen( font16->lfFaceName ));
4133 font32->lfFaceName[LF_FACESIZE-1] = 0;
4136 static void get_real_fontname( LOGFONTW *lf, WCHAR fullname[LF_FACESIZE] )
4138 struct font_enum_entry enum_entry;
4139 ULONG count = sizeof(enum_entry);
4140 HDC hdc;
4142 hdc = get_display_dc();
4143 NtGdiEnumFonts( hdc, 0, 0, lstrlenW( lf->lfFaceName ), lf->lfFaceName, lf->lfCharSet,
4144 &count, &enum_entry );
4145 release_display_dc( hdc );
4147 if (count)
4148 lstrcpyW( fullname, enum_entry.lf.elfFullName );
4149 else
4150 lstrcpyW( fullname, lf->lfFaceName );
4153 LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, int *height )
4155 SIZE sz;
4156 static const WCHAR abcdW[] =
4157 {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
4158 'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H',
4159 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
4161 if (metric && !NtGdiGetTextMetricsW( hdc, metric, 0 )) return 0;
4163 if (!NtGdiGetTextExtentExW( hdc, abcdW, ARRAYSIZE(abcdW), 0, NULL, NULL, &sz, 0 ))
4164 return 0;
4166 if (height) *height = sz.cy;
4167 return (sz.cx / 26 + 1) / 2;
4170 /* get text metrics and/or "average" char width of the specified logfont
4171 * for the specified dc */
4172 static void get_text_metr_size( HDC hdc, LOGFONTW *lf, TEXTMETRICW *metric, UINT *psz )
4174 HFONT hfont, hfontsav;
4175 TEXTMETRICW tm;
4176 UINT ret;
4177 if (!metric) metric = &tm;
4178 hfont = NtGdiHfontCreate( lf, sizeof(*lf), 0, 0, NULL );
4179 if (!hfont || !(hfontsav = NtGdiSelectFont( hdc, hfont )))
4181 metric->tmHeight = -1;
4182 if (psz) *psz = 10;
4183 if (hfont) NtGdiDeleteObjectApp( hfont );
4184 return;
4186 ret = get_char_dimensions( hdc, metric, NULL );
4187 if (psz) *psz = ret ? ret : 10;
4188 NtGdiSelectFont( hdc, hfontsav );
4189 NtGdiDeleteObjectApp( hfont );
4192 DWORD get_dialog_base_units(void)
4194 static int cx, cy;
4196 if (!cx)
4198 HDC hdc;
4200 if ((hdc = NtUserGetDC( 0 )))
4202 cx = get_char_dimensions( hdc, NULL, &cy );
4203 NtUserReleaseDC( 0, hdc );
4205 TRACE( "base units = %d,%d\n", cx, cy );
4208 return MAKELONG( muldiv( cx, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ),
4209 muldiv( cy, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ));
4212 /* adjust some of the raw values found in the registry */
4213 static void normalize_nonclientmetrics( NONCLIENTMETRICSW *pncm)
4215 TEXTMETRICW tm;
4216 HDC hdc = get_display_dc();
4218 if( pncm->iBorderWidth < 1) pncm->iBorderWidth = 1;
4219 if( pncm->iCaptionWidth < 8) pncm->iCaptionWidth = 8;
4220 if( pncm->iScrollWidth < 8) pncm->iScrollWidth = 8;
4221 if( pncm->iScrollHeight < 8) pncm->iScrollHeight = 8;
4223 /* adjust some heights to the corresponding font */
4224 get_text_metr_size( hdc, &pncm->lfMenuFont, &tm, NULL);
4225 pncm->iMenuHeight = max( pncm->iMenuHeight, 2 + tm.tmHeight + tm.tmExternalLeading );
4226 get_text_metr_size( hdc, &pncm->lfCaptionFont, &tm, NULL);
4227 pncm->iCaptionHeight = max( pncm->iCaptionHeight, 2 + tm.tmHeight);
4228 get_text_metr_size( hdc, &pncm->lfSmCaptionFont, &tm, NULL);
4229 pncm->iSmCaptionHeight = max( pncm->iSmCaptionHeight, 2 + tm.tmHeight);
4230 release_display_dc( hdc );
4233 /* load a font (binary) parameter from the registry */
4234 static BOOL get_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
4236 LOGFONTW font;
4238 if (!ptr_param) return FALSE;
4240 if (!entry->hdr.loaded)
4242 switch (load_entry( &entry->hdr, &font, sizeof(font) ))
4244 case sizeof(font):
4245 if (font.lfHeight > 0) /* positive height value means points ( inch/72 ) */
4246 font.lfHeight = -muldiv( font.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
4247 entry->font.val = font;
4248 break;
4249 case sizeof(LOGFONT16): /* win9x-winME format */
4250 logfont16to32( (LOGFONT16 *)&font, &entry->font.val );
4251 if (entry->font.val.lfHeight > 0)
4252 entry->font.val.lfHeight = -muldiv( entry->font.val.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
4253 break;
4254 default:
4255 WARN( "Unknown format in key %s value %s\n",
4256 debugstr_a( parameter_key_names[entry->hdr.base_key] ),
4257 debugstr_a( entry->hdr.regval ));
4258 /* fall through */
4259 case 0: /* use the default GUI font */
4260 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(font), &font );
4261 font.lfHeight = map_from_system_dpi( font.lfHeight );
4262 font.lfWeight = entry->font.weight;
4263 entry->font.val = font;
4264 break;
4266 get_real_fontname( &entry->font.val, entry->font.fullname );
4267 entry->hdr.loaded = TRUE;
4269 font = entry->font.val;
4270 font.lfHeight = map_to_dpi( font.lfHeight, dpi );
4271 lstrcpyW( font.lfFaceName, entry->font.fullname );
4272 *(LOGFONTW *)ptr_param = font;
4273 return TRUE;
4276 /* set a font (binary) parameter in the registry */
4277 static BOOL set_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
4279 LOGFONTW font;
4280 WCHAR *ptr;
4282 memcpy( &font, ptr_param, sizeof(font) );
4283 /* zero pad the end of lfFaceName so we don't save uninitialised data */
4284 for (ptr = font.lfFaceName; ptr < font.lfFaceName + LF_FACESIZE && *ptr; ptr++);
4285 if (ptr < font.lfFaceName + LF_FACESIZE)
4286 memset( ptr, 0, (font.lfFaceName + LF_FACESIZE - ptr) * sizeof(WCHAR) );
4287 if (font.lfHeight < 0) font.lfHeight = map_from_system_dpi( font.lfHeight );
4289 if (!save_entry( &entry->hdr, &font, sizeof(font), REG_BINARY, flags )) return FALSE;
4290 entry->font.val = font;
4291 get_real_fontname( &entry->font.val, entry->font.fullname );
4292 entry->hdr.loaded = TRUE;
4293 return TRUE;
4296 /* initialize a font (binary) parameter */
4297 static BOOL init_font_entry( union sysparam_all_entry *entry )
4299 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(entry->font.val), &entry->font.val );
4300 entry->font.val.lfHeight = map_from_system_dpi( entry->font.val.lfHeight );
4301 entry->font.val.lfWeight = entry->font.weight;
4302 get_real_fontname( &entry->font.val, entry->font.fullname );
4303 return init_entry( &entry->hdr, &entry->font.val, sizeof(entry->font.val), REG_BINARY );
4306 /* get a user pref parameter in the registry */
4307 static BOOL get_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
4309 union sysparam_all_entry *parent_entry = entry->pref.parent;
4310 BYTE prefs[8];
4312 if (!ptr_param) return FALSE;
4314 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, dpi )) return FALSE;
4315 *(BOOL *)ptr_param = (prefs[entry->pref.offset] & entry->pref.mask) != 0;
4316 return TRUE;
4319 /* set a user pref parameter in the registry */
4320 static BOOL set_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
4322 union sysparam_all_entry *parent_entry = entry->pref.parent;
4323 BYTE prefs[8];
4325 parent_entry->hdr.loaded = FALSE; /* force loading it again */
4326 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, get_system_dpi() )) return FALSE;
4328 if (PtrToUlong( ptr_param )) prefs[entry->pref.offset] |= entry->pref.mask;
4329 else prefs[entry->pref.offset] &= ~entry->pref.mask;
4331 return parent_entry->hdr.set( parent_entry, sizeof(prefs), prefs, flags );
4334 static BOOL get_entry_dpi( void *ptr, UINT int_param, void *ptr_param, UINT dpi )
4336 union sysparam_all_entry *entry = ptr;
4337 return entry->hdr.get( entry, int_param, ptr_param, dpi );
4340 static BOOL get_entry( void *ptr, UINT int_param, void *ptr_param )
4342 return get_entry_dpi( ptr, int_param, ptr_param, get_system_dpi() );
4345 static BOOL set_entry( void *ptr, UINT int_param, void *ptr_param, UINT flags )
4347 union sysparam_all_entry *entry = ptr;
4348 return entry->hdr.set( entry, int_param, ptr_param, flags );
4351 #define UINT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4352 { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg }, (val) } }
4354 #define UINT_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
4355 { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg, mirror_base, reg }, (val) } }
4357 #define INT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4358 { .uint = { { get_uint_entry, set_int_entry, init_int_entry, base, reg }, (val) } }
4360 #define BOOL_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4361 { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg }, (val) } }
4363 #define BOOL_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
4364 { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg, mirror_base, reg }, (val) } }
4366 #define TWIPS_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4367 { .uint = { { get_twips_entry, set_twips_entry, init_int_entry, base, reg }, (val) } }
4369 #define YESNO_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4370 { .bool = { { get_yesno_entry, set_yesno_entry, init_yesno_entry, base, reg }, (val) } }
4372 #define DWORD_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4373 { .dword = { { get_dword_entry, set_dword_entry, init_dword_entry, base, reg }, (val) } }
4375 #define BINARY_ENTRY(name,data,base,reg) union sysparam_all_entry entry_##name = \
4376 { .bin = { { get_binary_entry, set_binary_entry, init_binary_entry, base, reg }, data, sizeof(data) } }
4378 #define PATH_ENTRY(name,base,reg,buffer) union sysparam_all_entry entry_##name = \
4379 { .path = { { get_path_entry, set_path_entry, init_path_entry, base, reg }, buffer } }
4381 #define FONT_ENTRY(name,weight,base,reg) union sysparam_all_entry entry_##name = \
4382 { .font = { { get_font_entry, set_font_entry, init_font_entry, base, reg }, (weight) } }
4384 #define USERPREF_ENTRY(name,offset,mask) union sysparam_all_entry entry_##name = \
4385 { .pref = { { get_userpref_entry, set_userpref_entry }, &entry_USERPREFERENCESMASK, (offset), (mask) } }
4387 static UINT_ENTRY( DRAGWIDTH, 4, DESKTOP_KEY, "DragWidth" );
4388 static UINT_ENTRY( DRAGHEIGHT, 4, DESKTOP_KEY, "DragHeight" );
4389 static UINT_ENTRY( DOUBLECLICKTIME, 500, MOUSE_KEY, "DoubleClickSpeed" );
4390 static UINT_ENTRY( FONTSMOOTHING, 2, DESKTOP_KEY, "FontSmoothing" );
4391 static UINT_ENTRY( GRIDGRANULARITY, 0, DESKTOP_KEY, "GridGranularity" );
4392 static UINT_ENTRY( KEYBOARDDELAY, 1, KEYBOARD_KEY, "KeyboardDelay" );
4393 static UINT_ENTRY( KEYBOARDSPEED, 31, KEYBOARD_KEY, "KeyboardSpeed" );
4394 static UINT_ENTRY( MENUSHOWDELAY, 400, DESKTOP_KEY, "MenuShowDelay" );
4395 static UINT_ENTRY( MINARRANGE, ARW_HIDE, METRICS_KEY, "MinArrange" );
4396 static UINT_ENTRY( MINHORZGAP, 0, METRICS_KEY, "MinHorzGap" );
4397 static UINT_ENTRY( MINVERTGAP, 0, METRICS_KEY, "MinVertGap" );
4398 static UINT_ENTRY( MINWIDTH, 154, METRICS_KEY, "MinWidth" );
4399 static UINT_ENTRY( MOUSEHOVERHEIGHT, 4, MOUSE_KEY, "MouseHoverHeight" );
4400 static UINT_ENTRY( MOUSEHOVERTIME, 400, MOUSE_KEY, "MouseHoverTime" );
4401 static UINT_ENTRY( MOUSEHOVERWIDTH, 4, MOUSE_KEY, "MouseHoverWidth" );
4402 static UINT_ENTRY( MOUSESPEED, 10, MOUSE_KEY, "MouseSensitivity" );
4403 static UINT_ENTRY( MOUSETRAILS, 0, MOUSE_KEY, "MouseTrails" );
4404 static UINT_ENTRY( SCREENSAVETIMEOUT, 300, DESKTOP_KEY, "ScreenSaveTimeOut" );
4405 static UINT_ENTRY( WHEELSCROLLCHARS, 3, DESKTOP_KEY, "WheelScrollChars" );
4406 static UINT_ENTRY( WHEELSCROLLLINES, 3, DESKTOP_KEY, "WheelScrollLines" );
4407 static UINT_ENTRY_MIRROR( DOUBLECLKHEIGHT, 4, MOUSE_KEY, "DoubleClickHeight", DESKTOP_KEY );
4408 static UINT_ENTRY_MIRROR( DOUBLECLKWIDTH, 4, MOUSE_KEY, "DoubleClickWidth", DESKTOP_KEY );
4409 static UINT_ENTRY_MIRROR( MENUDROPALIGNMENT, 0, DESKTOP_KEY, "MenuDropAlignment", VERSION_KEY );
4411 static INT_ENTRY( MOUSETHRESHOLD1, 6, MOUSE_KEY, "MouseThreshold1" );
4412 static INT_ENTRY( MOUSETHRESHOLD2, 10, MOUSE_KEY, "MouseThreshold2" );
4413 static INT_ENTRY( MOUSEACCELERATION, 1, MOUSE_KEY, "MouseSpeed" );
4415 static BOOL_ENTRY( BLOCKSENDINPUTRESETS, FALSE, DESKTOP_KEY, "BlockSendInputResets" );
4416 static BOOL_ENTRY( DRAGFULLWINDOWS, FALSE, DESKTOP_KEY, "DragFullWindows" );
4417 static BOOL_ENTRY( KEYBOARDPREF, TRUE, KEYBOARDPREF_KEY, "On" );
4418 static BOOL_ENTRY( LOWPOWERACTIVE, FALSE, DESKTOP_KEY, "LowPowerActive" );
4419 static BOOL_ENTRY( MOUSEBUTTONSWAP, FALSE, MOUSE_KEY, "SwapMouseButtons" );
4420 static BOOL_ENTRY( POWEROFFACTIVE, FALSE, DESKTOP_KEY, "PowerOffActive" );
4421 static BOOL_ENTRY( SCREENREADER, FALSE, SCREENREADER_KEY, "On" );
4422 static BOOL_ENTRY( SCREENSAVEACTIVE, TRUE, DESKTOP_KEY, "ScreenSaveActive" );
4423 static BOOL_ENTRY( SCREENSAVERRUNNING, FALSE, DESKTOP_KEY, "WINE_ScreenSaverRunning" ); /* FIXME - real value */
4424 static BOOL_ENTRY( SHOWSOUNDS, FALSE, SHOWSOUNDS_KEY, "On" );
4425 static BOOL_ENTRY( SNAPTODEFBUTTON, FALSE, MOUSE_KEY, "SnapToDefaultButton" );
4426 static BOOL_ENTRY_MIRROR( ICONTITLEWRAP, TRUE, DESKTOP_KEY, "IconTitleWrap", METRICS_KEY );
4427 static BOOL_ENTRY( AUDIODESC_ON, FALSE, AUDIODESC_KEY, "On" );
4429 static TWIPS_ENTRY( BORDER, -15, METRICS_KEY, "BorderWidth" );
4430 static TWIPS_ENTRY( CAPTIONHEIGHT, -270, METRICS_KEY, "CaptionHeight" );
4431 static TWIPS_ENTRY( CAPTIONWIDTH, -270, METRICS_KEY, "CaptionWidth" );
4432 static TWIPS_ENTRY( ICONHORIZONTALSPACING, -1125, METRICS_KEY, "IconSpacing" );
4433 static TWIPS_ENTRY( ICONVERTICALSPACING, -1125, METRICS_KEY, "IconVerticalSpacing" );
4434 static TWIPS_ENTRY( MENUHEIGHT, -270, METRICS_KEY, "MenuHeight" );
4435 static TWIPS_ENTRY( MENUWIDTH, -270, METRICS_KEY, "MenuWidth" );
4436 static TWIPS_ENTRY( PADDEDBORDERWIDTH, 0, METRICS_KEY, "PaddedBorderWidth" );
4437 static TWIPS_ENTRY( SCROLLHEIGHT, -240, METRICS_KEY, "ScrollHeight" );
4438 static TWIPS_ENTRY( SCROLLWIDTH, -240, METRICS_KEY, "ScrollWidth" );
4439 static TWIPS_ENTRY( SMCAPTIONHEIGHT, -225, METRICS_KEY, "SmCaptionHeight" );
4440 static TWIPS_ENTRY( SMCAPTIONWIDTH, -225, METRICS_KEY, "SmCaptionWidth" );
4442 static YESNO_ENTRY( BEEP, TRUE, SOUND_KEY, "Beep" );
4444 static DWORD_ENTRY( ACTIVEWINDOWTRACKING, 0, MOUSE_KEY, "ActiveWindowTracking" );
4445 static DWORD_ENTRY( ACTIVEWNDTRKTIMEOUT, 0, DESKTOP_KEY, "ActiveWndTrackTimeout" );
4446 static DWORD_ENTRY( CARETWIDTH, 1, DESKTOP_KEY, "CaretWidth" );
4447 static DWORD_ENTRY( DPISCALINGVER, 0, DESKTOP_KEY, "DpiScalingVer" );
4448 static DWORD_ENTRY( FOCUSBORDERHEIGHT, 1, DESKTOP_KEY, "FocusBorderHeight" );
4449 static DWORD_ENTRY( FOCUSBORDERWIDTH, 1, DESKTOP_KEY, "FocusBorderWidth" );
4450 static DWORD_ENTRY( FONTSMOOTHINGCONTRAST, 0, DESKTOP_KEY, "FontSmoothingGamma" );
4451 static DWORD_ENTRY( FONTSMOOTHINGORIENTATION, FE_FONTSMOOTHINGORIENTATIONRGB, DESKTOP_KEY, "FontSmoothingOrientation" );
4452 static DWORD_ENTRY( FONTSMOOTHINGTYPE, FE_FONTSMOOTHINGSTANDARD, DESKTOP_KEY, "FontSmoothingType" );
4453 static DWORD_ENTRY( FOREGROUNDFLASHCOUNT, 3, DESKTOP_KEY, "ForegroundFlashCount" );
4454 static DWORD_ENTRY( FOREGROUNDLOCKTIMEOUT, 0, DESKTOP_KEY, "ForegroundLockTimeout" );
4455 static DWORD_ENTRY( LOGPIXELS, 0, DESKTOP_KEY, "LogPixels" );
4456 static DWORD_ENTRY( MOUSECLICKLOCKTIME, 1200, DESKTOP_KEY, "ClickLockTime" );
4457 static DWORD_ENTRY( AUDIODESC_LOCALE, 0, AUDIODESC_KEY, "Locale" );
4459 static WCHAR desk_pattern_path[MAX_PATH];
4460 static WCHAR desk_wallpaper_path[MAX_PATH];
4461 static PATH_ENTRY( DESKPATTERN, DESKTOP_KEY, "Pattern", desk_pattern_path );
4462 static PATH_ENTRY( DESKWALLPAPER, DESKTOP_KEY, "Wallpaper", desk_wallpaper_path );
4464 static BYTE user_prefs[8] = { 0x30, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, 0x00 };
4465 static BINARY_ENTRY( USERPREFERENCESMASK, user_prefs, DESKTOP_KEY, "UserPreferencesMask" );
4467 static FONT_ENTRY( CAPTIONLOGFONT, FW_BOLD, METRICS_KEY, "CaptionFont" );
4468 static FONT_ENTRY( ICONTITLELOGFONT, FW_NORMAL, METRICS_KEY, "IconFont" );
4469 static FONT_ENTRY( MENULOGFONT, FW_NORMAL, METRICS_KEY, "MenuFont" );
4470 static FONT_ENTRY( MESSAGELOGFONT, FW_NORMAL, METRICS_KEY, "MessageFont" );
4471 static FONT_ENTRY( SMCAPTIONLOGFONT, FW_NORMAL, METRICS_KEY, "SmCaptionFont" );
4472 static FONT_ENTRY( STATUSLOGFONT, FW_NORMAL, METRICS_KEY, "StatusFont" );
4474 static USERPREF_ENTRY( MENUANIMATION, 0, 0x02 );
4475 static USERPREF_ENTRY( COMBOBOXANIMATION, 0, 0x04 );
4476 static USERPREF_ENTRY( LISTBOXSMOOTHSCROLLING, 0, 0x08 );
4477 static USERPREF_ENTRY( GRADIENTCAPTIONS, 0, 0x10 );
4478 static USERPREF_ENTRY( KEYBOARDCUES, 0, 0x20 );
4479 static USERPREF_ENTRY( ACTIVEWNDTRKZORDER, 0, 0x40 );
4480 static USERPREF_ENTRY( HOTTRACKING, 0, 0x80 );
4481 static USERPREF_ENTRY( MENUFADE, 1, 0x02 );
4482 static USERPREF_ENTRY( SELECTIONFADE, 1, 0x04 );
4483 static USERPREF_ENTRY( TOOLTIPANIMATION, 1, 0x08 );
4484 static USERPREF_ENTRY( TOOLTIPFADE, 1, 0x10 );
4485 static USERPREF_ENTRY( CURSORSHADOW, 1, 0x20 );
4486 static USERPREF_ENTRY( MOUSESONAR, 1, 0x40 );
4487 static USERPREF_ENTRY( MOUSECLICKLOCK, 1, 0x80 );
4488 static USERPREF_ENTRY( MOUSEVANISH, 2, 0x01 );
4489 static USERPREF_ENTRY( FLATMENU, 2, 0x02 );
4490 static USERPREF_ENTRY( DROPSHADOW, 2, 0x04 );
4491 static USERPREF_ENTRY( UIEFFECTS, 3, 0x80 );
4492 static USERPREF_ENTRY( DISABLEOVERLAPPEDCONTENT, 4, 0x01 );
4493 static USERPREF_ENTRY( CLIENTAREAANIMATION, 4, 0x02 );
4494 static USERPREF_ENTRY( CLEARTYPE, 4, 0x10 );
4495 static USERPREF_ENTRY( SPEECHRECOGNITION, 4, 0x20 );
4497 /* System parameter indexes */
4498 enum spi_index
4500 SPI_SETWORKAREA_IDX,
4501 SPI_INDEX_COUNT
4504 /* indicators whether system parameter value is loaded */
4505 static char spi_loaded[SPI_INDEX_COUNT];
4507 static struct sysparam_rgb_entry system_colors[] =
4509 #define RGB_ENTRY(name,val,reg) { { get_rgb_entry, set_rgb_entry, init_rgb_entry, COLORS_KEY, reg }, (val) }
4510 RGB_ENTRY( COLOR_SCROLLBAR, RGB(212, 208, 200), "Scrollbar" ),
4511 RGB_ENTRY( COLOR_BACKGROUND, RGB(58, 110, 165), "Background" ),
4512 RGB_ENTRY( COLOR_ACTIVECAPTION, RGB(10, 36, 106), "ActiveTitle" ),
4513 RGB_ENTRY( COLOR_INACTIVECAPTION, RGB(128, 128, 128), "InactiveTitle" ),
4514 RGB_ENTRY( COLOR_MENU, RGB(212, 208, 200), "Menu" ),
4515 RGB_ENTRY( COLOR_WINDOW, RGB(255, 255, 255), "Window" ),
4516 RGB_ENTRY( COLOR_WINDOWFRAME, RGB(0, 0, 0), "WindowFrame" ),
4517 RGB_ENTRY( COLOR_MENUTEXT, RGB(0, 0, 0), "MenuText" ),
4518 RGB_ENTRY( COLOR_WINDOWTEXT, RGB(0, 0, 0), "WindowText" ),
4519 RGB_ENTRY( COLOR_CAPTIONTEXT, RGB(255, 255, 255), "TitleText" ),
4520 RGB_ENTRY( COLOR_ACTIVEBORDER, RGB(212, 208, 200), "ActiveBorder" ),
4521 RGB_ENTRY( COLOR_INACTIVEBORDER, RGB(212, 208, 200), "InactiveBorder" ),
4522 RGB_ENTRY( COLOR_APPWORKSPACE, RGB(128, 128, 128), "AppWorkSpace" ),
4523 RGB_ENTRY( COLOR_HIGHLIGHT, RGB(10, 36, 106), "Hilight" ),
4524 RGB_ENTRY( COLOR_HIGHLIGHTTEXT, RGB(255, 255, 255), "HilightText" ),
4525 RGB_ENTRY( COLOR_BTNFACE, RGB(212, 208, 200), "ButtonFace" ),
4526 RGB_ENTRY( COLOR_BTNSHADOW, RGB(128, 128, 128), "ButtonShadow" ),
4527 RGB_ENTRY( COLOR_GRAYTEXT, RGB(128, 128, 128), "GrayText" ),
4528 RGB_ENTRY( COLOR_BTNTEXT, RGB(0, 0, 0), "ButtonText" ),
4529 RGB_ENTRY( COLOR_INACTIVECAPTIONTEXT, RGB(212, 208, 200), "InactiveTitleText" ),
4530 RGB_ENTRY( COLOR_BTNHIGHLIGHT, RGB(255, 255, 255), "ButtonHilight" ),
4531 RGB_ENTRY( COLOR_3DDKSHADOW, RGB(64, 64, 64), "ButtonDkShadow" ),
4532 RGB_ENTRY( COLOR_3DLIGHT, RGB(212, 208, 200), "ButtonLight" ),
4533 RGB_ENTRY( COLOR_INFOTEXT, RGB(0, 0, 0), "InfoText" ),
4534 RGB_ENTRY( COLOR_INFOBK, RGB(255, 255, 225), "InfoWindow" ),
4535 RGB_ENTRY( COLOR_ALTERNATEBTNFACE, RGB(181, 181, 181), "ButtonAlternateFace" ),
4536 RGB_ENTRY( COLOR_HOTLIGHT, RGB(0, 0, 200), "HotTrackingColor" ),
4537 RGB_ENTRY( COLOR_GRADIENTACTIVECAPTION, RGB(166, 202, 240), "GradientActiveTitle" ),
4538 RGB_ENTRY( COLOR_GRADIENTINACTIVECAPTION, RGB(192, 192, 192), "GradientInactiveTitle" ),
4539 RGB_ENTRY( COLOR_MENUHILIGHT, RGB(10, 36, 106), "MenuHilight" ),
4540 RGB_ENTRY( COLOR_MENUBAR, RGB(212, 208, 200), "MenuBar" )
4541 #undef RGB_ENTRY
4544 /* entries that are initialized by default in the registry */
4545 static union sysparam_all_entry * const default_entries[] =
4547 (union sysparam_all_entry *)&entry_ACTIVEWINDOWTRACKING,
4548 (union sysparam_all_entry *)&entry_ACTIVEWNDTRKTIMEOUT,
4549 (union sysparam_all_entry *)&entry_BEEP,
4550 (union sysparam_all_entry *)&entry_BLOCKSENDINPUTRESETS,
4551 (union sysparam_all_entry *)&entry_BORDER,
4552 (union sysparam_all_entry *)&entry_CAPTIONHEIGHT,
4553 (union sysparam_all_entry *)&entry_CAPTIONWIDTH,
4554 (union sysparam_all_entry *)&entry_CARETWIDTH,
4555 (union sysparam_all_entry *)&entry_DESKWALLPAPER,
4556 (union sysparam_all_entry *)&entry_DOUBLECLICKTIME,
4557 (union sysparam_all_entry *)&entry_DOUBLECLKHEIGHT,
4558 (union sysparam_all_entry *)&entry_DOUBLECLKWIDTH,
4559 (union sysparam_all_entry *)&entry_DRAGFULLWINDOWS,
4560 (union sysparam_all_entry *)&entry_DRAGHEIGHT,
4561 (union sysparam_all_entry *)&entry_DRAGWIDTH,
4562 (union sysparam_all_entry *)&entry_FOCUSBORDERHEIGHT,
4563 (union sysparam_all_entry *)&entry_FOCUSBORDERWIDTH,
4564 (union sysparam_all_entry *)&entry_FONTSMOOTHING,
4565 (union sysparam_all_entry *)&entry_FONTSMOOTHINGCONTRAST,
4566 (union sysparam_all_entry *)&entry_FONTSMOOTHINGORIENTATION,
4567 (union sysparam_all_entry *)&entry_FONTSMOOTHINGTYPE,
4568 (union sysparam_all_entry *)&entry_FOREGROUNDFLASHCOUNT,
4569 (union sysparam_all_entry *)&entry_FOREGROUNDLOCKTIMEOUT,
4570 (union sysparam_all_entry *)&entry_ICONHORIZONTALSPACING,
4571 (union sysparam_all_entry *)&entry_ICONTITLEWRAP,
4572 (union sysparam_all_entry *)&entry_ICONVERTICALSPACING,
4573 (union sysparam_all_entry *)&entry_KEYBOARDDELAY,
4574 (union sysparam_all_entry *)&entry_KEYBOARDPREF,
4575 (union sysparam_all_entry *)&entry_KEYBOARDSPEED,
4576 (union sysparam_all_entry *)&entry_LOWPOWERACTIVE,
4577 (union sysparam_all_entry *)&entry_MENUHEIGHT,
4578 (union sysparam_all_entry *)&entry_MENUSHOWDELAY,
4579 (union sysparam_all_entry *)&entry_MENUWIDTH,
4580 (union sysparam_all_entry *)&entry_MOUSEACCELERATION,
4581 (union sysparam_all_entry *)&entry_MOUSEBUTTONSWAP,
4582 (union sysparam_all_entry *)&entry_MOUSECLICKLOCKTIME,
4583 (union sysparam_all_entry *)&entry_MOUSEHOVERHEIGHT,
4584 (union sysparam_all_entry *)&entry_MOUSEHOVERTIME,
4585 (union sysparam_all_entry *)&entry_MOUSEHOVERWIDTH,
4586 (union sysparam_all_entry *)&entry_MOUSESPEED,
4587 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD1,
4588 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD2,
4589 (union sysparam_all_entry *)&entry_PADDEDBORDERWIDTH,
4590 (union sysparam_all_entry *)&entry_SCREENREADER,
4591 (union sysparam_all_entry *)&entry_SCROLLHEIGHT,
4592 (union sysparam_all_entry *)&entry_SCROLLWIDTH,
4593 (union sysparam_all_entry *)&entry_SHOWSOUNDS,
4594 (union sysparam_all_entry *)&entry_SMCAPTIONHEIGHT,
4595 (union sysparam_all_entry *)&entry_SMCAPTIONWIDTH,
4596 (union sysparam_all_entry *)&entry_SNAPTODEFBUTTON,
4597 (union sysparam_all_entry *)&entry_USERPREFERENCESMASK,
4598 (union sysparam_all_entry *)&entry_WHEELSCROLLCHARS,
4599 (union sysparam_all_entry *)&entry_WHEELSCROLLLINES,
4600 (union sysparam_all_entry *)&entry_AUDIODESC_LOCALE,
4601 (union sysparam_all_entry *)&entry_AUDIODESC_ON,
4604 /***********************************************************************
4605 * get_config_key
4607 * Get a config key from either the app-specific or the default config
4609 static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
4610 WCHAR *buffer, DWORD size )
4612 WCHAR nameW[128];
4613 char buf[2048];
4614 KEY_VALUE_PARTIAL_INFORMATION *info = (void *)buf;
4616 asciiz_to_unicode( nameW, name );
4618 if (appkey && query_reg_value( appkey, nameW, info, sizeof(buf) ))
4620 size = min( info->DataLength, size - sizeof(WCHAR) );
4621 memcpy( buffer, info->Data, size );
4622 buffer[size / sizeof(WCHAR)] = 0;
4623 return 0;
4626 if (defkey && query_reg_value( defkey, nameW, info, sizeof(buf) ))
4628 size = min( info->DataLength, size - sizeof(WCHAR) );
4629 memcpy( buffer, info->Data, size );
4630 buffer[size / sizeof(WCHAR)] = 0;
4631 return 0;
4634 return ERROR_FILE_NOT_FOUND;
4637 void sysparams_init(void)
4639 WCHAR buffer[MAX_PATH+16], *p, *appname;
4640 DWORD i, dispos, dpi_scaling;
4641 WCHAR layout[KL_NAMELENGTH];
4642 pthread_mutexattr_t attr;
4643 HKEY hkey, appkey = 0;
4644 DWORD len;
4646 static const WCHAR software_wineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e'};
4647 static const WCHAR temporary_system_parametersW[] =
4648 {'T','e','m','p','o','r','a','r','y',' ','S','y','s','t','e','m',' ',
4649 'P','a','r','a','m','e','t','e','r','s'};
4650 static const WCHAR oneW[] = {'1',0};
4651 static const WCHAR kl_preloadW[] =
4652 {'K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','\\','P','r','e','l','o','a','d'};
4653 static const WCHAR x11driverW[] = {'\\','X','1','1',' ','D','r','i','v','e','r',0};
4655 pthread_mutexattr_init( &attr );
4656 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
4657 pthread_mutex_init( &user_mutex, &attr );
4658 pthread_mutexattr_destroy( &attr );
4660 if ((hkey = reg_create_key( hkcu_key, kl_preloadW, sizeof(kl_preloadW), 0, NULL )))
4662 if (NtUserGetKeyboardLayoutName( layout ))
4663 set_reg_value( hkey, oneW, REG_SZ, (const BYTE *)layout,
4664 (lstrlenW(layout) + 1) * sizeof(WCHAR) );
4665 NtClose( hkey );
4668 /* this one must be non-volatile */
4669 if (!(hkey = reg_create_key( hkcu_key, software_wineW, sizeof(software_wineW), 0, NULL )))
4671 ERR("Can't create wine registry branch\n");
4672 return;
4675 /* @@ Wine registry key: HKCU\Software\Wine\Temporary System Parameters */
4676 if (!(volatile_base_key = reg_create_key( hkey, temporary_system_parametersW,
4677 sizeof(temporary_system_parametersW),
4678 REG_OPTION_VOLATILE, &dispos )))
4679 ERR("Can't create non-permanent wine registry branch\n");
4681 NtClose( hkey );
4683 config_key = reg_create_key( NULL, config_keyW, sizeof(config_keyW), 0, NULL );
4685 get_dword_entry( (union sysparam_all_entry *)&entry_LOGPIXELS, 0, &system_dpi, 0 );
4686 if (!system_dpi) /* check fallback key */
4688 static const WCHAR log_pixelsW[] = {'L','o','g','P','i','x','e','l','s',0};
4689 static const WCHAR software_fontsW[] =
4690 {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s'};
4692 if ((hkey = reg_open_key( config_key, software_fontsW, sizeof(software_fontsW) )))
4694 char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(DWORD)])];
4695 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
4697 if (query_reg_value( hkey, log_pixelsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
4698 system_dpi = *(const DWORD *)value->Data;
4699 NtClose( hkey );
4702 if (!system_dpi) system_dpi = USER_DEFAULT_SCREEN_DPI;
4704 /* FIXME: what do the DpiScalingVer flags mean? */
4705 get_dword_entry( (union sysparam_all_entry *)&entry_DPISCALINGVER, 0, &dpi_scaling, 0 );
4706 if (!dpi_scaling) NtUserSetProcessDpiAwarenessContext( NTUSER_DPI_PER_MONITOR_AWARE, 0 );
4708 if (volatile_base_key && dispos == REG_CREATED_NEW_KEY) /* first process, initialize entries */
4710 for (i = 0; i < ARRAY_SIZE( default_entries ); i++)
4711 default_entries[i]->hdr.init( default_entries[i] );
4714 /* @@ Wine registry key: HKCU\Software\Wine\X11 Driver */
4715 hkey = reg_open_hkcu_key( "Software\\Wine\\X11 Driver" );
4717 /* open the app-specific key */
4719 appname = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer;
4720 if ((p = wcsrchr( appname, '/' ))) appname = p + 1;
4721 if ((p = wcsrchr( appname, '\\' ))) appname = p + 1;
4722 len = lstrlenW( appname );
4724 if (len && len < MAX_PATH)
4726 HKEY tmpkey;
4727 int i;
4729 for (i = 0; appname[i]; i++) buffer[i] = RtlDowncaseUnicodeChar( appname[i] );
4730 buffer[i] = 0;
4731 appname = buffer;
4732 memcpy( appname + i, x11driverW, sizeof(x11driverW) );
4734 /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\X11 Driver */
4735 if ((tmpkey = reg_open_hkcu_key( "Software\\Wine\\AppDefaults" )))
4737 appkey = reg_open_key( tmpkey, appname, lstrlenW( appname ) * sizeof(WCHAR) );
4738 NtClose( tmpkey );
4742 #define IS_OPTION_TRUE(ch) \
4743 ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
4745 if (!get_config_key( hkey, appkey, "GrabPointer", buffer, sizeof(buffer) ))
4746 grab_pointer = IS_OPTION_TRUE( buffer[0] );
4747 if (!get_config_key( hkey, appkey, "GrabFullscreen", buffer, sizeof(buffer) ))
4748 grab_fullscreen = IS_OPTION_TRUE( buffer[0] );
4750 #undef IS_OPTION_TRUE
4753 static BOOL update_desktop_wallpaper(void)
4755 /* FIXME: move implementation from user32 */
4756 entry_DESKWALLPAPER.hdr.loaded = entry_DESKPATTERN.hdr.loaded = FALSE;
4757 return TRUE;
4760 /***********************************************************************
4761 * NtUserSystemParametersInfoForDpi (win32u.@)
4763 BOOL WINAPI NtUserSystemParametersInfoForDpi( UINT action, UINT val, PVOID ptr, UINT winini, UINT dpi )
4765 BOOL ret = FALSE;
4767 switch (action)
4769 case SPI_GETICONTITLELOGFONT:
4770 ret = get_entry_dpi( &entry_ICONTITLELOGFONT, val, ptr, dpi );
4771 break;
4772 case SPI_GETNONCLIENTMETRICS:
4774 NONCLIENTMETRICSW *ncm = ptr;
4776 if (!ncm) break;
4777 ret = get_entry_dpi( &entry_BORDER, 0, &ncm->iBorderWidth, dpi ) &&
4778 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ncm->iScrollWidth, dpi ) &&
4779 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ncm->iScrollHeight, dpi ) &&
4780 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ncm->iCaptionWidth, dpi ) &&
4781 get_entry_dpi( &entry_CAPTIONHEIGHT, 0, &ncm->iCaptionHeight, dpi ) &&
4782 get_entry_dpi( &entry_CAPTIONLOGFONT, 0, &ncm->lfCaptionFont, dpi ) &&
4783 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ncm->iSmCaptionWidth, dpi ) &&
4784 get_entry_dpi( &entry_SMCAPTIONHEIGHT, 0, &ncm->iSmCaptionHeight, dpi ) &&
4785 get_entry_dpi( &entry_SMCAPTIONLOGFONT, 0, &ncm->lfSmCaptionFont, dpi ) &&
4786 get_entry_dpi( &entry_MENUWIDTH, 0, &ncm->iMenuWidth, dpi ) &&
4787 get_entry_dpi( &entry_MENUHEIGHT, 0, &ncm->iMenuHeight, dpi ) &&
4788 get_entry_dpi( &entry_MENULOGFONT, 0, &ncm->lfMenuFont, dpi ) &&
4789 get_entry_dpi( &entry_STATUSLOGFONT, 0, &ncm->lfStatusFont, dpi ) &&
4790 get_entry_dpi( &entry_MESSAGELOGFONT, 0, &ncm->lfMessageFont, dpi );
4791 if (ret && ncm->cbSize == sizeof(NONCLIENTMETRICSW))
4792 ret = get_entry_dpi( &entry_PADDEDBORDERWIDTH, 0, &ncm->iPaddedBorderWidth, dpi );
4793 normalize_nonclientmetrics( ncm );
4794 break;
4796 case SPI_GETICONMETRICS:
4798 ICONMETRICSW *im = ptr;
4799 if (im && im->cbSize == sizeof(*im))
4800 ret = get_entry_dpi( &entry_ICONHORIZONTALSPACING, 0, &im->iHorzSpacing, dpi ) &&
4801 get_entry_dpi( &entry_ICONVERTICALSPACING, 0, &im->iVertSpacing, dpi ) &&
4802 get_entry_dpi( &entry_ICONTITLEWRAP, 0, &im->iTitleWrap, dpi ) &&
4803 get_entry_dpi( &entry_ICONTITLELOGFONT, 0, &im->lfFont, dpi );
4804 break;
4806 default:
4807 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4808 break;
4810 return ret;
4813 /***********************************************************************
4814 * NtUserSystemParametersInfo (win32u.@)
4816 * Each system parameter has flag which shows whether the parameter
4817 * is loaded or not. Parameters, stored directly in SysParametersInfo are
4818 * loaded from registry only when they are requested and the flag is
4819 * "false", after the loading the flag is set to "true". On interprocess
4820 * notification of the parameter change the corresponding parameter flag is
4821 * set to "false". The parameter value will be reloaded when it is requested
4822 * the next time.
4823 * Parameters, backed by or depend on GetSystemMetrics are processed
4824 * differently. These parameters are always loaded. They are reloaded right
4825 * away on interprocess change notification. We can't do lazy loading because
4826 * we don't want to complicate GetSystemMetrics.
4827 * Parameters backed by driver settings are read from corresponding setting.
4828 * On the parameter change request the setting is changed. Interprocess change
4829 * notifications are ignored.
4830 * When parameter value is updated the changed value is stored in permanent
4831 * registry branch if saving is requested. Otherwise it is stored
4832 * in temporary branch
4834 * Some SPI values can also be stored as Twips values in the registry,
4835 * don't forget the conversion!
4837 BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT winini )
4839 #define WINE_SPI_FIXME(x) \
4840 case x: \
4842 static BOOL warn = TRUE; \
4843 if (warn) \
4845 warn = FALSE; \
4846 FIXME( "Unimplemented action: %u (%s)\n", x, #x ); \
4849 RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE ); \
4850 ret = FALSE; \
4851 break
4852 #define WINE_SPI_WARN(x) \
4853 case x: \
4854 WARN( "Ignored action: %u (%s)\n", x, #x ); \
4855 ret = TRUE; \
4856 break
4858 BOOL ret = user_driver->pSystemParametersInfo( action, val, ptr, winini );
4859 unsigned spi_idx = 0;
4861 if (!ret) switch (action)
4863 case SPI_GETBEEP:
4864 ret = get_entry( &entry_BEEP, val, ptr );
4865 break;
4866 case SPI_SETBEEP:
4867 ret = set_entry( &entry_BEEP, val, ptr, winini );
4868 break;
4869 case SPI_GETMOUSE:
4870 ret = get_entry( &entry_MOUSETHRESHOLD1, val, (INT *)ptr ) &&
4871 get_entry( &entry_MOUSETHRESHOLD2, val, (INT *)ptr + 1 ) &&
4872 get_entry( &entry_MOUSEACCELERATION, val, (INT *)ptr + 2 );
4873 break;
4874 case SPI_SETMOUSE:
4875 ret = set_entry( &entry_MOUSETHRESHOLD1, ((INT *)ptr)[0], ptr, winini ) &&
4876 set_entry( &entry_MOUSETHRESHOLD2, ((INT *)ptr)[1], ptr, winini ) &&
4877 set_entry( &entry_MOUSEACCELERATION, ((INT *)ptr)[2], ptr, winini );
4878 break;
4879 case SPI_GETBORDER:
4880 ret = get_entry( &entry_BORDER, val, ptr );
4881 if (*(INT*)ptr < 1) *(INT*)ptr = 1;
4882 break;
4883 case SPI_SETBORDER:
4884 ret = set_entry( &entry_BORDER, val, ptr, winini );
4885 break;
4886 case SPI_GETKEYBOARDSPEED:
4887 ret = get_entry( &entry_KEYBOARDSPEED, val, ptr );
4888 break;
4889 case SPI_SETKEYBOARDSPEED:
4890 if (val > 31) val = 31;
4891 ret = set_entry( &entry_KEYBOARDSPEED, val, ptr, winini );
4892 break;
4894 WINE_SPI_WARN(SPI_LANGDRIVER); /* not implemented in Windows */
4896 case SPI_ICONHORIZONTALSPACING:
4897 if (ptr != NULL)
4898 ret = get_entry( &entry_ICONHORIZONTALSPACING, val, ptr );
4899 else
4901 int min_val = map_to_dpi( 32, get_system_dpi() );
4902 ret = set_entry( &entry_ICONHORIZONTALSPACING, max( min_val, val ), ptr, winini );
4904 break;
4905 case SPI_GETSCREENSAVETIMEOUT:
4906 ret = get_entry( &entry_SCREENSAVETIMEOUT, val, ptr );
4907 break;
4908 case SPI_SETSCREENSAVETIMEOUT:
4909 ret = set_entry( &entry_SCREENSAVETIMEOUT, val, ptr, winini );
4910 break;
4911 case SPI_GETSCREENSAVEACTIVE:
4912 ret = get_entry( &entry_SCREENSAVEACTIVE, val, ptr );
4913 break;
4914 case SPI_SETSCREENSAVEACTIVE:
4915 ret = set_entry( &entry_SCREENSAVEACTIVE, val, ptr, winini );
4916 break;
4917 case SPI_GETGRIDGRANULARITY:
4918 ret = get_entry( &entry_GRIDGRANULARITY, val, ptr );
4919 break;
4920 case SPI_SETGRIDGRANULARITY:
4921 ret = set_entry( &entry_GRIDGRANULARITY, val, ptr, winini );
4922 break;
4923 case SPI_SETDESKWALLPAPER:
4924 if (!ptr || set_entry( &entry_DESKWALLPAPER, val, ptr, winini ))
4925 ret = update_desktop_wallpaper();
4926 break;
4927 case SPI_SETDESKPATTERN:
4928 if (!ptr || set_entry( &entry_DESKPATTERN, val, ptr, winini ))
4929 ret = update_desktop_wallpaper();
4930 break;
4931 case SPI_GETKEYBOARDDELAY:
4932 ret = get_entry( &entry_KEYBOARDDELAY, val, ptr );
4933 break;
4934 case SPI_SETKEYBOARDDELAY:
4935 ret = set_entry( &entry_KEYBOARDDELAY, val, ptr, winini );
4936 break;
4937 case SPI_ICONVERTICALSPACING:
4938 if (ptr != NULL)
4939 ret = get_entry( &entry_ICONVERTICALSPACING, val, ptr );
4940 else
4942 int min_val = map_to_dpi( 32, get_system_dpi() );
4943 ret = set_entry( &entry_ICONVERTICALSPACING, max( min_val, val ), ptr, winini );
4945 break;
4946 case SPI_GETICONTITLEWRAP:
4947 ret = get_entry( &entry_ICONTITLEWRAP, val, ptr );
4948 break;
4949 case SPI_SETICONTITLEWRAP:
4950 ret = set_entry( &entry_ICONTITLEWRAP, val, ptr, winini );
4951 break;
4952 case SPI_GETMENUDROPALIGNMENT:
4953 ret = get_entry( &entry_MENUDROPALIGNMENT, val, ptr );
4954 break;
4955 case SPI_SETMENUDROPALIGNMENT:
4956 ret = set_entry( &entry_MENUDROPALIGNMENT, val, ptr, winini );
4957 break;
4958 case SPI_SETDOUBLECLKWIDTH:
4959 ret = set_entry( &entry_DOUBLECLKWIDTH, val, ptr, winini );
4960 break;
4961 case SPI_SETDOUBLECLKHEIGHT:
4962 ret = set_entry( &entry_DOUBLECLKHEIGHT, val, ptr, winini );
4963 break;
4964 case SPI_GETICONTITLELOGFONT:
4965 ret = get_entry( &entry_ICONTITLELOGFONT, val, ptr );
4966 break;
4967 case SPI_SETDOUBLECLICKTIME:
4968 ret = set_entry( &entry_DOUBLECLICKTIME, val, ptr, winini );
4969 break;
4970 case SPI_SETMOUSEBUTTONSWAP:
4971 ret = set_entry( &entry_MOUSEBUTTONSWAP, val, ptr, winini );
4972 break;
4973 case SPI_SETICONTITLELOGFONT:
4974 ret = set_entry( &entry_ICONTITLELOGFONT, val, ptr, winini );
4975 break;
4976 case SPI_GETFASTTASKSWITCH:
4977 if (!ptr) return FALSE;
4978 *(BOOL *)ptr = TRUE;
4979 ret = TRUE;
4980 break;
4981 case SPI_SETFASTTASKSWITCH:
4982 /* the action is disabled */
4983 ret = FALSE;
4984 break;
4985 case SPI_SETDRAGFULLWINDOWS:
4986 ret = set_entry( &entry_DRAGFULLWINDOWS, val, ptr, winini );
4987 break;
4988 case SPI_GETDRAGFULLWINDOWS:
4989 ret = get_entry( &entry_DRAGFULLWINDOWS, val, ptr );
4990 break;
4991 case SPI_GETNONCLIENTMETRICS:
4993 NONCLIENTMETRICSW *nm = ptr;
4994 int padded_border;
4996 if (!ptr) return FALSE;
4998 ret = get_entry( &entry_BORDER, 0, &nm->iBorderWidth ) &&
4999 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border ) &&
5000 get_entry( &entry_SCROLLWIDTH, 0, &nm->iScrollWidth ) &&
5001 get_entry( &entry_SCROLLHEIGHT, 0, &nm->iScrollHeight ) &&
5002 get_entry( &entry_CAPTIONWIDTH, 0, &nm->iCaptionWidth ) &&
5003 get_entry( &entry_CAPTIONHEIGHT, 0, &nm->iCaptionHeight ) &&
5004 get_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont ) &&
5005 get_entry( &entry_SMCAPTIONWIDTH, 0, &nm->iSmCaptionWidth ) &&
5006 get_entry( &entry_SMCAPTIONHEIGHT, 0, &nm->iSmCaptionHeight ) &&
5007 get_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont ) &&
5008 get_entry( &entry_MENUWIDTH, 0, &nm->iMenuWidth ) &&
5009 get_entry( &entry_MENUHEIGHT, 0, &nm->iMenuHeight ) &&
5010 get_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont ) &&
5011 get_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont ) &&
5012 get_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont );
5013 if (ret)
5015 nm->iBorderWidth += padded_border;
5016 if (nm->cbSize == sizeof(NONCLIENTMETRICSW)) nm->iPaddedBorderWidth = 0;
5018 normalize_nonclientmetrics( nm );
5019 break;
5021 case SPI_SETNONCLIENTMETRICS:
5023 LPNONCLIENTMETRICSW nm = ptr;
5024 int padded_border;
5026 if (nm && (nm->cbSize == sizeof(NONCLIENTMETRICSW) ||
5027 nm->cbSize == FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth)))
5029 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border );
5031 ret = set_entry( &entry_BORDER, nm->iBorderWidth - padded_border, NULL, winini ) &&
5032 set_entry( &entry_SCROLLWIDTH, nm->iScrollWidth, NULL, winini ) &&
5033 set_entry( &entry_SCROLLHEIGHT, nm->iScrollHeight, NULL, winini ) &&
5034 set_entry( &entry_CAPTIONWIDTH, nm->iCaptionWidth, NULL, winini ) &&
5035 set_entry( &entry_CAPTIONHEIGHT, nm->iCaptionHeight, NULL, winini ) &&
5036 set_entry( &entry_SMCAPTIONWIDTH, nm->iSmCaptionWidth, NULL, winini ) &&
5037 set_entry( &entry_SMCAPTIONHEIGHT, nm->iSmCaptionHeight, NULL, winini ) &&
5038 set_entry( &entry_MENUWIDTH, nm->iMenuWidth, NULL, winini ) &&
5039 set_entry( &entry_MENUHEIGHT, nm->iMenuHeight, NULL, winini ) &&
5040 set_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont, winini ) &&
5041 set_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont, winini ) &&
5042 set_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont, winini ) &&
5043 set_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont, winini ) &&
5044 set_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont, winini );
5046 break;
5048 case SPI_GETMINIMIZEDMETRICS:
5050 MINIMIZEDMETRICS *mm = ptr;
5051 if (mm && mm->cbSize == sizeof(*mm)) {
5052 ret = get_entry( &entry_MINWIDTH, 0, &mm->iWidth ) &&
5053 get_entry( &entry_MINHORZGAP, 0, &mm->iHorzGap ) &&
5054 get_entry( &entry_MINVERTGAP, 0, &mm->iVertGap ) &&
5055 get_entry( &entry_MINARRANGE, 0, &mm->iArrange );
5056 mm->iWidth = max( 0, mm->iWidth );
5057 mm->iHorzGap = max( 0, mm->iHorzGap );
5058 mm->iVertGap = max( 0, mm->iVertGap );
5059 mm->iArrange &= 0x0f;
5061 break;
5063 case SPI_SETMINIMIZEDMETRICS:
5065 MINIMIZEDMETRICS *mm = ptr;
5066 if (mm && mm->cbSize == sizeof(*mm))
5067 ret = set_entry( &entry_MINWIDTH, max( 0, mm->iWidth ), NULL, winini ) &&
5068 set_entry( &entry_MINHORZGAP, max( 0, mm->iHorzGap ), NULL, winini ) &&
5069 set_entry( &entry_MINVERTGAP, max( 0, mm->iVertGap ), NULL, winini ) &&
5070 set_entry( &entry_MINARRANGE, mm->iArrange & 0x0f, NULL, winini );
5071 break;
5073 case SPI_GETICONMETRICS:
5075 ICONMETRICSW *icon = ptr;
5076 if(icon && icon->cbSize == sizeof(*icon))
5078 ret = get_entry( &entry_ICONHORIZONTALSPACING, 0, &icon->iHorzSpacing ) &&
5079 get_entry( &entry_ICONVERTICALSPACING, 0, &icon->iVertSpacing ) &&
5080 get_entry( &entry_ICONTITLEWRAP, 0, &icon->iTitleWrap ) &&
5081 get_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont );
5083 break;
5085 case SPI_SETICONMETRICS:
5087 ICONMETRICSW *icon = ptr;
5088 if (icon && icon->cbSize == sizeof(*icon))
5089 ret = set_entry( &entry_ICONVERTICALSPACING, max(32,icon->iVertSpacing), NULL, winini ) &&
5090 set_entry( &entry_ICONHORIZONTALSPACING, max(32,icon->iHorzSpacing), NULL, winini ) &&
5091 set_entry( &entry_ICONTITLEWRAP, icon->iTitleWrap, NULL, winini ) &&
5092 set_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont, winini );
5093 break;
5095 case SPI_SETWORKAREA:
5097 if (!ptr) return FALSE;
5098 spi_idx = SPI_SETWORKAREA_IDX;
5099 work_area = *(RECT*)ptr;
5100 spi_loaded[spi_idx] = TRUE;
5101 ret = TRUE;
5102 break;
5104 case SPI_GETWORKAREA:
5106 if (!ptr) return FALSE;
5108 spi_idx = SPI_SETWORKAREA_IDX;
5109 if (!spi_loaded[spi_idx])
5111 struct monitor *monitor;
5113 if (!lock_display_devices()) return FALSE;
5115 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
5117 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
5118 work_area = monitor->rc_work;
5119 break;
5122 unlock_display_devices();
5123 spi_loaded[spi_idx] = TRUE;
5125 *(RECT *)ptr = map_dpi_rect( work_area, system_dpi, get_thread_dpi() );
5126 ret = TRUE;
5127 TRACE("work area %s\n", wine_dbgstr_rect( &work_area ));
5128 break;
5131 WINE_SPI_FIXME(SPI_SETPENWINDOWS);
5133 case SPI_GETFILTERKEYS:
5135 LPFILTERKEYS filter_keys = ptr;
5136 WARN("SPI_GETFILTERKEYS not fully implemented\n");
5137 if (filter_keys && filter_keys->cbSize == sizeof(FILTERKEYS))
5139 /* Indicate that no FilterKeys feature available */
5140 filter_keys->dwFlags = 0;
5141 filter_keys->iWaitMSec = 0;
5142 filter_keys->iDelayMSec = 0;
5143 filter_keys->iRepeatMSec = 0;
5144 filter_keys->iBounceMSec = 0;
5145 ret = TRUE;
5147 break;
5149 WINE_SPI_FIXME(SPI_SETFILTERKEYS);
5151 case SPI_GETTOGGLEKEYS:
5153 LPTOGGLEKEYS toggle_keys = ptr;
5154 WARN("SPI_GETTOGGLEKEYS not fully implemented\n");
5155 if (toggle_keys && toggle_keys->cbSize == sizeof(TOGGLEKEYS))
5157 /* Indicate that no ToggleKeys feature available */
5158 toggle_keys->dwFlags = 0;
5159 ret = TRUE;
5161 break;
5164 WINE_SPI_FIXME(SPI_SETTOGGLEKEYS);
5166 case SPI_GETMOUSEKEYS:
5168 MOUSEKEYS *mouse_keys = ptr;
5169 WARN("SPI_GETMOUSEKEYS not fully implemented\n");
5170 if (mouse_keys && mouse_keys->cbSize == sizeof(MOUSEKEYS))
5172 /* Indicate that no MouseKeys feature available */
5173 mouse_keys->dwFlags = 0;
5174 mouse_keys->iMaxSpeed = 360;
5175 mouse_keys->iTimeToMaxSpeed = 1000;
5176 mouse_keys->iCtrlSpeed = 0;
5177 mouse_keys->dwReserved1 = 0;
5178 mouse_keys->dwReserved2 = 0;
5179 ret = TRUE;
5181 break;
5184 WINE_SPI_FIXME(SPI_SETMOUSEKEYS);
5186 case SPI_GETSHOWSOUNDS:
5187 ret = get_entry( &entry_SHOWSOUNDS, val, ptr );
5188 break;
5189 case SPI_SETSHOWSOUNDS:
5190 ret = set_entry( &entry_SHOWSOUNDS, val, ptr, winini );
5191 break;
5192 case SPI_GETSTICKYKEYS:
5194 STICKYKEYS *sticky_keys = ptr;
5195 WARN("SPI_GETSTICKYKEYS not fully implemented\n");
5196 if (sticky_keys && sticky_keys->cbSize == sizeof(STICKYKEYS))
5198 /* Indicate that no StickyKeys feature available */
5199 sticky_keys->dwFlags = 0;
5200 ret = TRUE;
5202 break;
5205 WINE_SPI_FIXME(SPI_SETSTICKYKEYS);
5207 case SPI_GETACCESSTIMEOUT:
5209 ACCESSTIMEOUT *access_timeout = ptr;
5210 WARN("SPI_GETACCESSTIMEOUT not fully implemented\n");
5211 if (access_timeout && access_timeout->cbSize == sizeof(ACCESSTIMEOUT))
5213 /* Indicate that no accessibility features timeout is available */
5214 access_timeout->dwFlags = 0;
5215 access_timeout->iTimeOutMSec = 0;
5216 ret = TRUE;
5218 break;
5221 WINE_SPI_FIXME(SPI_SETACCESSTIMEOUT);
5223 case SPI_GETSERIALKEYS:
5225 LPSERIALKEYSW serial_keys = ptr;
5226 WARN("SPI_GETSERIALKEYS not fully implemented\n");
5227 if (serial_keys && serial_keys->cbSize == sizeof(SERIALKEYSW))
5229 /* Indicate that no SerialKeys feature available */
5230 serial_keys->dwFlags = 0;
5231 serial_keys->lpszActivePort = NULL;
5232 serial_keys->lpszPort = NULL;
5233 serial_keys->iBaudRate = 0;
5234 serial_keys->iPortState = 0;
5235 ret = TRUE;
5237 break;
5240 WINE_SPI_FIXME(SPI_SETSERIALKEYS);
5242 case SPI_GETSOUNDSENTRY:
5244 SOUNDSENTRYW *sound_sentry = ptr;
5245 WARN("SPI_GETSOUNDSENTRY not fully implemented\n");
5246 if (sound_sentry && sound_sentry->cbSize == sizeof(SOUNDSENTRYW))
5248 /* Indicate that no SoundSentry feature available */
5249 sound_sentry->dwFlags = 0;
5250 sound_sentry->iFSTextEffect = 0;
5251 sound_sentry->iFSTextEffectMSec = 0;
5252 sound_sentry->iFSTextEffectColorBits = 0;
5253 sound_sentry->iFSGrafEffect = 0;
5254 sound_sentry->iFSGrafEffectMSec = 0;
5255 sound_sentry->iFSGrafEffectColor = 0;
5256 sound_sentry->iWindowsEffect = 0;
5257 sound_sentry->iWindowsEffectMSec = 0;
5258 sound_sentry->lpszWindowsEffectDLL = 0;
5259 sound_sentry->iWindowsEffectOrdinal = 0;
5260 ret = TRUE;
5262 break;
5265 WINE_SPI_FIXME(SPI_SETSOUNDSENTRY);
5267 case SPI_GETHIGHCONTRAST:
5269 HIGHCONTRASTW *high_contrast = ptr;
5270 WARN("SPI_GETHIGHCONTRAST not fully implemented\n");
5271 if (high_contrast && high_contrast->cbSize == sizeof(HIGHCONTRASTW))
5273 /* Indicate that no high contrast feature available */
5274 high_contrast->dwFlags = 0;
5275 high_contrast->lpszDefaultScheme = NULL;
5276 ret = TRUE;
5278 break;
5281 WINE_SPI_FIXME(SPI_SETHIGHCONTRAST);
5283 case SPI_GETKEYBOARDPREF:
5284 ret = get_entry( &entry_KEYBOARDPREF, val, ptr );
5285 break;
5286 case SPI_SETKEYBOARDPREF:
5287 ret = set_entry( &entry_KEYBOARDPREF, val, ptr, winini );
5288 break;
5289 case SPI_GETSCREENREADER:
5290 ret = get_entry( &entry_SCREENREADER, val, ptr );
5291 break;
5292 case SPI_SETSCREENREADER:
5293 ret = set_entry( &entry_SCREENREADER, val, ptr, winini );
5294 break;
5296 case SPI_GETANIMATION:
5298 ANIMATIONINFO *anim_info = ptr;
5300 /* Tell it "disabled" */
5301 if (anim_info && anim_info->cbSize == sizeof(ANIMATIONINFO))
5303 /* Minimize and restore animation is disabled (nonzero == enabled) */
5304 anim_info->iMinAnimate = 0;
5305 ret = TRUE;
5307 break;
5310 WINE_SPI_WARN(SPI_SETANIMATION);
5312 case SPI_GETFONTSMOOTHING:
5313 ret = get_entry( &entry_FONTSMOOTHING, val, ptr );
5314 if (ret) *(UINT *)ptr = (*(UINT *)ptr != 0);
5315 break;
5316 case SPI_SETFONTSMOOTHING:
5317 val = val ? 2 : 0; /* Win NT4/2k/XP behavior */
5318 ret = set_entry( &entry_FONTSMOOTHING, val, ptr, winini );
5319 break;
5320 case SPI_SETDRAGWIDTH:
5321 ret = set_entry( &entry_DRAGWIDTH, val, ptr, winini );
5322 break;
5323 case SPI_SETDRAGHEIGHT:
5324 ret = set_entry( &entry_DRAGHEIGHT, val, ptr, winini );
5325 break;
5327 WINE_SPI_FIXME(SPI_SETHANDHELD);
5328 WINE_SPI_FIXME(SPI_GETLOWPOWERTIMEOUT);
5329 WINE_SPI_FIXME(SPI_GETPOWEROFFTIMEOUT);
5330 WINE_SPI_FIXME(SPI_SETLOWPOWERTIMEOUT);
5331 WINE_SPI_FIXME(SPI_SETPOWEROFFTIMEOUT);
5333 case SPI_GETLOWPOWERACTIVE:
5334 ret = get_entry( &entry_LOWPOWERACTIVE, val, ptr );
5335 break;
5336 case SPI_SETLOWPOWERACTIVE:
5337 ret = set_entry( &entry_LOWPOWERACTIVE, val, ptr, winini );
5338 break;
5339 case SPI_GETPOWEROFFACTIVE:
5340 ret = get_entry( &entry_POWEROFFACTIVE, val, ptr );
5341 break;
5342 case SPI_SETPOWEROFFACTIVE:
5343 ret = set_entry( &entry_POWEROFFACTIVE, val, ptr, winini );
5344 break;
5346 WINE_SPI_FIXME(SPI_SETCURSORS);
5347 WINE_SPI_FIXME(SPI_SETICONS);
5349 case SPI_GETDEFAULTINPUTLANG:
5350 ret = NtUserGetKeyboardLayout(0) != 0;
5351 break;
5353 WINE_SPI_FIXME(SPI_SETDEFAULTINPUTLANG);
5354 WINE_SPI_FIXME(SPI_SETLANGTOGGLE);
5356 case SPI_GETWINDOWSEXTENSION:
5357 WARN( "pretend no support for Win9x Plus! for now.\n" );
5358 ret = FALSE; /* yes, this is the result value */
5359 break;
5360 case SPI_SETMOUSETRAILS:
5361 ret = set_entry( &entry_MOUSETRAILS, val, ptr, winini );
5362 break;
5363 case SPI_GETMOUSETRAILS:
5364 ret = get_entry( &entry_MOUSETRAILS, val, ptr );
5365 break;
5366 case SPI_GETSNAPTODEFBUTTON:
5367 ret = get_entry( &entry_SNAPTODEFBUTTON, val, ptr );
5368 break;
5369 case SPI_SETSNAPTODEFBUTTON:
5370 ret = set_entry( &entry_SNAPTODEFBUTTON, val, ptr, winini );
5371 break;
5372 case SPI_SETSCREENSAVERRUNNING:
5373 ret = set_entry( &entry_SCREENSAVERRUNNING, val, ptr, winini );
5374 break;
5375 case SPI_GETMOUSEHOVERWIDTH:
5376 ret = get_entry( &entry_MOUSEHOVERWIDTH, val, ptr );
5377 break;
5378 case SPI_SETMOUSEHOVERWIDTH:
5379 ret = set_entry( &entry_MOUSEHOVERWIDTH, val, ptr, winini );
5380 break;
5381 case SPI_GETMOUSEHOVERHEIGHT:
5382 ret = get_entry( &entry_MOUSEHOVERHEIGHT, val, ptr );
5383 break;
5384 case SPI_SETMOUSEHOVERHEIGHT:
5385 ret = set_entry( &entry_MOUSEHOVERHEIGHT, val, ptr, winini );
5386 break;
5387 case SPI_GETMOUSEHOVERTIME:
5388 ret = get_entry( &entry_MOUSEHOVERTIME, val, ptr );
5389 break;
5390 case SPI_SETMOUSEHOVERTIME:
5391 ret = set_entry( &entry_MOUSEHOVERTIME, val, ptr, winini );
5392 break;
5393 case SPI_GETWHEELSCROLLLINES:
5394 ret = get_entry( &entry_WHEELSCROLLLINES, val, ptr );
5395 break;
5396 case SPI_SETWHEELSCROLLLINES:
5397 ret = set_entry( &entry_WHEELSCROLLLINES, val, ptr, winini );
5398 break;
5399 case SPI_GETMENUSHOWDELAY:
5400 ret = get_entry( &entry_MENUSHOWDELAY, val, ptr );
5401 break;
5402 case SPI_SETMENUSHOWDELAY:
5403 ret = set_entry( &entry_MENUSHOWDELAY, val, ptr, winini );
5404 break;
5405 case SPI_GETWHEELSCROLLCHARS:
5406 ret = get_entry( &entry_WHEELSCROLLCHARS, val, ptr );
5407 break;
5408 case SPI_SETWHEELSCROLLCHARS:
5409 ret = set_entry( &entry_WHEELSCROLLCHARS, val, ptr, winini );
5410 break;
5412 WINE_SPI_FIXME(SPI_GETSHOWIMEUI);
5413 WINE_SPI_FIXME(SPI_SETSHOWIMEUI);
5415 case SPI_GETMOUSESPEED:
5416 ret = get_entry( &entry_MOUSESPEED, val, ptr );
5417 break;
5418 case SPI_SETMOUSESPEED:
5419 ret = set_entry( &entry_MOUSESPEED, val, ptr, winini );
5420 break;
5421 case SPI_GETSCREENSAVERRUNNING:
5422 ret = get_entry( &entry_SCREENSAVERRUNNING, val, ptr );
5423 break;
5424 case SPI_GETDESKWALLPAPER:
5425 ret = get_entry( &entry_DESKWALLPAPER, val, ptr );
5426 break;
5427 case SPI_GETACTIVEWINDOWTRACKING:
5428 ret = get_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr );
5429 break;
5430 case SPI_SETACTIVEWINDOWTRACKING:
5431 ret = set_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr, winini );
5432 break;
5433 case SPI_GETMENUANIMATION:
5434 ret = get_entry( &entry_MENUANIMATION, val, ptr );
5435 break;
5436 case SPI_SETMENUANIMATION:
5437 ret = set_entry( &entry_MENUANIMATION, val, ptr, winini );
5438 break;
5439 case SPI_GETCOMBOBOXANIMATION:
5440 ret = get_entry( &entry_COMBOBOXANIMATION, val, ptr );
5441 break;
5442 case SPI_SETCOMBOBOXANIMATION:
5443 ret = set_entry( &entry_COMBOBOXANIMATION, val, ptr, winini );
5444 break;
5445 case SPI_GETLISTBOXSMOOTHSCROLLING:
5446 ret = get_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr );
5447 break;
5448 case SPI_SETLISTBOXSMOOTHSCROLLING:
5449 ret = set_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr, winini );
5450 break;
5451 case SPI_GETGRADIENTCAPTIONS:
5452 ret = get_entry( &entry_GRADIENTCAPTIONS, val, ptr );
5453 break;
5454 case SPI_SETGRADIENTCAPTIONS:
5455 ret = set_entry( &entry_GRADIENTCAPTIONS, val, ptr, winini );
5456 break;
5457 case SPI_GETKEYBOARDCUES:
5458 ret = get_entry( &entry_KEYBOARDCUES, val, ptr );
5459 break;
5460 case SPI_SETKEYBOARDCUES:
5461 ret = set_entry( &entry_KEYBOARDCUES, val, ptr, winini );
5462 break;
5463 case SPI_GETACTIVEWNDTRKZORDER:
5464 ret = get_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr );
5465 break;
5466 case SPI_SETACTIVEWNDTRKZORDER:
5467 ret = set_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr, winini );
5468 break;
5469 case SPI_GETHOTTRACKING:
5470 ret = get_entry( &entry_HOTTRACKING, val, ptr );
5471 break;
5472 case SPI_SETHOTTRACKING:
5473 ret = set_entry( &entry_HOTTRACKING, val, ptr, winini );
5474 break;
5475 case SPI_GETMENUFADE:
5476 ret = get_entry( &entry_MENUFADE, val, ptr );
5477 break;
5478 case SPI_SETMENUFADE:
5479 ret = set_entry( &entry_MENUFADE, val, ptr, winini );
5480 break;
5481 case SPI_GETSELECTIONFADE:
5482 ret = get_entry( &entry_SELECTIONFADE, val, ptr );
5483 break;
5484 case SPI_SETSELECTIONFADE:
5485 ret = set_entry( &entry_SELECTIONFADE, val, ptr, winini );
5486 break;
5487 case SPI_GETTOOLTIPANIMATION:
5488 ret = get_entry( &entry_TOOLTIPANIMATION, val, ptr );
5489 break;
5490 case SPI_SETTOOLTIPANIMATION:
5491 ret = set_entry( &entry_TOOLTIPANIMATION, val, ptr, winini );
5492 break;
5493 case SPI_GETTOOLTIPFADE:
5494 ret = get_entry( &entry_TOOLTIPFADE, val, ptr );
5495 break;
5496 case SPI_SETTOOLTIPFADE:
5497 ret = set_entry( &entry_TOOLTIPFADE, val, ptr, winini );
5498 break;
5499 case SPI_GETCURSORSHADOW:
5500 ret = get_entry( &entry_CURSORSHADOW, val, ptr );
5501 break;
5502 case SPI_SETCURSORSHADOW:
5503 ret = set_entry( &entry_CURSORSHADOW, val, ptr, winini );
5504 break;
5505 case SPI_GETMOUSESONAR:
5506 ret = get_entry( &entry_MOUSESONAR, val, ptr );
5507 break;
5508 case SPI_SETMOUSESONAR:
5509 ret = set_entry( &entry_MOUSESONAR, val, ptr, winini );
5510 break;
5511 case SPI_GETMOUSECLICKLOCK:
5512 ret = get_entry( &entry_MOUSECLICKLOCK, val, ptr );
5513 break;
5514 case SPI_SETMOUSECLICKLOCK:
5515 ret = set_entry( &entry_MOUSECLICKLOCK, val, ptr, winini );
5516 break;
5517 case SPI_GETMOUSEVANISH:
5518 ret = get_entry( &entry_MOUSEVANISH, val, ptr );
5519 break;
5520 case SPI_SETMOUSEVANISH:
5521 ret = set_entry( &entry_MOUSEVANISH, val, ptr, winini );
5522 break;
5523 case SPI_GETFLATMENU:
5524 ret = get_entry( &entry_FLATMENU, val, ptr );
5525 break;
5526 case SPI_SETFLATMENU:
5527 ret = set_entry( &entry_FLATMENU, val, ptr, winini );
5528 break;
5529 case SPI_GETDROPSHADOW:
5530 ret = get_entry( &entry_DROPSHADOW, val, ptr );
5531 break;
5532 case SPI_SETDROPSHADOW:
5533 ret = set_entry( &entry_DROPSHADOW, val, ptr, winini );
5534 break;
5535 case SPI_GETBLOCKSENDINPUTRESETS:
5536 ret = get_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr );
5537 break;
5538 case SPI_SETBLOCKSENDINPUTRESETS:
5539 ret = set_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr, winini );
5540 break;
5541 case SPI_GETUIEFFECTS:
5542 ret = get_entry( &entry_UIEFFECTS, val, ptr );
5543 break;
5544 case SPI_SETUIEFFECTS:
5545 /* FIXME: this probably should mask other UI effect values when unset */
5546 ret = set_entry( &entry_UIEFFECTS, val, ptr, winini );
5547 break;
5548 case SPI_GETDISABLEOVERLAPPEDCONTENT:
5549 ret = get_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr );
5550 break;
5551 case SPI_SETDISABLEOVERLAPPEDCONTENT:
5552 ret = set_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr, winini );
5553 break;
5554 case SPI_GETCLIENTAREAANIMATION:
5555 ret = get_entry( &entry_CLIENTAREAANIMATION, val, ptr );
5556 break;
5557 case SPI_SETCLIENTAREAANIMATION:
5558 ret = set_entry( &entry_CLIENTAREAANIMATION, val, ptr, winini );
5559 break;
5560 case SPI_GETCLEARTYPE:
5561 ret = get_entry( &entry_CLEARTYPE, val, ptr );
5562 break;
5563 case SPI_SETCLEARTYPE:
5564 ret = set_entry( &entry_CLEARTYPE, val, ptr, winini );
5565 break;
5566 case SPI_GETSPEECHRECOGNITION:
5567 ret = get_entry( &entry_SPEECHRECOGNITION, val, ptr );
5568 break;
5569 case SPI_SETSPEECHRECOGNITION:
5570 ret = set_entry( &entry_SPEECHRECOGNITION, val, ptr, winini );
5571 break;
5572 case SPI_GETFOREGROUNDLOCKTIMEOUT:
5573 ret = get_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr );
5574 break;
5575 case SPI_SETFOREGROUNDLOCKTIMEOUT:
5576 /* FIXME: this should check that the calling thread
5577 * is able to change the foreground window */
5578 ret = set_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr, winini );
5579 break;
5580 case SPI_GETACTIVEWNDTRKTIMEOUT:
5581 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
5582 break;
5583 case SPI_SETACTIVEWNDTRKTIMEOUT:
5584 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
5585 break;
5586 case SPI_GETFOREGROUNDFLASHCOUNT:
5587 ret = get_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr );
5588 break;
5589 case SPI_SETFOREGROUNDFLASHCOUNT:
5590 ret = set_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr, winini );
5591 break;
5592 case SPI_GETCARETWIDTH:
5593 ret = get_entry( &entry_CARETWIDTH, val, ptr );
5594 break;
5595 case SPI_SETCARETWIDTH:
5596 ret = set_entry( &entry_CARETWIDTH, val, ptr, winini );
5597 break;
5598 case SPI_GETMOUSECLICKLOCKTIME:
5599 ret = get_entry( &entry_MOUSECLICKLOCKTIME, val, ptr );
5600 break;
5601 case SPI_SETMOUSECLICKLOCKTIME:
5602 ret = set_entry( &entry_MOUSECLICKLOCKTIME, val, ptr, winini );
5603 break;
5604 case SPI_GETFONTSMOOTHINGTYPE:
5605 ret = get_entry( &entry_FONTSMOOTHINGTYPE, val, ptr );
5606 break;
5607 case SPI_SETFONTSMOOTHINGTYPE:
5608 ret = set_entry( &entry_FONTSMOOTHINGTYPE, val, ptr, winini );
5609 break;
5610 case SPI_GETFONTSMOOTHINGCONTRAST:
5611 ret = get_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr );
5612 break;
5613 case SPI_SETFONTSMOOTHINGCONTRAST:
5614 ret = set_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr, winini );
5615 break;
5616 case SPI_GETFOCUSBORDERWIDTH:
5617 ret = get_entry( &entry_FOCUSBORDERWIDTH, val, ptr );
5618 break;
5619 case SPI_GETFOCUSBORDERHEIGHT:
5620 ret = get_entry( &entry_FOCUSBORDERHEIGHT, val, ptr );
5621 break;
5622 case SPI_SETFOCUSBORDERWIDTH:
5623 ret = set_entry( &entry_FOCUSBORDERWIDTH, val, ptr, winini );
5624 break;
5625 case SPI_SETFOCUSBORDERHEIGHT:
5626 ret = set_entry( &entry_FOCUSBORDERHEIGHT, val, ptr, winini );
5627 break;
5628 case SPI_GETFONTSMOOTHINGORIENTATION:
5629 ret = get_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr );
5630 break;
5631 case SPI_SETFONTSMOOTHINGORIENTATION:
5632 ret = set_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr, winini );
5633 break;
5634 case SPI_GETAUDIODESCRIPTION:
5636 AUDIODESCRIPTION *audio = ptr;
5637 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
5639 ret = get_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled ) &&
5640 get_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale );
5642 break;
5644 case SPI_SETAUDIODESCRIPTION:
5646 AUDIODESCRIPTION *audio = ptr;
5647 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
5649 ret = set_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled, winini) &&
5650 set_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale, winini );
5652 break;
5654 default:
5655 FIXME( "Unknown action: %u\n", action );
5656 RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE );
5657 ret = FALSE;
5658 break;
5661 if (ret && (winini & SPIF_UPDATEINIFILE))
5663 static const WCHAR emptyW[1];
5664 if (winini & (SPIF_SENDWININICHANGE | SPIF_SENDCHANGE))
5665 send_message_timeout( HWND_BROADCAST, WM_SETTINGCHANGE, action, (LPARAM) emptyW,
5666 SMTO_ABORTIFHUNG, 2000, FALSE );
5668 TRACE( "(%u, %u, %p, %u) ret %d\n", action, val, ptr, winini, ret );
5669 return ret;
5671 #undef WINE_SPI_FIXME
5672 #undef WINE_SPI_WARN
5675 int get_system_metrics( int index )
5677 NONCLIENTMETRICSW ncm;
5678 MINIMIZEDMETRICS mm;
5679 ICONMETRICSW im;
5680 RECT rect;
5681 UINT ret;
5682 HDC hdc;
5684 /* some metrics are dynamic */
5685 switch (index)
5687 case SM_CXVSCROLL:
5688 case SM_CYHSCROLL:
5689 get_entry( &entry_SCROLLWIDTH, 0, &ret );
5690 return max( ret, 8 );
5691 case SM_CYCAPTION:
5692 ncm.cbSize = sizeof(ncm);
5693 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5694 return ncm.iCaptionHeight + 1;
5695 case SM_CXBORDER:
5696 case SM_CYBORDER:
5697 /* SM_C{X,Y}BORDER always returns 1 regardless of 'BorderWidth' value in registry */
5698 return 1;
5699 case SM_CXDLGFRAME:
5700 case SM_CYDLGFRAME:
5701 return 3;
5702 case SM_CYVTHUMB:
5703 case SM_CXHTHUMB:
5704 case SM_CYVSCROLL:
5705 case SM_CXHSCROLL:
5706 get_entry( &entry_SCROLLHEIGHT, 0, &ret );
5707 return max( ret, 8 );
5708 case SM_CXICON:
5709 case SM_CYICON:
5710 return map_to_dpi( 32, get_system_dpi() );
5711 case SM_CXCURSOR:
5712 case SM_CYCURSOR:
5713 ret = map_to_dpi( 32, get_system_dpi() );
5714 if (ret >= 64) return 64;
5715 if (ret >= 48) return 48;
5716 return 32;
5717 case SM_CYMENU:
5718 ncm.cbSize = sizeof(ncm);
5719 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5720 return ncm.iMenuHeight + 1;
5721 case SM_CXFULLSCREEN:
5722 /* see the remark for SM_CXMAXIMIZED, at least this formulation is correct */
5723 return get_system_metrics( SM_CXMAXIMIZED ) - 2 * get_system_metrics( SM_CXFRAME );
5724 case SM_CYFULLSCREEN:
5725 /* see the remark for SM_CYMAXIMIZED, at least this formulation is
5726 * correct */
5727 return get_system_metrics( SM_CYMAXIMIZED ) - get_system_metrics( SM_CYMIN );
5728 case SM_CYKANJIWINDOW:
5729 return 0;
5730 case SM_MOUSEPRESENT:
5731 return 1;
5732 case SM_DEBUG:
5733 return 0;
5734 case SM_SWAPBUTTON:
5735 get_entry( &entry_MOUSEBUTTONSWAP, 0, &ret );
5736 return ret;
5737 case SM_RESERVED1:
5738 case SM_RESERVED2:
5739 case SM_RESERVED3:
5740 case SM_RESERVED4:
5741 return 0;
5742 case SM_CXMIN:
5743 ncm.cbSize = sizeof(ncm);
5744 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5745 hdc = get_display_dc();
5746 get_text_metr_size( hdc, &ncm.lfCaptionFont, NULL, &ret );
5747 release_display_dc( hdc );
5748 return 3 * ncm.iCaptionWidth + ncm.iCaptionHeight + 4 * ret +
5749 2 * get_system_metrics( SM_CXFRAME ) + 4;
5750 case SM_CYMIN:
5751 return get_system_metrics( SM_CYCAPTION ) + 2 * get_system_metrics( SM_CYFRAME );
5752 case SM_CXSIZE:
5753 get_entry( &entry_CAPTIONWIDTH, 0, &ret );
5754 return max( ret, 8 );
5755 case SM_CYSIZE:
5756 ncm.cbSize = sizeof(ncm);
5757 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5758 return ncm.iCaptionHeight;
5759 case SM_CXFRAME:
5760 get_entry( &entry_BORDER, 0, &ret );
5761 ret = max( ret, 1 );
5762 return get_system_metrics( SM_CXDLGFRAME ) + ret;
5763 case SM_CYFRAME:
5764 get_entry( &entry_BORDER, 0, &ret );
5765 ret = max( ret, 1 );
5766 return get_system_metrics( SM_CYDLGFRAME ) + ret;
5767 case SM_CXMINTRACK:
5768 return get_system_metrics( SM_CXMIN );
5769 case SM_CYMINTRACK:
5770 return get_system_metrics( SM_CYMIN );
5771 case SM_CXDOUBLECLK:
5772 get_entry( &entry_DOUBLECLKWIDTH, 0, &ret );
5773 return ret;
5774 case SM_CYDOUBLECLK:
5775 get_entry( &entry_DOUBLECLKHEIGHT, 0, &ret );
5776 return ret;
5777 case SM_CXICONSPACING:
5778 im.cbSize = sizeof(im);
5779 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
5780 return im.iHorzSpacing;
5781 case SM_CYICONSPACING:
5782 im.cbSize = sizeof(im);
5783 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
5784 return im.iVertSpacing;
5785 case SM_MENUDROPALIGNMENT:
5786 NtUserSystemParametersInfo( SPI_GETMENUDROPALIGNMENT, 0, &ret, 0 );
5787 return ret;
5788 case SM_PENWINDOWS:
5789 return 0;
5790 case SM_DBCSENABLED:
5791 return ansi_cp.MaximumCharacterSize > 1;
5792 case SM_CMOUSEBUTTONS:
5793 return 3;
5794 case SM_SECURE:
5795 return 0;
5796 case SM_CXEDGE:
5797 return get_system_metrics( SM_CXBORDER ) + 1;
5798 case SM_CYEDGE:
5799 return get_system_metrics( SM_CYBORDER ) + 1;
5800 case SM_CXMINSPACING:
5801 mm.cbSize = sizeof(mm);
5802 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5803 return get_system_metrics( SM_CXMINIMIZED ) + mm.iHorzGap;
5804 case SM_CYMINSPACING:
5805 mm.cbSize = sizeof(mm);
5806 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5807 return get_system_metrics( SM_CYMINIMIZED ) + mm.iVertGap;
5808 case SM_CXSMICON:
5809 case SM_CYSMICON:
5810 return map_to_dpi( 16, get_system_dpi() ) & ~1;
5811 case SM_CYSMCAPTION:
5812 ncm.cbSize = sizeof(ncm);
5813 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5814 return ncm.iSmCaptionHeight + 1;
5815 case SM_CXSMSIZE:
5816 get_entry( &entry_SMCAPTIONWIDTH, 0, &ret );
5817 return ret;
5818 case SM_CYSMSIZE:
5819 ncm.cbSize = sizeof(ncm);
5820 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5821 return ncm.iSmCaptionHeight;
5822 case SM_CXMENUSIZE:
5823 get_entry( &entry_MENUWIDTH, 0, &ret );
5824 return ret;
5825 case SM_CYMENUSIZE:
5826 ncm.cbSize = sizeof(ncm);
5827 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5828 return ncm.iMenuHeight;
5829 case SM_ARRANGE:
5830 mm.cbSize = sizeof(mm);
5831 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5832 return mm.iArrange;
5833 case SM_CXMINIMIZED:
5834 mm.cbSize = sizeof(mm);
5835 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5836 return mm.iWidth + 6;
5837 case SM_CYMINIMIZED:
5838 ncm.cbSize = sizeof(ncm);
5839 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5840 return ncm.iCaptionHeight + 6;
5841 case SM_CXMAXTRACK:
5842 return get_system_metrics( SM_CXVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CXFRAME );
5843 case SM_CYMAXTRACK:
5844 return get_system_metrics( SM_CYVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CYFRAME );
5845 case SM_CXMAXIMIZED:
5846 /* FIXME: subtract the width of any vertical application toolbars*/
5847 return get_system_metrics( SM_CXSCREEN ) + 2 * get_system_metrics( SM_CXFRAME );
5848 case SM_CYMAXIMIZED:
5849 /* FIXME: subtract the width of any horizontal application toolbars*/
5850 return get_system_metrics( SM_CYSCREEN ) + 2 * get_system_metrics( SM_CYCAPTION );
5851 case SM_NETWORK:
5852 return 3; /* FIXME */
5853 case SM_CLEANBOOT:
5854 return 0; /* 0 = ok, 1 = failsafe, 2 = failsafe + network */
5855 case SM_CXDRAG:
5856 get_entry( &entry_DRAGWIDTH, 0, &ret );
5857 return ret;
5858 case SM_CYDRAG:
5859 get_entry( &entry_DRAGHEIGHT, 0, &ret );
5860 return ret;
5861 case SM_SHOWSOUNDS:
5862 get_entry( &entry_SHOWSOUNDS, 0, &ret );
5863 return ret;
5864 case SM_CXMENUCHECK:
5865 case SM_CYMENUCHECK:
5867 TEXTMETRICW tm;
5868 ncm.cbSize = sizeof(ncm);
5869 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5870 hdc = get_display_dc();
5871 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL );
5872 release_display_dc( hdc );
5873 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading + 1) / 2) * 2 - 1;
5875 case SM_SLOWMACHINE:
5876 return 0; /* Never true */
5877 case SM_MIDEASTENABLED:
5878 return 0; /* FIXME */
5879 case SM_MOUSEWHEELPRESENT:
5880 return 1;
5881 case SM_CXSCREEN:
5882 rect = get_primary_monitor_rect( get_thread_dpi() );
5883 return rect.right - rect.left;
5884 case SM_CYSCREEN:
5885 rect = get_primary_monitor_rect( get_thread_dpi() );
5886 return rect.bottom - rect.top;
5887 case SM_XVIRTUALSCREEN:
5888 rect = get_virtual_screen_rect( get_thread_dpi() );
5889 return rect.left;
5890 case SM_YVIRTUALSCREEN:
5891 rect = get_virtual_screen_rect( get_thread_dpi() );
5892 return rect.top;
5893 case SM_CXVIRTUALSCREEN:
5894 rect = get_virtual_screen_rect( get_thread_dpi() );
5895 return rect.right - rect.left;
5896 case SM_CYVIRTUALSCREEN:
5897 rect = get_virtual_screen_rect( get_thread_dpi() );
5898 return rect.bottom - rect.top;
5899 case SM_CMONITORS:
5900 if (!lock_display_devices()) return FALSE;
5901 ret = active_monitor_count();
5902 unlock_display_devices();
5903 return ret;
5904 case SM_SAMEDISPLAYFORMAT:
5905 return 1;
5906 case SM_IMMENABLED:
5907 return 0; /* FIXME */
5908 case SM_CXFOCUSBORDER:
5909 case SM_CYFOCUSBORDER:
5910 return 1;
5911 case SM_TABLETPC:
5912 case SM_MEDIACENTER:
5913 return 0;
5914 case SM_CMETRICS:
5915 return SM_CMETRICS;
5916 default:
5917 return 0;
5921 static int get_system_metrics_for_dpi( int index, unsigned int dpi )
5923 NONCLIENTMETRICSW ncm;
5924 ICONMETRICSW im;
5925 UINT ret;
5926 HDC hdc;
5928 /* some metrics are dynamic */
5929 switch (index)
5931 case SM_CXVSCROLL:
5932 case SM_CYHSCROLL:
5933 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ret, dpi );
5934 return max( ret, 8 );
5935 case SM_CYCAPTION:
5936 ncm.cbSize = sizeof(ncm);
5937 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5938 return ncm.iCaptionHeight + 1;
5939 case SM_CYVTHUMB:
5940 case SM_CXHTHUMB:
5941 case SM_CYVSCROLL:
5942 case SM_CXHSCROLL:
5943 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ret, dpi );
5944 return max( ret, 8 );
5945 case SM_CXICON:
5946 case SM_CYICON:
5947 return map_to_dpi( 32, dpi );
5948 case SM_CXCURSOR:
5949 case SM_CYCURSOR:
5950 ret = map_to_dpi( 32, dpi );
5951 if (ret >= 64) return 64;
5952 if (ret >= 48) return 48;
5953 return 32;
5954 case SM_CYMENU:
5955 ncm.cbSize = sizeof(ncm);
5956 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5957 return ncm.iMenuHeight + 1;
5958 case SM_CXSIZE:
5959 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ret, dpi );
5960 return max( ret, 8 );
5961 case SM_CYSIZE:
5962 ncm.cbSize = sizeof(ncm);
5963 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5964 return ncm.iCaptionHeight;
5965 case SM_CXFRAME:
5966 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
5967 ret = max( ret, 1 );
5968 return get_system_metrics_for_dpi( SM_CXDLGFRAME, dpi ) + ret;
5969 case SM_CYFRAME:
5970 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
5971 ret = max( ret, 1 );
5972 return get_system_metrics_for_dpi( SM_CYDLGFRAME, dpi ) + ret;
5973 case SM_CXICONSPACING:
5974 im.cbSize = sizeof(im);
5975 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
5976 return im.iHorzSpacing;
5977 case SM_CYICONSPACING:
5978 im.cbSize = sizeof(im);
5979 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
5980 return im.iVertSpacing;
5981 case SM_CXSMICON:
5982 case SM_CYSMICON:
5983 return map_to_dpi( 16, dpi ) & ~1;
5984 case SM_CYSMCAPTION:
5985 ncm.cbSize = sizeof(ncm);
5986 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5987 return ncm.iSmCaptionHeight + 1;
5988 case SM_CXSMSIZE:
5989 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ret, dpi );
5990 return ret;
5991 case SM_CYSMSIZE:
5992 ncm.cbSize = sizeof(ncm);
5993 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5994 return ncm.iSmCaptionHeight;
5995 case SM_CXMENUSIZE:
5996 get_entry_dpi( &entry_MENUWIDTH, 0, &ret, dpi );
5997 return ret;
5998 case SM_CYMENUSIZE:
5999 ncm.cbSize = sizeof(ncm);
6000 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
6001 return ncm.iMenuHeight;
6002 case SM_CXMENUCHECK:
6003 case SM_CYMENUCHECK:
6005 TEXTMETRICW tm;
6006 ncm.cbSize = sizeof(ncm);
6007 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
6008 hdc = get_display_dc();
6009 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL);
6010 release_display_dc( hdc );
6011 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading - 1) | 1);
6013 default:
6014 return get_system_metrics( index );
6018 COLORREF get_sys_color( int index )
6020 COLORREF ret = 0;
6022 if (index >= 0 && index < ARRAY_SIZE( system_colors ))
6023 get_entry( &system_colors[index], 0, &ret );
6024 return ret;
6027 HBRUSH get_55aa_brush(void)
6029 static const WORD pattern[] = { 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa };
6030 static HBRUSH brush_55aa;
6032 if (!brush_55aa)
6034 HBITMAP bitmap = NtGdiCreateBitmap( 8, 8, 1, 1, pattern );
6035 HBRUSH brush = NtGdiCreatePatternBrushInternal( bitmap, FALSE, FALSE );
6036 NtGdiDeleteObjectApp( bitmap );
6037 make_gdi_object_system( brush, TRUE );
6038 if (InterlockedCompareExchangePointer( (void **)&brush_55aa, brush, 0 ))
6040 make_gdi_object_system( brush, FALSE );
6041 NtGdiDeleteObjectApp( brush );
6044 return brush_55aa;
6047 HBRUSH get_sys_color_brush( unsigned int index )
6049 if (index == COLOR_55AA_BRUSH) return get_55aa_brush();
6050 if (index >= ARRAY_SIZE( system_colors )) return 0;
6052 if (!system_colors[index].brush)
6054 HBRUSH brush = NtGdiCreateSolidBrush( get_sys_color( index ), NULL );
6055 make_gdi_object_system( brush, TRUE );
6056 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].brush, brush, 0 ))
6058 make_gdi_object_system( brush, FALSE );
6059 NtGdiDeleteObjectApp( brush );
6062 return system_colors[index].brush;
6065 HPEN get_sys_color_pen( unsigned int index )
6067 if (index >= ARRAY_SIZE( system_colors )) return 0;
6069 if (!system_colors[index].pen)
6071 HPEN pen = NtGdiCreatePen( PS_SOLID, 1, get_sys_color( index ), NULL );
6072 make_gdi_object_system( pen, TRUE );
6073 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].pen, pen, 0 ))
6075 make_gdi_object_system( pen, FALSE );
6076 NtGdiDeleteObjectApp( pen );
6079 return system_colors[index].pen;
6082 /**********************************************************************
6083 * NtUserGetDoubleClickTime (win32u.@)
6085 UINT WINAPI NtUserGetDoubleClickTime(void)
6087 UINT time = 0;
6089 get_entry( &entry_DOUBLECLICKTIME, 0, &time );
6090 if (!time) time = 500;
6091 return time;
6094 /*************************************************************************
6095 * NtUserSetSysColors (win32u.@)
6097 BOOL WINAPI NtUserSetSysColors( INT count, const INT *colors, const COLORREF *values )
6099 int i;
6101 if (IS_INTRESOURCE(colors)) return FALSE; /* stupid app passes a color instead of an array */
6103 for (i = 0; i < count; i++)
6104 if (colors[i] >= 0 && colors[i] <= ARRAY_SIZE( system_colors ))
6105 set_entry( &system_colors[colors[i]], values[i], 0, 0 );
6107 /* Send WM_SYSCOLORCHANGE message to all windows */
6108 send_message_timeout( HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0,
6109 SMTO_ABORTIFHUNG, 2000, FALSE );
6110 /* Repaint affected portions of all visible windows */
6111 NtUserRedrawWindow( 0, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN );
6112 return TRUE;
6116 static LONG dpi_awareness;
6118 /***********************************************************************
6119 * NtUserSetProcessDpiAwarenessContext (win32u.@)
6121 BOOL WINAPI NtUserSetProcessDpiAwarenessContext( ULONG awareness, ULONG unknown )
6123 switch (awareness)
6125 case NTUSER_DPI_UNAWARE:
6126 case NTUSER_DPI_SYSTEM_AWARE:
6127 case NTUSER_DPI_PER_MONITOR_AWARE:
6128 case NTUSER_DPI_PER_MONITOR_AWARE_V2:
6129 case NTUSER_DPI_PER_UNAWARE_GDISCALED:
6130 break;
6131 default:
6132 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
6133 return FALSE;
6136 return !InterlockedCompareExchange( &dpi_awareness, awareness, 0 );
6139 /***********************************************************************
6140 * NtUserGetProcessDpiAwarenessContext (win32u.@)
6142 ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process )
6144 DPI_AWARENESS val;
6146 if (process && process != GetCurrentProcess())
6148 WARN( "not supported on other process %p\n", process );
6149 return NTUSER_DPI_UNAWARE;
6152 val = ReadNoFence( &dpi_awareness );
6153 if (!val) return NTUSER_DPI_UNAWARE;
6154 return val;
6157 BOOL message_beep( UINT i )
6159 BOOL active = TRUE;
6160 NtUserSystemParametersInfo( SPI_GETBEEP, 0, &active, FALSE );
6161 if (active) user_driver->pBeep();
6162 return TRUE;
6165 static DWORD exiting_thread_id;
6167 /**********************************************************************
6168 * is_exiting_thread
6170 BOOL is_exiting_thread( DWORD tid )
6172 return tid == exiting_thread_id;
6175 static void thread_detach(void)
6177 struct user_thread_info *thread_info = get_user_thread_info();
6179 user_driver->pThreadDetach();
6181 free( thread_info->key_state );
6182 thread_info->key_state = 0;
6183 free( thread_info->rawinput );
6185 destroy_thread_windows();
6186 cleanup_imm_thread();
6187 NtClose( thread_info->server_queue );
6189 exiting_thread_id = 0;
6192 /***********************************************************************
6193 * NtUserCallNoParam (win32u.@)
6195 ULONG_PTR WINAPI NtUserCallNoParam( ULONG code )
6197 switch(code)
6199 case NtUserCallNoParam_DestroyCaret:
6200 return destroy_caret();
6202 case NtUserCallNoParam_GetDesktopWindow:
6203 return HandleToUlong( get_desktop_window() );
6205 case NtUserCallNoParam_GetDialogBaseUnits:
6206 return get_dialog_base_units();
6208 case NtUserCallNoParam_GetInputState:
6209 return get_input_state();
6211 case NtUserCallNoParam_GetProcessDefaultLayout:
6212 return process_layout;
6214 case NtUserCallNoParam_GetProgmanWindow:
6215 return HandleToUlong( get_progman_window() );
6217 case NtUserCallNoParam_GetShellWindow:
6218 return HandleToUlong( get_shell_window() );
6220 case NtUserCallNoParam_GetTaskmanWindow:
6221 return HandleToUlong( get_taskman_window() );
6223 case NtUserCallNoParam_ReleaseCapture:
6224 return release_capture();
6226 /* temporary exports */
6227 case NtUserExitingThread:
6228 exiting_thread_id = GetCurrentThreadId();
6229 return 0;
6231 case NtUserThreadDetach:
6232 thread_detach();
6233 return 0;
6235 default:
6236 FIXME( "invalid code %u\n", (int)code );
6237 return 0;
6241 /***********************************************************************
6242 * NtUserCallOneParam (win32u.@)
6244 ULONG_PTR WINAPI NtUserCallOneParam( ULONG_PTR arg, ULONG code )
6246 switch(code)
6248 case NtUserCallOneParam_BeginDeferWindowPos:
6249 return HandleToUlong( begin_defer_window_pos( arg ));
6251 case NtUserCallOneParam_CreateCursorIcon:
6252 return HandleToUlong( alloc_cursoricon_handle( arg ));
6254 case NtUserCallOneParam_CreateMenu:
6255 return HandleToUlong( create_menu( arg ) );
6257 case NtUserCallOneParam_EnableDC:
6258 return set_dce_flags( UlongToHandle(arg), DCHF_ENABLEDC );
6260 case NtUserCallOneParam_EnableThunkLock:
6261 enable_thunk_lock = arg;
6262 return 0;
6264 case NtUserCallOneParam_EnumClipboardFormats:
6265 return enum_clipboard_formats( arg );
6267 case NtUserCallOneParam_GetClipCursor:
6268 return get_clip_cursor( (RECT *)arg );
6270 case NtUserCallOneParam_GetCursorPos:
6271 return get_cursor_pos( (POINT *)arg );
6273 case NtUserCallOneParam_GetIconParam:
6274 return get_icon_param( UlongToHandle(arg) );
6276 case NtUserCallOneParam_GetMenuItemCount:
6277 return get_menu_item_count( UlongToHandle(arg) );
6279 case NtUserCallOneParam_GetSysColor:
6280 return get_sys_color( arg );
6282 case NtUserCallOneParam_IsWindowRectFullScreen:
6283 return is_window_rect_full_screen( (const RECT *)arg );
6285 case NtUserCallOneParam_RealizePalette:
6286 return realize_palette( UlongToHandle(arg) );
6288 case NtUserCallOneParam_GetPrimaryMonitorRect:
6289 *(RECT *)arg = get_primary_monitor_rect( 0 );
6290 return 1;
6292 case NtUserCallOneParam_GetSysColorBrush:
6293 return HandleToUlong( get_sys_color_brush(arg) );
6295 case NtUserCallOneParam_GetSysColorPen:
6296 return HandleToUlong( get_sys_color_pen(arg) );
6298 case NtUserCallOneParam_GetSystemMetrics:
6299 return get_system_metrics( arg );
6301 case NtUserCallOneParam_GetVirtualScreenRect:
6302 *(RECT *)arg = get_virtual_screen_rect( 0 );
6303 return 1;
6305 case NtUserCallOneParam_MessageBeep:
6306 return message_beep( arg );
6308 case NtUserCallOneParam_ReplyMessage:
6309 return reply_message_result( arg );
6311 case NtUserCallOneParam_SetCaretBlinkTime:
6312 return set_caret_blink_time( arg );
6314 case NtUserCallOneParam_SetProcessDefaultLayout:
6315 process_layout = arg;
6316 return TRUE;
6318 /* temporary exports */
6319 case NtUserGetDeskPattern:
6320 return get_entry( &entry_DESKPATTERN, 256, (WCHAR *)arg );
6322 default:
6323 FIXME( "invalid code %u\n", (int)code );
6324 return 0;
6328 /***********************************************************************
6329 * NtUserCallTwoParam (win32u.@)
6331 ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code )
6333 switch(code)
6335 case NtUserCallTwoParam_GetDialogProc:
6336 return (ULONG_PTR)get_dialog_proc( (DLGPROC)arg1, arg2 );
6338 case NtUserCallTwoParam_GetMenuInfo:
6339 return get_menu_info( UlongToHandle(arg1), (MENUINFO *)arg2 );
6341 case NtUserCallTwoParam_GetMonitorInfo:
6342 return get_monitor_info( UlongToHandle(arg1), (MONITORINFO *)arg2 );
6344 case NtUserCallTwoParam_GetSystemMetricsForDpi:
6345 return get_system_metrics_for_dpi( arg1, arg2 );
6347 case NtUserCallTwoParam_MonitorFromRect:
6348 return HandleToUlong( monitor_from_rect( (const RECT *)arg1, arg2, get_thread_dpi() ));
6350 case NtUserCallTwoParam_SetCaretPos:
6351 return set_caret_pos( arg1, arg2 );
6353 case NtUserCallTwoParam_SetIconParam:
6354 return set_icon_param( UlongToHandle(arg1), arg2 );
6356 case NtUserCallTwoParam_UnhookWindowsHook:
6357 return unhook_windows_hook( arg1, (HOOKPROC)arg2 );
6359 /* temporary exports */
6360 case NtUserAllocWinProc:
6361 return (UINT_PTR)alloc_winproc( (WNDPROC)arg1, arg2 );
6363 default:
6364 FIXME( "invalid code %u\n", (int)code );
6365 return 0;
6369 /***********************************************************************
6370 * NtUserDisplayConfigGetDeviceInfo (win32u.@)
6372 NTSTATUS WINAPI NtUserDisplayConfigGetDeviceInfo( DISPLAYCONFIG_DEVICE_INFO_HEADER *packet )
6374 NTSTATUS ret = STATUS_UNSUCCESSFUL;
6376 TRACE( "packet %p.\n", packet );
6378 if (!packet || packet->size < sizeof(*packet))
6379 return STATUS_UNSUCCESSFUL;
6381 switch (packet->type)
6383 case DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME:
6385 DISPLAYCONFIG_SOURCE_DEVICE_NAME *source_name = (DISPLAYCONFIG_SOURCE_DEVICE_NAME *)packet;
6386 struct adapter *adapter;
6388 TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME.\n" );
6390 if (packet->size < sizeof(*source_name))
6391 return STATUS_INVALID_PARAMETER;
6393 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
6395 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
6397 if (source_name->header.id != adapter->id) continue;
6398 if (memcmp( &source_name->header.adapterId, &adapter->gpu_luid, sizeof(adapter->gpu_luid) )) continue;
6400 lstrcpyW( source_name->viewGdiDeviceName, adapter->dev.device_name );
6401 ret = STATUS_SUCCESS;
6402 break;
6405 unlock_display_devices();
6406 return ret;
6408 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME:
6410 DISPLAYCONFIG_TARGET_DEVICE_NAME *target_name = (DISPLAYCONFIG_TARGET_DEVICE_NAME *)packet;
6411 char buffer[ARRAY_SIZE(target_name->monitorFriendlyDeviceName)];
6412 struct monitor *monitor;
6414 TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME.\n" );
6416 if (packet->size < sizeof(*target_name))
6417 return STATUS_INVALID_PARAMETER;
6419 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
6421 memset( &target_name->flags, 0, sizeof(*target_name) - offsetof(DISPLAYCONFIG_TARGET_DEVICE_NAME, flags) );
6423 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
6425 if (target_name->header.id != monitor->output_id) continue;
6426 if (memcmp( &target_name->header.adapterId, &monitor->adapter->gpu_luid,
6427 sizeof(monitor->adapter->gpu_luid) ))
6428 continue;
6430 target_name->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL;
6431 snprintf( buffer, ARRAY_SIZE(buffer), "Display%u", monitor->output_id + 1 );
6432 asciiz_to_unicode( target_name->monitorFriendlyDeviceName, buffer );
6433 lstrcpyW( target_name->monitorDevicePath, monitor->dev.interface_name );
6434 if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
6436 target_name->edidManufactureId = monitor->edid_info.manufacturer;
6437 target_name->edidProductCodeId = monitor->edid_info.product_code;
6438 target_name->flags.edidIdsValid = 1;
6440 if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_NAME)
6442 wcscpy( target_name->monitorFriendlyDeviceName, monitor->edid_info.monitor_name );
6443 target_name->flags.friendlyNameFromEdid = 1;
6445 ret = STATUS_SUCCESS;
6446 break;
6449 unlock_display_devices();
6450 return ret;
6452 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE:
6454 DISPLAYCONFIG_TARGET_PREFERRED_MODE *preferred_mode = (DISPLAYCONFIG_TARGET_PREFERRED_MODE *)packet;
6455 DISPLAYCONFIG_VIDEO_SIGNAL_INFO *signal_info = &preferred_mode->targetMode.targetVideoSignalInfo;
6456 unsigned int i, display_freq;
6457 DEVMODEW *found_mode = NULL;
6458 BOOL have_edid_mode = FALSE;
6459 struct monitor *monitor;
6461 FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE semi-stub.\n" );
6463 if (packet->size < sizeof(*preferred_mode))
6464 return STATUS_INVALID_PARAMETER;
6466 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
6468 memset( &preferred_mode->width, 0, sizeof(*preferred_mode) - offsetof(DISPLAYCONFIG_TARGET_PREFERRED_MODE, width) );
6470 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
6472 if (preferred_mode->header.id != monitor->output_id) continue;
6473 if (memcmp( &preferred_mode->header.adapterId, &monitor->adapter->gpu_luid,
6474 sizeof(monitor->adapter->gpu_luid) ))
6475 continue;
6477 for (i = 0; i < monitor->adapter->mode_count; ++i)
6479 DEVMODEW *mode = &monitor->adapter->modes[i];
6481 if (!have_edid_mode && monitor->edid_info.flags & MONITOR_INFO_HAS_PREFERRED_MODE
6482 && mode->dmPelsWidth == monitor->edid_info.preferred_width
6483 && mode->dmPelsHeight == monitor->edid_info.preferred_height)
6485 found_mode = mode;
6486 have_edid_mode = TRUE;
6489 if (!have_edid_mode && (!found_mode
6490 || (mode->dmPelsWidth > found_mode->dmPelsWidth && mode->dmPelsHeight >= found_mode->dmPelsHeight)
6491 || (mode->dmPelsHeight > found_mode->dmPelsHeight && mode->dmPelsWidth >= found_mode->dmPelsWidth)))
6492 found_mode = mode;
6494 if (mode->dmPelsWidth == found_mode->dmPelsWidth
6495 && mode->dmPelsHeight == found_mode->dmPelsHeight
6496 && mode->dmDisplayFrequency > found_mode->dmDisplayFrequency)
6497 found_mode = mode;
6500 if (!found_mode)
6502 ERR( "No mode found.\n" );
6503 break;
6505 preferred_mode->width = found_mode->dmPelsWidth;
6506 preferred_mode->height = found_mode->dmPelsHeight;
6507 display_freq = found_mode->dmDisplayFrequency;
6509 signal_info->pixelRate = display_freq * preferred_mode->width * preferred_mode->height;
6510 signal_info->hSyncFreq.Numerator = display_freq * preferred_mode->width;
6511 signal_info->hSyncFreq.Denominator = 1;
6512 signal_info->vSyncFreq.Numerator = display_freq;
6513 signal_info->vSyncFreq.Denominator = 1;
6514 signal_info->activeSize.cx = preferred_mode->width;
6515 signal_info->activeSize.cy = preferred_mode->height;
6516 signal_info->totalSize.cx = preferred_mode->width;
6517 signal_info->totalSize.cy = preferred_mode->height;
6518 signal_info->videoStandard = D3DKMDT_VSS_OTHER;
6519 if (!(found_mode->dmFields & DM_DISPLAYFLAGS))
6520 signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
6521 else if (found_mode->dmDisplayFlags & DM_INTERLACED)
6522 signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED;
6523 else
6524 signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
6525 ret = STATUS_SUCCESS;
6526 break;
6529 unlock_display_devices();
6530 return ret;
6532 case DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME:
6534 DISPLAYCONFIG_ADAPTER_NAME *adapter_name = (DISPLAYCONFIG_ADAPTER_NAME *)packet;
6536 FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME stub.\n" );
6538 if (packet->size < sizeof(*adapter_name))
6539 return STATUS_INVALID_PARAMETER;
6541 return STATUS_NOT_SUPPORTED;
6543 case DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE:
6544 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE:
6545 case DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION:
6546 case DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION:
6547 case DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO:
6548 case DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE:
6549 case DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL:
6550 default:
6551 FIXME( "Unimplemented packet type %u.\n", packet->type );
6552 return STATUS_INVALID_PARAMETER;