cmd: DIR command outputs free space for the path.
[wine.git] / dlls / win32u / sysparams.c
blobfed309ff3d83f8cdf8cceb142c88947d777b9699
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.Length = name.MaximumLength = asciiz_to_unicode( bufferW, buffer );
455 InitializeObjectAttributes( &attr, &name, OBJ_OPENIF, NULL, NULL );
456 if (NtCreateMutant( &mutex, MUTEX_ALL_ACCESS, &attr, FALSE ) < 0) return 0;
457 NtWaitForSingleObject( mutex, FALSE, NULL );
458 return mutex;
461 static void release_display_device_init_mutex( HANDLE mutex )
463 NtReleaseMutant( mutex, NULL );
464 NtClose( mutex );
467 static struct adapter *adapter_acquire( struct adapter *adapter )
469 InterlockedIncrement( &adapter->refcount );
470 return adapter;
473 static void adapter_release( struct adapter *adapter )
475 if (!InterlockedDecrement( &adapter->refcount ))
477 free( adapter->modes );
478 free( adapter );
482 C_ASSERT(sizeof(DEVMODEW) - offsetof(DEVMODEW, dmFields) == 0x94);
484 static void get_monitor_info_from_edid( struct edid_monitor_info *info, const unsigned char *edid, unsigned int edid_len )
486 unsigned int i, j;
487 unsigned short w;
488 unsigned char d;
489 const char *s;
491 info->flags = 0;
492 if (!edid || edid_len < 128) return;
494 w = (edid[8] << 8) | edid[9]; /* Manufacturer ID, big endian. */
495 for (i = 0; i < 3; ++i)
497 d = w & 0x1f;
498 if (!d || d - 1 > 'Z' - 'A') return;
499 info->monitor_id_string[2 - i] = 'A' + d - 1;
500 w >>= 5;
502 if (w) return;
503 w = edid[10] | (edid[11] << 8); /* Product code, little endian. */
504 info->manufacturer = *(unsigned short *)(edid + 8);
505 info->product_code = w;
506 sprintf( info->monitor_id_string + 3, "%04X", w );
507 info->flags = MONITOR_INFO_HAS_MONITOR_ID;
508 TRACE( "Monitor id %s.\n", info->monitor_id_string );
510 for (i = 0; i < 4; ++i)
512 if (edid[54 + i * 18] || edid[54 + i * 18 + 1])
514 /* Detailed timing descriptor. */
515 if (info->flags & MONITOR_INFO_HAS_PREFERRED_MODE) continue;
516 info->preferred_width = edid[54 + i * 18 + 2] | ((UINT32)(edid[54 + i * 18 + 4] & 0xf0) << 4);
517 info->preferred_height = edid[54 + i * 18 + 5] | ((UINT32)(edid[54 + i * 18 + 7] & 0xf0) << 4);
518 if (info->preferred_width && info->preferred_height)
519 info->flags |= MONITOR_INFO_HAS_PREFERRED_MODE;
520 continue;
522 if (edid[54 + i * 18 + 3] != 0xfc) continue;
523 /* "Display name" ASCII descriptor. */
524 s = (const char *)&edid[54 + i * 18 + 5];
525 for (j = 0; s[j] && j < 13; ++j)
526 info->monitor_name[j] = s[j];
527 while (j && isspace(s[j - 1])) --j;
528 info->monitor_name[j] = 0;
529 info->flags |= MONITOR_INFO_HAS_MONITOR_NAME;
530 break;
534 static BOOL write_adapter_mode( HKEY adapter_key, UINT index, const DEVMODEW *mode )
536 WCHAR bufferW[MAX_PATH] = {0};
537 char buffer[MAX_PATH];
539 sprintf( buffer, "Modes\\%08X", index );
540 asciiz_to_unicode( bufferW, buffer );
541 return set_reg_value( adapter_key, bufferW, REG_BINARY, &mode->dmFields, sizeof(*mode) - offsetof(DEVMODEW, dmFields) );
544 static BOOL read_adapter_mode( HKEY adapter_key, UINT index, DEVMODEW *mode )
546 char value_buf[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(*mode)])];
547 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)value_buf;
548 WCHAR bufferW[MAX_PATH] = {0};
549 char buffer[MAX_PATH];
551 sprintf( buffer, "Modes\\%08X", index );
552 asciiz_to_unicode( bufferW, buffer );
553 if (!query_reg_value( adapter_key, bufferW, value, sizeof(value_buf) )) return FALSE;
555 memcpy( &mode->dmFields, value->Data, sizeof(*mode) - offsetof(DEVMODEW, dmFields) );
556 return TRUE;
559 static BOOL adapter_get_registry_settings( const struct adapter *adapter, DEVMODEW *mode )
561 BOOL ret = FALSE;
562 HANDLE mutex;
563 HKEY hkey;
565 mutex = get_display_device_init_mutex();
567 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
568 else if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
569 else
571 ret = read_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode );
572 NtClose( hkey );
575 release_display_device_init_mutex( mutex );
576 return ret;
579 static BOOL adapter_set_registry_settings( const struct adapter *adapter, const DEVMODEW *mode )
581 HANDLE mutex;
582 HKEY hkey;
583 BOOL ret;
585 mutex = get_display_device_init_mutex();
587 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
588 if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
589 else
591 ret = write_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode );
592 NtClose( hkey );
595 release_display_device_init_mutex( mutex );
596 return ret;
599 static BOOL adapter_get_current_settings( const struct adapter *adapter, DEVMODEW *mode )
601 BOOL is_primary = !!(adapter->dev.state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
602 HANDLE mutex;
603 HKEY hkey;
604 BOOL ret;
606 /* use the default implementation in virtual desktop mode */
607 if (is_virtual_desktop()) ret = FALSE;
608 else ret = user_driver->pGetCurrentDisplaySettings( adapter->dev.device_name, is_primary, mode );
610 if (ret) return TRUE;
612 /* default implementation: read current display settings from the registry. */
614 mutex = get_display_device_init_mutex();
616 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
617 else if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
618 else
620 ret = read_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
621 NtClose( hkey );
624 release_display_device_init_mutex( mutex );
625 return ret;
628 static BOOL adapter_set_current_settings( const struct adapter *adapter, const DEVMODEW *mode )
630 HANDLE mutex;
631 HKEY hkey;
632 BOOL ret;
634 mutex = get_display_device_init_mutex();
636 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
637 if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
638 else
640 ret = write_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
641 NtClose( hkey );
644 release_display_device_init_mutex( mutex );
645 return ret;
648 static int mode_compare(const void *p1, const void *p2)
650 BOOL a_interlaced, b_interlaced, a_stretched, b_stretched;
651 DWORD a_width, a_height, b_width, b_height;
652 const DEVMODEW *a = p1, *b = p2;
653 int ret;
655 /* Depth in descending order */
656 if ((ret = b->dmBitsPerPel - a->dmBitsPerPel)) return ret;
658 /* Use the width and height in landscape mode for comparison */
659 if (a->dmDisplayOrientation == DMDO_DEFAULT || a->dmDisplayOrientation == DMDO_180)
661 a_width = a->dmPelsWidth;
662 a_height = a->dmPelsHeight;
664 else
666 a_width = a->dmPelsHeight;
667 a_height = a->dmPelsWidth;
670 if (b->dmDisplayOrientation == DMDO_DEFAULT || b->dmDisplayOrientation == DMDO_180)
672 b_width = b->dmPelsWidth;
673 b_height = b->dmPelsHeight;
675 else
677 b_width = b->dmPelsHeight;
678 b_height = b->dmPelsWidth;
681 /* Width in ascending order */
682 if ((ret = a_width - b_width)) return ret;
684 /* Height in ascending order */
685 if ((ret = a_height - b_height)) return ret;
687 /* Frequency in descending order */
688 if ((ret = b->dmDisplayFrequency - a->dmDisplayFrequency)) return ret;
690 /* Orientation in ascending order */
691 if ((ret = a->dmDisplayOrientation - b->dmDisplayOrientation)) return ret;
693 if (!(a->dmFields & DM_DISPLAYFLAGS)) a_interlaced = FALSE;
694 else a_interlaced = !!(a->dmDisplayFlags & DM_INTERLACED);
695 if (!(b->dmFields & DM_DISPLAYFLAGS)) b_interlaced = FALSE;
696 else b_interlaced = !!(b->dmDisplayFlags & DM_INTERLACED);
698 /* Interlaced in ascending order */
699 if ((ret = a_interlaced - b_interlaced)) return ret;
701 if (!(a->dmFields & DM_DISPLAYFIXEDOUTPUT)) a_stretched = FALSE;
702 else a_stretched = a->dmDisplayFixedOutput == DMDFO_STRETCH;
703 if (!(b->dmFields & DM_DISPLAYFIXEDOUTPUT)) b_stretched = FALSE;
704 else b_stretched = b->dmDisplayFixedOutput == DMDFO_STRETCH;
706 /* Stretched in ascending order */
707 if ((ret = a_stretched - b_stretched)) return ret;
709 return 0;
712 static unsigned int query_reg_subkey_value( HKEY hkey, const WCHAR *name, unsigned int name_size,
713 KEY_VALUE_PARTIAL_INFORMATION *value, unsigned int size )
715 HKEY subkey;
717 if (!(subkey = reg_open_key( hkey, name, name_size ))) return 0;
718 size = query_reg_value( subkey, NULL, value, size );
719 NtClose( subkey );
720 return size;
723 static BOOL read_display_adapter_settings( unsigned int index, struct adapter *info )
725 char buffer[4096];
726 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
727 WCHAR *value_str = (WCHAR *)value->Data;
728 DEVMODEW *mode;
729 DWORD i, size;
730 HKEY hkey;
732 if (!enum_key && !(enum_key = reg_open_key( NULL, enum_keyW, sizeof(enum_keyW) )))
733 return FALSE;
735 /* Find adapter */
736 sprintf( buffer, "\\Device\\Video%d", index );
737 size = query_reg_ascii_value( video_key, buffer, value, sizeof(buffer) );
738 if (!size || value->Type != REG_SZ ||
739 value->DataLength <= sizeof("\\Registry\\Machine\\") * sizeof(WCHAR))
740 return FALSE;
742 /* DeviceKey */
743 memcpy( info->dev.device_key, value_str, value->DataLength );
744 info->config_key = info->dev.device_key + sizeof("\\Registry\\Machine\\") - 1;
746 if (!(hkey = reg_open_key( NULL, value_str, value->DataLength - sizeof(WCHAR) )))
747 return FALSE;
749 /* DeviceString */
750 if (query_reg_value( hkey, driver_descW, value, sizeof(buffer) ) && value->Type == REG_SZ)
751 memcpy( info->dev.device_string, value_str, value->DataLength );
752 NtClose( hkey );
754 /* DeviceName */
755 sprintf( buffer, "\\\\.\\DISPLAY%d", index + 1 );
756 asciiz_to_unicode( info->dev.device_name, buffer );
758 if (!(hkey = reg_open_key( config_key, info->config_key,
759 lstrlenW( info->config_key ) * sizeof(WCHAR) )))
760 return FALSE;
762 /* StateFlags */
763 if (query_reg_value( hkey, state_flagsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
764 info->dev.state_flags = *(const DWORD *)value->Data;
766 /* Interface name */
767 info->dev.interface_name[0] = 0;
769 /* ModeCount */
770 if (query_reg_value( hkey, mode_countW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
771 info->mode_count = *(const DWORD *)value->Data;
773 /* Modes, allocate an extra mode for easier iteration */
774 if ((info->modes = calloc( info->mode_count + 1, sizeof(DEVMODEW) )))
776 for (i = 0, mode = info->modes; i < info->mode_count; i++)
778 mode->dmSize = offsetof(DEVMODEW, dmICMMethod);
779 if (!read_adapter_mode( hkey, i, mode )) break;
780 mode = NEXT_DEVMODEW(mode);
782 info->mode_count = i;
784 qsort(info->modes, info->mode_count, sizeof(*info->modes) + info->modes->dmDriverExtra, mode_compare);
787 /* DeviceID */
788 size = query_reg_value( hkey, gpu_idW, value, sizeof(buffer) );
789 NtClose( hkey );
790 if (!size || value->Type != REG_SZ || !info->mode_count || !info->modes) return FALSE;
792 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
793 return FALSE;
795 size = query_reg_subkey_value( hkey, devpropkey_gpu_luidW, sizeof(devpropkey_gpu_luidW), value, sizeof(buffer) );
796 if (size != sizeof(info->gpu_luid))
798 NtClose( hkey );
799 return FALSE;
801 memcpy( &info->gpu_luid, value->Data, sizeof(info->gpu_luid) );
803 size = query_reg_value( hkey, hardware_idW, value, sizeof(buffer) );
804 NtClose( hkey );
805 if (!size || (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
806 return FALSE;
808 lstrcpyW( info->dev.device_id, value_str );
809 return TRUE;
812 static BOOL read_monitor_settings( struct adapter *adapter, UINT index, struct monitor *monitor )
814 char buffer[4096];
815 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
816 WCHAR *device_name, *value_str = (WCHAR *)value->Data, *ptr;
817 HKEY hkey, subkey;
818 DWORD size, len;
820 monitor->flags = adapter->id ? 0 : MONITORINFOF_PRIMARY;
822 /* DeviceName */
823 sprintf( buffer, "\\\\.\\DISPLAY%d\\Monitor%d", adapter->id + 1, index );
824 asciiz_to_unicode( monitor->dev.device_name, buffer );
826 if (!(hkey = reg_open_key( config_key, adapter->config_key,
827 lstrlenW( adapter->config_key ) * sizeof(WCHAR) )))
828 return FALSE;
830 /* Interface name */
831 sprintf( buffer, "MonitorID%u", index );
832 size = query_reg_ascii_value( hkey, buffer, value, sizeof(buffer) );
833 NtClose( hkey );
834 if (!size || value->Type != REG_SZ) return FALSE;
835 len = asciiz_to_unicode( monitor->dev.interface_name, "\\\\\?\\" ) / sizeof(WCHAR) - 1;
836 memcpy( monitor->dev.interface_name + len, value_str, value->DataLength - sizeof(WCHAR) );
837 len += value->DataLength / sizeof(WCHAR) - 1;
838 monitor->dev.interface_name[len++] = '#';
839 memcpy( monitor->dev.interface_name + len, guid_devinterface_monitorW,
840 sizeof(guid_devinterface_monitorW) );
842 /* Replace '\\' with '#' after prefix */
843 for (ptr = monitor->dev.interface_name + ARRAYSIZE("\\\\\?\\") - 1; *ptr; ptr++)
844 if (*ptr == '\\') *ptr = '#';
846 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
847 return FALSE;
849 /* StateFlags, WINE_DEVPROPKEY_MONITOR_STATEFLAGS */
850 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_stateflagsW,
851 sizeof(wine_devpropkey_monitor_stateflagsW),
852 value, sizeof(buffer) );
853 if (size != sizeof(monitor->dev.state_flags))
855 NtClose( hkey );
856 return FALSE;
858 monitor->dev.state_flags = *(const DWORD *)value->Data;
860 /* Output ID */
861 size = query_reg_subkey_value( hkey, devpropkey_monitor_output_idW,
862 sizeof(devpropkey_monitor_output_idW),
863 value, sizeof(buffer) );
864 if (size != sizeof(monitor->output_id))
866 NtClose( hkey );
867 return FALSE;
869 monitor->output_id = *(const unsigned int *)value->Data;
871 /* rc_monitor, WINE_DEVPROPKEY_MONITOR_RCMONITOR */
872 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcmonitorW,
873 sizeof(wine_devpropkey_monitor_rcmonitorW),
874 value, sizeof(buffer) );
875 if (size != sizeof(monitor->rc_monitor))
877 NtClose( hkey );
878 return FALSE;
880 monitor->rc_monitor = *(const RECT *)value->Data;
882 /* rc_work, WINE_DEVPROPKEY_MONITOR_RCWORK */
883 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcworkW,
884 sizeof(wine_devpropkey_monitor_rcworkW),
885 value, sizeof(buffer) );
886 if (size != sizeof(monitor->rc_work))
888 NtClose( hkey );
889 return FALSE;
891 monitor->rc_work = *(const RECT *)value->Data;
893 /* DeviceString */
894 if (!query_reg_value( hkey, device_descW, value, sizeof(buffer) ) || value->Type != REG_SZ)
896 NtClose( hkey );
897 return FALSE;
899 memcpy( monitor->dev.device_string, value->Data, value->DataLength );
901 /* DeviceKey */
902 if (!query_reg_value( hkey, driverW, value, sizeof(buffer) ) || value->Type != REG_SZ)
904 NtClose( hkey );
905 return FALSE;
907 size = asciiz_to_unicode( monitor->dev.device_key,
908 "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\" );
909 device_name = &monitor->dev.device_key[size / sizeof(WCHAR) - 1];
910 memcpy( device_name, value_str, value->DataLength );
912 /* DeviceID */
913 if (!query_reg_value( hkey, hardware_idW, value, sizeof(buffer) ) ||
914 (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
916 NtClose( hkey );
917 return FALSE;
919 size = lstrlenW( value_str );
920 memcpy( monitor->dev.device_id, value_str, size * sizeof(WCHAR) );
921 monitor->dev.device_id[size++] = '\\';
922 lstrcpyW( monitor->dev.device_id + size, device_name );
924 /* EDID */
925 if ((subkey = reg_open_key( hkey, device_parametersW, sizeof(device_parametersW) )))
927 if (query_reg_value( subkey, edidW, value, sizeof(buffer) ))
928 get_monitor_info_from_edid( &monitor->edid_info, value->Data, value->DataLength );
929 NtClose( subkey );
932 NtClose( hkey );
933 return TRUE;
936 static void reg_empty_key( HKEY root, const char *key_name )
938 char buffer[4096];
939 KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer;
940 KEY_VALUE_FULL_INFORMATION *value = (KEY_VALUE_FULL_INFORMATION *)buffer;
941 WCHAR bufferW[512];
942 DWORD size;
943 HKEY hkey;
945 if (key_name)
946 hkey = reg_open_key( root, bufferW, asciiz_to_unicode( bufferW, key_name ) - sizeof(WCHAR) );
947 else
948 hkey = root;
950 while (!NtEnumerateKey( hkey, 0, KeyNodeInformation, key, sizeof(buffer), &size ))
951 reg_delete_tree( hkey, key->Name, key->NameLength );
953 while (!NtEnumerateValueKey( hkey, 0, KeyValueFullInformation, value, sizeof(buffer), &size ))
955 UNICODE_STRING name = { value->NameLength, value->NameLength, value->Name };
956 NtDeleteValueKey( hkey, &name );
959 if (hkey != root) NtClose( hkey );
962 static void prepare_devices(void)
964 char buffer[4096];
965 KEY_NODE_INFORMATION *key = (void *)buffer;
966 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
967 WCHAR *value_str = (WCHAR *)value->Data;
968 WCHAR bufferW[128];
969 unsigned i = 0;
970 DWORD size;
971 HKEY hkey, subkey, device_key, prop_key;
973 if (!enum_key) enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL );
974 if (!control_key) control_key = reg_create_key( NULL, control_keyW, sizeof(control_keyW), 0, NULL );
975 if (!video_key) video_key = reg_create_key( NULL, devicemap_video_keyW, sizeof(devicemap_video_keyW),
976 REG_OPTION_VOLATILE, NULL );
978 /* delete monitors */
979 reg_empty_key( enum_key, "DISPLAY" );
980 sprintf( buffer, "Class\\%s", guid_devclass_monitorA );
981 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
982 0, NULL );
983 reg_empty_key( hkey, NULL );
984 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
985 NtClose( hkey );
987 /* delete adapters */
988 reg_empty_key( video_key, NULL );
990 /* clean GPUs */
991 sprintf( buffer, "Class\\%s", guid_devclass_displayA );
992 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
993 0, NULL );
994 reg_empty_key( hkey, NULL );
995 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
996 NtClose( hkey );
998 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
1000 /* To preserve GPU GUIDs, mark them as not present and delete them in cleanup_devices if needed. */
1001 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
1003 unsigned int j = 0;
1005 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
1007 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
1009 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
1010 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1011 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
1013 NtClose( device_key );
1014 continue;
1017 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1018 if (size == sizeof(guid_devclass_displayW) &&
1019 !wcscmp( (const WCHAR *)value->Data, guid_devclass_displayW ) &&
1020 (prop_key = reg_create_key( device_key, devpropkey_device_ispresentW,
1021 sizeof(devpropkey_device_ispresentW), 0, NULL )))
1023 BOOL present = FALSE;
1024 set_reg_value( prop_key, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
1025 &present, sizeof(present) );
1026 NtClose( prop_key );
1029 NtClose( device_key );
1032 NtClose( subkey );
1035 NtClose( hkey );
1038 static void cleanup_devices(void)
1040 char buffer[4096];
1041 KEY_NODE_INFORMATION *key = (void *)buffer;
1042 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1043 WCHAR bufferW[512], *value_str = (WCHAR *)value->Data;
1044 unsigned i = 0;
1045 DWORD size;
1046 HKEY hkey, subkey, device_key, prop_key;
1048 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
1050 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
1052 unsigned int j = 0;
1054 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
1056 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
1058 BOOL present = FALSE;
1060 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
1061 memcpy( bufferW, key->Name, key->NameLength );
1062 bufferW[key->NameLength / sizeof(WCHAR)] = 0;
1064 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1065 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
1067 NtClose( device_key );
1068 continue;
1071 if ((prop_key = reg_open_key( device_key, devpropkey_device_ispresentW,
1072 sizeof(devpropkey_device_ispresentW) )))
1074 if (query_reg_value( prop_key, NULL, value, sizeof(buffer) ) == sizeof(BOOL))
1075 present = *(const BOOL *)value->Data;
1076 NtClose( prop_key );
1079 NtClose( device_key );
1081 if (!present && reg_delete_tree( subkey, bufferW, lstrlenW( bufferW ) * sizeof(WCHAR) ))
1082 j = 0;
1085 NtClose( subkey );
1088 NtClose( hkey );
1091 /* see UuidCreate */
1092 static void uuid_create( GUID *uuid )
1094 char buf[4096];
1095 NtQuerySystemInformation( SystemInterruptInformation, buf, sizeof(buf), NULL );
1096 memcpy( uuid, buf, sizeof(*uuid) );
1097 uuid->Data3 &= 0x0fff;
1098 uuid->Data3 |= (4 << 12);
1099 uuid->Data4[0] &= 0x3f;
1100 uuid->Data4[0] |= 0x80;
1103 #define TICKSPERSEC 10000000
1104 #define SECSPERDAY 86400
1105 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
1106 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
1108 static unsigned int format_date( WCHAR *bufferW, LONGLONG time )
1110 int cleaps, years, yearday, months, days;
1111 unsigned int day, month, year;
1112 char buffer[32];
1114 days = time / TICKSPERSEC / SECSPERDAY;
1116 /* compute year, month and day of month, see RtlTimeToTimeFields */
1117 cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
1118 days += 28188 + cleaps;
1119 years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
1120 yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
1121 months = (64 * yearday) / 1959;
1122 if (months < 14)
1124 month = months - 1;
1125 year = years + 1524;
1127 else
1129 month = months - 13;
1130 year = years + 1525;
1132 day = yearday - (1959 * months) / 64 ;
1134 sprintf( buffer, "%u-%u-%u", month, day, year );
1135 return asciiz_to_unicode( bufferW, buffer );
1138 struct device_manager_ctx
1140 unsigned int gpu_count;
1141 unsigned int adapter_count;
1142 unsigned int video_count;
1143 unsigned int monitor_count;
1144 unsigned int output_count;
1145 unsigned int mode_count;
1146 HANDLE mutex;
1147 WCHAR gpuid[128];
1148 WCHAR gpu_guid[64];
1149 LUID gpu_luid;
1150 HKEY adapter_key;
1151 /* for the virtual desktop settings */
1152 BOOL is_primary;
1153 UINT primary_bpp;
1154 UINT primary_width;
1155 UINT primary_height;
1158 static void link_device( const WCHAR *instance, const WCHAR *class )
1160 unsigned int instance_len = lstrlenW( instance ), len;
1161 unsigned int class_len = lstrlenW( class );
1162 WCHAR buffer[MAX_PATH], *ptr;
1163 HKEY hkey, subkey;
1165 static const WCHAR symbolic_linkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k',0};
1166 static const WCHAR hashW[] = {'#'};
1168 len = asciiz_to_unicode( buffer, "DeviceClasses\\" ) / sizeof(WCHAR) - 1;
1169 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
1170 len += class_len;
1171 len += asciiz_to_unicode( buffer + len, "\\##?#" ) / sizeof(WCHAR) - 1;
1172 memcpy( buffer + len, instance, instance_len * sizeof(WCHAR) );
1173 for (ptr = buffer + len; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
1174 len += instance_len;
1175 buffer[len++] = '#';
1176 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
1177 len += class_len;
1178 hkey = reg_create_key( control_key, buffer, len * sizeof(WCHAR), 0, NULL );
1180 set_reg_value( hkey, device_instanceW, REG_SZ, instance, (instance_len + 1) * sizeof(WCHAR) );
1182 subkey = reg_create_key( hkey, hashW, sizeof(hashW), REG_OPTION_VOLATILE, NULL );
1183 NtClose( hkey );
1184 hkey = subkey;
1186 len = asciiz_to_unicode( buffer, "\\\\?\\" ) / sizeof(WCHAR) - 1;
1187 memcpy( buffer + len, instance, (instance_len + 1) * sizeof(WCHAR) );
1188 len += instance_len;
1189 memcpy( buffer + len, class, (class_len + 1) * sizeof(WCHAR) );
1190 len += class_len + 1;
1191 for (ptr = buffer + 4; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
1192 set_reg_value( hkey, symbolic_linkW, REG_SZ, buffer, len * sizeof(WCHAR) );
1194 if ((subkey = reg_create_key( hkey, controlW, sizeof(controlW), REG_OPTION_VOLATILE, NULL )))
1196 const DWORD linked = 1;
1197 set_reg_value( subkey, linkedW, REG_DWORD, &linked, sizeof(linked) );
1198 NtClose( subkey );
1202 static void add_gpu( const struct gdi_gpu *gpu, void *param )
1204 struct device_manager_ctx *ctx = param;
1205 const WCHAR *desc;
1206 char buffer[4096];
1207 WCHAR bufferW[512];
1208 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1209 unsigned int gpu_index, size;
1210 HKEY hkey, subkey;
1211 LARGE_INTEGER ft;
1213 static const BOOL present = TRUE;
1214 static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0};
1215 static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0};
1216 static const WCHAR driver_date_dataW[] =
1217 {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
1218 static const WCHAR adapter_stringW[] =
1219 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n',
1220 '.','A','d','a','p','t','e','r','S','t','r','i','n','g',0};
1221 static const WCHAR bios_stringW[] =
1222 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1223 'B','i','o','s','S','t','r','i','n','g',0};
1224 static const WCHAR chip_typeW[] =
1225 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1226 'C','h','i','p','T','y','p','e',0};
1227 static const WCHAR dac_typeW[] =
1228 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1229 'D','a','c','T','y','p','e',0};
1230 static const WCHAR ramdacW[] =
1231 {'I','n','t','e','r','g','r','a','t','e','d',' ','R','A','M','D','A','C',0};
1232 static const WCHAR driver_dateW[] = {'D','r','i','v','e','r','D','a','t','e',0};
1233 static const WCHAR driver_versionW[] =
1234 {'D','r','i','v','e','r','V','e','r','s','i','o','n',0};
1236 TRACE( "%s %04X %04X %08X %02X\n", debugstr_w(gpu->name),
1237 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
1239 gpu_index = ctx->gpu_count++;
1240 ctx->adapter_count = 0;
1241 ctx->monitor_count = 0;
1242 ctx->mode_count = 0;
1244 if (!enum_key && !(enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL )))
1245 return;
1247 if (!ctx->mutex)
1249 ctx->mutex = get_display_device_init_mutex();
1250 pthread_mutex_lock( &display_lock );
1251 prepare_devices();
1254 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X\\%08X",
1255 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index );
1256 size = asciiz_to_unicode( ctx->gpuid, buffer );
1257 if (!(hkey = reg_create_key( enum_key, ctx->gpuid, size - sizeof(WCHAR), 0, NULL ))) return;
1259 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
1260 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_displayW,
1261 sizeof(guid_devclass_displayW) );
1262 sprintf( buffer, "%s\\%04X", guid_devclass_displayA, gpu_index );
1263 set_reg_value( hkey, driverW, REG_SZ, bufferW, asciiz_to_unicode( bufferW, buffer ));
1265 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X",
1266 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
1267 size = asciiz_to_unicode( bufferW, buffer );
1268 bufferW[size / sizeof(WCHAR)] = 0; /* for REG_MULTI_SZ */
1269 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, size + sizeof(WCHAR) );
1271 if ((subkey = reg_create_key( hkey, devpkey_device_matching_device_id,
1272 sizeof(devpkey_device_matching_device_id), 0, NULL )))
1274 if (gpu->vendor_id && gpu->device_id)
1275 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW, size );
1276 else
1277 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
1278 asciiz_to_unicode( bufferW, "ROOT\\BasicRender" ));
1279 NtClose( subkey );
1282 if (gpu->vendor_id && gpu->device_id)
1284 if ((subkey = reg_create_key( hkey, devpkey_device_bus_number,
1285 sizeof(devpkey_device_bus_number), 0, NULL )))
1287 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1288 &gpu_index, sizeof(gpu_index) );
1289 NtClose( subkey );
1293 if ((subkey = reg_create_key( hkey, devpkey_device_removal_policy,
1294 sizeof(devpkey_device_removal_policy), 0, NULL )))
1296 unsigned int removal_policy = CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL;
1298 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1299 &removal_policy, sizeof(removal_policy) );
1300 NtClose( subkey );
1303 desc = gpu->name;
1304 if (!desc[0]) desc = wine_adapterW;
1305 set_reg_value( hkey, device_descW, REG_SZ, desc, (lstrlenW( desc ) + 1) * sizeof(WCHAR) );
1307 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
1309 if (!query_reg_value( subkey, video_idW, value, sizeof(buffer) ))
1311 GUID guid;
1312 uuid_create( &guid );
1313 sprintf( buffer, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
1314 (unsigned int)guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
1315 guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] );
1316 size = asciiz_to_unicode( ctx->gpu_guid, buffer );
1317 TRACE( "created guid %s\n", debugstr_w(ctx->gpu_guid) );
1318 set_reg_value( subkey, video_idW, REG_SZ, ctx->gpu_guid, size );
1320 else
1322 memcpy( ctx->gpu_guid, value->Data, value->DataLength );
1323 TRACE( "got guid %s\n", debugstr_w(ctx->gpu_guid) );
1325 NtClose( subkey );
1328 if ((subkey = reg_create_key( hkey, devpropkey_gpu_vulkan_uuidW,
1329 sizeof(devpropkey_gpu_vulkan_uuidW), 0, NULL )))
1331 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_GUID,
1332 &gpu->vulkan_uuid, sizeof(gpu->vulkan_uuid) );
1333 NtClose( subkey );
1336 if ((subkey = reg_create_key( hkey, devpropkey_device_ispresentW,
1337 sizeof(devpropkey_device_ispresentW), 0, NULL )))
1339 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
1340 &present, sizeof(present) );
1341 NtClose( subkey );
1344 if ((subkey = reg_create_key( hkey, devpropkey_gpu_luidW, sizeof(devpropkey_gpu_luidW), 0, NULL )))
1346 if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) != sizeof(LUID))
1348 NtAllocateLocallyUniqueId( &ctx->gpu_luid );
1349 TRACE("allocated luid %08x%08x\n", (int)ctx->gpu_luid.HighPart, (int)ctx->gpu_luid.LowPart );
1350 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT64,
1351 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
1353 else
1355 memcpy( &ctx->gpu_luid, value->Data, sizeof(ctx->gpu_luid) );
1356 TRACE("got luid %08x%08x\n", (int)ctx->gpu_luid.HighPart, (int)ctx->gpu_luid.LowPart );
1358 NtClose( subkey );
1361 NtClose( hkey );
1363 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_displayA, gpu_index );
1364 hkey = reg_create_key( control_key, bufferW,
1365 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
1367 NtQuerySystemTime( &ft );
1368 set_reg_value( hkey, driver_dateW, REG_SZ, bufferW, format_date( bufferW, ft.QuadPart ));
1370 set_reg_value( hkey, driver_date_dataW, REG_BINARY, &ft, sizeof(ft) );
1372 size = (lstrlenW( desc ) + 1) * sizeof(WCHAR);
1373 set_reg_value( hkey, driver_descW, REG_SZ, desc, size );
1374 set_reg_value( hkey, adapter_stringW, REG_BINARY, desc, size );
1375 set_reg_value( hkey, bios_stringW, REG_BINARY, desc, size );
1376 set_reg_value( hkey, chip_typeW, REG_BINARY, desc, size );
1377 set_reg_value( hkey, dac_typeW, REG_BINARY, ramdacW, sizeof(ramdacW) );
1379 if (gpu->vendor_id && gpu->device_id)
1381 /* The last seven digits are the driver number. */
1382 switch (gpu->vendor_id)
1384 /* Intel */
1385 case 0x8086:
1386 sprintf( buffer, "31.0.101.4576" );
1387 break;
1388 /* AMD */
1389 case 0x1002:
1390 sprintf( buffer, "31.0.14051.5006" );
1391 break;
1392 /* Nvidia */
1393 case 0x10de:
1394 sprintf( buffer, "31.0.15.3625" );
1395 break;
1396 /* Default value for any other vendor. */
1397 default:
1398 sprintf( buffer, "31.0.10.1000" );
1399 break;
1401 set_reg_value( hkey, driver_versionW, REG_SZ, bufferW, asciiz_to_unicode( bufferW, buffer ) );
1404 NtClose( hkey );
1406 link_device( ctx->gpuid, guid_devinterface_display_adapterW );
1407 link_device( ctx->gpuid, guid_display_device_arrivalW );
1410 static void add_adapter( const struct gdi_adapter *adapter, void *param )
1412 struct device_manager_ctx *ctx = param;
1413 unsigned int adapter_index, video_index, len;
1414 char name[64], buffer[MAX_PATH];
1415 WCHAR nameW[64], bufferW[MAX_PATH];
1416 HKEY hkey;
1418 TRACE( "\n" );
1420 if (ctx->adapter_key)
1422 NtClose( ctx->adapter_key );
1423 ctx->adapter_key = NULL;
1426 adapter_index = ctx->adapter_count++;
1427 video_index = ctx->video_count++;
1428 ctx->monitor_count = 0;
1429 ctx->mode_count = 0;
1431 len = asciiz_to_unicode( bufferW, "\\Registry\\Machine\\System\\CurrentControlSet\\"
1432 "Control\\Video\\" ) / sizeof(WCHAR) - 1;
1433 lstrcpyW( bufferW + len, ctx->gpu_guid );
1434 len += lstrlenW( bufferW + len );
1435 sprintf( buffer, "\\%04x", adapter_index );
1436 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1437 hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1438 REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, NULL );
1439 if (!hkey) hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1440 REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK, NULL );
1442 sprintf( name, "\\Device\\Video%u", video_index );
1443 asciiz_to_unicode( nameW, name );
1444 set_reg_value( video_key, nameW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1446 if (hkey)
1448 sprintf( buffer, "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\"
1449 "%s\\%04X", guid_devclass_displayA, ctx->gpu_count - 1 );
1450 len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR);
1451 set_reg_value( hkey, symbolic_link_valueW, REG_LINK, bufferW, len );
1452 NtClose( hkey );
1454 else ERR( "failed to create link key\n" );
1456 /* Following information is Wine specific, it doesn't really exist on Windows. */
1457 len = asciiz_to_unicode( bufferW, "System\\CurrentControlSet\\Control\\Video\\" )
1458 / sizeof(WCHAR) - 1;
1459 lstrcpyW( bufferW + len, ctx->gpu_guid );
1460 len += lstrlenW( bufferW + len );
1461 sprintf( buffer, "\\%04x", adapter_index );
1462 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1463 ctx->adapter_key = reg_create_key( config_key, bufferW, len * sizeof(WCHAR),
1464 REG_OPTION_VOLATILE, NULL );
1466 set_reg_value( ctx->adapter_key, gpu_idW, REG_SZ, ctx->gpuid,
1467 (lstrlenW( ctx->gpuid ) + 1) * sizeof(WCHAR) );
1468 set_reg_value( ctx->adapter_key, state_flagsW, REG_DWORD, &adapter->state_flags,
1469 sizeof(adapter->state_flags) );
1472 static void add_monitor( const struct gdi_monitor *monitor, void *param )
1474 struct device_manager_ctx *ctx = param;
1475 char buffer[MAX_PATH], instance[64];
1476 unsigned int monitor_index, output_index;
1477 struct edid_monitor_info monitor_info;
1478 char monitor_id_string[16];
1479 WCHAR bufferW[MAX_PATH];
1480 HKEY hkey, subkey;
1481 unsigned int len;
1483 monitor_index = ctx->monitor_count++;
1484 output_index = ctx->output_count++;
1486 TRACE( "%u %s %s\n", monitor_index, wine_dbgstr_rect(&monitor->rc_monitor), wine_dbgstr_rect(&monitor->rc_work) );
1488 get_monitor_info_from_edid( &monitor_info, monitor->edid, monitor->edid_len );
1489 if (monitor_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
1490 strcpy( monitor_id_string, monitor_info.monitor_id_string );
1491 else
1492 strcpy( monitor_id_string, "Default_Monitor" );
1494 sprintf( buffer, "MonitorID%u", monitor_index );
1495 sprintf( instance, "DISPLAY\\%s\\%04X&%04X", monitor_id_string, ctx->video_count - 1, monitor_index );
1496 set_reg_ascii_value( ctx->adapter_key, buffer, instance );
1498 hkey = reg_create_key( enum_key, bufferW, asciiz_to_unicode( bufferW, instance ) - sizeof(WCHAR),
1499 0, NULL );
1500 if (!hkey) return;
1502 link_device( bufferW, guid_devinterface_monitorW );
1504 asciiz_to_unicode( bufferW, "Generic Non-PnP Monitor" );
1505 set_reg_value( hkey, device_descW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1507 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
1508 sprintf( buffer, "%s\\%04X", guid_devclass_monitorA, output_index );
1509 set_reg_ascii_value( hkey, "Driver", buffer );
1510 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_monitorW, sizeof(guid_devclass_monitorW) );
1512 sprintf( buffer, "MONITOR\\%s", monitor_id_string );
1513 len = asciiz_to_unicode( bufferW, buffer );
1514 bufferW[len / sizeof(WCHAR)] = 0;
1515 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, len + sizeof(WCHAR) );
1517 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
1519 static const WCHAR bad_edidW[] = {'B','A','D','_','E','D','I','D',0};
1521 if (monitor->edid_len)
1522 set_reg_value( subkey, edidW, REG_BINARY, monitor->edid, monitor->edid_len );
1523 else
1524 set_reg_value( subkey, bad_edidW, REG_BINARY, NULL, 0 );
1525 NtClose( subkey );
1528 /* StateFlags */
1529 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_stateflagsW,
1530 sizeof(wine_devpropkey_monitor_stateflagsW), 0, NULL )))
1532 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32, &monitor->state_flags,
1533 sizeof(monitor->state_flags) );
1534 NtClose( subkey );
1537 /* WINE_DEVPROPKEY_MONITOR_RCMONITOR */
1538 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcmonitorW,
1539 sizeof(wine_devpropkey_monitor_rcmonitorW), 0, NULL )))
1541 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_monitor,
1542 sizeof(monitor->rc_monitor) );
1543 NtClose( subkey );
1546 /* WINE_DEVPROPKEY_MONITOR_RCWORK */
1547 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcworkW,
1548 sizeof(wine_devpropkey_monitor_rcworkW), 0, NULL )))
1550 TRACE( "rc_work %s\n", wine_dbgstr_rect(&monitor->rc_work) );
1551 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_work,
1552 sizeof(monitor->rc_work) );
1553 NtClose( subkey );
1556 /* WINE_DEVPROPKEY_MONITOR_ADAPTERNAME */
1557 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_adapternameW,
1558 sizeof(wine_devpropkey_monitor_adapternameW), 0, NULL )))
1560 sprintf( buffer, "\\\\.\\DISPLAY%u", ctx->video_count );
1561 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
1562 asciiz_to_unicode( bufferW, buffer ));
1563 NtClose( subkey );
1566 /* DEVPROPKEY_MONITOR_GPU_LUID */
1567 if ((subkey = reg_create_key( hkey, devpropkey_monitor_gpu_luidW,
1568 sizeof(devpropkey_monitor_gpu_luidW), 0, NULL )))
1570 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_INT64,
1571 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
1572 NtClose( subkey );
1575 /* DEVPROPKEY_MONITOR_OUTPUT_ID */
1576 if ((subkey = reg_create_key( hkey, devpropkey_monitor_output_idW,
1577 sizeof(devpropkey_monitor_output_idW), 0, NULL )))
1579 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1580 &output_index, sizeof(output_index) );
1581 NtClose( subkey );
1584 NtClose( hkey );
1586 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_monitorA, output_index );
1587 hkey = reg_create_key( control_key, bufferW,
1588 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
1589 if (hkey) NtClose( hkey );
1592 static void add_mode( const DEVMODEW *mode, BOOL current, void *param )
1594 struct device_manager_ctx *ctx = param;
1595 DEVMODEW nopos_mode;
1597 if (!ctx->adapter_count)
1599 static const struct gdi_adapter default_adapter =
1601 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1603 TRACE( "adding default fake adapter\n" );
1604 add_adapter( &default_adapter, ctx );
1607 nopos_mode = *mode;
1608 nopos_mode.dmPosition.x = 0;
1609 nopos_mode.dmPosition.y = 0;
1610 nopos_mode.dmFields &= ~DM_POSITION;
1612 if (write_adapter_mode( ctx->adapter_key, ctx->mode_count, &nopos_mode ))
1614 ctx->mode_count++;
1615 set_reg_value( ctx->adapter_key, mode_countW, REG_DWORD, &ctx->mode_count, sizeof(ctx->mode_count) );
1616 if (current)
1618 if (!read_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, &nopos_mode ))
1619 write_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, mode );
1620 write_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, mode );
1625 static const struct gdi_device_manager device_manager =
1627 add_gpu,
1628 add_adapter,
1629 add_monitor,
1630 add_mode,
1633 static void reset_display_manager_ctx( struct device_manager_ctx *ctx )
1635 HANDLE mutex = ctx->mutex;
1637 if (ctx->adapter_key)
1639 NtClose( ctx->adapter_key );
1640 last_query_display_time = 0;
1642 if (ctx->gpu_count) cleanup_devices();
1644 memset( ctx, 0, sizeof(*ctx) );
1645 if ((ctx->mutex = mutex)) prepare_devices();
1648 static void release_display_manager_ctx( struct device_manager_ctx *ctx )
1650 if (ctx->mutex)
1652 pthread_mutex_unlock( &display_lock );
1653 release_display_device_init_mutex( ctx->mutex );
1654 ctx->mutex = 0;
1656 reset_display_manager_ctx( ctx );
1659 static void clear_display_devices(void)
1661 struct adapter *adapter;
1662 struct monitor *monitor;
1664 if (list_head( &monitors ) == &virtual_monitor.entry)
1666 list_init( &monitors );
1667 return;
1670 while (!list_empty( &monitors ))
1672 monitor = LIST_ENTRY( list_head( &monitors ), struct monitor, entry );
1673 adapter_release( monitor->adapter );
1674 list_remove( &monitor->entry );
1675 free( monitor );
1678 while (!list_empty( &adapters ))
1680 adapter = LIST_ENTRY( list_head( &adapters ), struct adapter, entry );
1681 list_remove( &adapter->entry );
1682 adapter_release( adapter );
1686 static BOOL update_display_cache_from_registry(void)
1688 DWORD adapter_id, monitor_id, monitor_count = 0, size;
1689 KEY_BASIC_INFORMATION key;
1690 struct adapter *adapter;
1691 struct monitor *monitor, *monitor2;
1692 HANDLE mutex = NULL;
1693 NTSTATUS status;
1694 BOOL ret;
1696 /* If user driver did initialize the registry, then exit */
1697 if (!video_key && !(video_key = reg_open_key( NULL, devicemap_video_keyW,
1698 sizeof(devicemap_video_keyW) )))
1699 return FALSE;
1701 status = NtQueryKey( video_key, KeyBasicInformation, &key,
1702 offsetof(KEY_BASIC_INFORMATION, Name), &size );
1703 if (status && status != STATUS_BUFFER_OVERFLOW)
1704 return FALSE;
1706 if (key.LastWriteTime.QuadPart <= last_query_display_time) return TRUE;
1708 mutex = get_display_device_init_mutex();
1709 pthread_mutex_lock( &display_lock );
1711 clear_display_devices();
1713 for (adapter_id = 0;; adapter_id++)
1715 if (!(adapter = calloc( 1, sizeof(*adapter) ))) break;
1716 adapter->refcount = 1;
1717 adapter->id = adapter_id;
1719 if (!read_display_adapter_settings( adapter_id, adapter ))
1721 adapter_release( adapter );
1722 break;
1725 list_add_tail( &adapters, &adapter->entry );
1726 for (monitor_id = 0;; monitor_id++)
1728 if (!(monitor = calloc( 1, sizeof(*monitor) ))) break;
1729 if (!read_monitor_settings( adapter, monitor_id, monitor ))
1731 free( monitor );
1732 break;
1735 monitor->id = monitor_id;
1736 monitor->adapter = adapter_acquire( adapter );
1738 LIST_FOR_EACH_ENTRY(monitor2, &monitors, struct monitor, entry)
1740 if (EqualRect(&monitor2->rc_monitor, &monitor->rc_monitor))
1742 monitor->is_clone = TRUE;
1743 break;
1747 monitor->handle = UlongToHandle( ++monitor_count );
1748 list_add_tail( &monitors, &monitor->entry );
1752 if ((ret = !list_empty( &adapters ) && !list_empty( &monitors )))
1753 last_query_display_time = key.LastWriteTime.QuadPart;
1754 pthread_mutex_unlock( &display_lock );
1755 release_display_device_init_mutex( mutex );
1756 return ret;
1759 static BOOL is_same_devmode( const DEVMODEW *a, const DEVMODEW *b )
1761 return a->dmDisplayOrientation == b->dmDisplayOrientation &&
1762 a->dmBitsPerPel == b->dmBitsPerPel &&
1763 a->dmPelsWidth == b->dmPelsWidth &&
1764 a->dmPelsHeight == b->dmPelsHeight &&
1765 a->dmDisplayFrequency == b->dmDisplayFrequency;
1768 static BOOL default_update_display_devices( const struct gdi_device_manager *manager, BOOL force, struct device_manager_ctx *ctx )
1770 /* default implementation: expose an adapter and a monitor with a few standard modes,
1771 * and read / write current display settings from / to the registry.
1773 static const DEVMODEW modes[] =
1775 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1776 .dmBitsPerPel = 32, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
1777 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1778 .dmBitsPerPel = 32, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
1779 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1780 .dmBitsPerPel = 32, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
1781 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1782 .dmBitsPerPel = 16, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
1783 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1784 .dmBitsPerPel = 16, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
1785 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1786 .dmBitsPerPel = 16, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
1788 static const struct gdi_gpu gpu;
1789 static const struct gdi_adapter adapter =
1791 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1793 struct gdi_monitor monitor =
1795 .state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED,
1797 DEVMODEW mode = {{0}};
1798 UINT i;
1800 if (!force) return TRUE;
1802 manager->add_gpu( &gpu, ctx );
1803 manager->add_adapter( &adapter, ctx );
1805 if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, &mode ))
1807 mode = modes[2];
1808 mode.dmFields |= DM_POSITION;
1810 monitor.rc_monitor.right = mode.dmPelsWidth;
1811 monitor.rc_monitor.bottom = mode.dmPelsHeight;
1812 monitor.rc_work.right = mode.dmPelsWidth;
1813 monitor.rc_work.bottom = mode.dmPelsHeight;
1815 manager->add_monitor( &monitor, ctx );
1816 for (i = 0; i < ARRAY_SIZE(modes); ++i)
1818 if (is_same_devmode( modes + i, &mode )) manager->add_mode( &mode, TRUE, ctx );
1819 else manager->add_mode( modes + i, FALSE, ctx );
1822 return TRUE;
1825 static BOOL update_display_devices( const struct gdi_device_manager *manager, BOOL force, struct device_manager_ctx *ctx )
1827 if (user_driver->pUpdateDisplayDevices( manager, force, ctx )) return TRUE;
1828 return default_update_display_devices( manager, force, ctx );
1831 /* parse the desktop size specification */
1832 static BOOL parse_size( const WCHAR *size, unsigned int *width, unsigned int *height )
1834 WCHAR *end;
1836 *width = wcstoul( size, &end, 10 );
1837 if (end == size) return FALSE;
1838 if (*end != 'x') return FALSE;
1839 size = end + 1;
1840 *height = wcstoul( size, &end, 10 );
1841 return !*end;
1844 /* retrieve the default desktop size from the registry */
1845 static BOOL get_default_desktop_size( unsigned int *width, unsigned int *height )
1847 static const WCHAR defaultW[] = {'D','e','f','a','u','l','t',0};
1848 WCHAR buffer[4096];
1849 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1850 DWORD size;
1851 HKEY hkey;
1853 /* @@ Wine registry key: HKCU\Software\Wine\Explorer\Desktops */
1854 if (!(hkey = reg_open_hkcu_key( "Software\\Wine\\Explorer\\Desktops" ))) return FALSE;
1856 size = query_reg_value( hkey, defaultW, value, sizeof(buffer) );
1857 NtClose( hkey );
1858 if (!size || value->Type != REG_SZ) return FALSE;
1860 if (!parse_size( (const WCHAR *)value->Data, width, height )) return FALSE;
1861 return TRUE;
1864 static void desktop_add_gpu( const struct gdi_gpu *gpu, void *param )
1868 static void desktop_add_adapter( const struct gdi_adapter *adapter, void *param )
1870 struct device_manager_ctx *ctx = param;
1871 ctx->is_primary = !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
1874 static void desktop_add_monitor( const struct gdi_monitor *monitor, void *param )
1878 static void desktop_add_mode( const DEVMODEW *mode, BOOL current, void *param )
1880 struct device_manager_ctx *ctx = param;
1882 if (ctx->is_primary && current)
1884 ctx->primary_bpp = mode->dmBitsPerPel;
1885 ctx->primary_width = mode->dmPelsWidth;
1886 ctx->primary_height = mode->dmPelsHeight;
1890 static const struct gdi_device_manager desktop_device_manager =
1892 desktop_add_gpu,
1893 desktop_add_adapter,
1894 desktop_add_monitor,
1895 desktop_add_mode,
1898 static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ctx *ctx )
1900 static const struct gdi_gpu gpu;
1901 static const struct gdi_adapter adapter =
1903 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1905 struct gdi_monitor monitor =
1907 .state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED,
1909 static struct screen_size
1911 unsigned int width;
1912 unsigned int height;
1913 } screen_sizes[] = {
1914 /* 4:3 */
1915 { 320, 240},
1916 { 400, 300},
1917 { 512, 384},
1918 { 640, 480},
1919 { 768, 576},
1920 { 800, 600},
1921 {1024, 768},
1922 {1152, 864},
1923 {1280, 960},
1924 {1400, 1050},
1925 {1600, 1200},
1926 {2048, 1536},
1927 /* 5:4 */
1928 {1280, 1024},
1929 {2560, 2048},
1930 /* 16:9 */
1931 {1280, 720},
1932 {1366, 768},
1933 {1600, 900},
1934 {1920, 1080},
1935 {2560, 1440},
1936 {3840, 2160},
1937 /* 16:10 */
1938 { 320, 200},
1939 { 640, 400},
1940 {1280, 800},
1941 {1440, 900},
1942 {1680, 1050},
1943 {1920, 1200},
1944 {2560, 1600}
1947 struct device_manager_ctx desktop_ctx = {0};
1948 UINT screen_width, screen_height, max_width, max_height;
1949 unsigned int depths[] = {8, 16, 0};
1950 DEVMODEW current, mode =
1952 .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1953 .dmDisplayFrequency = 60,
1955 UINT i, j;
1957 if (!force) return TRUE;
1958 /* in virtual desktop mode, read the device list from the user driver but expose virtual devices */
1959 if (!update_display_devices( &desktop_device_manager, TRUE, &desktop_ctx )) return FALSE;
1961 max_width = desktop_ctx.primary_width;
1962 max_height = desktop_ctx.primary_height;
1963 depths[ARRAY_SIZE(depths) - 1] = desktop_ctx.primary_bpp;
1965 if (!get_default_desktop_size( &screen_width, &screen_height ))
1967 screen_width = max_width;
1968 screen_height = max_height;
1971 add_gpu( &gpu, ctx );
1972 add_adapter( &adapter, ctx );
1973 if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, &current ))
1975 current = mode;
1976 current.dmFields |= DM_POSITION;
1977 current.dmBitsPerPel = desktop_ctx.primary_bpp;
1978 current.dmPelsWidth = screen_width;
1979 current.dmPelsHeight = screen_height;
1982 monitor.rc_monitor.right = current.dmPelsWidth;
1983 monitor.rc_monitor.bottom = current.dmPelsHeight;
1984 monitor.rc_work.right = current.dmPelsWidth;
1985 monitor.rc_work.bottom = current.dmPelsHeight;
1986 add_monitor( &monitor, ctx );
1988 for (i = 0; i < ARRAY_SIZE(depths); ++i)
1990 mode.dmBitsPerPel = depths[i];
1992 for (j = 0; j < ARRAY_SIZE(screen_sizes); ++j)
1994 mode.dmPelsWidth = screen_sizes[j].width;
1995 mode.dmPelsHeight = screen_sizes[j].height;
1997 if (mode.dmPelsWidth > max_width || mode.dmPelsHeight > max_height) continue;
1998 if (mode.dmPelsWidth == max_width && mode.dmPelsHeight == max_height) continue;
1999 if (mode.dmPelsWidth == screen_width && mode.dmPelsHeight == screen_height) continue;
2001 if (is_same_devmode( &mode, &current )) add_mode( &current, TRUE, ctx );
2002 else add_mode( &mode, FALSE, ctx );
2005 mode.dmPelsWidth = screen_width;
2006 mode.dmPelsHeight = screen_height;
2007 if (is_same_devmode( &mode, &current )) add_mode( &current, TRUE, ctx );
2008 else add_mode( &mode, FALSE, ctx );
2010 if (max_width != screen_width || max_height != screen_height)
2012 mode.dmPelsWidth = max_width;
2013 mode.dmPelsHeight = max_height;
2014 if (is_same_devmode( &mode, &current )) add_mode( &current, TRUE, ctx );
2015 else add_mode( &mode, FALSE, ctx );
2019 return TRUE;
2022 BOOL update_display_cache( BOOL force )
2024 static const WCHAR wine_service_station_name[] =
2025 {'_','_','w','i','n','e','s','e','r','v','i','c','e','_','w','i','n','s','t','a','t','i','o','n',0};
2026 HWINSTA winstation = NtUserGetProcessWindowStation();
2027 struct device_manager_ctx ctx = {0};
2028 BOOL was_virtual_desktop, ret;
2029 WCHAR name[MAX_PATH];
2031 /* services do not have any adapters, only a virtual monitor */
2032 if (NtUserGetObjectInformation( winstation, UOI_NAME, name, sizeof(name), NULL )
2033 && !wcscmp( name, wine_service_station_name ))
2035 pthread_mutex_lock( &display_lock );
2036 clear_display_devices();
2037 list_add_tail( &monitors, &virtual_monitor.entry );
2038 pthread_mutex_unlock( &display_lock );
2039 return TRUE;
2042 if ((was_virtual_desktop = is_virtual_desktop())) ret = TRUE;
2043 else ret = update_display_devices( &device_manager, force, &ctx );
2045 /* as update_display_devices calls the user driver, it starts explorer and may change the virtual desktop state */
2046 if (ret && is_virtual_desktop())
2048 reset_display_manager_ctx( &ctx );
2049 ret = desktop_update_display_devices( force || !was_virtual_desktop, &ctx );
2052 release_display_manager_ctx( &ctx );
2053 if (!ret) WARN( "Failed to update display devices\n" );
2055 if (!update_display_cache_from_registry())
2057 if (force)
2059 ERR( "Failed to read display config.\n" );
2060 return FALSE;
2063 if (ctx.gpu_count)
2065 ERR( "Driver reported devices, but we failed to read them.\n" );
2066 return FALSE;
2069 return update_display_cache( TRUE );
2072 return TRUE;
2075 static BOOL lock_display_devices(void)
2077 if (!update_display_cache( FALSE )) return FALSE;
2078 pthread_mutex_lock( &display_lock );
2079 return TRUE;
2082 static void unlock_display_devices(void)
2084 pthread_mutex_unlock( &display_lock );
2087 static HDC get_display_dc(void)
2089 pthread_mutex_lock( &display_dc_lock );
2090 if (!display_dc)
2092 HDC dc;
2094 pthread_mutex_unlock( &display_dc_lock );
2095 dc = NtGdiOpenDCW( NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL );
2096 pthread_mutex_lock( &display_dc_lock );
2097 if (display_dc)
2098 NtGdiDeleteObjectApp( dc );
2099 else
2100 display_dc = dc;
2102 return display_dc;
2105 static void release_display_dc( HDC hdc )
2107 pthread_mutex_unlock( &display_dc_lock );
2110 /**********************************************************************
2111 * get_monitor_dpi
2113 UINT get_monitor_dpi( HMONITOR monitor )
2115 /* FIXME: use the monitor DPI instead */
2116 return system_dpi;
2119 /**********************************************************************
2120 * get_win_monitor_dpi
2122 UINT get_win_monitor_dpi( HWND hwnd )
2124 /* FIXME: use the monitor DPI instead */
2125 return system_dpi;
2128 /**********************************************************************
2129 * get_thread_dpi_awareness
2131 DPI_AWARENESS get_thread_dpi_awareness(void)
2133 struct ntuser_thread_info *info = NtUserGetThreadInfo();
2134 ULONG_PTR context = info->dpi_awareness;
2136 if (!context) context = NtUserGetProcessDpiAwarenessContext( NULL );
2138 switch (context)
2140 case 0x10:
2141 case 0x11:
2142 case 0x12:
2143 case 0x80000010:
2144 case 0x80000011:
2145 case 0x80000012:
2146 return context & 3;
2147 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
2148 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
2149 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
2150 return ~context;
2151 default:
2152 return DPI_AWARENESS_INVALID;
2156 DWORD get_process_layout(void)
2158 return process_layout == ~0u ? 0 : process_layout;
2161 /**********************************************************************
2162 * get_thread_dpi
2164 UINT get_thread_dpi(void)
2166 switch (get_thread_dpi_awareness())
2168 case DPI_AWARENESS_UNAWARE: return USER_DEFAULT_SCREEN_DPI;
2169 case DPI_AWARENESS_SYSTEM_AWARE: return system_dpi;
2170 default: return 0; /* no scaling */
2174 /* see GetDpiForSystem */
2175 UINT get_system_dpi(void)
2177 if (get_thread_dpi_awareness() == DPI_AWARENESS_UNAWARE) return USER_DEFAULT_SCREEN_DPI;
2178 return system_dpi;
2181 /* see GetAwarenessFromDpiAwarenessContext */
2182 static DPI_AWARENESS get_awareness_from_dpi_awareness_context( DPI_AWARENESS_CONTEXT context )
2184 switch ((ULONG_PTR)context)
2186 case 0x10:
2187 case 0x11:
2188 case 0x12:
2189 case 0x80000010:
2190 case 0x80000011:
2191 case 0x80000012:
2192 return (ULONG_PTR)context & 3;
2193 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
2194 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
2195 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
2196 return ~(ULONG_PTR)context;
2197 default:
2198 return DPI_AWARENESS_INVALID;
2202 /**********************************************************************
2203 * SetThreadDpiAwarenessContext (win32u.so)
2205 DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT context )
2207 struct ntuser_thread_info *info = NtUserGetThreadInfo();
2208 DPI_AWARENESS prev, val = get_awareness_from_dpi_awareness_context( context );
2210 if (val == DPI_AWARENESS_INVALID)
2212 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2213 return 0;
2215 if (!(prev = info->dpi_awareness))
2217 prev = NtUserGetProcessDpiAwarenessContext( GetCurrentProcess() ) & 3;
2218 prev |= 0x80000010; /* restore to process default */
2220 if (((ULONG_PTR)context & ~(ULONG_PTR)0x13) == 0x80000000) info->dpi_awareness = 0;
2221 else info->dpi_awareness = val | 0x10;
2222 return ULongToHandle( prev );
2225 /**********************************************************************
2226 * map_dpi_rect
2228 RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to )
2230 if (dpi_from && dpi_to && dpi_from != dpi_to)
2232 rect.left = muldiv( rect.left, dpi_to, dpi_from );
2233 rect.top = muldiv( rect.top, dpi_to, dpi_from );
2234 rect.right = muldiv( rect.right, dpi_to, dpi_from );
2235 rect.bottom = muldiv( rect.bottom, dpi_to, dpi_from );
2237 return rect;
2240 /**********************************************************************
2241 * map_dpi_point
2243 POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to )
2245 if (dpi_from && dpi_to && dpi_from != dpi_to)
2247 pt.x = muldiv( pt.x, dpi_to, dpi_from );
2248 pt.y = muldiv( pt.y, dpi_to, dpi_from );
2250 return pt;
2253 /**********************************************************************
2254 * point_win_to_phys_dpi
2256 static POINT point_win_to_phys_dpi( HWND hwnd, POINT pt )
2258 return map_dpi_point( pt, get_dpi_for_window( hwnd ), get_win_monitor_dpi( hwnd ) );
2261 /**********************************************************************
2262 * point_phys_to_win_dpi
2264 POINT point_phys_to_win_dpi( HWND hwnd, POINT pt )
2266 return map_dpi_point( pt, get_win_monitor_dpi( hwnd ), get_dpi_for_window( hwnd ));
2269 /**********************************************************************
2270 * point_thread_to_win_dpi
2272 POINT point_thread_to_win_dpi( HWND hwnd, POINT pt )
2274 UINT dpi = get_thread_dpi();
2275 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
2276 return map_dpi_point( pt, dpi, get_dpi_for_window( hwnd ));
2279 /**********************************************************************
2280 * rect_thread_to_win_dpi
2282 RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect )
2284 UINT dpi = get_thread_dpi();
2285 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
2286 return map_dpi_rect( rect, dpi, get_dpi_for_window( hwnd ) );
2289 /* map value from system dpi to standard 96 dpi for storing in the registry */
2290 static int map_from_system_dpi( int val )
2292 return muldiv( val, USER_DEFAULT_SCREEN_DPI, get_system_dpi() );
2295 /* map value from 96 dpi to system or custom dpi */
2296 static int map_to_dpi( int val, UINT dpi )
2298 if (!dpi) dpi = get_system_dpi();
2299 return muldiv( val, dpi, USER_DEFAULT_SCREEN_DPI );
2302 RECT get_virtual_screen_rect( UINT dpi )
2304 struct monitor *monitor;
2305 RECT rect = {0};
2307 if (!lock_display_devices()) return rect;
2309 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2311 union_rect( &rect, &rect, &monitor->rc_monitor );
2314 unlock_display_devices();
2316 if (dpi) rect = map_dpi_rect( rect, system_dpi, dpi );
2317 return rect;
2320 static BOOL is_window_rect_full_screen( const RECT *rect )
2322 struct monitor *monitor;
2323 BOOL ret = FALSE;
2325 if (!lock_display_devices()) return FALSE;
2327 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2329 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2330 continue;
2332 if (rect->left <= monitor->rc_monitor.left && rect->right >= monitor->rc_monitor.right &&
2333 rect->top <= monitor->rc_monitor.top && rect->bottom >= monitor->rc_monitor.bottom)
2335 ret = TRUE;
2336 break;
2340 unlock_display_devices();
2341 return ret;
2344 RECT get_display_rect( const WCHAR *display )
2346 struct monitor *monitor;
2347 RECT rect = {0};
2349 if (!lock_display_devices()) return rect;
2351 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2353 if (!monitor->adapter || wcsicmp( monitor->adapter->dev.device_name, display )) continue;
2354 rect = monitor->rc_monitor;
2355 break;
2358 unlock_display_devices();
2359 return map_dpi_rect( rect, system_dpi, get_thread_dpi() );
2362 RECT get_primary_monitor_rect( UINT dpi )
2364 struct monitor *monitor;
2365 RECT rect = {0};
2367 if (!lock_display_devices()) return rect;
2369 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2371 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
2372 rect = monitor->rc_monitor;
2373 break;
2376 unlock_display_devices();
2377 return map_dpi_rect( rect, system_dpi, dpi );
2380 /**********************************************************************
2381 * NtUserGetDisplayConfigBufferSizes (win32u.@)
2383 LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_info,
2384 UINT32 *num_mode_info )
2386 struct monitor *monitor;
2387 UINT32 count = 0;
2389 TRACE( "(0x%x %p %p)\n", flags, num_path_info, num_mode_info );
2391 if (!num_path_info || !num_mode_info)
2392 return ERROR_INVALID_PARAMETER;
2394 *num_path_info = 0;
2396 switch (flags)
2398 case QDC_ALL_PATHS:
2399 case QDC_ONLY_ACTIVE_PATHS:
2400 case QDC_DATABASE_CURRENT:
2401 break;
2402 default:
2403 return ERROR_INVALID_PARAMETER;
2406 /* FIXME: semi-stub */
2407 if (flags != QDC_ONLY_ACTIVE_PATHS)
2408 FIXME( "only returning active paths\n" );
2410 if (lock_display_devices())
2412 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2414 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2415 continue;
2416 count++;
2418 unlock_display_devices();
2421 *num_path_info = count;
2422 *num_mode_info = count * 2;
2423 TRACE( "returning %u paths %u modes\n", *num_path_info, *num_mode_info );
2424 return ERROR_SUCCESS;
2427 static DISPLAYCONFIG_ROTATION get_dc_rotation( const DEVMODEW *devmode )
2429 if (devmode->dmFields & DM_DISPLAYORIENTATION)
2430 return devmode->dmDisplayOrientation + 1;
2431 else
2432 return DISPLAYCONFIG_ROTATION_IDENTITY;
2435 static DISPLAYCONFIG_SCANLINE_ORDERING get_dc_scanline_ordering( const DEVMODEW *devmode )
2437 if (!(devmode->dmFields & DM_DISPLAYFLAGS))
2438 return DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
2439 else if (devmode->dmDisplayFlags & DM_INTERLACED)
2440 return DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED;
2441 else
2442 return DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
2445 static DISPLAYCONFIG_PIXELFORMAT get_dc_pixelformat( DWORD dmBitsPerPel )
2447 if ((dmBitsPerPel == 8) || (dmBitsPerPel == 16) ||
2448 (dmBitsPerPel == 24) || (dmBitsPerPel == 32))
2449 return dmBitsPerPel / 8;
2450 else
2451 return DISPLAYCONFIG_PIXELFORMAT_NONGDI;
2454 static void set_mode_target_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid, UINT32 target_id,
2455 UINT32 flags, const DEVMODEW *devmode )
2457 DISPLAYCONFIG_TARGET_MODE *mode = &info->targetMode;
2459 info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET;
2460 info->adapterId = *gpu_luid;
2461 info->id = target_id;
2463 /* FIXME: Populate pixelRate/hSyncFreq/totalSize with real data */
2464 mode->targetVideoSignalInfo.pixelRate = devmode->dmDisplayFrequency * devmode->dmPelsWidth * devmode->dmPelsHeight;
2465 mode->targetVideoSignalInfo.hSyncFreq.Numerator = devmode->dmDisplayFrequency * devmode->dmPelsWidth;
2466 mode->targetVideoSignalInfo.hSyncFreq.Denominator = 1;
2467 mode->targetVideoSignalInfo.vSyncFreq.Numerator = devmode->dmDisplayFrequency;
2468 mode->targetVideoSignalInfo.vSyncFreq.Denominator = 1;
2469 mode->targetVideoSignalInfo.activeSize.cx = devmode->dmPelsWidth;
2470 mode->targetVideoSignalInfo.activeSize.cy = devmode->dmPelsHeight;
2471 if (flags & QDC_DATABASE_CURRENT)
2473 mode->targetVideoSignalInfo.totalSize.cx = 0;
2474 mode->targetVideoSignalInfo.totalSize.cy = 0;
2476 else
2478 mode->targetVideoSignalInfo.totalSize.cx = devmode->dmPelsWidth;
2479 mode->targetVideoSignalInfo.totalSize.cy = devmode->dmPelsHeight;
2481 mode->targetVideoSignalInfo.videoStandard = D3DKMDT_VSS_OTHER;
2482 mode->targetVideoSignalInfo.scanLineOrdering = get_dc_scanline_ordering( devmode );
2485 static void set_path_target_info( DISPLAYCONFIG_PATH_TARGET_INFO *info, const LUID *gpu_luid,
2486 UINT32 target_id, UINT32 mode_index, const DEVMODEW *devmode )
2488 info->adapterId = *gpu_luid;
2489 info->id = target_id;
2490 info->modeInfoIdx = mode_index;
2491 info->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL;
2492 info->rotation = get_dc_rotation( devmode );
2493 info->scaling = DISPLAYCONFIG_SCALING_IDENTITY;
2494 info->refreshRate.Numerator = devmode->dmDisplayFrequency;
2495 info->refreshRate.Denominator = 1;
2496 info->scanLineOrdering = get_dc_scanline_ordering( devmode );
2497 info->targetAvailable = TRUE;
2498 info->statusFlags = DISPLAYCONFIG_TARGET_IN_USE;
2501 static void set_mode_source_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid,
2502 UINT32 source_id, const DEVMODEW *devmode )
2504 DISPLAYCONFIG_SOURCE_MODE *mode = &info->sourceMode;
2506 info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE;
2507 info->adapterId = *gpu_luid;
2508 info->id = source_id;
2510 mode->width = devmode->dmPelsWidth;
2511 mode->height = devmode->dmPelsHeight;
2512 mode->pixelFormat = get_dc_pixelformat( devmode->dmBitsPerPel );
2513 if (devmode->dmFields & DM_POSITION)
2515 mode->position = devmode->dmPosition;
2517 else
2519 mode->position.x = 0;
2520 mode->position.y = 0;
2524 static void set_path_source_info( DISPLAYCONFIG_PATH_SOURCE_INFO *info, const LUID *gpu_luid,
2525 UINT32 source_id, UINT32 mode_index )
2527 info->adapterId = *gpu_luid;
2528 info->id = source_id;
2529 info->modeInfoIdx = mode_index;
2530 info->statusFlags = DISPLAYCONFIG_SOURCE_IN_USE;
2533 static BOOL source_mode_exists( const DISPLAYCONFIG_MODE_INFO *modes, UINT32 modes_count,
2534 UINT32 source_id, UINT32 *found_mode_index )
2536 UINT32 i;
2538 for (i = 0; i < modes_count; i++)
2540 if (modes[i].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE &&
2541 modes[i].id == source_id)
2543 *found_mode_index = i;
2544 return TRUE;
2547 return FALSE;
2550 /***********************************************************************
2551 * NtUserQueryDisplayConfig (win32u.@)
2553 LONG WINAPI NtUserQueryDisplayConfig( UINT32 flags, UINT32 *paths_count, DISPLAYCONFIG_PATH_INFO *paths,
2554 UINT32 *modes_count, DISPLAYCONFIG_MODE_INFO *modes,
2555 DISPLAYCONFIG_TOPOLOGY_ID *topology_id )
2557 ULONG adapter_index;
2558 LONG ret;
2559 UINT32 output_id, source_mode_index, path_index = 0, mode_index = 0;
2560 const LUID *gpu_luid;
2561 DEVMODEW devmode;
2562 struct monitor *monitor;
2564 FIXME( "flags %#x, paths_count %p, paths %p, modes_count %p, modes %p, topology_id %p semi-stub\n",
2565 flags, paths_count, paths, modes_count, modes, topology_id );
2567 if (!paths_count || !modes_count)
2568 return ERROR_INVALID_PARAMETER;
2570 if (!*paths_count || !*modes_count)
2571 return ERROR_INVALID_PARAMETER;
2573 if (flags != QDC_ALL_PATHS &&
2574 flags != QDC_ONLY_ACTIVE_PATHS &&
2575 flags != QDC_DATABASE_CURRENT)
2576 return ERROR_INVALID_PARAMETER;
2578 if (((flags == QDC_DATABASE_CURRENT) && !topology_id) ||
2579 ((flags != QDC_DATABASE_CURRENT) && topology_id))
2580 return ERROR_INVALID_PARAMETER;
2582 if (flags != QDC_ONLY_ACTIVE_PATHS)
2583 FIXME( "only returning active paths\n" );
2585 if (topology_id)
2587 FIXME( "setting toplogyid to DISPLAYCONFIG_TOPOLOGY_INTERNAL\n" );
2588 *topology_id = DISPLAYCONFIG_TOPOLOGY_INTERNAL;
2591 if (!lock_display_devices())
2592 return ERROR_GEN_FAILURE;
2594 ret = ERROR_GEN_FAILURE;
2596 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2598 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2599 continue;
2601 if (!monitor->adapter)
2602 continue;
2604 adapter_index = monitor->adapter->id;
2605 gpu_luid = &monitor->adapter->gpu_luid;
2606 output_id = monitor->output_id;
2608 memset( &devmode, 0, sizeof(devmode) );
2609 devmode.dmSize = sizeof(devmode);
2610 if (!adapter_get_current_settings( monitor->adapter, &devmode ))
2612 goto done;
2615 if (path_index == *paths_count || mode_index == *modes_count)
2617 ret = ERROR_INSUFFICIENT_BUFFER;
2618 goto done;
2621 paths[path_index].flags = DISPLAYCONFIG_PATH_ACTIVE;
2622 set_mode_target_info( &modes[mode_index], gpu_luid, output_id, flags, &devmode );
2623 set_path_target_info( &paths[path_index].targetInfo, gpu_luid, output_id, mode_index, &devmode );
2625 mode_index++;
2626 if (mode_index == *modes_count)
2628 ret = ERROR_INSUFFICIENT_BUFFER;
2629 goto done;
2632 /* Multiple targets can be driven by the same source, ensure a mode
2633 * hasn't already been added for this source.
2635 if (!source_mode_exists( modes, mode_index, adapter_index, &source_mode_index ))
2637 set_mode_source_info( &modes[mode_index], gpu_luid, adapter_index, &devmode );
2638 source_mode_index = mode_index;
2639 mode_index++;
2641 set_path_source_info( &paths[path_index].sourceInfo, gpu_luid, adapter_index, source_mode_index );
2642 path_index++;
2645 *paths_count = path_index;
2646 *modes_count = mode_index;
2647 ret = ERROR_SUCCESS;
2649 done:
2650 unlock_display_devices();
2651 return ret;
2654 /* display_lock mutex must be held */
2655 static struct display_device *find_monitor_device( struct display_device *adapter, UINT index )
2657 struct monitor *monitor;
2659 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2660 if (&monitor->adapter->dev == adapter && index == monitor->id)
2661 return &monitor->dev;
2663 WARN( "Failed to find adapter %s monitor with id %u.\n", debugstr_w(adapter->device_name), index );
2664 return NULL;
2667 /* display_lock mutex must be held */
2668 static struct display_device *find_adapter_device_by_id( UINT index )
2670 struct adapter *adapter;
2672 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
2673 if (index == adapter->id) return &adapter->dev;
2675 WARN( "Failed to find adapter with id %u.\n", index );
2676 return NULL;
2679 /* display_lock mutex must be held */
2680 static struct display_device *find_adapter_device_by_name( UNICODE_STRING *name )
2682 SIZE_T len = name->Length / sizeof(WCHAR);
2683 struct adapter *adapter;
2685 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
2686 if (!wcsnicmp( name->Buffer, adapter->dev.device_name, len ) && !adapter->dev.device_name[len])
2687 return &adapter->dev;
2689 WARN( "Failed to find adapter with name %s.\n", debugstr_us(name) );
2690 return NULL;
2693 /* Find and acquire the adapter matching name, or primary adapter if name is NULL.
2694 * If not NULL, the returned adapter needs to be released with adapter_release.
2696 static struct adapter *find_adapter( UNICODE_STRING *name )
2698 struct display_device *device;
2699 struct adapter *adapter;
2701 if (!lock_display_devices()) return NULL;
2703 if (name && name->Length) device = find_adapter_device_by_name( name );
2704 else device = find_adapter_device_by_id( 0 ); /* use primary adapter */
2706 if (!device) adapter = NULL;
2707 else adapter = adapter_acquire( CONTAINING_RECORD( device, struct adapter, dev ) );
2709 unlock_display_devices();
2710 return adapter;
2713 /***********************************************************************
2714 * NtUserEnumDisplayDevices (win32u.@)
2716 NTSTATUS WINAPI NtUserEnumDisplayDevices( UNICODE_STRING *device, DWORD index,
2717 DISPLAY_DEVICEW *info, DWORD flags )
2719 struct display_device *found = NULL;
2721 TRACE( "%s %u %p %#x\n", debugstr_us( device ), (int)index, info, (int)flags );
2723 if (!info || !info->cb) return STATUS_UNSUCCESSFUL;
2725 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
2727 if (!device || !device->Length) found = find_adapter_device_by_id( index );
2728 else if ((found = find_adapter_device_by_name( device ))) found = find_monitor_device( found, index );
2730 if (found)
2732 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceName) + sizeof(info->DeviceName))
2733 lstrcpyW( info->DeviceName, found->device_name );
2734 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceString) + sizeof(info->DeviceString))
2735 lstrcpyW( info->DeviceString, found->device_string );
2736 if (info->cb >= offsetof(DISPLAY_DEVICEW, StateFlags) + sizeof(info->StateFlags))
2737 info->StateFlags = found->state_flags;
2738 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID))
2739 lstrcpyW( info->DeviceID, (flags & EDD_GET_DEVICE_INTERFACE_NAME)
2740 ? found->interface_name : found->device_id );
2741 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey))
2742 lstrcpyW( info->DeviceKey, found->device_key );
2744 unlock_display_devices();
2745 return found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
2748 #define _X_FIELD(prefix, bits) \
2749 if ((fields) & prefix##_##bits) \
2751 p += sprintf( p, "%s%s", first ? "" : ",", #bits ); \
2752 first = FALSE; \
2755 static const char *_CDS_flags( DWORD fields )
2757 BOOL first = TRUE;
2758 CHAR buf[128];
2759 CHAR *p = buf;
2761 _X_FIELD(CDS, UPDATEREGISTRY)
2762 _X_FIELD(CDS, TEST)
2763 _X_FIELD(CDS, FULLSCREEN)
2764 _X_FIELD(CDS, GLOBAL)
2765 _X_FIELD(CDS, SET_PRIMARY)
2766 _X_FIELD(CDS, VIDEOPARAMETERS)
2767 _X_FIELD(CDS, ENABLE_UNSAFE_MODES)
2768 _X_FIELD(CDS, DISABLE_UNSAFE_MODES)
2769 _X_FIELD(CDS, RESET)
2770 _X_FIELD(CDS, RESET_EX)
2771 _X_FIELD(CDS, NORESET)
2773 *p = 0;
2774 return wine_dbg_sprintf( "%s", buf );
2777 static const char *_DM_fields( DWORD fields )
2779 BOOL first = TRUE;
2780 CHAR buf[128];
2781 CHAR *p = buf;
2783 _X_FIELD(DM, BITSPERPEL)
2784 _X_FIELD(DM, PELSWIDTH)
2785 _X_FIELD(DM, PELSHEIGHT)
2786 _X_FIELD(DM, DISPLAYFLAGS)
2787 _X_FIELD(DM, DISPLAYFREQUENCY)
2788 _X_FIELD(DM, POSITION)
2789 _X_FIELD(DM, DISPLAYORIENTATION)
2791 *p = 0;
2792 return wine_dbg_sprintf( "%s", buf );
2795 #undef _X_FIELD
2797 static void trace_devmode( const DEVMODEW *devmode )
2799 TRACE( "dmFields=%s ", _DM_fields(devmode->dmFields) );
2800 if (devmode->dmFields & DM_BITSPERPEL)
2801 TRACE( "dmBitsPerPel=%u ", (int)devmode->dmBitsPerPel );
2802 if (devmode->dmFields & DM_PELSWIDTH)
2803 TRACE( "dmPelsWidth=%u ", (int)devmode->dmPelsWidth );
2804 if (devmode->dmFields & DM_PELSHEIGHT)
2805 TRACE( "dmPelsHeight=%u ", (int)devmode->dmPelsHeight );
2806 if (devmode->dmFields & DM_DISPLAYFREQUENCY)
2807 TRACE( "dmDisplayFrequency=%u ", (int)devmode->dmDisplayFrequency );
2808 if (devmode->dmFields & DM_POSITION)
2809 TRACE( "dmPosition=(%d,%d) ", (int)devmode->dmPosition.x, (int)devmode->dmPosition.y );
2810 if (devmode->dmFields & DM_DISPLAYFLAGS)
2811 TRACE( "dmDisplayFlags=%#x ", (int)devmode->dmDisplayFlags );
2812 if (devmode->dmFields & DM_DISPLAYORIENTATION)
2813 TRACE( "dmDisplayOrientation=%u ", (int)devmode->dmDisplayOrientation );
2814 TRACE("\n");
2817 static BOOL is_detached_mode( const DEVMODEW *mode )
2819 return mode->dmFields & DM_POSITION &&
2820 mode->dmFields & DM_PELSWIDTH &&
2821 mode->dmFields & DM_PELSHEIGHT &&
2822 mode->dmPelsWidth == 0 &&
2823 mode->dmPelsHeight == 0;
2826 static const DEVMODEW *find_display_mode( const DEVMODEW *modes, DEVMODEW *devmode )
2828 const DEVMODEW *mode;
2830 if (is_detached_mode( devmode )) return devmode;
2832 for (mode = modes; mode && mode->dmSize; mode = NEXT_DEVMODEW(mode))
2834 if ((mode->dmFields & DM_DISPLAYFLAGS) && (mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
2835 continue;
2836 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel && devmode->dmBitsPerPel != mode->dmBitsPerPel)
2837 continue;
2838 if ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth != mode->dmPelsWidth)
2839 continue;
2840 if ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight != mode->dmPelsHeight)
2841 continue;
2842 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != mode->dmDisplayFrequency
2843 && devmode->dmDisplayFrequency > 1 && mode->dmDisplayFrequency)
2844 continue;
2845 if ((devmode->dmFields & DM_DISPLAYORIENTATION) && devmode->dmDisplayOrientation != mode->dmDisplayOrientation)
2846 continue;
2847 if ((devmode->dmFields & DM_DISPLAYFLAGS) && (mode->dmFields & DM_DISPLAYFLAGS) &&
2848 (devmode->dmDisplayFlags & DM_INTERLACED) != (mode->dmDisplayFlags & DM_INTERLACED))
2849 continue;
2850 if ((devmode->dmFields & DM_DISPLAYFIXEDOUTPUT) && (mode->dmFields & DM_DISPLAYFIXEDOUTPUT) &&
2851 devmode->dmDisplayFixedOutput != mode->dmDisplayFixedOutput)
2852 continue;
2854 return mode;
2857 return NULL;
2860 static BOOL adapter_get_full_mode( const struct adapter *adapter, const DEVMODEW *devmode, DEVMODEW *full_mode )
2862 const DEVMODEW *adapter_mode;
2864 if (devmode)
2866 trace_devmode( devmode );
2868 if (devmode->dmSize < offsetof(DEVMODEW, dmICMMethod)) return FALSE;
2869 if (!is_detached_mode( devmode ) &&
2870 (!(devmode->dmFields & DM_BITSPERPEL) || !devmode->dmBitsPerPel) &&
2871 (!(devmode->dmFields & DM_PELSWIDTH) || !devmode->dmPelsWidth) &&
2872 (!(devmode->dmFields & DM_PELSHEIGHT) || !devmode->dmPelsHeight) &&
2873 (!(devmode->dmFields & DM_DISPLAYFREQUENCY) || !devmode->dmDisplayFrequency))
2874 devmode = NULL;
2877 if (devmode) memcpy( full_mode, devmode, devmode->dmSize );
2878 else
2880 if (!adapter_get_registry_settings( adapter, full_mode )) return FALSE;
2881 TRACE( "Return to original display mode\n" );
2884 if ((full_mode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
2886 WARN( "devmode doesn't specify the resolution: %#x\n", (int)full_mode->dmFields );
2887 return FALSE;
2890 if (!is_detached_mode( full_mode ) && (!full_mode->dmPelsWidth || !full_mode->dmPelsHeight || !(full_mode->dmFields & DM_POSITION)))
2892 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2894 if (!adapter_get_current_settings( adapter, &current_mode )) return FALSE;
2895 if (!full_mode->dmPelsWidth) full_mode->dmPelsWidth = current_mode.dmPelsWidth;
2896 if (!full_mode->dmPelsHeight) full_mode->dmPelsHeight = current_mode.dmPelsHeight;
2897 if (!(full_mode->dmFields & DM_POSITION))
2899 full_mode->dmPosition = current_mode.dmPosition;
2900 full_mode->dmFields |= DM_POSITION;
2904 if ((adapter_mode = find_display_mode( adapter->modes, full_mode )) && adapter_mode != full_mode)
2906 POINTL position = full_mode->dmPosition;
2907 *full_mode = *adapter_mode;
2908 full_mode->dmFields |= DM_POSITION;
2909 full_mode->dmPosition = position;
2912 return adapter_mode != NULL;
2915 static DEVMODEW *get_display_settings( const WCHAR *devname, const DEVMODEW *devmode )
2917 DEVMODEW *mode, *displays;
2918 struct adapter *adapter;
2919 BOOL ret;
2921 /* allocate an extra mode for easier iteration */
2922 if (!(displays = calloc( list_count( &adapters ) + 1, sizeof(DEVMODEW) ))) return NULL;
2923 mode = displays;
2925 LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
2927 mode->dmSize = sizeof(DEVMODEW);
2928 if (devmode && !wcsicmp( devname, adapter->dev.device_name ))
2929 memcpy( &mode->dmFields, &devmode->dmFields, devmode->dmSize - offsetof(DEVMODEW, dmFields) );
2930 else
2932 if (!devname) ret = adapter_get_registry_settings( adapter, mode );
2933 else ret = adapter_get_current_settings( adapter, mode );
2934 if (!ret)
2936 free( displays );
2937 return NULL;
2941 lstrcpyW( mode->dmDeviceName, adapter->dev.device_name );
2942 mode = NEXT_DEVMODEW(mode);
2945 return displays;
2948 static INT offset_length( POINT offset )
2950 return offset.x * offset.x + offset.y * offset.y;
2953 static void set_rect_from_devmode( RECT *rect, const DEVMODEW *mode )
2955 SetRect( rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth,
2956 mode->dmPosition.y + mode->dmPelsHeight );
2959 /* Check if a rect overlaps with placed display rects */
2960 static BOOL overlap_placed_displays( const RECT *rect, const DEVMODEW *displays )
2962 const DEVMODEW *mode;
2963 RECT intersect;
2965 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2967 set_rect_from_devmode( &intersect, mode );
2968 if ((mode->dmFields & DM_POSITION) && intersect_rect( &intersect, &intersect, rect )) return TRUE;
2971 return FALSE;
2974 /* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */
2975 static POINT get_placement_offset( const DEVMODEW *displays, const DEVMODEW *placing )
2977 POINT points[8], left_top, offset, min_offset = {0, 0};
2978 INT point_idx, point_count, vertex_idx;
2979 BOOL has_placed = FALSE, first = TRUE;
2980 RECT desired_rect, rect;
2981 const DEVMODEW *mode;
2982 INT width, height;
2984 set_rect_from_devmode( &desired_rect, placing );
2986 /* If the display to be placed is detached, no offset is needed to place it */
2987 if (IsRectEmpty( &desired_rect )) return min_offset;
2989 /* If there is no placed and attached display, place this display as it is */
2990 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2992 set_rect_from_devmode( &rect, mode );
2993 if ((mode->dmFields & DM_POSITION) && !IsRectEmpty( &rect ))
2995 has_placed = TRUE;
2996 break;
3000 if (!has_placed) return min_offset;
3002 /* Try to place this display with each of its four vertices at every vertex of the placed
3003 * displays and see which combination has the minimum offset length */
3004 width = desired_rect.right - desired_rect.left;
3005 height = desired_rect.bottom - desired_rect.top;
3007 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
3009 set_rect_from_devmode( &rect, mode );
3010 if (!(mode->dmFields & DM_POSITION) || IsRectEmpty( &rect )) continue;
3012 /* Get four vertices of the placed display rectangle */
3013 points[0].x = rect.left;
3014 points[0].y = rect.top;
3015 points[1].x = rect.left;
3016 points[1].y = rect.bottom;
3017 points[2].x = rect.right;
3018 points[2].y = rect.top;
3019 points[3].x = rect.right;
3020 points[3].y = rect.bottom;
3021 point_count = 4;
3023 /* Intersected points when moving the display to be placed horizontally */
3024 if (desired_rect.bottom >= rect.top && desired_rect.top <= rect.bottom)
3026 points[point_count].x = rect.left;
3027 points[point_count++].y = desired_rect.top;
3028 points[point_count].x = rect.right;
3029 points[point_count++].y = desired_rect.top;
3031 /* Intersected points when moving the display to be placed vertically */
3032 if (desired_rect.left <= rect.right && desired_rect.right >= rect.left)
3034 points[point_count].x = desired_rect.left;
3035 points[point_count++].y = rect.top;
3036 points[point_count].x = desired_rect.left;
3037 points[point_count++].y = rect.bottom;
3040 /* Try moving each vertex of the display rectangle to each points */
3041 for (point_idx = 0; point_idx < point_count; ++point_idx)
3043 for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx)
3045 switch (vertex_idx)
3047 /* Move the bottom right vertex to the point */
3048 case 0:
3049 left_top.x = points[point_idx].x - width;
3050 left_top.y = points[point_idx].y - height;
3051 break;
3052 /* Move the bottom left vertex to the point */
3053 case 1:
3054 left_top.x = points[point_idx].x;
3055 left_top.y = points[point_idx].y - height;
3056 break;
3057 /* Move the top right vertex to the point */
3058 case 2:
3059 left_top.x = points[point_idx].x - width;
3060 left_top.y = points[point_idx].y;
3061 break;
3062 /* Move the top left vertex to the point */
3063 case 3:
3064 left_top.x = points[point_idx].x;
3065 left_top.y = points[point_idx].y;
3066 break;
3069 offset.x = left_top.x - desired_rect.left;
3070 offset.y = left_top.y - desired_rect.top;
3071 rect = desired_rect;
3072 OffsetRect( &rect, offset.x, offset.y );
3073 if (!overlap_placed_displays( &rect, displays ))
3075 if (first)
3077 min_offset = offset;
3078 first = FALSE;
3079 continue;
3082 if (offset_length( offset ) < offset_length( min_offset )) min_offset = offset;
3088 return min_offset;
3091 static void place_all_displays( DEVMODEW *displays )
3093 POINT min_offset, offset;
3094 DEVMODEW *mode, *placing;
3096 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
3097 mode->dmFields &= ~DM_POSITION;
3099 /* Place all displays with no extra space between them and no overlapping */
3100 while (1)
3102 /* Place the unplaced display with the minimum offset length first */
3103 placing = NULL;
3104 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
3106 if (mode->dmFields & DM_POSITION) continue;
3108 offset = get_placement_offset( displays, mode );
3109 if (!placing || offset_length( offset ) < offset_length( min_offset ))
3111 min_offset = offset;
3112 placing = mode;
3116 /* If all displays are placed */
3117 if (!placing) break;
3119 placing->dmPosition.x += min_offset.x;
3120 placing->dmPosition.y += min_offset.y;
3121 placing->dmFields |= DM_POSITION;
3125 static BOOL all_detached_settings( const DEVMODEW *displays )
3127 const DEVMODEW *mode;
3129 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
3130 if (!is_detached_mode( mode )) return FALSE;
3132 return TRUE;
3135 static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmode,
3136 HWND hwnd, DWORD flags, void *lparam )
3138 WCHAR primary_name[CCHDEVICENAME];
3139 struct display_device *primary;
3140 DEVMODEW *mode, *displays;
3141 struct adapter *adapter;
3142 LONG ret;
3144 if (!lock_display_devices()) return DISP_CHANGE_FAILED;
3145 if (!(displays = get_display_settings( devname, devmode )))
3147 unlock_display_devices();
3148 return DISP_CHANGE_FAILED;
3151 if (all_detached_settings( displays ))
3153 unlock_display_devices();
3154 WARN( "Detaching all modes is not permitted.\n" );
3155 free( displays );
3156 return DISP_CHANGE_SUCCESSFUL;
3159 place_all_displays( displays );
3161 if (!(primary = find_adapter_device_by_id( 0 ))) primary_name[0] = 0;
3162 else wcscpy( primary_name, primary->device_name );
3164 /* use the default implementation in virtual desktop mode */
3165 if (is_virtual_desktop()) ret = E_NOTIMPL;
3166 else ret = user_driver->pChangeDisplaySettings( displays, primary_name, hwnd, flags, lparam );
3168 if (ret == E_NOTIMPL)
3170 /* default implementation: write current display settings to the registry. */
3171 mode = displays;
3172 LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
3174 if (!adapter_set_current_settings( adapter, mode ))
3175 WARN( "Failed to write adapter %s current mode.\n", debugstr_w(adapter->dev.device_name) );
3176 mode = NEXT_DEVMODEW(mode);
3178 ret = DISP_CHANGE_SUCCESSFUL;
3180 unlock_display_devices();
3182 free( displays );
3183 if (ret) return ret;
3185 if (!update_display_cache( TRUE ))
3186 WARN( "Failed to update display cache after mode change.\n" );
3188 if ((adapter = find_adapter( NULL )))
3190 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
3192 if (!adapter_get_current_settings( adapter, &current_mode )) WARN( "Failed to get primary adapter current display settings.\n" );
3193 adapter_release( adapter );
3195 NtUserClipCursor( NULL );
3196 send_notify_message( NtUserGetDesktopWindow(), WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
3197 MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), FALSE );
3198 send_message_timeout( HWND_BROADCAST, WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
3199 MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ),
3200 SMTO_ABORTIFHUNG, 2000, FALSE );
3201 /* post clip_fullscreen_window request to the foreground window */
3202 NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_CLIPCURSOR, SET_CURSOR_FSCLIP, 0 );
3205 return ret;
3208 /***********************************************************************
3209 * NtUserChangeDisplaySettings (win32u.@)
3211 LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devmode, HWND hwnd,
3212 DWORD flags, void *lparam )
3214 DEVMODEW full_mode = {.dmSize = sizeof(DEVMODEW)};
3215 int ret = DISP_CHANGE_SUCCESSFUL;
3216 struct adapter *adapter;
3218 TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, (int)flags, lparam );
3219 TRACE( "flags=%s\n", _CDS_flags(flags) );
3221 if ((!devname || !devname->Length) && !devmode) return apply_display_settings( NULL, NULL, hwnd, flags, lparam );
3223 if (!(adapter = find_adapter( devname ))) return DISP_CHANGE_BADPARAM;
3225 if (!adapter_get_full_mode( adapter, devmode, &full_mode )) ret = DISP_CHANGE_BADMODE;
3226 else if ((flags & CDS_UPDATEREGISTRY) && !adapter_set_registry_settings( adapter, &full_mode )) ret = DISP_CHANGE_NOTUPDATED;
3227 else if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL;
3228 else ret = apply_display_settings( adapter->dev.device_name, &full_mode, hwnd, flags, lparam );
3229 adapter_release( adapter );
3231 if (ret) ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret );
3232 return ret;
3235 static BOOL adapter_enum_display_settings( const struct adapter *adapter, UINT index, DEVMODEW *devmode, UINT flags )
3237 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
3238 const DEVMODEW *adapter_mode;
3240 if (!(flags & EDS_ROTATEDMODE) && !adapter_get_current_settings( adapter, &current_mode ))
3242 WARN( "Failed to query current display mode for EDS_ROTATEDMODE flag.\n" );
3243 return FALSE;
3246 for (adapter_mode = adapter->modes; adapter_mode->dmSize; adapter_mode = NEXT_DEVMODEW(adapter_mode))
3248 if (!(flags & EDS_ROTATEDMODE) && (adapter_mode->dmFields & DM_DISPLAYORIENTATION) &&
3249 adapter_mode->dmDisplayOrientation != current_mode.dmDisplayOrientation)
3250 continue;
3251 if (!(flags & EDS_RAWMODE) && (adapter_mode->dmFields & DM_DISPLAYFLAGS) &&
3252 (adapter_mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
3253 continue;
3254 if (!index--)
3256 memcpy( &devmode->dmFields, &adapter_mode->dmFields, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields) );
3257 devmode->dmDisplayFlags &= ~WINE_DM_UNSUPPORTED;
3258 return TRUE;
3262 WARN( "device %s, index %#x, flags %#x display mode not found.\n",
3263 debugstr_w( adapter->dev.device_name ), index, flags );
3264 RtlSetLastWin32Error( ERROR_NO_MORE_FILES );
3265 return FALSE;
3268 /***********************************************************************
3269 * NtUserEnumDisplaySettings (win32u.@)
3271 BOOL WINAPI NtUserEnumDisplaySettings( UNICODE_STRING *device, DWORD index, DEVMODEW *devmode, DWORD flags )
3273 static const WCHAR wine_display_driverW[] = {'W','i','n','e',' ','D','i','s','p','l','a','y',' ','D','r','i','v','e','r',0};
3274 struct adapter *adapter;
3275 BOOL ret;
3277 TRACE( "device %s, index %#x, devmode %p, flags %#x\n",
3278 debugstr_us(device), (int)index, devmode, (int)flags );
3280 if (!(adapter = find_adapter( device ))) return FALSE;
3282 lstrcpynW( devmode->dmDeviceName, wine_display_driverW, ARRAY_SIZE(devmode->dmDeviceName) );
3283 devmode->dmSpecVersion = DM_SPECVERSION;
3284 devmode->dmDriverVersion = DM_SPECVERSION;
3285 devmode->dmSize = offsetof(DEVMODEW, dmICMMethod);
3286 devmode->dmDriverExtra = 0;
3288 if (index == ENUM_REGISTRY_SETTINGS) ret = adapter_get_registry_settings( adapter, devmode );
3289 else if (index == ENUM_CURRENT_SETTINGS) ret = adapter_get_current_settings( adapter, devmode );
3290 else ret = adapter_enum_display_settings( adapter, index, devmode, flags );
3291 adapter_release( adapter );
3293 if (!ret) WARN( "Failed to query %s display settings.\n", debugstr_us(device) );
3294 else TRACE( "position %dx%d, resolution %ux%u, frequency %u, depth %u, orientation %#x.\n",
3295 (int)devmode->dmPosition.x, (int)devmode->dmPosition.y, (int)devmode->dmPelsWidth,
3296 (int)devmode->dmPelsHeight, (int)devmode->dmDisplayFrequency,
3297 (int)devmode->dmBitsPerPel, (int)devmode->dmDisplayOrientation );
3298 return ret;
3301 struct monitor_enum_info
3303 HANDLE handle;
3304 RECT rect;
3307 static unsigned int active_monitor_count(void)
3309 struct monitor *monitor;
3310 unsigned int count = 0;
3312 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
3314 if ((monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) count++;
3316 return count;
3319 INT get_display_depth( UNICODE_STRING *name )
3321 struct display_device *device;
3322 BOOL is_primary;
3323 INT depth;
3325 if (!lock_display_devices())
3326 return 32;
3328 if (name && name->Length)
3329 device = find_adapter_device_by_name( name );
3330 else
3331 device = find_adapter_device_by_id( 0 ); /* use primary adapter */
3333 if (!device)
3335 unlock_display_devices();
3336 return 32;
3339 is_primary = !!(device->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
3341 /* use the default implementation in virtual desktop mode */
3342 if (is_virtual_desktop()) depth = -1;
3343 else depth = user_driver->pGetDisplayDepth( device->device_name, is_primary );
3345 if (depth < 0)
3347 struct adapter *adapter = CONTAINING_RECORD( device, struct adapter, dev );
3348 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
3350 if (!adapter_get_current_settings( adapter, &current_mode )) depth = 32;
3351 else depth = current_mode.dmBitsPerPel;
3354 unlock_display_devices();
3355 return depth;
3358 /***********************************************************************
3359 * NtUserEnumDisplayMonitors (win32u.@)
3361 BOOL WINAPI NtUserEnumDisplayMonitors( HDC hdc, RECT *rect, MONITORENUMPROC proc, LPARAM lparam )
3363 struct monitor_enum_info enum_buf[8], *enum_info = enum_buf;
3364 struct enum_display_monitor_params params;
3365 struct monitor *monitor;
3366 unsigned int count = 0, i;
3367 POINT origin;
3368 RECT limit;
3369 BOOL ret = TRUE;
3371 if (hdc)
3373 DC *dc;
3374 if (!(dc = get_dc_ptr( hdc ))) return FALSE;
3375 origin.x = dc->attr->vis_rect.left;
3376 origin.y = dc->attr->vis_rect.top;
3377 release_dc_ptr( dc );
3378 if (NtGdiGetAppClipBox( hdc, &limit ) == ERROR) return FALSE;
3380 else
3382 origin.x = origin.y = 0;
3383 limit.left = limit.top = INT_MIN;
3384 limit.right = limit.bottom = INT_MAX;
3386 if (rect && !intersect_rect( &limit, &limit, rect )) return TRUE;
3388 if (!lock_display_devices()) return FALSE;
3390 count = list_count( &monitors );
3391 if (!count || (count > ARRAYSIZE(enum_buf) &&
3392 !(enum_info = malloc( count * sizeof(*enum_info) ))))
3394 unlock_display_devices();
3395 return FALSE;
3398 count = 0;
3399 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
3401 RECT monrect;
3403 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) continue;
3405 monrect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
3406 get_thread_dpi() );
3407 OffsetRect( &monrect, -origin.x, -origin.y );
3408 if (!intersect_rect( &monrect, &monrect, &limit )) continue;
3409 if (monitor->is_clone) continue;
3411 enum_info[count].handle = monitor->handle;
3412 enum_info[count].rect = monrect;
3413 count++;
3416 unlock_display_devices();
3418 params.proc = proc;
3419 params.hdc = hdc;
3420 params.lparam = lparam;
3421 for (i = 0; i < count; i++)
3423 void *ret_ptr;
3424 ULONG ret_len;
3425 params.monitor = enum_info[i].handle;
3426 params.rect = enum_info[i].rect;
3427 if (!KeUserModeCallback( NtUserCallEnumDisplayMonitor, &params, sizeof(params),
3428 &ret_ptr, &ret_len ))
3430 ret = FALSE;
3431 break;
3434 if (enum_info != enum_buf) free( enum_info );
3435 return ret;
3438 BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info )
3440 struct monitor *monitor;
3441 UINT dpi_from, dpi_to;
3443 if (info->cbSize != sizeof(MONITORINFOEXW) && info->cbSize != sizeof(MONITORINFO)) return FALSE;
3445 if (!lock_display_devices()) return FALSE;
3447 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
3449 if (monitor->handle != handle) continue;
3450 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) break;
3452 /* FIXME: map dpi */
3453 info->rcMonitor = monitor->rc_monitor;
3454 info->rcWork = monitor->rc_work;
3455 info->dwFlags = monitor->flags;
3456 if (info->cbSize >= sizeof(MONITORINFOEXW))
3458 if (monitor->adapter)
3459 lstrcpyW( ((MONITORINFOEXW *)info)->szDevice, monitor->adapter->dev.device_name );
3460 else
3461 asciiz_to_unicode( ((MONITORINFOEXW *)info)->szDevice, "WinDisc" );
3463 unlock_display_devices();
3465 if ((dpi_to = get_thread_dpi()))
3467 dpi_from = get_monitor_dpi( handle );
3468 info->rcMonitor = map_dpi_rect( info->rcMonitor, dpi_from, dpi_to );
3469 info->rcWork = map_dpi_rect( info->rcWork, dpi_from, dpi_to );
3471 TRACE( "flags %04x, monitor %s, work %s\n", (int)info->dwFlags,
3472 wine_dbgstr_rect(&info->rcMonitor), wine_dbgstr_rect(&info->rcWork));
3473 return TRUE;
3476 unlock_display_devices();
3477 WARN( "invalid handle %p\n", handle );
3478 RtlSetLastWin32Error( ERROR_INVALID_MONITOR_HANDLE );
3479 return FALSE;
3482 HMONITOR monitor_from_rect( const RECT *rect, UINT flags, UINT dpi )
3484 HMONITOR primary = 0, nearest = 0, ret = 0;
3485 UINT max_area = 0, min_distance = ~0u;
3486 struct monitor *monitor;
3487 RECT r;
3489 r = map_dpi_rect( *rect, dpi, system_dpi );
3490 if (IsRectEmpty( &r ))
3492 r.right = r.left + 1;
3493 r.bottom = r.top + 1;
3496 if (!lock_display_devices()) return 0;
3498 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
3500 RECT intersect;
3501 RECT monitor_rect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
3502 system_dpi );
3504 if (intersect_rect( &intersect, &monitor_rect, &r ))
3506 /* check for larger intersecting area */
3507 UINT area = (intersect.right - intersect.left) * (intersect.bottom - intersect.top);
3508 if (area > max_area)
3510 max_area = area;
3511 ret = monitor->handle;
3514 else if (!max_area) /* if not intersecting, check for min distance */
3516 UINT distance;
3517 UINT x, y;
3519 if (r.right <= monitor_rect.left) x = monitor_rect.left - r.right;
3520 else if (monitor_rect.right <= r.left) x = r.left - monitor_rect.right;
3521 else x = 0;
3522 if (r.bottom <= monitor_rect.top) y = monitor_rect.top - r.bottom;
3523 else if (monitor_rect.bottom <= r.top) y = r.top - monitor_rect.bottom;
3524 else y = 0;
3525 distance = x * x + y * y;
3526 if (distance < min_distance)
3528 min_distance = distance;
3529 nearest = monitor->handle;
3533 if (monitor->flags & MONITORINFOF_PRIMARY) primary = monitor->handle;
3536 unlock_display_devices();
3538 if (!ret)
3540 if (flags & MONITOR_DEFAULTTOPRIMARY) ret = primary;
3541 else if (flags & MONITOR_DEFAULTTONEAREST) ret = nearest;
3544 TRACE( "%s flags %x returning %p\n", wine_dbgstr_rect(rect), flags, ret );
3545 return ret;
3548 HMONITOR monitor_from_point( POINT pt, UINT flags, UINT dpi )
3550 RECT rect;
3551 SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 );
3552 return monitor_from_rect( &rect, flags, dpi );
3555 /* see MonitorFromWindow */
3556 HMONITOR monitor_from_window( HWND hwnd, UINT flags, UINT dpi )
3558 RECT rect;
3559 WINDOWPLACEMENT wp;
3561 TRACE( "(%p, 0x%08x)\n", hwnd, flags );
3563 wp.length = sizeof(wp);
3564 if (is_iconic( hwnd ) && NtUserGetWindowPlacement( hwnd, &wp ))
3565 return monitor_from_rect( &wp.rcNormalPosition, flags, dpi );
3567 if (get_window_rect( hwnd, &rect, dpi ))
3568 return monitor_from_rect( &rect, flags, dpi );
3570 if (!(flags & (MONITOR_DEFAULTTOPRIMARY|MONITOR_DEFAULTTONEAREST))) return 0;
3571 /* retrieve the primary */
3572 SetRect( &rect, 0, 0, 1, 1 );
3573 return monitor_from_rect( &rect, flags, dpi );
3576 /***********************************************************************
3577 * NtUserGetSystemDpiForProcess (win32u.@)
3579 ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process )
3581 if (process && process != GetCurrentProcess())
3583 FIXME( "not supported on other process %p\n", process );
3584 return 0;
3587 return system_dpi;
3590 /***********************************************************************
3591 * NtUserGetDpiForMonitor (win32u.@)
3593 BOOL WINAPI NtUserGetDpiForMonitor( HMONITOR monitor, UINT type, UINT *x, UINT *y )
3595 if (type > 2)
3597 RtlSetLastWin32Error( ERROR_BAD_ARGUMENTS );
3598 return FALSE;
3600 if (!x || !y)
3602 RtlSetLastWin32Error( ERROR_INVALID_ADDRESS );
3603 return FALSE;
3605 switch (get_thread_dpi_awareness())
3607 case DPI_AWARENESS_UNAWARE: *x = *y = USER_DEFAULT_SCREEN_DPI; break;
3608 case DPI_AWARENESS_SYSTEM_AWARE: *x = *y = system_dpi; break;
3609 default: *x = *y = get_monitor_dpi( monitor ); break;
3611 return TRUE;
3614 /**********************************************************************
3615 * LogicalToPhysicalPointForPerMonitorDPI (win32u.@)
3617 BOOL WINAPI NtUserLogicalToPerMonitorDPIPhysicalPoint( HWND hwnd, POINT *pt )
3619 RECT rect;
3621 if (!get_window_rect( hwnd, &rect, get_thread_dpi() )) return FALSE;
3622 if (pt->x < rect.left || pt->y < rect.top || pt->x > rect.right || pt->y > rect.bottom) return FALSE;
3623 *pt = point_win_to_phys_dpi( hwnd, *pt );
3624 return TRUE;
3627 /**********************************************************************
3628 * NtUserPerMonitorDPIPhysicalToLogicalPoint (win32u.@)
3630 BOOL WINAPI NtUserPerMonitorDPIPhysicalToLogicalPoint( HWND hwnd, POINT *pt )
3632 RECT rect;
3633 BOOL ret = FALSE;
3635 if (get_window_rect( hwnd, &rect, 0 ) &&
3636 pt->x >= rect.left && pt->y >= rect.top && pt->x <= rect.right && pt->y <= rect.bottom)
3638 *pt = point_phys_to_win_dpi( hwnd, *pt );
3639 ret = TRUE;
3641 return ret;
3644 /* retrieve the cached base keys for a given entry */
3645 static BOOL get_base_keys( enum parameter_key index, HKEY *base_key, HKEY *volatile_key )
3647 static HKEY base_keys[NB_PARAM_KEYS];
3648 static HKEY volatile_keys[NB_PARAM_KEYS];
3649 WCHAR bufferW[128];
3650 HKEY key;
3652 if (!base_keys[index] && base_key)
3654 if (!(key = reg_create_key( hkcu_key, bufferW,
3655 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
3656 0, NULL )))
3657 return FALSE;
3658 if (InterlockedCompareExchangePointer( (void **)&base_keys[index], key, 0 ))
3659 NtClose( key );
3661 if (!volatile_keys[index] && volatile_key)
3663 if (!(key = reg_create_key( volatile_base_key, bufferW,
3664 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
3665 REG_OPTION_VOLATILE, NULL )))
3666 return FALSE;
3667 if (InterlockedCompareExchangePointer( (void **)&volatile_keys[index], key, 0 ))
3668 NtClose( key );
3670 if (base_key) *base_key = base_keys[index];
3671 if (volatile_key) *volatile_key = volatile_keys[index];
3672 return TRUE;
3675 /* load a value to a registry entry */
3676 static DWORD load_entry( struct sysparam_entry *entry, void *data, DWORD size )
3678 char buffer[4096];
3679 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
3680 DWORD count;
3681 HKEY base_key, volatile_key;
3683 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
3685 if (!(count = query_reg_ascii_value( volatile_key, entry->regval, value, sizeof(buffer) )))
3686 count = query_reg_ascii_value( base_key, entry->regval, value, sizeof(buffer) );
3687 if (count > size)
3689 count = size;
3690 /* make sure strings are null-terminated */
3691 if (value->Type == REG_SZ) ((WCHAR *)value->Data)[count / sizeof(WCHAR) - 1] = 0;
3693 if (count) memcpy( data, value->Data, count );
3694 entry->loaded = TRUE;
3695 return count;
3698 /* save a value to a registry entry */
3699 static BOOL save_entry( const struct sysparam_entry *entry, const void *data, DWORD size,
3700 DWORD type, UINT flags )
3702 HKEY base_key, volatile_key;
3703 WCHAR nameW[64];
3705 asciiz_to_unicode( nameW, entry->regval );
3706 if (flags & SPIF_UPDATEINIFILE)
3708 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
3709 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
3710 reg_delete_value( volatile_key, nameW );
3712 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
3714 asciiz_to_unicode( nameW, entry->mirror );
3715 set_reg_value( base_key, nameW, type, data, size );
3718 else
3720 if (!get_base_keys( entry->base_key, NULL, &volatile_key )) return FALSE;
3721 if (!set_reg_value( volatile_key, nameW, type, data, size )) return FALSE;
3723 return TRUE;
3726 /* save a string value to a registry entry */
3727 static BOOL save_entry_string( const struct sysparam_entry *entry, const WCHAR *str, UINT flags )
3729 return save_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ, flags );
3732 /* initialize an entry in the registry if missing */
3733 static BOOL init_entry( struct sysparam_entry *entry, const void *data, DWORD size, DWORD type )
3735 KEY_VALUE_PARTIAL_INFORMATION value;
3736 UNICODE_STRING name;
3737 WCHAR nameW[64];
3738 HKEY base_key;
3739 DWORD count;
3740 NTSTATUS status;
3742 if (!get_base_keys( entry->base_key, &base_key, NULL )) return FALSE;
3744 name.Buffer = nameW;
3745 name.MaximumLength = asciiz_to_unicode( nameW, entry->regval );
3746 name.Length = name.MaximumLength - sizeof(WCHAR);
3747 status = NtQueryValueKey( base_key, &name, KeyValuePartialInformation,
3748 &value, sizeof(value), &count );
3749 if (!status || status == STATUS_BUFFER_OVERFLOW) return TRUE;
3751 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
3752 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
3754 asciiz_to_unicode( nameW, entry->mirror );
3755 set_reg_value( base_key, nameW, type, data, size );
3757 entry->loaded = TRUE;
3758 return TRUE;
3761 /* initialize a string value in the registry if missing */
3762 static BOOL init_entry_string( struct sysparam_entry *entry, const WCHAR *str )
3764 return init_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ );
3767 /* set an int parameter in the registry */
3768 static BOOL set_int_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3770 WCHAR bufW[32];
3771 char buf[32];
3773 sprintf( buf, "%d", int_param );
3774 asciiz_to_unicode( bufW, buf );
3775 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3776 entry->uint.val = int_param;
3777 entry->hdr.loaded = TRUE;
3778 return TRUE;
3781 /* initialize an int parameter */
3782 static BOOL init_int_entry( union sysparam_all_entry *entry )
3784 WCHAR bufW[32];
3785 char buf[32];
3787 sprintf( buf, "%d", entry->uint.val );
3788 asciiz_to_unicode( bufW, buf );
3789 return init_entry_string( &entry->hdr, bufW );
3792 /* load a uint parameter from the registry */
3793 static BOOL get_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3795 if (!ptr_param) return FALSE;
3797 if (!entry->hdr.loaded)
3799 WCHAR buf[32];
3800 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
3802 *(UINT *)ptr_param = entry->uint.val;
3803 return TRUE;
3806 /* set a uint parameter in the registry */
3807 static BOOL set_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3809 WCHAR bufW[32];
3810 char buf[32];
3812 sprintf( buf, "%u", int_param );
3813 asciiz_to_unicode( bufW, buf );
3814 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3815 entry->uint.val = int_param;
3816 entry->hdr.loaded = TRUE;
3817 return TRUE;
3820 /* initialize a uint parameter */
3821 static BOOL init_uint_entry( union sysparam_all_entry *entry )
3823 WCHAR bufW[32];
3824 char buf[32];
3826 sprintf( buf, "%u", entry->uint.val );
3827 asciiz_to_unicode( bufW, buf );
3828 return init_entry_string( &entry->hdr, bufW );
3831 /* load a twips parameter from the registry */
3832 static BOOL get_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3834 int val;
3836 if (!ptr_param) return FALSE;
3838 if (!entry->hdr.loaded)
3840 WCHAR buf[32];
3841 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
3844 /* Dimensions are quoted as being "twips" values if negative and pixels if positive.
3845 * One inch is 1440 twips.
3846 * See for example
3847 * Technical Reference to the Windows 2000 Registry ->
3848 * HKEY_CURRENT_USER -> Control Panel -> Desktop -> WindowMetrics
3850 val = entry->uint.val;
3851 if (val < 0)
3852 val = muldiv( -val, dpi, 1440 );
3853 else
3854 val = map_to_dpi( val, dpi );
3856 *(int *)ptr_param = val;
3857 return TRUE;
3860 /* set a twips parameter in the registry */
3861 static BOOL set_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3863 int val = int_param;
3864 if (val > 0) val = map_from_system_dpi( val );
3865 return set_int_entry( entry, val, ptr_param, flags );
3868 /* load a bool parameter from the registry */
3869 static BOOL get_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3871 if (!ptr_param) return FALSE;
3873 if (!entry->hdr.loaded)
3875 WCHAR buf[32];
3876 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = wcstol( buf, NULL, 10 ) != 0;
3878 *(UINT *)ptr_param = entry->bool.val;
3879 return TRUE;
3882 /* set a bool parameter in the registry */
3883 static BOOL set_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3885 WCHAR buf[] = { int_param ? '1' : '0', 0 };
3887 if (!save_entry_string( &entry->hdr, buf, flags )) return FALSE;
3888 entry->bool.val = int_param != 0;
3889 entry->hdr.loaded = TRUE;
3890 return TRUE;
3893 /* initialize a bool parameter */
3894 static BOOL init_bool_entry( union sysparam_all_entry *entry )
3896 WCHAR buf[] = { entry->bool.val ? '1' : '0', 0 };
3898 return init_entry_string( &entry->hdr, buf );
3901 /* load a bool parameter using Yes/No strings from the registry */
3902 static BOOL get_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3904 if (!ptr_param) return FALSE;
3906 if (!entry->hdr.loaded)
3908 WCHAR buf[32];
3909 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = !wcsicmp( yesW, buf );
3911 *(UINT *)ptr_param = entry->bool.val;
3912 return TRUE;
3915 /* set a bool parameter using Yes/No strings from the registry */
3916 static BOOL set_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3918 const WCHAR *str = int_param ? yesW : noW;
3920 if (!save_entry_string( &entry->hdr, str, flags )) return FALSE;
3921 entry->bool.val = int_param != 0;
3922 entry->hdr.loaded = TRUE;
3923 return TRUE;
3926 /* initialize a bool parameter using Yes/No strings */
3927 static BOOL init_yesno_entry( union sysparam_all_entry *entry )
3929 return init_entry_string( &entry->hdr, entry->bool.val ? yesW : noW );
3932 /* load a dword (binary) parameter from the registry */
3933 static BOOL get_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3935 if (!ptr_param) return FALSE;
3937 if (!entry->hdr.loaded)
3939 DWORD val;
3940 if (load_entry( &entry->hdr, &val, sizeof(val) ) == sizeof(DWORD)) entry->dword.val = val;
3942 *(DWORD *)ptr_param = entry->dword.val;
3943 return TRUE;
3946 /* set a dword (binary) parameter in the registry */
3947 static BOOL set_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3949 DWORD val = PtrToUlong( ptr_param );
3951 if (!save_entry( &entry->hdr, &val, sizeof(val), REG_DWORD, flags )) return FALSE;
3952 entry->dword.val = val;
3953 entry->hdr.loaded = TRUE;
3954 return TRUE;
3957 /* initialize a dword parameter */
3958 static BOOL init_dword_entry( union sysparam_all_entry *entry )
3960 return init_entry( &entry->hdr, &entry->dword.val, sizeof(entry->dword.val), REG_DWORD );
3963 /* load an RGB parameter from the registry */
3964 static BOOL get_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3966 if (!ptr_param) return FALSE;
3968 if (!entry->hdr.loaded)
3970 WCHAR buf[32];
3972 if (load_entry( &entry->hdr, buf, sizeof(buf) ))
3974 DWORD r, g, b;
3975 WCHAR *end, *str = buf;
3977 r = wcstoul( str, &end, 10 );
3978 if (end == str || !*end) goto done;
3979 str = end + 1;
3980 g = wcstoul( str, &end, 10 );
3981 if (end == str || !*end) goto done;
3982 str = end + 1;
3983 b = wcstoul( str, &end, 10 );
3984 if (end == str) goto done;
3985 if (r > 255 || g > 255 || b > 255) goto done;
3986 entry->rgb.val = RGB( r, g, b );
3989 done:
3990 *(COLORREF *)ptr_param = entry->rgb.val;
3991 return TRUE;
3994 /* set an RGB parameter in the registry */
3995 static BOOL set_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3997 WCHAR bufW[32];
3998 char buf[32];
3999 HBRUSH brush;
4000 HPEN pen;
4002 sprintf( buf, "%u %u %u", GetRValue(int_param), GetGValue(int_param), GetBValue(int_param) );
4003 asciiz_to_unicode( bufW, buf );
4004 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
4005 entry->rgb.val = int_param;
4006 entry->hdr.loaded = TRUE;
4007 if ((brush = InterlockedExchangePointer( (void **)&entry->rgb.brush, 0 )))
4009 make_gdi_object_system( brush, FALSE );
4010 NtGdiDeleteObjectApp( brush );
4012 if ((pen = InterlockedExchangePointer( (void **)&entry->rgb.pen, 0 )))
4014 make_gdi_object_system( pen, FALSE );
4015 NtGdiDeleteObjectApp( pen );
4017 return TRUE;
4020 /* initialize an RGB parameter */
4021 static BOOL init_rgb_entry( union sysparam_all_entry *entry )
4023 WCHAR bufW[32];
4024 char buf[32];
4026 sprintf( buf, "%u %u %u", GetRValue(entry->rgb.val), GetGValue(entry->rgb.val),
4027 GetBValue(entry->rgb.val) );
4028 asciiz_to_unicode( bufW, buf );
4029 return init_entry_string( &entry->hdr, bufW );
4032 /* get a path parameter in the registry */
4033 static BOOL get_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
4035 if (!ptr_param) return FALSE;
4037 if (!entry->hdr.loaded)
4039 WCHAR buffer[MAX_PATH];
4041 if (load_entry( &entry->hdr, buffer, sizeof(buffer) ))
4042 lstrcpynW( entry->path.path, buffer, MAX_PATH );
4044 lstrcpynW( ptr_param, entry->path.path, int_param );
4045 return TRUE;
4048 /* set a path parameter in the registry */
4049 static BOOL set_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
4051 WCHAR buffer[MAX_PATH];
4052 BOOL ret;
4054 lstrcpynW( buffer, ptr_param, MAX_PATH );
4055 ret = save_entry_string( &entry->hdr, buffer, flags );
4056 if (ret)
4058 lstrcpyW( entry->path.path, buffer );
4059 entry->hdr.loaded = TRUE;
4061 return ret;
4064 /* initialize a path parameter */
4065 static BOOL init_path_entry( union sysparam_all_entry *entry )
4067 return init_entry_string( &entry->hdr, entry->path.path );
4070 /* get a binary parameter in the registry */
4071 static BOOL get_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
4073 if (!ptr_param) return FALSE;
4075 if (!entry->hdr.loaded)
4077 void *buffer = malloc( entry->bin.size );
4078 DWORD len = load_entry( &entry->hdr, buffer, entry->bin.size );
4080 if (len)
4082 memcpy( entry->bin.ptr, buffer, entry->bin.size );
4083 memset( (char *)entry->bin.ptr + len, 0, entry->bin.size - len );
4085 free( buffer );
4087 memcpy( ptr_param, entry->bin.ptr, min( int_param, entry->bin.size ) );
4088 return TRUE;
4091 /* set a binary parameter in the registry */
4092 static BOOL set_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
4094 BOOL ret;
4095 void *buffer = malloc( entry->bin.size );
4097 memcpy( buffer, entry->bin.ptr, entry->bin.size );
4098 memcpy( buffer, ptr_param, min( int_param, entry->bin.size ));
4099 ret = save_entry( &entry->hdr, buffer, entry->bin.size, REG_BINARY, flags );
4100 if (ret)
4102 memcpy( entry->bin.ptr, buffer, entry->bin.size );
4103 entry->hdr.loaded = TRUE;
4105 free( buffer );
4106 return ret;
4109 /* initialize a binary parameter */
4110 static BOOL init_binary_entry( union sysparam_all_entry *entry )
4112 return init_entry( &entry->hdr, entry->bin.ptr, entry->bin.size, REG_BINARY );
4115 static void logfont16to32( const LOGFONT16 *font16, LPLOGFONTW font32 )
4117 font32->lfHeight = font16->lfHeight;
4118 font32->lfWidth = font16->lfWidth;
4119 font32->lfEscapement = font16->lfEscapement;
4120 font32->lfOrientation = font16->lfOrientation;
4121 font32->lfWeight = font16->lfWeight;
4122 font32->lfItalic = font16->lfItalic;
4123 font32->lfUnderline = font16->lfUnderline;
4124 font32->lfStrikeOut = font16->lfStrikeOut;
4125 font32->lfCharSet = font16->lfCharSet;
4126 font32->lfOutPrecision = font16->lfOutPrecision;
4127 font32->lfClipPrecision = font16->lfClipPrecision;
4128 font32->lfQuality = font16->lfQuality;
4129 font32->lfPitchAndFamily = font16->lfPitchAndFamily;
4130 win32u_mbtowc( &ansi_cp, font32->lfFaceName, LF_FACESIZE, font16->lfFaceName,
4131 strlen( font16->lfFaceName ));
4132 font32->lfFaceName[LF_FACESIZE-1] = 0;
4135 static void get_real_fontname( LOGFONTW *lf, WCHAR fullname[LF_FACESIZE] )
4137 struct font_enum_entry enum_entry;
4138 ULONG count = sizeof(enum_entry);
4139 HDC hdc;
4141 hdc = get_display_dc();
4142 NtGdiEnumFonts( hdc, 0, 0, lstrlenW( lf->lfFaceName ), lf->lfFaceName, lf->lfCharSet,
4143 &count, &enum_entry );
4144 release_display_dc( hdc );
4146 if (count)
4147 lstrcpyW( fullname, enum_entry.lf.elfFullName );
4148 else
4149 lstrcpyW( fullname, lf->lfFaceName );
4152 LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, int *height )
4154 SIZE sz;
4155 static const WCHAR abcdW[] =
4156 {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
4157 'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H',
4158 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
4160 if (metric && !NtGdiGetTextMetricsW( hdc, metric, 0 )) return 0;
4162 if (!NtGdiGetTextExtentExW( hdc, abcdW, ARRAYSIZE(abcdW), 0, NULL, NULL, &sz, 0 ))
4163 return 0;
4165 if (height) *height = sz.cy;
4166 return (sz.cx / 26 + 1) / 2;
4169 /* get text metrics and/or "average" char width of the specified logfont
4170 * for the specified dc */
4171 static void get_text_metr_size( HDC hdc, LOGFONTW *lf, TEXTMETRICW *metric, UINT *psz )
4173 HFONT hfont, hfontsav;
4174 TEXTMETRICW tm;
4175 UINT ret;
4176 if (!metric) metric = &tm;
4177 hfont = NtGdiHfontCreate( lf, sizeof(*lf), 0, 0, NULL );
4178 if (!hfont || !(hfontsav = NtGdiSelectFont( hdc, hfont )))
4180 metric->tmHeight = -1;
4181 if (psz) *psz = 10;
4182 if (hfont) NtGdiDeleteObjectApp( hfont );
4183 return;
4185 ret = get_char_dimensions( hdc, metric, NULL );
4186 if (psz) *psz = ret ? ret : 10;
4187 NtGdiSelectFont( hdc, hfontsav );
4188 NtGdiDeleteObjectApp( hfont );
4191 DWORD get_dialog_base_units(void)
4193 static int cx, cy;
4195 if (!cx)
4197 HDC hdc;
4199 if ((hdc = NtUserGetDC( 0 )))
4201 cx = get_char_dimensions( hdc, NULL, &cy );
4202 NtUserReleaseDC( 0, hdc );
4204 TRACE( "base units = %d,%d\n", cx, cy );
4207 return MAKELONG( muldiv( cx, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ),
4208 muldiv( cy, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ));
4211 /* adjust some of the raw values found in the registry */
4212 static void normalize_nonclientmetrics( NONCLIENTMETRICSW *pncm)
4214 TEXTMETRICW tm;
4215 HDC hdc = get_display_dc();
4217 if( pncm->iBorderWidth < 1) pncm->iBorderWidth = 1;
4218 if( pncm->iCaptionWidth < 8) pncm->iCaptionWidth = 8;
4219 if( pncm->iScrollWidth < 8) pncm->iScrollWidth = 8;
4220 if( pncm->iScrollHeight < 8) pncm->iScrollHeight = 8;
4222 /* adjust some heights to the corresponding font */
4223 get_text_metr_size( hdc, &pncm->lfMenuFont, &tm, NULL);
4224 pncm->iMenuHeight = max( pncm->iMenuHeight, 2 + tm.tmHeight + tm.tmExternalLeading );
4225 get_text_metr_size( hdc, &pncm->lfCaptionFont, &tm, NULL);
4226 pncm->iCaptionHeight = max( pncm->iCaptionHeight, 2 + tm.tmHeight);
4227 get_text_metr_size( hdc, &pncm->lfSmCaptionFont, &tm, NULL);
4228 pncm->iSmCaptionHeight = max( pncm->iSmCaptionHeight, 2 + tm.tmHeight);
4229 release_display_dc( hdc );
4232 /* load a font (binary) parameter from the registry */
4233 static BOOL get_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
4235 LOGFONTW font;
4237 if (!ptr_param) return FALSE;
4239 if (!entry->hdr.loaded)
4241 switch (load_entry( &entry->hdr, &font, sizeof(font) ))
4243 case sizeof(font):
4244 if (font.lfHeight > 0) /* positive height value means points ( inch/72 ) */
4245 font.lfHeight = -muldiv( font.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
4246 entry->font.val = font;
4247 break;
4248 case sizeof(LOGFONT16): /* win9x-winME format */
4249 logfont16to32( (LOGFONT16 *)&font, &entry->font.val );
4250 if (entry->font.val.lfHeight > 0)
4251 entry->font.val.lfHeight = -muldiv( entry->font.val.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
4252 break;
4253 default:
4254 WARN( "Unknown format in key %s value %s\n",
4255 debugstr_a( parameter_key_names[entry->hdr.base_key] ),
4256 debugstr_a( entry->hdr.regval ));
4257 /* fall through */
4258 case 0: /* use the default GUI font */
4259 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(font), &font );
4260 font.lfHeight = map_from_system_dpi( font.lfHeight );
4261 font.lfWeight = entry->font.weight;
4262 entry->font.val = font;
4263 break;
4265 get_real_fontname( &entry->font.val, entry->font.fullname );
4266 entry->hdr.loaded = TRUE;
4268 font = entry->font.val;
4269 font.lfHeight = map_to_dpi( font.lfHeight, dpi );
4270 lstrcpyW( font.lfFaceName, entry->font.fullname );
4271 *(LOGFONTW *)ptr_param = font;
4272 return TRUE;
4275 /* set a font (binary) parameter in the registry */
4276 static BOOL set_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
4278 LOGFONTW font;
4279 WCHAR *ptr;
4281 memcpy( &font, ptr_param, sizeof(font) );
4282 /* zero pad the end of lfFaceName so we don't save uninitialised data */
4283 for (ptr = font.lfFaceName; ptr < font.lfFaceName + LF_FACESIZE && *ptr; ptr++);
4284 if (ptr < font.lfFaceName + LF_FACESIZE)
4285 memset( ptr, 0, (font.lfFaceName + LF_FACESIZE - ptr) * sizeof(WCHAR) );
4286 if (font.lfHeight < 0) font.lfHeight = map_from_system_dpi( font.lfHeight );
4288 if (!save_entry( &entry->hdr, &font, sizeof(font), REG_BINARY, flags )) return FALSE;
4289 entry->font.val = font;
4290 get_real_fontname( &entry->font.val, entry->font.fullname );
4291 entry->hdr.loaded = TRUE;
4292 return TRUE;
4295 /* initialize a font (binary) parameter */
4296 static BOOL init_font_entry( union sysparam_all_entry *entry )
4298 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(entry->font.val), &entry->font.val );
4299 entry->font.val.lfHeight = map_from_system_dpi( entry->font.val.lfHeight );
4300 entry->font.val.lfWeight = entry->font.weight;
4301 get_real_fontname( &entry->font.val, entry->font.fullname );
4302 return init_entry( &entry->hdr, &entry->font.val, sizeof(entry->font.val), REG_BINARY );
4305 /* get a user pref parameter in the registry */
4306 static BOOL get_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
4308 union sysparam_all_entry *parent_entry = entry->pref.parent;
4309 BYTE prefs[8];
4311 if (!ptr_param) return FALSE;
4313 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, dpi )) return FALSE;
4314 *(BOOL *)ptr_param = (prefs[entry->pref.offset] & entry->pref.mask) != 0;
4315 return TRUE;
4318 /* set a user pref parameter in the registry */
4319 static BOOL set_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
4321 union sysparam_all_entry *parent_entry = entry->pref.parent;
4322 BYTE prefs[8];
4324 parent_entry->hdr.loaded = FALSE; /* force loading it again */
4325 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, get_system_dpi() )) return FALSE;
4327 if (PtrToUlong( ptr_param )) prefs[entry->pref.offset] |= entry->pref.mask;
4328 else prefs[entry->pref.offset] &= ~entry->pref.mask;
4330 return parent_entry->hdr.set( parent_entry, sizeof(prefs), prefs, flags );
4333 static BOOL get_entry_dpi( void *ptr, UINT int_param, void *ptr_param, UINT dpi )
4335 union sysparam_all_entry *entry = ptr;
4336 return entry->hdr.get( entry, int_param, ptr_param, dpi );
4339 static BOOL get_entry( void *ptr, UINT int_param, void *ptr_param )
4341 return get_entry_dpi( ptr, int_param, ptr_param, get_system_dpi() );
4344 static BOOL set_entry( void *ptr, UINT int_param, void *ptr_param, UINT flags )
4346 union sysparam_all_entry *entry = ptr;
4347 return entry->hdr.set( entry, int_param, ptr_param, flags );
4350 #define UINT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4351 { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg }, (val) } }
4353 #define UINT_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
4354 { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg, mirror_base, reg }, (val) } }
4356 #define INT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4357 { .uint = { { get_uint_entry, set_int_entry, init_int_entry, base, reg }, (val) } }
4359 #define BOOL_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4360 { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg }, (val) } }
4362 #define BOOL_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
4363 { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg, mirror_base, reg }, (val) } }
4365 #define TWIPS_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4366 { .uint = { { get_twips_entry, set_twips_entry, init_int_entry, base, reg }, (val) } }
4368 #define YESNO_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4369 { .bool = { { get_yesno_entry, set_yesno_entry, init_yesno_entry, base, reg }, (val) } }
4371 #define DWORD_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
4372 { .dword = { { get_dword_entry, set_dword_entry, init_dword_entry, base, reg }, (val) } }
4374 #define BINARY_ENTRY(name,data,base,reg) union sysparam_all_entry entry_##name = \
4375 { .bin = { { get_binary_entry, set_binary_entry, init_binary_entry, base, reg }, data, sizeof(data) } }
4377 #define PATH_ENTRY(name,base,reg,buffer) union sysparam_all_entry entry_##name = \
4378 { .path = { { get_path_entry, set_path_entry, init_path_entry, base, reg }, buffer } }
4380 #define FONT_ENTRY(name,weight,base,reg) union sysparam_all_entry entry_##name = \
4381 { .font = { { get_font_entry, set_font_entry, init_font_entry, base, reg }, (weight) } }
4383 #define USERPREF_ENTRY(name,offset,mask) union sysparam_all_entry entry_##name = \
4384 { .pref = { { get_userpref_entry, set_userpref_entry }, &entry_USERPREFERENCESMASK, (offset), (mask) } }
4386 static UINT_ENTRY( DRAGWIDTH, 4, DESKTOP_KEY, "DragWidth" );
4387 static UINT_ENTRY( DRAGHEIGHT, 4, DESKTOP_KEY, "DragHeight" );
4388 static UINT_ENTRY( DOUBLECLICKTIME, 500, MOUSE_KEY, "DoubleClickSpeed" );
4389 static UINT_ENTRY( FONTSMOOTHING, 2, DESKTOP_KEY, "FontSmoothing" );
4390 static UINT_ENTRY( GRIDGRANULARITY, 0, DESKTOP_KEY, "GridGranularity" );
4391 static UINT_ENTRY( KEYBOARDDELAY, 1, KEYBOARD_KEY, "KeyboardDelay" );
4392 static UINT_ENTRY( KEYBOARDSPEED, 31, KEYBOARD_KEY, "KeyboardSpeed" );
4393 static UINT_ENTRY( MENUSHOWDELAY, 400, DESKTOP_KEY, "MenuShowDelay" );
4394 static UINT_ENTRY( MINARRANGE, ARW_HIDE, METRICS_KEY, "MinArrange" );
4395 static UINT_ENTRY( MINHORZGAP, 0, METRICS_KEY, "MinHorzGap" );
4396 static UINT_ENTRY( MINVERTGAP, 0, METRICS_KEY, "MinVertGap" );
4397 static UINT_ENTRY( MINWIDTH, 154, METRICS_KEY, "MinWidth" );
4398 static UINT_ENTRY( MOUSEHOVERHEIGHT, 4, MOUSE_KEY, "MouseHoverHeight" );
4399 static UINT_ENTRY( MOUSEHOVERTIME, 400, MOUSE_KEY, "MouseHoverTime" );
4400 static UINT_ENTRY( MOUSEHOVERWIDTH, 4, MOUSE_KEY, "MouseHoverWidth" );
4401 static UINT_ENTRY( MOUSESPEED, 10, MOUSE_KEY, "MouseSensitivity" );
4402 static UINT_ENTRY( MOUSETRAILS, 0, MOUSE_KEY, "MouseTrails" );
4403 static UINT_ENTRY( SCREENSAVETIMEOUT, 300, DESKTOP_KEY, "ScreenSaveTimeOut" );
4404 static UINT_ENTRY( WHEELSCROLLCHARS, 3, DESKTOP_KEY, "WheelScrollChars" );
4405 static UINT_ENTRY( WHEELSCROLLLINES, 3, DESKTOP_KEY, "WheelScrollLines" );
4406 static UINT_ENTRY_MIRROR( DOUBLECLKHEIGHT, 4, MOUSE_KEY, "DoubleClickHeight", DESKTOP_KEY );
4407 static UINT_ENTRY_MIRROR( DOUBLECLKWIDTH, 4, MOUSE_KEY, "DoubleClickWidth", DESKTOP_KEY );
4408 static UINT_ENTRY_MIRROR( MENUDROPALIGNMENT, 0, DESKTOP_KEY, "MenuDropAlignment", VERSION_KEY );
4410 static INT_ENTRY( MOUSETHRESHOLD1, 6, MOUSE_KEY, "MouseThreshold1" );
4411 static INT_ENTRY( MOUSETHRESHOLD2, 10, MOUSE_KEY, "MouseThreshold2" );
4412 static INT_ENTRY( MOUSEACCELERATION, 1, MOUSE_KEY, "MouseSpeed" );
4414 static BOOL_ENTRY( BLOCKSENDINPUTRESETS, FALSE, DESKTOP_KEY, "BlockSendInputResets" );
4415 static BOOL_ENTRY( DRAGFULLWINDOWS, FALSE, DESKTOP_KEY, "DragFullWindows" );
4416 static BOOL_ENTRY( KEYBOARDPREF, TRUE, KEYBOARDPREF_KEY, "On" );
4417 static BOOL_ENTRY( LOWPOWERACTIVE, FALSE, DESKTOP_KEY, "LowPowerActive" );
4418 static BOOL_ENTRY( MOUSEBUTTONSWAP, FALSE, MOUSE_KEY, "SwapMouseButtons" );
4419 static BOOL_ENTRY( POWEROFFACTIVE, FALSE, DESKTOP_KEY, "PowerOffActive" );
4420 static BOOL_ENTRY( SCREENREADER, FALSE, SCREENREADER_KEY, "On" );
4421 static BOOL_ENTRY( SCREENSAVEACTIVE, TRUE, DESKTOP_KEY, "ScreenSaveActive" );
4422 static BOOL_ENTRY( SCREENSAVERRUNNING, FALSE, DESKTOP_KEY, "WINE_ScreenSaverRunning" ); /* FIXME - real value */
4423 static BOOL_ENTRY( SHOWSOUNDS, FALSE, SHOWSOUNDS_KEY, "On" );
4424 static BOOL_ENTRY( SNAPTODEFBUTTON, FALSE, MOUSE_KEY, "SnapToDefaultButton" );
4425 static BOOL_ENTRY_MIRROR( ICONTITLEWRAP, TRUE, DESKTOP_KEY, "IconTitleWrap", METRICS_KEY );
4426 static BOOL_ENTRY( AUDIODESC_ON, FALSE, AUDIODESC_KEY, "On" );
4428 static TWIPS_ENTRY( BORDER, -15, METRICS_KEY, "BorderWidth" );
4429 static TWIPS_ENTRY( CAPTIONHEIGHT, -270, METRICS_KEY, "CaptionHeight" );
4430 static TWIPS_ENTRY( CAPTIONWIDTH, -270, METRICS_KEY, "CaptionWidth" );
4431 static TWIPS_ENTRY( ICONHORIZONTALSPACING, -1125, METRICS_KEY, "IconSpacing" );
4432 static TWIPS_ENTRY( ICONVERTICALSPACING, -1125, METRICS_KEY, "IconVerticalSpacing" );
4433 static TWIPS_ENTRY( MENUHEIGHT, -270, METRICS_KEY, "MenuHeight" );
4434 static TWIPS_ENTRY( MENUWIDTH, -270, METRICS_KEY, "MenuWidth" );
4435 static TWIPS_ENTRY( PADDEDBORDERWIDTH, 0, METRICS_KEY, "PaddedBorderWidth" );
4436 static TWIPS_ENTRY( SCROLLHEIGHT, -240, METRICS_KEY, "ScrollHeight" );
4437 static TWIPS_ENTRY( SCROLLWIDTH, -240, METRICS_KEY, "ScrollWidth" );
4438 static TWIPS_ENTRY( SMCAPTIONHEIGHT, -225, METRICS_KEY, "SmCaptionHeight" );
4439 static TWIPS_ENTRY( SMCAPTIONWIDTH, -225, METRICS_KEY, "SmCaptionWidth" );
4441 static YESNO_ENTRY( BEEP, TRUE, SOUND_KEY, "Beep" );
4443 static DWORD_ENTRY( ACTIVEWINDOWTRACKING, 0, MOUSE_KEY, "ActiveWindowTracking" );
4444 static DWORD_ENTRY( ACTIVEWNDTRKTIMEOUT, 0, DESKTOP_KEY, "ActiveWndTrackTimeout" );
4445 static DWORD_ENTRY( CARETWIDTH, 1, DESKTOP_KEY, "CaretWidth" );
4446 static DWORD_ENTRY( DPISCALINGVER, 0, DESKTOP_KEY, "DpiScalingVer" );
4447 static DWORD_ENTRY( FOCUSBORDERHEIGHT, 1, DESKTOP_KEY, "FocusBorderHeight" );
4448 static DWORD_ENTRY( FOCUSBORDERWIDTH, 1, DESKTOP_KEY, "FocusBorderWidth" );
4449 static DWORD_ENTRY( FONTSMOOTHINGCONTRAST, 0, DESKTOP_KEY, "FontSmoothingGamma" );
4450 static DWORD_ENTRY( FONTSMOOTHINGORIENTATION, FE_FONTSMOOTHINGORIENTATIONRGB, DESKTOP_KEY, "FontSmoothingOrientation" );
4451 static DWORD_ENTRY( FONTSMOOTHINGTYPE, FE_FONTSMOOTHINGSTANDARD, DESKTOP_KEY, "FontSmoothingType" );
4452 static DWORD_ENTRY( FOREGROUNDFLASHCOUNT, 3, DESKTOP_KEY, "ForegroundFlashCount" );
4453 static DWORD_ENTRY( FOREGROUNDLOCKTIMEOUT, 0, DESKTOP_KEY, "ForegroundLockTimeout" );
4454 static DWORD_ENTRY( LOGPIXELS, 0, DESKTOP_KEY, "LogPixels" );
4455 static DWORD_ENTRY( MOUSECLICKLOCKTIME, 1200, DESKTOP_KEY, "ClickLockTime" );
4456 static DWORD_ENTRY( AUDIODESC_LOCALE, 0, AUDIODESC_KEY, "Locale" );
4458 static WCHAR desk_pattern_path[MAX_PATH];
4459 static WCHAR desk_wallpaper_path[MAX_PATH];
4460 static PATH_ENTRY( DESKPATTERN, DESKTOP_KEY, "Pattern", desk_pattern_path );
4461 static PATH_ENTRY( DESKWALLPAPER, DESKTOP_KEY, "Wallpaper", desk_wallpaper_path );
4463 static BYTE user_prefs[8] = { 0x30, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, 0x00 };
4464 static BINARY_ENTRY( USERPREFERENCESMASK, user_prefs, DESKTOP_KEY, "UserPreferencesMask" );
4466 static FONT_ENTRY( CAPTIONLOGFONT, FW_BOLD, METRICS_KEY, "CaptionFont" );
4467 static FONT_ENTRY( ICONTITLELOGFONT, FW_NORMAL, METRICS_KEY, "IconFont" );
4468 static FONT_ENTRY( MENULOGFONT, FW_NORMAL, METRICS_KEY, "MenuFont" );
4469 static FONT_ENTRY( MESSAGELOGFONT, FW_NORMAL, METRICS_KEY, "MessageFont" );
4470 static FONT_ENTRY( SMCAPTIONLOGFONT, FW_NORMAL, METRICS_KEY, "SmCaptionFont" );
4471 static FONT_ENTRY( STATUSLOGFONT, FW_NORMAL, METRICS_KEY, "StatusFont" );
4473 static USERPREF_ENTRY( MENUANIMATION, 0, 0x02 );
4474 static USERPREF_ENTRY( COMBOBOXANIMATION, 0, 0x04 );
4475 static USERPREF_ENTRY( LISTBOXSMOOTHSCROLLING, 0, 0x08 );
4476 static USERPREF_ENTRY( GRADIENTCAPTIONS, 0, 0x10 );
4477 static USERPREF_ENTRY( KEYBOARDCUES, 0, 0x20 );
4478 static USERPREF_ENTRY( ACTIVEWNDTRKZORDER, 0, 0x40 );
4479 static USERPREF_ENTRY( HOTTRACKING, 0, 0x80 );
4480 static USERPREF_ENTRY( MENUFADE, 1, 0x02 );
4481 static USERPREF_ENTRY( SELECTIONFADE, 1, 0x04 );
4482 static USERPREF_ENTRY( TOOLTIPANIMATION, 1, 0x08 );
4483 static USERPREF_ENTRY( TOOLTIPFADE, 1, 0x10 );
4484 static USERPREF_ENTRY( CURSORSHADOW, 1, 0x20 );
4485 static USERPREF_ENTRY( MOUSESONAR, 1, 0x40 );
4486 static USERPREF_ENTRY( MOUSECLICKLOCK, 1, 0x80 );
4487 static USERPREF_ENTRY( MOUSEVANISH, 2, 0x01 );
4488 static USERPREF_ENTRY( FLATMENU, 2, 0x02 );
4489 static USERPREF_ENTRY( DROPSHADOW, 2, 0x04 );
4490 static USERPREF_ENTRY( UIEFFECTS, 3, 0x80 );
4491 static USERPREF_ENTRY( DISABLEOVERLAPPEDCONTENT, 4, 0x01 );
4492 static USERPREF_ENTRY( CLIENTAREAANIMATION, 4, 0x02 );
4493 static USERPREF_ENTRY( CLEARTYPE, 4, 0x10 );
4494 static USERPREF_ENTRY( SPEECHRECOGNITION, 4, 0x20 );
4496 /* System parameter indexes */
4497 enum spi_index
4499 SPI_SETWORKAREA_IDX,
4500 SPI_INDEX_COUNT
4503 /* indicators whether system parameter value is loaded */
4504 static char spi_loaded[SPI_INDEX_COUNT];
4506 static struct sysparam_rgb_entry system_colors[] =
4508 #define RGB_ENTRY(name,val,reg) { { get_rgb_entry, set_rgb_entry, init_rgb_entry, COLORS_KEY, reg }, (val) }
4509 RGB_ENTRY( COLOR_SCROLLBAR, RGB(212, 208, 200), "Scrollbar" ),
4510 RGB_ENTRY( COLOR_BACKGROUND, RGB(58, 110, 165), "Background" ),
4511 RGB_ENTRY( COLOR_ACTIVECAPTION, RGB(10, 36, 106), "ActiveTitle" ),
4512 RGB_ENTRY( COLOR_INACTIVECAPTION, RGB(128, 128, 128), "InactiveTitle" ),
4513 RGB_ENTRY( COLOR_MENU, RGB(212, 208, 200), "Menu" ),
4514 RGB_ENTRY( COLOR_WINDOW, RGB(255, 255, 255), "Window" ),
4515 RGB_ENTRY( COLOR_WINDOWFRAME, RGB(0, 0, 0), "WindowFrame" ),
4516 RGB_ENTRY( COLOR_MENUTEXT, RGB(0, 0, 0), "MenuText" ),
4517 RGB_ENTRY( COLOR_WINDOWTEXT, RGB(0, 0, 0), "WindowText" ),
4518 RGB_ENTRY( COLOR_CAPTIONTEXT, RGB(255, 255, 255), "TitleText" ),
4519 RGB_ENTRY( COLOR_ACTIVEBORDER, RGB(212, 208, 200), "ActiveBorder" ),
4520 RGB_ENTRY( COLOR_INACTIVEBORDER, RGB(212, 208, 200), "InactiveBorder" ),
4521 RGB_ENTRY( COLOR_APPWORKSPACE, RGB(128, 128, 128), "AppWorkSpace" ),
4522 RGB_ENTRY( COLOR_HIGHLIGHT, RGB(10, 36, 106), "Hilight" ),
4523 RGB_ENTRY( COLOR_HIGHLIGHTTEXT, RGB(255, 255, 255), "HilightText" ),
4524 RGB_ENTRY( COLOR_BTNFACE, RGB(212, 208, 200), "ButtonFace" ),
4525 RGB_ENTRY( COLOR_BTNSHADOW, RGB(128, 128, 128), "ButtonShadow" ),
4526 RGB_ENTRY( COLOR_GRAYTEXT, RGB(128, 128, 128), "GrayText" ),
4527 RGB_ENTRY( COLOR_BTNTEXT, RGB(0, 0, 0), "ButtonText" ),
4528 RGB_ENTRY( COLOR_INACTIVECAPTIONTEXT, RGB(212, 208, 200), "InactiveTitleText" ),
4529 RGB_ENTRY( COLOR_BTNHIGHLIGHT, RGB(255, 255, 255), "ButtonHilight" ),
4530 RGB_ENTRY( COLOR_3DDKSHADOW, RGB(64, 64, 64), "ButtonDkShadow" ),
4531 RGB_ENTRY( COLOR_3DLIGHT, RGB(212, 208, 200), "ButtonLight" ),
4532 RGB_ENTRY( COLOR_INFOTEXT, RGB(0, 0, 0), "InfoText" ),
4533 RGB_ENTRY( COLOR_INFOBK, RGB(255, 255, 225), "InfoWindow" ),
4534 RGB_ENTRY( COLOR_ALTERNATEBTNFACE, RGB(181, 181, 181), "ButtonAlternateFace" ),
4535 RGB_ENTRY( COLOR_HOTLIGHT, RGB(0, 0, 200), "HotTrackingColor" ),
4536 RGB_ENTRY( COLOR_GRADIENTACTIVECAPTION, RGB(166, 202, 240), "GradientActiveTitle" ),
4537 RGB_ENTRY( COLOR_GRADIENTINACTIVECAPTION, RGB(192, 192, 192), "GradientInactiveTitle" ),
4538 RGB_ENTRY( COLOR_MENUHILIGHT, RGB(10, 36, 106), "MenuHilight" ),
4539 RGB_ENTRY( COLOR_MENUBAR, RGB(212, 208, 200), "MenuBar" )
4540 #undef RGB_ENTRY
4543 /* entries that are initialized by default in the registry */
4544 static union sysparam_all_entry * const default_entries[] =
4546 (union sysparam_all_entry *)&entry_ACTIVEWINDOWTRACKING,
4547 (union sysparam_all_entry *)&entry_ACTIVEWNDTRKTIMEOUT,
4548 (union sysparam_all_entry *)&entry_BEEP,
4549 (union sysparam_all_entry *)&entry_BLOCKSENDINPUTRESETS,
4550 (union sysparam_all_entry *)&entry_BORDER,
4551 (union sysparam_all_entry *)&entry_CAPTIONHEIGHT,
4552 (union sysparam_all_entry *)&entry_CAPTIONWIDTH,
4553 (union sysparam_all_entry *)&entry_CARETWIDTH,
4554 (union sysparam_all_entry *)&entry_DESKWALLPAPER,
4555 (union sysparam_all_entry *)&entry_DOUBLECLICKTIME,
4556 (union sysparam_all_entry *)&entry_DOUBLECLKHEIGHT,
4557 (union sysparam_all_entry *)&entry_DOUBLECLKWIDTH,
4558 (union sysparam_all_entry *)&entry_DRAGFULLWINDOWS,
4559 (union sysparam_all_entry *)&entry_DRAGHEIGHT,
4560 (union sysparam_all_entry *)&entry_DRAGWIDTH,
4561 (union sysparam_all_entry *)&entry_FOCUSBORDERHEIGHT,
4562 (union sysparam_all_entry *)&entry_FOCUSBORDERWIDTH,
4563 (union sysparam_all_entry *)&entry_FONTSMOOTHING,
4564 (union sysparam_all_entry *)&entry_FONTSMOOTHINGCONTRAST,
4565 (union sysparam_all_entry *)&entry_FONTSMOOTHINGORIENTATION,
4566 (union sysparam_all_entry *)&entry_FONTSMOOTHINGTYPE,
4567 (union sysparam_all_entry *)&entry_FOREGROUNDFLASHCOUNT,
4568 (union sysparam_all_entry *)&entry_FOREGROUNDLOCKTIMEOUT,
4569 (union sysparam_all_entry *)&entry_ICONHORIZONTALSPACING,
4570 (union sysparam_all_entry *)&entry_ICONTITLEWRAP,
4571 (union sysparam_all_entry *)&entry_ICONVERTICALSPACING,
4572 (union sysparam_all_entry *)&entry_KEYBOARDDELAY,
4573 (union sysparam_all_entry *)&entry_KEYBOARDPREF,
4574 (union sysparam_all_entry *)&entry_KEYBOARDSPEED,
4575 (union sysparam_all_entry *)&entry_LOWPOWERACTIVE,
4576 (union sysparam_all_entry *)&entry_MENUHEIGHT,
4577 (union sysparam_all_entry *)&entry_MENUSHOWDELAY,
4578 (union sysparam_all_entry *)&entry_MENUWIDTH,
4579 (union sysparam_all_entry *)&entry_MOUSEACCELERATION,
4580 (union sysparam_all_entry *)&entry_MOUSEBUTTONSWAP,
4581 (union sysparam_all_entry *)&entry_MOUSECLICKLOCKTIME,
4582 (union sysparam_all_entry *)&entry_MOUSEHOVERHEIGHT,
4583 (union sysparam_all_entry *)&entry_MOUSEHOVERTIME,
4584 (union sysparam_all_entry *)&entry_MOUSEHOVERWIDTH,
4585 (union sysparam_all_entry *)&entry_MOUSESPEED,
4586 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD1,
4587 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD2,
4588 (union sysparam_all_entry *)&entry_PADDEDBORDERWIDTH,
4589 (union sysparam_all_entry *)&entry_SCREENREADER,
4590 (union sysparam_all_entry *)&entry_SCROLLHEIGHT,
4591 (union sysparam_all_entry *)&entry_SCROLLWIDTH,
4592 (union sysparam_all_entry *)&entry_SHOWSOUNDS,
4593 (union sysparam_all_entry *)&entry_SMCAPTIONHEIGHT,
4594 (union sysparam_all_entry *)&entry_SMCAPTIONWIDTH,
4595 (union sysparam_all_entry *)&entry_SNAPTODEFBUTTON,
4596 (union sysparam_all_entry *)&entry_USERPREFERENCESMASK,
4597 (union sysparam_all_entry *)&entry_WHEELSCROLLCHARS,
4598 (union sysparam_all_entry *)&entry_WHEELSCROLLLINES,
4599 (union sysparam_all_entry *)&entry_AUDIODESC_LOCALE,
4600 (union sysparam_all_entry *)&entry_AUDIODESC_ON,
4603 /***********************************************************************
4604 * get_config_key
4606 * Get a config key from either the app-specific or the default config
4608 static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
4609 WCHAR *buffer, DWORD size )
4611 WCHAR nameW[128];
4612 char buf[2048];
4613 KEY_VALUE_PARTIAL_INFORMATION *info = (void *)buf;
4615 asciiz_to_unicode( nameW, name );
4617 if (appkey && query_reg_value( appkey, nameW, info, sizeof(buf) ))
4619 size = min( info->DataLength, size - sizeof(WCHAR) );
4620 memcpy( buffer, info->Data, size );
4621 buffer[size / sizeof(WCHAR)] = 0;
4622 return 0;
4625 if (defkey && query_reg_value( defkey, nameW, info, sizeof(buf) ))
4627 size = min( info->DataLength, size - sizeof(WCHAR) );
4628 memcpy( buffer, info->Data, size );
4629 buffer[size / sizeof(WCHAR)] = 0;
4630 return 0;
4633 return ERROR_FILE_NOT_FOUND;
4636 void sysparams_init(void)
4638 WCHAR buffer[MAX_PATH+16], *p, *appname;
4639 DWORD i, dispos, dpi_scaling;
4640 WCHAR layout[KL_NAMELENGTH];
4641 pthread_mutexattr_t attr;
4642 HKEY hkey, appkey = 0;
4643 DWORD len;
4645 static const WCHAR software_wineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e'};
4646 static const WCHAR temporary_system_parametersW[] =
4647 {'T','e','m','p','o','r','a','r','y',' ','S','y','s','t','e','m',' ',
4648 'P','a','r','a','m','e','t','e','r','s'};
4649 static const WCHAR oneW[] = {'1',0};
4650 static const WCHAR kl_preloadW[] =
4651 {'K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','\\','P','r','e','l','o','a','d'};
4652 static const WCHAR x11driverW[] = {'\\','X','1','1',' ','D','r','i','v','e','r',0};
4654 pthread_mutexattr_init( &attr );
4655 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
4656 pthread_mutex_init( &user_mutex, &attr );
4657 pthread_mutexattr_destroy( &attr );
4659 if ((hkey = reg_create_key( hkcu_key, kl_preloadW, sizeof(kl_preloadW), 0, NULL )))
4661 if (NtUserGetKeyboardLayoutName( layout ))
4662 set_reg_value( hkey, oneW, REG_SZ, (const BYTE *)layout,
4663 (lstrlenW(layout) + 1) * sizeof(WCHAR) );
4664 NtClose( hkey );
4667 /* this one must be non-volatile */
4668 if (!(hkey = reg_create_key( hkcu_key, software_wineW, sizeof(software_wineW), 0, NULL )))
4670 ERR("Can't create wine registry branch\n");
4671 return;
4674 /* @@ Wine registry key: HKCU\Software\Wine\Temporary System Parameters */
4675 if (!(volatile_base_key = reg_create_key( hkey, temporary_system_parametersW,
4676 sizeof(temporary_system_parametersW),
4677 REG_OPTION_VOLATILE, &dispos )))
4678 ERR("Can't create non-permanent wine registry branch\n");
4680 NtClose( hkey );
4682 config_key = reg_create_key( NULL, config_keyW, sizeof(config_keyW), 0, NULL );
4684 get_dword_entry( (union sysparam_all_entry *)&entry_LOGPIXELS, 0, &system_dpi, 0 );
4685 if (!system_dpi) /* check fallback key */
4687 static const WCHAR log_pixelsW[] = {'L','o','g','P','i','x','e','l','s',0};
4688 static const WCHAR software_fontsW[] =
4689 {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s'};
4691 if ((hkey = reg_open_key( config_key, software_fontsW, sizeof(software_fontsW) )))
4693 char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(DWORD)])];
4694 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
4696 if (query_reg_value( hkey, log_pixelsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
4697 system_dpi = *(const DWORD *)value->Data;
4698 NtClose( hkey );
4701 if (!system_dpi) system_dpi = USER_DEFAULT_SCREEN_DPI;
4703 /* FIXME: what do the DpiScalingVer flags mean? */
4704 get_dword_entry( (union sysparam_all_entry *)&entry_DPISCALINGVER, 0, &dpi_scaling, 0 );
4705 if (!dpi_scaling) NtUserSetProcessDpiAwarenessContext( NTUSER_DPI_PER_MONITOR_AWARE, 0 );
4707 if (volatile_base_key && dispos == REG_CREATED_NEW_KEY) /* first process, initialize entries */
4709 for (i = 0; i < ARRAY_SIZE( default_entries ); i++)
4710 default_entries[i]->hdr.init( default_entries[i] );
4713 /* @@ Wine registry key: HKCU\Software\Wine\X11 Driver */
4714 hkey = reg_open_hkcu_key( "Software\\Wine\\X11 Driver" );
4716 /* open the app-specific key */
4718 appname = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer;
4719 if ((p = wcsrchr( appname, '/' ))) appname = p + 1;
4720 if ((p = wcsrchr( appname, '\\' ))) appname = p + 1;
4721 len = lstrlenW( appname );
4723 if (len && len < MAX_PATH)
4725 HKEY tmpkey;
4726 int i;
4728 for (i = 0; appname[i]; i++) buffer[i] = RtlDowncaseUnicodeChar( appname[i] );
4729 buffer[i] = 0;
4730 appname = buffer;
4731 memcpy( appname + i, x11driverW, sizeof(x11driverW) );
4733 /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\X11 Driver */
4734 if ((tmpkey = reg_open_hkcu_key( "Software\\Wine\\AppDefaults" )))
4736 appkey = reg_open_key( tmpkey, appname, lstrlenW( appname ) * sizeof(WCHAR) );
4737 NtClose( tmpkey );
4741 #define IS_OPTION_TRUE(ch) \
4742 ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
4744 if (!get_config_key( hkey, appkey, "GrabPointer", buffer, sizeof(buffer) ))
4745 grab_pointer = IS_OPTION_TRUE( buffer[0] );
4746 if (!get_config_key( hkey, appkey, "GrabFullscreen", buffer, sizeof(buffer) ))
4747 grab_fullscreen = IS_OPTION_TRUE( buffer[0] );
4749 #undef IS_OPTION_TRUE
4752 static BOOL update_desktop_wallpaper(void)
4754 /* FIXME: move implementation from user32 */
4755 entry_DESKWALLPAPER.hdr.loaded = entry_DESKPATTERN.hdr.loaded = FALSE;
4756 return TRUE;
4759 /***********************************************************************
4760 * NtUserSystemParametersInfoForDpi (win32u.@)
4762 BOOL WINAPI NtUserSystemParametersInfoForDpi( UINT action, UINT val, PVOID ptr, UINT winini, UINT dpi )
4764 BOOL ret = FALSE;
4766 switch (action)
4768 case SPI_GETICONTITLELOGFONT:
4769 ret = get_entry_dpi( &entry_ICONTITLELOGFONT, val, ptr, dpi );
4770 break;
4771 case SPI_GETNONCLIENTMETRICS:
4773 NONCLIENTMETRICSW *ncm = ptr;
4775 if (!ncm) break;
4776 ret = get_entry_dpi( &entry_BORDER, 0, &ncm->iBorderWidth, dpi ) &&
4777 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ncm->iScrollWidth, dpi ) &&
4778 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ncm->iScrollHeight, dpi ) &&
4779 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ncm->iCaptionWidth, dpi ) &&
4780 get_entry_dpi( &entry_CAPTIONHEIGHT, 0, &ncm->iCaptionHeight, dpi ) &&
4781 get_entry_dpi( &entry_CAPTIONLOGFONT, 0, &ncm->lfCaptionFont, dpi ) &&
4782 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ncm->iSmCaptionWidth, dpi ) &&
4783 get_entry_dpi( &entry_SMCAPTIONHEIGHT, 0, &ncm->iSmCaptionHeight, dpi ) &&
4784 get_entry_dpi( &entry_SMCAPTIONLOGFONT, 0, &ncm->lfSmCaptionFont, dpi ) &&
4785 get_entry_dpi( &entry_MENUWIDTH, 0, &ncm->iMenuWidth, dpi ) &&
4786 get_entry_dpi( &entry_MENUHEIGHT, 0, &ncm->iMenuHeight, dpi ) &&
4787 get_entry_dpi( &entry_MENULOGFONT, 0, &ncm->lfMenuFont, dpi ) &&
4788 get_entry_dpi( &entry_STATUSLOGFONT, 0, &ncm->lfStatusFont, dpi ) &&
4789 get_entry_dpi( &entry_MESSAGELOGFONT, 0, &ncm->lfMessageFont, dpi );
4790 if (ret && ncm->cbSize == sizeof(NONCLIENTMETRICSW))
4791 ret = get_entry_dpi( &entry_PADDEDBORDERWIDTH, 0, &ncm->iPaddedBorderWidth, dpi );
4792 normalize_nonclientmetrics( ncm );
4793 break;
4795 case SPI_GETICONMETRICS:
4797 ICONMETRICSW *im = ptr;
4798 if (im && im->cbSize == sizeof(*im))
4799 ret = get_entry_dpi( &entry_ICONHORIZONTALSPACING, 0, &im->iHorzSpacing, dpi ) &&
4800 get_entry_dpi( &entry_ICONVERTICALSPACING, 0, &im->iVertSpacing, dpi ) &&
4801 get_entry_dpi( &entry_ICONTITLEWRAP, 0, &im->iTitleWrap, dpi ) &&
4802 get_entry_dpi( &entry_ICONTITLELOGFONT, 0, &im->lfFont, dpi );
4803 break;
4805 default:
4806 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4807 break;
4809 return ret;
4812 /***********************************************************************
4813 * NtUserSystemParametersInfo (win32u.@)
4815 * Each system parameter has flag which shows whether the parameter
4816 * is loaded or not. Parameters, stored directly in SysParametersInfo are
4817 * loaded from registry only when they are requested and the flag is
4818 * "false", after the loading the flag is set to "true". On interprocess
4819 * notification of the parameter change the corresponding parameter flag is
4820 * set to "false". The parameter value will be reloaded when it is requested
4821 * the next time.
4822 * Parameters, backed by or depend on GetSystemMetrics are processed
4823 * differently. These parameters are always loaded. They are reloaded right
4824 * away on interprocess change notification. We can't do lazy loading because
4825 * we don't want to complicate GetSystemMetrics.
4826 * Parameters backed by driver settings are read from corresponding setting.
4827 * On the parameter change request the setting is changed. Interprocess change
4828 * notifications are ignored.
4829 * When parameter value is updated the changed value is stored in permanent
4830 * registry branch if saving is requested. Otherwise it is stored
4831 * in temporary branch
4833 * Some SPI values can also be stored as Twips values in the registry,
4834 * don't forget the conversion!
4836 BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT winini )
4838 #define WINE_SPI_FIXME(x) \
4839 case x: \
4841 static BOOL warn = TRUE; \
4842 if (warn) \
4844 warn = FALSE; \
4845 FIXME( "Unimplemented action: %u (%s)\n", x, #x ); \
4848 RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE ); \
4849 ret = FALSE; \
4850 break
4851 #define WINE_SPI_WARN(x) \
4852 case x: \
4853 WARN( "Ignored action: %u (%s)\n", x, #x ); \
4854 ret = TRUE; \
4855 break
4857 BOOL ret = user_driver->pSystemParametersInfo( action, val, ptr, winini );
4858 unsigned spi_idx = 0;
4860 if (!ret) switch (action)
4862 case SPI_GETBEEP:
4863 ret = get_entry( &entry_BEEP, val, ptr );
4864 break;
4865 case SPI_SETBEEP:
4866 ret = set_entry( &entry_BEEP, val, ptr, winini );
4867 break;
4868 case SPI_GETMOUSE:
4869 ret = get_entry( &entry_MOUSETHRESHOLD1, val, (INT *)ptr ) &&
4870 get_entry( &entry_MOUSETHRESHOLD2, val, (INT *)ptr + 1 ) &&
4871 get_entry( &entry_MOUSEACCELERATION, val, (INT *)ptr + 2 );
4872 break;
4873 case SPI_SETMOUSE:
4874 ret = set_entry( &entry_MOUSETHRESHOLD1, ((INT *)ptr)[0], ptr, winini ) &&
4875 set_entry( &entry_MOUSETHRESHOLD2, ((INT *)ptr)[1], ptr, winini ) &&
4876 set_entry( &entry_MOUSEACCELERATION, ((INT *)ptr)[2], ptr, winini );
4877 break;
4878 case SPI_GETBORDER:
4879 ret = get_entry( &entry_BORDER, val, ptr );
4880 if (*(INT*)ptr < 1) *(INT*)ptr = 1;
4881 break;
4882 case SPI_SETBORDER:
4883 ret = set_entry( &entry_BORDER, val, ptr, winini );
4884 break;
4885 case SPI_GETKEYBOARDSPEED:
4886 ret = get_entry( &entry_KEYBOARDSPEED, val, ptr );
4887 break;
4888 case SPI_SETKEYBOARDSPEED:
4889 if (val > 31) val = 31;
4890 ret = set_entry( &entry_KEYBOARDSPEED, val, ptr, winini );
4891 break;
4893 WINE_SPI_WARN(SPI_LANGDRIVER); /* not implemented in Windows */
4895 case SPI_ICONHORIZONTALSPACING:
4896 if (ptr != NULL)
4897 ret = get_entry( &entry_ICONHORIZONTALSPACING, val, ptr );
4898 else
4900 int min_val = map_to_dpi( 32, get_system_dpi() );
4901 ret = set_entry( &entry_ICONHORIZONTALSPACING, max( min_val, val ), ptr, winini );
4903 break;
4904 case SPI_GETSCREENSAVETIMEOUT:
4905 ret = get_entry( &entry_SCREENSAVETIMEOUT, val, ptr );
4906 break;
4907 case SPI_SETSCREENSAVETIMEOUT:
4908 ret = set_entry( &entry_SCREENSAVETIMEOUT, val, ptr, winini );
4909 break;
4910 case SPI_GETSCREENSAVEACTIVE:
4911 ret = get_entry( &entry_SCREENSAVEACTIVE, val, ptr );
4912 break;
4913 case SPI_SETSCREENSAVEACTIVE:
4914 ret = set_entry( &entry_SCREENSAVEACTIVE, val, ptr, winini );
4915 break;
4916 case SPI_GETGRIDGRANULARITY:
4917 ret = get_entry( &entry_GRIDGRANULARITY, val, ptr );
4918 break;
4919 case SPI_SETGRIDGRANULARITY:
4920 ret = set_entry( &entry_GRIDGRANULARITY, val, ptr, winini );
4921 break;
4922 case SPI_SETDESKWALLPAPER:
4923 if (!ptr || set_entry( &entry_DESKWALLPAPER, val, ptr, winini ))
4924 ret = update_desktop_wallpaper();
4925 break;
4926 case SPI_SETDESKPATTERN:
4927 if (!ptr || set_entry( &entry_DESKPATTERN, val, ptr, winini ))
4928 ret = update_desktop_wallpaper();
4929 break;
4930 case SPI_GETKEYBOARDDELAY:
4931 ret = get_entry( &entry_KEYBOARDDELAY, val, ptr );
4932 break;
4933 case SPI_SETKEYBOARDDELAY:
4934 ret = set_entry( &entry_KEYBOARDDELAY, val, ptr, winini );
4935 break;
4936 case SPI_ICONVERTICALSPACING:
4937 if (ptr != NULL)
4938 ret = get_entry( &entry_ICONVERTICALSPACING, val, ptr );
4939 else
4941 int min_val = map_to_dpi( 32, get_system_dpi() );
4942 ret = set_entry( &entry_ICONVERTICALSPACING, max( min_val, val ), ptr, winini );
4944 break;
4945 case SPI_GETICONTITLEWRAP:
4946 ret = get_entry( &entry_ICONTITLEWRAP, val, ptr );
4947 break;
4948 case SPI_SETICONTITLEWRAP:
4949 ret = set_entry( &entry_ICONTITLEWRAP, val, ptr, winini );
4950 break;
4951 case SPI_GETMENUDROPALIGNMENT:
4952 ret = get_entry( &entry_MENUDROPALIGNMENT, val, ptr );
4953 break;
4954 case SPI_SETMENUDROPALIGNMENT:
4955 ret = set_entry( &entry_MENUDROPALIGNMENT, val, ptr, winini );
4956 break;
4957 case SPI_SETDOUBLECLKWIDTH:
4958 ret = set_entry( &entry_DOUBLECLKWIDTH, val, ptr, winini );
4959 break;
4960 case SPI_SETDOUBLECLKHEIGHT:
4961 ret = set_entry( &entry_DOUBLECLKHEIGHT, val, ptr, winini );
4962 break;
4963 case SPI_GETICONTITLELOGFONT:
4964 ret = get_entry( &entry_ICONTITLELOGFONT, val, ptr );
4965 break;
4966 case SPI_SETDOUBLECLICKTIME:
4967 ret = set_entry( &entry_DOUBLECLICKTIME, val, ptr, winini );
4968 break;
4969 case SPI_SETMOUSEBUTTONSWAP:
4970 ret = set_entry( &entry_MOUSEBUTTONSWAP, val, ptr, winini );
4971 break;
4972 case SPI_SETICONTITLELOGFONT:
4973 ret = set_entry( &entry_ICONTITLELOGFONT, val, ptr, winini );
4974 break;
4975 case SPI_GETFASTTASKSWITCH:
4976 if (!ptr) return FALSE;
4977 *(BOOL *)ptr = TRUE;
4978 ret = TRUE;
4979 break;
4980 case SPI_SETFASTTASKSWITCH:
4981 /* the action is disabled */
4982 ret = FALSE;
4983 break;
4984 case SPI_SETDRAGFULLWINDOWS:
4985 ret = set_entry( &entry_DRAGFULLWINDOWS, val, ptr, winini );
4986 break;
4987 case SPI_GETDRAGFULLWINDOWS:
4988 ret = get_entry( &entry_DRAGFULLWINDOWS, val, ptr );
4989 break;
4990 case SPI_GETNONCLIENTMETRICS:
4992 NONCLIENTMETRICSW *nm = ptr;
4993 int padded_border;
4995 if (!ptr) return FALSE;
4997 ret = get_entry( &entry_BORDER, 0, &nm->iBorderWidth ) &&
4998 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border ) &&
4999 get_entry( &entry_SCROLLWIDTH, 0, &nm->iScrollWidth ) &&
5000 get_entry( &entry_SCROLLHEIGHT, 0, &nm->iScrollHeight ) &&
5001 get_entry( &entry_CAPTIONWIDTH, 0, &nm->iCaptionWidth ) &&
5002 get_entry( &entry_CAPTIONHEIGHT, 0, &nm->iCaptionHeight ) &&
5003 get_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont ) &&
5004 get_entry( &entry_SMCAPTIONWIDTH, 0, &nm->iSmCaptionWidth ) &&
5005 get_entry( &entry_SMCAPTIONHEIGHT, 0, &nm->iSmCaptionHeight ) &&
5006 get_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont ) &&
5007 get_entry( &entry_MENUWIDTH, 0, &nm->iMenuWidth ) &&
5008 get_entry( &entry_MENUHEIGHT, 0, &nm->iMenuHeight ) &&
5009 get_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont ) &&
5010 get_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont ) &&
5011 get_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont );
5012 if (ret)
5014 nm->iBorderWidth += padded_border;
5015 if (nm->cbSize == sizeof(NONCLIENTMETRICSW)) nm->iPaddedBorderWidth = 0;
5017 normalize_nonclientmetrics( nm );
5018 break;
5020 case SPI_SETNONCLIENTMETRICS:
5022 LPNONCLIENTMETRICSW nm = ptr;
5023 int padded_border;
5025 if (nm && (nm->cbSize == sizeof(NONCLIENTMETRICSW) ||
5026 nm->cbSize == FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth)))
5028 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border );
5030 ret = set_entry( &entry_BORDER, nm->iBorderWidth - padded_border, NULL, winini ) &&
5031 set_entry( &entry_SCROLLWIDTH, nm->iScrollWidth, NULL, winini ) &&
5032 set_entry( &entry_SCROLLHEIGHT, nm->iScrollHeight, NULL, winini ) &&
5033 set_entry( &entry_CAPTIONWIDTH, nm->iCaptionWidth, NULL, winini ) &&
5034 set_entry( &entry_CAPTIONHEIGHT, nm->iCaptionHeight, NULL, winini ) &&
5035 set_entry( &entry_SMCAPTIONWIDTH, nm->iSmCaptionWidth, NULL, winini ) &&
5036 set_entry( &entry_SMCAPTIONHEIGHT, nm->iSmCaptionHeight, NULL, winini ) &&
5037 set_entry( &entry_MENUWIDTH, nm->iMenuWidth, NULL, winini ) &&
5038 set_entry( &entry_MENUHEIGHT, nm->iMenuHeight, NULL, winini ) &&
5039 set_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont, winini ) &&
5040 set_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont, winini ) &&
5041 set_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont, winini ) &&
5042 set_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont, winini ) &&
5043 set_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont, winini );
5045 break;
5047 case SPI_GETMINIMIZEDMETRICS:
5049 MINIMIZEDMETRICS *mm = ptr;
5050 if (mm && mm->cbSize == sizeof(*mm)) {
5051 ret = get_entry( &entry_MINWIDTH, 0, &mm->iWidth ) &&
5052 get_entry( &entry_MINHORZGAP, 0, &mm->iHorzGap ) &&
5053 get_entry( &entry_MINVERTGAP, 0, &mm->iVertGap ) &&
5054 get_entry( &entry_MINARRANGE, 0, &mm->iArrange );
5055 mm->iWidth = max( 0, mm->iWidth );
5056 mm->iHorzGap = max( 0, mm->iHorzGap );
5057 mm->iVertGap = max( 0, mm->iVertGap );
5058 mm->iArrange &= 0x0f;
5060 break;
5062 case SPI_SETMINIMIZEDMETRICS:
5064 MINIMIZEDMETRICS *mm = ptr;
5065 if (mm && mm->cbSize == sizeof(*mm))
5066 ret = set_entry( &entry_MINWIDTH, max( 0, mm->iWidth ), NULL, winini ) &&
5067 set_entry( &entry_MINHORZGAP, max( 0, mm->iHorzGap ), NULL, winini ) &&
5068 set_entry( &entry_MINVERTGAP, max( 0, mm->iVertGap ), NULL, winini ) &&
5069 set_entry( &entry_MINARRANGE, mm->iArrange & 0x0f, NULL, winini );
5070 break;
5072 case SPI_GETICONMETRICS:
5074 ICONMETRICSW *icon = ptr;
5075 if(icon && icon->cbSize == sizeof(*icon))
5077 ret = get_entry( &entry_ICONHORIZONTALSPACING, 0, &icon->iHorzSpacing ) &&
5078 get_entry( &entry_ICONVERTICALSPACING, 0, &icon->iVertSpacing ) &&
5079 get_entry( &entry_ICONTITLEWRAP, 0, &icon->iTitleWrap ) &&
5080 get_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont );
5082 break;
5084 case SPI_SETICONMETRICS:
5086 ICONMETRICSW *icon = ptr;
5087 if (icon && icon->cbSize == sizeof(*icon))
5088 ret = set_entry( &entry_ICONVERTICALSPACING, max(32,icon->iVertSpacing), NULL, winini ) &&
5089 set_entry( &entry_ICONHORIZONTALSPACING, max(32,icon->iHorzSpacing), NULL, winini ) &&
5090 set_entry( &entry_ICONTITLEWRAP, icon->iTitleWrap, NULL, winini ) &&
5091 set_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont, winini );
5092 break;
5094 case SPI_SETWORKAREA:
5096 if (!ptr) return FALSE;
5097 spi_idx = SPI_SETWORKAREA_IDX;
5098 work_area = *(RECT*)ptr;
5099 spi_loaded[spi_idx] = TRUE;
5100 ret = TRUE;
5101 break;
5103 case SPI_GETWORKAREA:
5105 if (!ptr) return FALSE;
5107 spi_idx = SPI_SETWORKAREA_IDX;
5108 if (!spi_loaded[spi_idx])
5110 struct monitor *monitor;
5112 if (!lock_display_devices()) return FALSE;
5114 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
5116 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
5117 work_area = monitor->rc_work;
5118 break;
5121 unlock_display_devices();
5122 spi_loaded[spi_idx] = TRUE;
5124 *(RECT *)ptr = map_dpi_rect( work_area, system_dpi, get_thread_dpi() );
5125 ret = TRUE;
5126 TRACE("work area %s\n", wine_dbgstr_rect( &work_area ));
5127 break;
5130 WINE_SPI_FIXME(SPI_SETPENWINDOWS);
5132 case SPI_GETFILTERKEYS:
5134 LPFILTERKEYS filter_keys = ptr;
5135 WARN("SPI_GETFILTERKEYS not fully implemented\n");
5136 if (filter_keys && filter_keys->cbSize == sizeof(FILTERKEYS))
5138 /* Indicate that no FilterKeys feature available */
5139 filter_keys->dwFlags = 0;
5140 filter_keys->iWaitMSec = 0;
5141 filter_keys->iDelayMSec = 0;
5142 filter_keys->iRepeatMSec = 0;
5143 filter_keys->iBounceMSec = 0;
5144 ret = TRUE;
5146 break;
5148 WINE_SPI_FIXME(SPI_SETFILTERKEYS);
5150 case SPI_GETTOGGLEKEYS:
5152 LPTOGGLEKEYS toggle_keys = ptr;
5153 WARN("SPI_GETTOGGLEKEYS not fully implemented\n");
5154 if (toggle_keys && toggle_keys->cbSize == sizeof(TOGGLEKEYS))
5156 /* Indicate that no ToggleKeys feature available */
5157 toggle_keys->dwFlags = 0;
5158 ret = TRUE;
5160 break;
5163 WINE_SPI_FIXME(SPI_SETTOGGLEKEYS);
5165 case SPI_GETMOUSEKEYS:
5167 MOUSEKEYS *mouse_keys = ptr;
5168 WARN("SPI_GETMOUSEKEYS not fully implemented\n");
5169 if (mouse_keys && mouse_keys->cbSize == sizeof(MOUSEKEYS))
5171 /* Indicate that no MouseKeys feature available */
5172 mouse_keys->dwFlags = 0;
5173 mouse_keys->iMaxSpeed = 360;
5174 mouse_keys->iTimeToMaxSpeed = 1000;
5175 mouse_keys->iCtrlSpeed = 0;
5176 mouse_keys->dwReserved1 = 0;
5177 mouse_keys->dwReserved2 = 0;
5178 ret = TRUE;
5180 break;
5183 WINE_SPI_FIXME(SPI_SETMOUSEKEYS);
5185 case SPI_GETSHOWSOUNDS:
5186 ret = get_entry( &entry_SHOWSOUNDS, val, ptr );
5187 break;
5188 case SPI_SETSHOWSOUNDS:
5189 ret = set_entry( &entry_SHOWSOUNDS, val, ptr, winini );
5190 break;
5191 case SPI_GETSTICKYKEYS:
5193 STICKYKEYS *sticky_keys = ptr;
5194 WARN("SPI_GETSTICKYKEYS not fully implemented\n");
5195 if (sticky_keys && sticky_keys->cbSize == sizeof(STICKYKEYS))
5197 /* Indicate that no StickyKeys feature available */
5198 sticky_keys->dwFlags = 0;
5199 ret = TRUE;
5201 break;
5204 WINE_SPI_FIXME(SPI_SETSTICKYKEYS);
5206 case SPI_GETACCESSTIMEOUT:
5208 ACCESSTIMEOUT *access_timeout = ptr;
5209 WARN("SPI_GETACCESSTIMEOUT not fully implemented\n");
5210 if (access_timeout && access_timeout->cbSize == sizeof(ACCESSTIMEOUT))
5212 /* Indicate that no accessibility features timeout is available */
5213 access_timeout->dwFlags = 0;
5214 access_timeout->iTimeOutMSec = 0;
5215 ret = TRUE;
5217 break;
5220 WINE_SPI_FIXME(SPI_SETACCESSTIMEOUT);
5222 case SPI_GETSERIALKEYS:
5224 LPSERIALKEYSW serial_keys = ptr;
5225 WARN("SPI_GETSERIALKEYS not fully implemented\n");
5226 if (serial_keys && serial_keys->cbSize == sizeof(SERIALKEYSW))
5228 /* Indicate that no SerialKeys feature available */
5229 serial_keys->dwFlags = 0;
5230 serial_keys->lpszActivePort = NULL;
5231 serial_keys->lpszPort = NULL;
5232 serial_keys->iBaudRate = 0;
5233 serial_keys->iPortState = 0;
5234 ret = TRUE;
5236 break;
5239 WINE_SPI_FIXME(SPI_SETSERIALKEYS);
5241 case SPI_GETSOUNDSENTRY:
5243 SOUNDSENTRYW *sound_sentry = ptr;
5244 WARN("SPI_GETSOUNDSENTRY not fully implemented\n");
5245 if (sound_sentry && sound_sentry->cbSize == sizeof(SOUNDSENTRYW))
5247 /* Indicate that no SoundSentry feature available */
5248 sound_sentry->dwFlags = 0;
5249 sound_sentry->iFSTextEffect = 0;
5250 sound_sentry->iFSTextEffectMSec = 0;
5251 sound_sentry->iFSTextEffectColorBits = 0;
5252 sound_sentry->iFSGrafEffect = 0;
5253 sound_sentry->iFSGrafEffectMSec = 0;
5254 sound_sentry->iFSGrafEffectColor = 0;
5255 sound_sentry->iWindowsEffect = 0;
5256 sound_sentry->iWindowsEffectMSec = 0;
5257 sound_sentry->lpszWindowsEffectDLL = 0;
5258 sound_sentry->iWindowsEffectOrdinal = 0;
5259 ret = TRUE;
5261 break;
5264 WINE_SPI_FIXME(SPI_SETSOUNDSENTRY);
5266 case SPI_GETHIGHCONTRAST:
5268 HIGHCONTRASTW *high_contrast = ptr;
5269 WARN("SPI_GETHIGHCONTRAST not fully implemented\n");
5270 if (high_contrast && high_contrast->cbSize == sizeof(HIGHCONTRASTW))
5272 /* Indicate that no high contrast feature available */
5273 high_contrast->dwFlags = 0;
5274 high_contrast->lpszDefaultScheme = NULL;
5275 ret = TRUE;
5277 break;
5280 WINE_SPI_FIXME(SPI_SETHIGHCONTRAST);
5282 case SPI_GETKEYBOARDPREF:
5283 ret = get_entry( &entry_KEYBOARDPREF, val, ptr );
5284 break;
5285 case SPI_SETKEYBOARDPREF:
5286 ret = set_entry( &entry_KEYBOARDPREF, val, ptr, winini );
5287 break;
5288 case SPI_GETSCREENREADER:
5289 ret = get_entry( &entry_SCREENREADER, val, ptr );
5290 break;
5291 case SPI_SETSCREENREADER:
5292 ret = set_entry( &entry_SCREENREADER, val, ptr, winini );
5293 break;
5295 case SPI_GETANIMATION:
5297 ANIMATIONINFO *anim_info = ptr;
5299 /* Tell it "disabled" */
5300 if (anim_info && anim_info->cbSize == sizeof(ANIMATIONINFO))
5302 /* Minimize and restore animation is disabled (nonzero == enabled) */
5303 anim_info->iMinAnimate = 0;
5304 ret = TRUE;
5306 break;
5309 WINE_SPI_WARN(SPI_SETANIMATION);
5311 case SPI_GETFONTSMOOTHING:
5312 ret = get_entry( &entry_FONTSMOOTHING, val, ptr );
5313 if (ret) *(UINT *)ptr = (*(UINT *)ptr != 0);
5314 break;
5315 case SPI_SETFONTSMOOTHING:
5316 val = val ? 2 : 0; /* Win NT4/2k/XP behavior */
5317 ret = set_entry( &entry_FONTSMOOTHING, val, ptr, winini );
5318 break;
5319 case SPI_SETDRAGWIDTH:
5320 ret = set_entry( &entry_DRAGWIDTH, val, ptr, winini );
5321 break;
5322 case SPI_SETDRAGHEIGHT:
5323 ret = set_entry( &entry_DRAGHEIGHT, val, ptr, winini );
5324 break;
5326 WINE_SPI_FIXME(SPI_SETHANDHELD);
5327 WINE_SPI_FIXME(SPI_GETLOWPOWERTIMEOUT);
5328 WINE_SPI_FIXME(SPI_GETPOWEROFFTIMEOUT);
5329 WINE_SPI_FIXME(SPI_SETLOWPOWERTIMEOUT);
5330 WINE_SPI_FIXME(SPI_SETPOWEROFFTIMEOUT);
5332 case SPI_GETLOWPOWERACTIVE:
5333 ret = get_entry( &entry_LOWPOWERACTIVE, val, ptr );
5334 break;
5335 case SPI_SETLOWPOWERACTIVE:
5336 ret = set_entry( &entry_LOWPOWERACTIVE, val, ptr, winini );
5337 break;
5338 case SPI_GETPOWEROFFACTIVE:
5339 ret = get_entry( &entry_POWEROFFACTIVE, val, ptr );
5340 break;
5341 case SPI_SETPOWEROFFACTIVE:
5342 ret = set_entry( &entry_POWEROFFACTIVE, val, ptr, winini );
5343 break;
5345 WINE_SPI_FIXME(SPI_SETCURSORS);
5346 WINE_SPI_FIXME(SPI_SETICONS);
5348 case SPI_GETDEFAULTINPUTLANG:
5349 ret = NtUserGetKeyboardLayout(0) != 0;
5350 break;
5352 WINE_SPI_FIXME(SPI_SETDEFAULTINPUTLANG);
5353 WINE_SPI_FIXME(SPI_SETLANGTOGGLE);
5355 case SPI_GETWINDOWSEXTENSION:
5356 WARN( "pretend no support for Win9x Plus! for now.\n" );
5357 ret = FALSE; /* yes, this is the result value */
5358 break;
5359 case SPI_SETMOUSETRAILS:
5360 ret = set_entry( &entry_MOUSETRAILS, val, ptr, winini );
5361 break;
5362 case SPI_GETMOUSETRAILS:
5363 ret = get_entry( &entry_MOUSETRAILS, val, ptr );
5364 break;
5365 case SPI_GETSNAPTODEFBUTTON:
5366 ret = get_entry( &entry_SNAPTODEFBUTTON, val, ptr );
5367 break;
5368 case SPI_SETSNAPTODEFBUTTON:
5369 ret = set_entry( &entry_SNAPTODEFBUTTON, val, ptr, winini );
5370 break;
5371 case SPI_SETSCREENSAVERRUNNING:
5372 ret = set_entry( &entry_SCREENSAVERRUNNING, val, ptr, winini );
5373 break;
5374 case SPI_GETMOUSEHOVERWIDTH:
5375 ret = get_entry( &entry_MOUSEHOVERWIDTH, val, ptr );
5376 break;
5377 case SPI_SETMOUSEHOVERWIDTH:
5378 ret = set_entry( &entry_MOUSEHOVERWIDTH, val, ptr, winini );
5379 break;
5380 case SPI_GETMOUSEHOVERHEIGHT:
5381 ret = get_entry( &entry_MOUSEHOVERHEIGHT, val, ptr );
5382 break;
5383 case SPI_SETMOUSEHOVERHEIGHT:
5384 ret = set_entry( &entry_MOUSEHOVERHEIGHT, val, ptr, winini );
5385 break;
5386 case SPI_GETMOUSEHOVERTIME:
5387 ret = get_entry( &entry_MOUSEHOVERTIME, val, ptr );
5388 break;
5389 case SPI_SETMOUSEHOVERTIME:
5390 ret = set_entry( &entry_MOUSEHOVERTIME, val, ptr, winini );
5391 break;
5392 case SPI_GETWHEELSCROLLLINES:
5393 ret = get_entry( &entry_WHEELSCROLLLINES, val, ptr );
5394 break;
5395 case SPI_SETWHEELSCROLLLINES:
5396 ret = set_entry( &entry_WHEELSCROLLLINES, val, ptr, winini );
5397 break;
5398 case SPI_GETMENUSHOWDELAY:
5399 ret = get_entry( &entry_MENUSHOWDELAY, val, ptr );
5400 break;
5401 case SPI_SETMENUSHOWDELAY:
5402 ret = set_entry( &entry_MENUSHOWDELAY, val, ptr, winini );
5403 break;
5404 case SPI_GETWHEELSCROLLCHARS:
5405 ret = get_entry( &entry_WHEELSCROLLCHARS, val, ptr );
5406 break;
5407 case SPI_SETWHEELSCROLLCHARS:
5408 ret = set_entry( &entry_WHEELSCROLLCHARS, val, ptr, winini );
5409 break;
5411 WINE_SPI_FIXME(SPI_GETSHOWIMEUI);
5412 WINE_SPI_FIXME(SPI_SETSHOWIMEUI);
5414 case SPI_GETMOUSESPEED:
5415 ret = get_entry( &entry_MOUSESPEED, val, ptr );
5416 break;
5417 case SPI_SETMOUSESPEED:
5418 ret = set_entry( &entry_MOUSESPEED, val, ptr, winini );
5419 break;
5420 case SPI_GETSCREENSAVERRUNNING:
5421 ret = get_entry( &entry_SCREENSAVERRUNNING, val, ptr );
5422 break;
5423 case SPI_GETDESKWALLPAPER:
5424 ret = get_entry( &entry_DESKWALLPAPER, val, ptr );
5425 break;
5426 case SPI_GETACTIVEWINDOWTRACKING:
5427 ret = get_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr );
5428 break;
5429 case SPI_SETACTIVEWINDOWTRACKING:
5430 ret = set_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr, winini );
5431 break;
5432 case SPI_GETMENUANIMATION:
5433 ret = get_entry( &entry_MENUANIMATION, val, ptr );
5434 break;
5435 case SPI_SETMENUANIMATION:
5436 ret = set_entry( &entry_MENUANIMATION, val, ptr, winini );
5437 break;
5438 case SPI_GETCOMBOBOXANIMATION:
5439 ret = get_entry( &entry_COMBOBOXANIMATION, val, ptr );
5440 break;
5441 case SPI_SETCOMBOBOXANIMATION:
5442 ret = set_entry( &entry_COMBOBOXANIMATION, val, ptr, winini );
5443 break;
5444 case SPI_GETLISTBOXSMOOTHSCROLLING:
5445 ret = get_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr );
5446 break;
5447 case SPI_SETLISTBOXSMOOTHSCROLLING:
5448 ret = set_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr, winini );
5449 break;
5450 case SPI_GETGRADIENTCAPTIONS:
5451 ret = get_entry( &entry_GRADIENTCAPTIONS, val, ptr );
5452 break;
5453 case SPI_SETGRADIENTCAPTIONS:
5454 ret = set_entry( &entry_GRADIENTCAPTIONS, val, ptr, winini );
5455 break;
5456 case SPI_GETKEYBOARDCUES:
5457 ret = get_entry( &entry_KEYBOARDCUES, val, ptr );
5458 break;
5459 case SPI_SETKEYBOARDCUES:
5460 ret = set_entry( &entry_KEYBOARDCUES, val, ptr, winini );
5461 break;
5462 case SPI_GETACTIVEWNDTRKZORDER:
5463 ret = get_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr );
5464 break;
5465 case SPI_SETACTIVEWNDTRKZORDER:
5466 ret = set_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr, winini );
5467 break;
5468 case SPI_GETHOTTRACKING:
5469 ret = get_entry( &entry_HOTTRACKING, val, ptr );
5470 break;
5471 case SPI_SETHOTTRACKING:
5472 ret = set_entry( &entry_HOTTRACKING, val, ptr, winini );
5473 break;
5474 case SPI_GETMENUFADE:
5475 ret = get_entry( &entry_MENUFADE, val, ptr );
5476 break;
5477 case SPI_SETMENUFADE:
5478 ret = set_entry( &entry_MENUFADE, val, ptr, winini );
5479 break;
5480 case SPI_GETSELECTIONFADE:
5481 ret = get_entry( &entry_SELECTIONFADE, val, ptr );
5482 break;
5483 case SPI_SETSELECTIONFADE:
5484 ret = set_entry( &entry_SELECTIONFADE, val, ptr, winini );
5485 break;
5486 case SPI_GETTOOLTIPANIMATION:
5487 ret = get_entry( &entry_TOOLTIPANIMATION, val, ptr );
5488 break;
5489 case SPI_SETTOOLTIPANIMATION:
5490 ret = set_entry( &entry_TOOLTIPANIMATION, val, ptr, winini );
5491 break;
5492 case SPI_GETTOOLTIPFADE:
5493 ret = get_entry( &entry_TOOLTIPFADE, val, ptr );
5494 break;
5495 case SPI_SETTOOLTIPFADE:
5496 ret = set_entry( &entry_TOOLTIPFADE, val, ptr, winini );
5497 break;
5498 case SPI_GETCURSORSHADOW:
5499 ret = get_entry( &entry_CURSORSHADOW, val, ptr );
5500 break;
5501 case SPI_SETCURSORSHADOW:
5502 ret = set_entry( &entry_CURSORSHADOW, val, ptr, winini );
5503 break;
5504 case SPI_GETMOUSESONAR:
5505 ret = get_entry( &entry_MOUSESONAR, val, ptr );
5506 break;
5507 case SPI_SETMOUSESONAR:
5508 ret = set_entry( &entry_MOUSESONAR, val, ptr, winini );
5509 break;
5510 case SPI_GETMOUSECLICKLOCK:
5511 ret = get_entry( &entry_MOUSECLICKLOCK, val, ptr );
5512 break;
5513 case SPI_SETMOUSECLICKLOCK:
5514 ret = set_entry( &entry_MOUSECLICKLOCK, val, ptr, winini );
5515 break;
5516 case SPI_GETMOUSEVANISH:
5517 ret = get_entry( &entry_MOUSEVANISH, val, ptr );
5518 break;
5519 case SPI_SETMOUSEVANISH:
5520 ret = set_entry( &entry_MOUSEVANISH, val, ptr, winini );
5521 break;
5522 case SPI_GETFLATMENU:
5523 ret = get_entry( &entry_FLATMENU, val, ptr );
5524 break;
5525 case SPI_SETFLATMENU:
5526 ret = set_entry( &entry_FLATMENU, val, ptr, winini );
5527 break;
5528 case SPI_GETDROPSHADOW:
5529 ret = get_entry( &entry_DROPSHADOW, val, ptr );
5530 break;
5531 case SPI_SETDROPSHADOW:
5532 ret = set_entry( &entry_DROPSHADOW, val, ptr, winini );
5533 break;
5534 case SPI_GETBLOCKSENDINPUTRESETS:
5535 ret = get_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr );
5536 break;
5537 case SPI_SETBLOCKSENDINPUTRESETS:
5538 ret = set_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr, winini );
5539 break;
5540 case SPI_GETUIEFFECTS:
5541 ret = get_entry( &entry_UIEFFECTS, val, ptr );
5542 break;
5543 case SPI_SETUIEFFECTS:
5544 /* FIXME: this probably should mask other UI effect values when unset */
5545 ret = set_entry( &entry_UIEFFECTS, val, ptr, winini );
5546 break;
5547 case SPI_GETDISABLEOVERLAPPEDCONTENT:
5548 ret = get_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr );
5549 break;
5550 case SPI_SETDISABLEOVERLAPPEDCONTENT:
5551 ret = set_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr, winini );
5552 break;
5553 case SPI_GETCLIENTAREAANIMATION:
5554 ret = get_entry( &entry_CLIENTAREAANIMATION, val, ptr );
5555 break;
5556 case SPI_SETCLIENTAREAANIMATION:
5557 ret = set_entry( &entry_CLIENTAREAANIMATION, val, ptr, winini );
5558 break;
5559 case SPI_GETCLEARTYPE:
5560 ret = get_entry( &entry_CLEARTYPE, val, ptr );
5561 break;
5562 case SPI_SETCLEARTYPE:
5563 ret = set_entry( &entry_CLEARTYPE, val, ptr, winini );
5564 break;
5565 case SPI_GETSPEECHRECOGNITION:
5566 ret = get_entry( &entry_SPEECHRECOGNITION, val, ptr );
5567 break;
5568 case SPI_SETSPEECHRECOGNITION:
5569 ret = set_entry( &entry_SPEECHRECOGNITION, val, ptr, winini );
5570 break;
5571 case SPI_GETFOREGROUNDLOCKTIMEOUT:
5572 ret = get_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr );
5573 break;
5574 case SPI_SETFOREGROUNDLOCKTIMEOUT:
5575 /* FIXME: this should check that the calling thread
5576 * is able to change the foreground window */
5577 ret = set_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr, winini );
5578 break;
5579 case SPI_GETACTIVEWNDTRKTIMEOUT:
5580 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
5581 break;
5582 case SPI_SETACTIVEWNDTRKTIMEOUT:
5583 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
5584 break;
5585 case SPI_GETFOREGROUNDFLASHCOUNT:
5586 ret = get_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr );
5587 break;
5588 case SPI_SETFOREGROUNDFLASHCOUNT:
5589 ret = set_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr, winini );
5590 break;
5591 case SPI_GETCARETWIDTH:
5592 ret = get_entry( &entry_CARETWIDTH, val, ptr );
5593 break;
5594 case SPI_SETCARETWIDTH:
5595 ret = set_entry( &entry_CARETWIDTH, val, ptr, winini );
5596 break;
5597 case SPI_GETMOUSECLICKLOCKTIME:
5598 ret = get_entry( &entry_MOUSECLICKLOCKTIME, val, ptr );
5599 break;
5600 case SPI_SETMOUSECLICKLOCKTIME:
5601 ret = set_entry( &entry_MOUSECLICKLOCKTIME, val, ptr, winini );
5602 break;
5603 case SPI_GETFONTSMOOTHINGTYPE:
5604 ret = get_entry( &entry_FONTSMOOTHINGTYPE, val, ptr );
5605 break;
5606 case SPI_SETFONTSMOOTHINGTYPE:
5607 ret = set_entry( &entry_FONTSMOOTHINGTYPE, val, ptr, winini );
5608 break;
5609 case SPI_GETFONTSMOOTHINGCONTRAST:
5610 ret = get_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr );
5611 break;
5612 case SPI_SETFONTSMOOTHINGCONTRAST:
5613 ret = set_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr, winini );
5614 break;
5615 case SPI_GETFOCUSBORDERWIDTH:
5616 ret = get_entry( &entry_FOCUSBORDERWIDTH, val, ptr );
5617 break;
5618 case SPI_GETFOCUSBORDERHEIGHT:
5619 ret = get_entry( &entry_FOCUSBORDERHEIGHT, val, ptr );
5620 break;
5621 case SPI_SETFOCUSBORDERWIDTH:
5622 ret = set_entry( &entry_FOCUSBORDERWIDTH, val, ptr, winini );
5623 break;
5624 case SPI_SETFOCUSBORDERHEIGHT:
5625 ret = set_entry( &entry_FOCUSBORDERHEIGHT, val, ptr, winini );
5626 break;
5627 case SPI_GETFONTSMOOTHINGORIENTATION:
5628 ret = get_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr );
5629 break;
5630 case SPI_SETFONTSMOOTHINGORIENTATION:
5631 ret = set_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr, winini );
5632 break;
5633 case SPI_GETAUDIODESCRIPTION:
5635 AUDIODESCRIPTION *audio = ptr;
5636 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
5638 ret = get_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled ) &&
5639 get_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale );
5641 break;
5643 case SPI_SETAUDIODESCRIPTION:
5645 AUDIODESCRIPTION *audio = ptr;
5646 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
5648 ret = set_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled, winini) &&
5649 set_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale, winini );
5651 break;
5653 default:
5654 FIXME( "Unknown action: %u\n", action );
5655 RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE );
5656 ret = FALSE;
5657 break;
5660 if (ret && (winini & SPIF_UPDATEINIFILE))
5662 static const WCHAR emptyW[1];
5663 if (winini & (SPIF_SENDWININICHANGE | SPIF_SENDCHANGE))
5664 send_message_timeout( HWND_BROADCAST, WM_SETTINGCHANGE, action, (LPARAM) emptyW,
5665 SMTO_ABORTIFHUNG, 2000, FALSE );
5667 TRACE( "(%u, %u, %p, %u) ret %d\n", action, val, ptr, winini, ret );
5668 return ret;
5670 #undef WINE_SPI_FIXME
5671 #undef WINE_SPI_WARN
5674 int get_system_metrics( int index )
5676 NONCLIENTMETRICSW ncm;
5677 MINIMIZEDMETRICS mm;
5678 ICONMETRICSW im;
5679 RECT rect;
5680 UINT ret;
5681 HDC hdc;
5683 /* some metrics are dynamic */
5684 switch (index)
5686 case SM_CXVSCROLL:
5687 case SM_CYHSCROLL:
5688 get_entry( &entry_SCROLLWIDTH, 0, &ret );
5689 return max( ret, 8 );
5690 case SM_CYCAPTION:
5691 ncm.cbSize = sizeof(ncm);
5692 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5693 return ncm.iCaptionHeight + 1;
5694 case SM_CXBORDER:
5695 case SM_CYBORDER:
5696 /* SM_C{X,Y}BORDER always returns 1 regardless of 'BorderWidth' value in registry */
5697 return 1;
5698 case SM_CXDLGFRAME:
5699 case SM_CYDLGFRAME:
5700 return 3;
5701 case SM_CYVTHUMB:
5702 case SM_CXHTHUMB:
5703 case SM_CYVSCROLL:
5704 case SM_CXHSCROLL:
5705 get_entry( &entry_SCROLLHEIGHT, 0, &ret );
5706 return max( ret, 8 );
5707 case SM_CXICON:
5708 case SM_CYICON:
5709 return map_to_dpi( 32, get_system_dpi() );
5710 case SM_CXCURSOR:
5711 case SM_CYCURSOR:
5712 ret = map_to_dpi( 32, get_system_dpi() );
5713 if (ret >= 64) return 64;
5714 if (ret >= 48) return 48;
5715 return 32;
5716 case SM_CYMENU:
5717 ncm.cbSize = sizeof(ncm);
5718 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5719 return ncm.iMenuHeight + 1;
5720 case SM_CXFULLSCREEN:
5721 /* see the remark for SM_CXMAXIMIZED, at least this formulation is correct */
5722 return get_system_metrics( SM_CXMAXIMIZED ) - 2 * get_system_metrics( SM_CXFRAME );
5723 case SM_CYFULLSCREEN:
5724 /* see the remark for SM_CYMAXIMIZED, at least this formulation is
5725 * correct */
5726 return get_system_metrics( SM_CYMAXIMIZED ) - get_system_metrics( SM_CYMIN );
5727 case SM_CYKANJIWINDOW:
5728 return 0;
5729 case SM_MOUSEPRESENT:
5730 return 1;
5731 case SM_DEBUG:
5732 return 0;
5733 case SM_SWAPBUTTON:
5734 get_entry( &entry_MOUSEBUTTONSWAP, 0, &ret );
5735 return ret;
5736 case SM_RESERVED1:
5737 case SM_RESERVED2:
5738 case SM_RESERVED3:
5739 case SM_RESERVED4:
5740 return 0;
5741 case SM_CXMIN:
5742 ncm.cbSize = sizeof(ncm);
5743 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5744 hdc = get_display_dc();
5745 get_text_metr_size( hdc, &ncm.lfCaptionFont, NULL, &ret );
5746 release_display_dc( hdc );
5747 return 3 * ncm.iCaptionWidth + ncm.iCaptionHeight + 4 * ret +
5748 2 * get_system_metrics( SM_CXFRAME ) + 4;
5749 case SM_CYMIN:
5750 return get_system_metrics( SM_CYCAPTION ) + 2 * get_system_metrics( SM_CYFRAME );
5751 case SM_CXSIZE:
5752 get_entry( &entry_CAPTIONWIDTH, 0, &ret );
5753 return max( ret, 8 );
5754 case SM_CYSIZE:
5755 ncm.cbSize = sizeof(ncm);
5756 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5757 return ncm.iCaptionHeight;
5758 case SM_CXFRAME:
5759 get_entry( &entry_BORDER, 0, &ret );
5760 ret = max( ret, 1 );
5761 return get_system_metrics( SM_CXDLGFRAME ) + ret;
5762 case SM_CYFRAME:
5763 get_entry( &entry_BORDER, 0, &ret );
5764 ret = max( ret, 1 );
5765 return get_system_metrics( SM_CYDLGFRAME ) + ret;
5766 case SM_CXMINTRACK:
5767 return get_system_metrics( SM_CXMIN );
5768 case SM_CYMINTRACK:
5769 return get_system_metrics( SM_CYMIN );
5770 case SM_CXDOUBLECLK:
5771 get_entry( &entry_DOUBLECLKWIDTH, 0, &ret );
5772 return ret;
5773 case SM_CYDOUBLECLK:
5774 get_entry( &entry_DOUBLECLKHEIGHT, 0, &ret );
5775 return ret;
5776 case SM_CXICONSPACING:
5777 im.cbSize = sizeof(im);
5778 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
5779 return im.iHorzSpacing;
5780 case SM_CYICONSPACING:
5781 im.cbSize = sizeof(im);
5782 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
5783 return im.iVertSpacing;
5784 case SM_MENUDROPALIGNMENT:
5785 NtUserSystemParametersInfo( SPI_GETMENUDROPALIGNMENT, 0, &ret, 0 );
5786 return ret;
5787 case SM_PENWINDOWS:
5788 return 0;
5789 case SM_DBCSENABLED:
5790 return ansi_cp.MaximumCharacterSize > 1;
5791 case SM_CMOUSEBUTTONS:
5792 return 3;
5793 case SM_SECURE:
5794 return 0;
5795 case SM_CXEDGE:
5796 return get_system_metrics( SM_CXBORDER ) + 1;
5797 case SM_CYEDGE:
5798 return get_system_metrics( SM_CYBORDER ) + 1;
5799 case SM_CXMINSPACING:
5800 mm.cbSize = sizeof(mm);
5801 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5802 return get_system_metrics( SM_CXMINIMIZED ) + mm.iHorzGap;
5803 case SM_CYMINSPACING:
5804 mm.cbSize = sizeof(mm);
5805 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5806 return get_system_metrics( SM_CYMINIMIZED ) + mm.iVertGap;
5807 case SM_CXSMICON:
5808 case SM_CYSMICON:
5809 return map_to_dpi( 16, get_system_dpi() ) & ~1;
5810 case SM_CYSMCAPTION:
5811 ncm.cbSize = sizeof(ncm);
5812 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5813 return ncm.iSmCaptionHeight + 1;
5814 case SM_CXSMSIZE:
5815 get_entry( &entry_SMCAPTIONWIDTH, 0, &ret );
5816 return ret;
5817 case SM_CYSMSIZE:
5818 ncm.cbSize = sizeof(ncm);
5819 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5820 return ncm.iSmCaptionHeight;
5821 case SM_CXMENUSIZE:
5822 get_entry( &entry_MENUWIDTH, 0, &ret );
5823 return ret;
5824 case SM_CYMENUSIZE:
5825 ncm.cbSize = sizeof(ncm);
5826 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5827 return ncm.iMenuHeight;
5828 case SM_ARRANGE:
5829 mm.cbSize = sizeof(mm);
5830 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5831 return mm.iArrange;
5832 case SM_CXMINIMIZED:
5833 mm.cbSize = sizeof(mm);
5834 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5835 return mm.iWidth + 6;
5836 case SM_CYMINIMIZED:
5837 ncm.cbSize = sizeof(ncm);
5838 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5839 return ncm.iCaptionHeight + 6;
5840 case SM_CXMAXTRACK:
5841 return get_system_metrics( SM_CXVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CXFRAME );
5842 case SM_CYMAXTRACK:
5843 return get_system_metrics( SM_CYVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CYFRAME );
5844 case SM_CXMAXIMIZED:
5845 /* FIXME: subtract the width of any vertical application toolbars*/
5846 return get_system_metrics( SM_CXSCREEN ) + 2 * get_system_metrics( SM_CXFRAME );
5847 case SM_CYMAXIMIZED:
5848 /* FIXME: subtract the width of any horizontal application toolbars*/
5849 return get_system_metrics( SM_CYSCREEN ) + 2 * get_system_metrics( SM_CYCAPTION );
5850 case SM_NETWORK:
5851 return 3; /* FIXME */
5852 case SM_CLEANBOOT:
5853 return 0; /* 0 = ok, 1 = failsafe, 2 = failsafe + network */
5854 case SM_CXDRAG:
5855 get_entry( &entry_DRAGWIDTH, 0, &ret );
5856 return ret;
5857 case SM_CYDRAG:
5858 get_entry( &entry_DRAGHEIGHT, 0, &ret );
5859 return ret;
5860 case SM_SHOWSOUNDS:
5861 get_entry( &entry_SHOWSOUNDS, 0, &ret );
5862 return ret;
5863 case SM_CXMENUCHECK:
5864 case SM_CYMENUCHECK:
5866 TEXTMETRICW tm;
5867 ncm.cbSize = sizeof(ncm);
5868 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5869 hdc = get_display_dc();
5870 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL );
5871 release_display_dc( hdc );
5872 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading + 1) / 2) * 2 - 1;
5874 case SM_SLOWMACHINE:
5875 return 0; /* Never true */
5876 case SM_MIDEASTENABLED:
5877 return 0; /* FIXME */
5878 case SM_MOUSEWHEELPRESENT:
5879 return 1;
5880 case SM_CXSCREEN:
5881 rect = get_primary_monitor_rect( get_thread_dpi() );
5882 return rect.right - rect.left;
5883 case SM_CYSCREEN:
5884 rect = get_primary_monitor_rect( get_thread_dpi() );
5885 return rect.bottom - rect.top;
5886 case SM_XVIRTUALSCREEN:
5887 rect = get_virtual_screen_rect( get_thread_dpi() );
5888 return rect.left;
5889 case SM_YVIRTUALSCREEN:
5890 rect = get_virtual_screen_rect( get_thread_dpi() );
5891 return rect.top;
5892 case SM_CXVIRTUALSCREEN:
5893 rect = get_virtual_screen_rect( get_thread_dpi() );
5894 return rect.right - rect.left;
5895 case SM_CYVIRTUALSCREEN:
5896 rect = get_virtual_screen_rect( get_thread_dpi() );
5897 return rect.bottom - rect.top;
5898 case SM_CMONITORS:
5899 if (!lock_display_devices()) return FALSE;
5900 ret = active_monitor_count();
5901 unlock_display_devices();
5902 return ret;
5903 case SM_SAMEDISPLAYFORMAT:
5904 return 1;
5905 case SM_IMMENABLED:
5906 return 0; /* FIXME */
5907 case SM_CXFOCUSBORDER:
5908 case SM_CYFOCUSBORDER:
5909 return 1;
5910 case SM_TABLETPC:
5911 case SM_MEDIACENTER:
5912 return 0;
5913 case SM_CMETRICS:
5914 return SM_CMETRICS;
5915 default:
5916 return 0;
5920 static int get_system_metrics_for_dpi( int index, unsigned int dpi )
5922 NONCLIENTMETRICSW ncm;
5923 ICONMETRICSW im;
5924 UINT ret;
5925 HDC hdc;
5927 /* some metrics are dynamic */
5928 switch (index)
5930 case SM_CXVSCROLL:
5931 case SM_CYHSCROLL:
5932 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ret, dpi );
5933 return max( ret, 8 );
5934 case SM_CYCAPTION:
5935 ncm.cbSize = sizeof(ncm);
5936 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5937 return ncm.iCaptionHeight + 1;
5938 case SM_CYVTHUMB:
5939 case SM_CXHTHUMB:
5940 case SM_CYVSCROLL:
5941 case SM_CXHSCROLL:
5942 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ret, dpi );
5943 return max( ret, 8 );
5944 case SM_CXICON:
5945 case SM_CYICON:
5946 return map_to_dpi( 32, dpi );
5947 case SM_CXCURSOR:
5948 case SM_CYCURSOR:
5949 ret = map_to_dpi( 32, dpi );
5950 if (ret >= 64) return 64;
5951 if (ret >= 48) return 48;
5952 return 32;
5953 case SM_CYMENU:
5954 ncm.cbSize = sizeof(ncm);
5955 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5956 return ncm.iMenuHeight + 1;
5957 case SM_CXSIZE:
5958 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ret, dpi );
5959 return max( ret, 8 );
5960 case SM_CYSIZE:
5961 ncm.cbSize = sizeof(ncm);
5962 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5963 return ncm.iCaptionHeight;
5964 case SM_CXFRAME:
5965 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
5966 ret = max( ret, 1 );
5967 return get_system_metrics_for_dpi( SM_CXDLGFRAME, dpi ) + ret;
5968 case SM_CYFRAME:
5969 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
5970 ret = max( ret, 1 );
5971 return get_system_metrics_for_dpi( SM_CYDLGFRAME, dpi ) + ret;
5972 case SM_CXICONSPACING:
5973 im.cbSize = sizeof(im);
5974 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
5975 return im.iHorzSpacing;
5976 case SM_CYICONSPACING:
5977 im.cbSize = sizeof(im);
5978 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
5979 return im.iVertSpacing;
5980 case SM_CXSMICON:
5981 case SM_CYSMICON:
5982 return map_to_dpi( 16, dpi ) & ~1;
5983 case SM_CYSMCAPTION:
5984 ncm.cbSize = sizeof(ncm);
5985 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5986 return ncm.iSmCaptionHeight + 1;
5987 case SM_CXSMSIZE:
5988 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ret, dpi );
5989 return ret;
5990 case SM_CYSMSIZE:
5991 ncm.cbSize = sizeof(ncm);
5992 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5993 return ncm.iSmCaptionHeight;
5994 case SM_CXMENUSIZE:
5995 get_entry_dpi( &entry_MENUWIDTH, 0, &ret, dpi );
5996 return ret;
5997 case SM_CYMENUSIZE:
5998 ncm.cbSize = sizeof(ncm);
5999 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
6000 return ncm.iMenuHeight;
6001 case SM_CXMENUCHECK:
6002 case SM_CYMENUCHECK:
6004 TEXTMETRICW tm;
6005 ncm.cbSize = sizeof(ncm);
6006 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
6007 hdc = get_display_dc();
6008 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL);
6009 release_display_dc( hdc );
6010 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading - 1) | 1);
6012 default:
6013 return get_system_metrics( index );
6017 COLORREF get_sys_color( int index )
6019 COLORREF ret = 0;
6021 if (index >= 0 && index < ARRAY_SIZE( system_colors ))
6022 get_entry( &system_colors[index], 0, &ret );
6023 return ret;
6026 HBRUSH get_55aa_brush(void)
6028 static const WORD pattern[] = { 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa };
6029 static HBRUSH brush_55aa;
6031 if (!brush_55aa)
6033 HBITMAP bitmap = NtGdiCreateBitmap( 8, 8, 1, 1, pattern );
6034 HBRUSH brush = NtGdiCreatePatternBrushInternal( bitmap, FALSE, FALSE );
6035 NtGdiDeleteObjectApp( bitmap );
6036 make_gdi_object_system( brush, TRUE );
6037 if (InterlockedCompareExchangePointer( (void **)&brush_55aa, brush, 0 ))
6039 make_gdi_object_system( brush, FALSE );
6040 NtGdiDeleteObjectApp( brush );
6043 return brush_55aa;
6046 HBRUSH get_sys_color_brush( unsigned int index )
6048 if (index == COLOR_55AA_BRUSH) return get_55aa_brush();
6049 if (index >= ARRAY_SIZE( system_colors )) return 0;
6051 if (!system_colors[index].brush)
6053 HBRUSH brush = NtGdiCreateSolidBrush( get_sys_color( index ), NULL );
6054 make_gdi_object_system( brush, TRUE );
6055 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].brush, brush, 0 ))
6057 make_gdi_object_system( brush, FALSE );
6058 NtGdiDeleteObjectApp( brush );
6061 return system_colors[index].brush;
6064 HPEN get_sys_color_pen( unsigned int index )
6066 if (index >= ARRAY_SIZE( system_colors )) return 0;
6068 if (!system_colors[index].pen)
6070 HPEN pen = NtGdiCreatePen( PS_SOLID, 1, get_sys_color( index ), NULL );
6071 make_gdi_object_system( pen, TRUE );
6072 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].pen, pen, 0 ))
6074 make_gdi_object_system( pen, FALSE );
6075 NtGdiDeleteObjectApp( pen );
6078 return system_colors[index].pen;
6081 /**********************************************************************
6082 * NtUserGetDoubleClickTime (win32u.@)
6084 UINT WINAPI NtUserGetDoubleClickTime(void)
6086 UINT time = 0;
6088 get_entry( &entry_DOUBLECLICKTIME, 0, &time );
6089 if (!time) time = 500;
6090 return time;
6093 /*************************************************************************
6094 * NtUserSetSysColors (win32u.@)
6096 BOOL WINAPI NtUserSetSysColors( INT count, const INT *colors, const COLORREF *values )
6098 int i;
6100 if (IS_INTRESOURCE(colors)) return FALSE; /* stupid app passes a color instead of an array */
6102 for (i = 0; i < count; i++)
6103 if (colors[i] >= 0 && colors[i] <= ARRAY_SIZE( system_colors ))
6104 set_entry( &system_colors[colors[i]], values[i], 0, 0 );
6106 /* Send WM_SYSCOLORCHANGE message to all windows */
6107 send_message_timeout( HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0,
6108 SMTO_ABORTIFHUNG, 2000, FALSE );
6109 /* Repaint affected portions of all visible windows */
6110 NtUserRedrawWindow( 0, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN );
6111 return TRUE;
6115 static LONG dpi_awareness;
6117 /***********************************************************************
6118 * NtUserSetProcessDpiAwarenessContext (win32u.@)
6120 BOOL WINAPI NtUserSetProcessDpiAwarenessContext( ULONG awareness, ULONG unknown )
6122 switch (awareness)
6124 case NTUSER_DPI_UNAWARE:
6125 case NTUSER_DPI_SYSTEM_AWARE:
6126 case NTUSER_DPI_PER_MONITOR_AWARE:
6127 case NTUSER_DPI_PER_MONITOR_AWARE_V2:
6128 case NTUSER_DPI_PER_UNAWARE_GDISCALED:
6129 break;
6130 default:
6131 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
6132 return FALSE;
6135 return !InterlockedCompareExchange( &dpi_awareness, awareness, 0 );
6138 /***********************************************************************
6139 * NtUserGetProcessDpiAwarenessContext (win32u.@)
6141 ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process )
6143 DPI_AWARENESS val;
6145 if (process && process != GetCurrentProcess())
6147 WARN( "not supported on other process %p\n", process );
6148 return NTUSER_DPI_UNAWARE;
6151 val = ReadNoFence( &dpi_awareness );
6152 if (!val) return NTUSER_DPI_UNAWARE;
6153 return val;
6156 BOOL message_beep( UINT i )
6158 BOOL active = TRUE;
6159 NtUserSystemParametersInfo( SPI_GETBEEP, 0, &active, FALSE );
6160 if (active) user_driver->pBeep();
6161 return TRUE;
6164 static DWORD exiting_thread_id;
6166 /**********************************************************************
6167 * is_exiting_thread
6169 BOOL is_exiting_thread( DWORD tid )
6171 return tid == exiting_thread_id;
6174 static void thread_detach(void)
6176 struct user_thread_info *thread_info = get_user_thread_info();
6178 user_driver->pThreadDetach();
6180 free( thread_info->key_state );
6181 thread_info->key_state = 0;
6182 free( thread_info->rawinput );
6184 destroy_thread_windows();
6185 cleanup_imm_thread();
6186 NtClose( thread_info->server_queue );
6188 exiting_thread_id = 0;
6191 /***********************************************************************
6192 * NtUserCallNoParam (win32u.@)
6194 ULONG_PTR WINAPI NtUserCallNoParam( ULONG code )
6196 switch(code)
6198 case NtUserCallNoParam_DestroyCaret:
6199 return destroy_caret();
6201 case NtUserCallNoParam_GetDesktopWindow:
6202 return HandleToUlong( get_desktop_window() );
6204 case NtUserCallNoParam_GetDialogBaseUnits:
6205 return get_dialog_base_units();
6207 case NtUserCallNoParam_GetInputState:
6208 return get_input_state();
6210 case NtUserCallNoParam_GetProcessDefaultLayout:
6211 return process_layout;
6213 case NtUserCallNoParam_GetProgmanWindow:
6214 return HandleToUlong( get_progman_window() );
6216 case NtUserCallNoParam_GetShellWindow:
6217 return HandleToUlong( get_shell_window() );
6219 case NtUserCallNoParam_GetTaskmanWindow:
6220 return HandleToUlong( get_taskman_window() );
6222 case NtUserCallNoParam_ReleaseCapture:
6223 return release_capture();
6225 /* temporary exports */
6226 case NtUserExitingThread:
6227 exiting_thread_id = GetCurrentThreadId();
6228 return 0;
6230 case NtUserThreadDetach:
6231 thread_detach();
6232 return 0;
6234 default:
6235 FIXME( "invalid code %u\n", (int)code );
6236 return 0;
6240 /***********************************************************************
6241 * NtUserCallOneParam (win32u.@)
6243 ULONG_PTR WINAPI NtUserCallOneParam( ULONG_PTR arg, ULONG code )
6245 switch(code)
6247 case NtUserCallOneParam_BeginDeferWindowPos:
6248 return HandleToUlong( begin_defer_window_pos( arg ));
6250 case NtUserCallOneParam_CreateCursorIcon:
6251 return HandleToUlong( alloc_cursoricon_handle( arg ));
6253 case NtUserCallOneParam_CreateMenu:
6254 return HandleToUlong( create_menu( arg ) );
6256 case NtUserCallOneParam_EnableDC:
6257 return set_dce_flags( UlongToHandle(arg), DCHF_ENABLEDC );
6259 case NtUserCallOneParam_EnableThunkLock:
6260 enable_thunk_lock = arg;
6261 return 0;
6263 case NtUserCallOneParam_EnumClipboardFormats:
6264 return enum_clipboard_formats( arg );
6266 case NtUserCallOneParam_GetClipCursor:
6267 return get_clip_cursor( (RECT *)arg );
6269 case NtUserCallOneParam_GetCursorPos:
6270 return get_cursor_pos( (POINT *)arg );
6272 case NtUserCallOneParam_GetIconParam:
6273 return get_icon_param( UlongToHandle(arg) );
6275 case NtUserCallOneParam_GetMenuItemCount:
6276 return get_menu_item_count( UlongToHandle(arg) );
6278 case NtUserCallOneParam_GetSysColor:
6279 return get_sys_color( arg );
6281 case NtUserCallOneParam_IsWindowRectFullScreen:
6282 return is_window_rect_full_screen( (const RECT *)arg );
6284 case NtUserCallOneParam_RealizePalette:
6285 return realize_palette( UlongToHandle(arg) );
6287 case NtUserCallOneParam_GetPrimaryMonitorRect:
6288 *(RECT *)arg = get_primary_monitor_rect( 0 );
6289 return 1;
6291 case NtUserCallOneParam_GetSysColorBrush:
6292 return HandleToUlong( get_sys_color_brush(arg) );
6294 case NtUserCallOneParam_GetSysColorPen:
6295 return HandleToUlong( get_sys_color_pen(arg) );
6297 case NtUserCallOneParam_GetSystemMetrics:
6298 return get_system_metrics( arg );
6300 case NtUserCallOneParam_GetVirtualScreenRect:
6301 *(RECT *)arg = get_virtual_screen_rect( 0 );
6302 return 1;
6304 case NtUserCallOneParam_MessageBeep:
6305 return message_beep( arg );
6307 case NtUserCallOneParam_ReplyMessage:
6308 return reply_message_result( arg );
6310 case NtUserCallOneParam_SetCaretBlinkTime:
6311 return set_caret_blink_time( arg );
6313 case NtUserCallOneParam_SetProcessDefaultLayout:
6314 process_layout = arg;
6315 return TRUE;
6317 /* temporary exports */
6318 case NtUserGetDeskPattern:
6319 return get_entry( &entry_DESKPATTERN, 256, (WCHAR *)arg );
6321 default:
6322 FIXME( "invalid code %u\n", (int)code );
6323 return 0;
6327 /***********************************************************************
6328 * NtUserCallTwoParam (win32u.@)
6330 ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code )
6332 switch(code)
6334 case NtUserCallTwoParam_GetDialogProc:
6335 return (ULONG_PTR)get_dialog_proc( (DLGPROC)arg1, arg2 );
6337 case NtUserCallTwoParam_GetMenuInfo:
6338 return get_menu_info( UlongToHandle(arg1), (MENUINFO *)arg2 );
6340 case NtUserCallTwoParam_GetMonitorInfo:
6341 return get_monitor_info( UlongToHandle(arg1), (MONITORINFO *)arg2 );
6343 case NtUserCallTwoParam_GetSystemMetricsForDpi:
6344 return get_system_metrics_for_dpi( arg1, arg2 );
6346 case NtUserCallTwoParam_MonitorFromRect:
6347 return HandleToUlong( monitor_from_rect( (const RECT *)arg1, arg2, get_thread_dpi() ));
6349 case NtUserCallTwoParam_SetCaretPos:
6350 return set_caret_pos( arg1, arg2 );
6352 case NtUserCallTwoParam_SetIconParam:
6353 return set_icon_param( UlongToHandle(arg1), arg2 );
6355 case NtUserCallTwoParam_UnhookWindowsHook:
6356 return unhook_windows_hook( arg1, (HOOKPROC)arg2 );
6358 /* temporary exports */
6359 case NtUserAllocWinProc:
6360 return (UINT_PTR)alloc_winproc( (WNDPROC)arg1, arg2 );
6362 default:
6363 FIXME( "invalid code %u\n", (int)code );
6364 return 0;
6368 /***********************************************************************
6369 * NtUserDisplayConfigGetDeviceInfo (win32u.@)
6371 NTSTATUS WINAPI NtUserDisplayConfigGetDeviceInfo( DISPLAYCONFIG_DEVICE_INFO_HEADER *packet )
6373 NTSTATUS ret = STATUS_UNSUCCESSFUL;
6375 TRACE( "packet %p.\n", packet );
6377 if (!packet || packet->size < sizeof(*packet))
6378 return STATUS_UNSUCCESSFUL;
6380 switch (packet->type)
6382 case DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME:
6384 DISPLAYCONFIG_SOURCE_DEVICE_NAME *source_name = (DISPLAYCONFIG_SOURCE_DEVICE_NAME *)packet;
6385 struct adapter *adapter;
6387 TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME.\n" );
6389 if (packet->size < sizeof(*source_name))
6390 return STATUS_INVALID_PARAMETER;
6392 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
6394 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
6396 if (source_name->header.id != adapter->id) continue;
6397 if (memcmp( &source_name->header.adapterId, &adapter->gpu_luid, sizeof(adapter->gpu_luid) )) continue;
6399 lstrcpyW( source_name->viewGdiDeviceName, adapter->dev.device_name );
6400 ret = STATUS_SUCCESS;
6401 break;
6404 unlock_display_devices();
6405 return ret;
6407 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME:
6409 DISPLAYCONFIG_TARGET_DEVICE_NAME *target_name = (DISPLAYCONFIG_TARGET_DEVICE_NAME *)packet;
6410 char buffer[ARRAY_SIZE(target_name->monitorFriendlyDeviceName)];
6411 struct monitor *monitor;
6413 TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME.\n" );
6415 if (packet->size < sizeof(*target_name))
6416 return STATUS_INVALID_PARAMETER;
6418 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
6420 memset( &target_name->flags, 0, sizeof(*target_name) - offsetof(DISPLAYCONFIG_TARGET_DEVICE_NAME, flags) );
6422 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
6424 if (target_name->header.id != monitor->output_id) continue;
6425 if (memcmp( &target_name->header.adapterId, &monitor->adapter->gpu_luid,
6426 sizeof(monitor->adapter->gpu_luid) ))
6427 continue;
6429 target_name->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL;
6430 snprintf( buffer, ARRAY_SIZE(buffer), "Display%u", monitor->output_id + 1 );
6431 asciiz_to_unicode( target_name->monitorFriendlyDeviceName, buffer );
6432 lstrcpyW( target_name->monitorDevicePath, monitor->dev.interface_name );
6433 if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
6435 target_name->edidManufactureId = monitor->edid_info.manufacturer;
6436 target_name->edidProductCodeId = monitor->edid_info.product_code;
6437 target_name->flags.edidIdsValid = 1;
6439 if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_NAME)
6441 wcscpy( target_name->monitorFriendlyDeviceName, monitor->edid_info.monitor_name );
6442 target_name->flags.friendlyNameFromEdid = 1;
6444 ret = STATUS_SUCCESS;
6445 break;
6448 unlock_display_devices();
6449 return ret;
6451 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE:
6453 DISPLAYCONFIG_TARGET_PREFERRED_MODE *preferred_mode = (DISPLAYCONFIG_TARGET_PREFERRED_MODE *)packet;
6454 DISPLAYCONFIG_VIDEO_SIGNAL_INFO *signal_info = &preferred_mode->targetMode.targetVideoSignalInfo;
6455 unsigned int i, display_freq;
6456 DEVMODEW *found_mode = NULL;
6457 BOOL have_edid_mode = FALSE;
6458 struct monitor *monitor;
6460 FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE semi-stub.\n" );
6462 if (packet->size < sizeof(*preferred_mode))
6463 return STATUS_INVALID_PARAMETER;
6465 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
6467 memset( &preferred_mode->width, 0, sizeof(*preferred_mode) - offsetof(DISPLAYCONFIG_TARGET_PREFERRED_MODE, width) );
6469 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
6471 if (preferred_mode->header.id != monitor->output_id) continue;
6472 if (memcmp( &preferred_mode->header.adapterId, &monitor->adapter->gpu_luid,
6473 sizeof(monitor->adapter->gpu_luid) ))
6474 continue;
6476 for (i = 0; i < monitor->adapter->mode_count; ++i)
6478 DEVMODEW *mode = &monitor->adapter->modes[i];
6480 if (!have_edid_mode && monitor->edid_info.flags & MONITOR_INFO_HAS_PREFERRED_MODE
6481 && mode->dmPelsWidth == monitor->edid_info.preferred_width
6482 && mode->dmPelsHeight == monitor->edid_info.preferred_height)
6484 found_mode = mode;
6485 have_edid_mode = TRUE;
6488 if (!have_edid_mode && (!found_mode
6489 || (mode->dmPelsWidth > found_mode->dmPelsWidth && mode->dmPelsHeight >= found_mode->dmPelsHeight)
6490 || (mode->dmPelsHeight > found_mode->dmPelsHeight && mode->dmPelsWidth >= found_mode->dmPelsWidth)))
6491 found_mode = mode;
6493 if (mode->dmPelsWidth == found_mode->dmPelsWidth
6494 && mode->dmPelsHeight == found_mode->dmPelsHeight
6495 && mode->dmDisplayFrequency > found_mode->dmDisplayFrequency)
6496 found_mode = mode;
6499 if (!found_mode)
6501 ERR( "No mode found.\n" );
6502 break;
6504 preferred_mode->width = found_mode->dmPelsWidth;
6505 preferred_mode->height = found_mode->dmPelsHeight;
6506 display_freq = found_mode->dmDisplayFrequency;
6508 signal_info->pixelRate = display_freq * preferred_mode->width * preferred_mode->height;
6509 signal_info->hSyncFreq.Numerator = display_freq * preferred_mode->width;
6510 signal_info->hSyncFreq.Denominator = 1;
6511 signal_info->vSyncFreq.Numerator = display_freq;
6512 signal_info->vSyncFreq.Denominator = 1;
6513 signal_info->activeSize.cx = preferred_mode->width;
6514 signal_info->activeSize.cy = preferred_mode->height;
6515 signal_info->totalSize.cx = preferred_mode->width;
6516 signal_info->totalSize.cy = preferred_mode->height;
6517 signal_info->videoStandard = D3DKMDT_VSS_OTHER;
6518 if (!(found_mode->dmFields & DM_DISPLAYFLAGS))
6519 signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
6520 else if (found_mode->dmDisplayFlags & DM_INTERLACED)
6521 signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED;
6522 else
6523 signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
6524 ret = STATUS_SUCCESS;
6525 break;
6528 unlock_display_devices();
6529 return ret;
6531 case DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME:
6533 DISPLAYCONFIG_ADAPTER_NAME *adapter_name = (DISPLAYCONFIG_ADAPTER_NAME *)packet;
6535 FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME stub.\n" );
6537 if (packet->size < sizeof(*adapter_name))
6538 return STATUS_INVALID_PARAMETER;
6540 return STATUS_NOT_SUPPORTED;
6542 case DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE:
6543 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE:
6544 case DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION:
6545 case DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION:
6546 case DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO:
6547 case DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE:
6548 case DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL:
6549 default:
6550 FIXME( "Unimplemented packet type %u.\n", packet->type );
6551 return STATUS_INVALID_PARAMETER;