windows.gaming.input: Avoid leaking IDirectInputEffect reference (Valgrind).
[wine.git] / dlls / win32u / sysparams.c
blob832b56eb3807d87acf8834923c8a833823a03857
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 if ((ret = user_driver->pGetCurrentDisplaySettings( adapter->dev.device_name, is_primary, mode ))) return TRUE;
608 /* default implementation: read current display settings from the registry. */
610 mutex = get_display_device_init_mutex();
612 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
613 else if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
614 else
616 ret = read_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
617 NtClose( hkey );
620 release_display_device_init_mutex( mutex );
621 return ret;
624 static BOOL adapter_set_current_settings( const struct adapter *adapter, const DEVMODEW *mode )
626 HANDLE mutex;
627 HKEY hkey;
628 BOOL ret;
630 mutex = get_display_device_init_mutex();
632 if (!config_key && !(config_key = reg_open_key( NULL, config_keyW, sizeof(config_keyW) ))) ret = FALSE;
633 if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
634 else
636 ret = write_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
637 NtClose( hkey );
640 release_display_device_init_mutex( mutex );
641 return ret;
644 static int mode_compare(const void *p1, const void *p2)
646 BOOL a_interlaced, b_interlaced, a_stretched, b_stretched;
647 DWORD a_width, a_height, b_width, b_height;
648 const DEVMODEW *a = p1, *b = p2;
649 int ret;
651 /* Depth in descending order */
652 if ((ret = b->dmBitsPerPel - a->dmBitsPerPel)) return ret;
654 /* Use the width and height in landscape mode for comparison */
655 if (a->dmDisplayOrientation == DMDO_DEFAULT || a->dmDisplayOrientation == DMDO_180)
657 a_width = a->dmPelsWidth;
658 a_height = a->dmPelsHeight;
660 else
662 a_width = a->dmPelsHeight;
663 a_height = a->dmPelsWidth;
666 if (b->dmDisplayOrientation == DMDO_DEFAULT || b->dmDisplayOrientation == DMDO_180)
668 b_width = b->dmPelsWidth;
669 b_height = b->dmPelsHeight;
671 else
673 b_width = b->dmPelsHeight;
674 b_height = b->dmPelsWidth;
677 /* Width in ascending order */
678 if ((ret = a_width - b_width)) return ret;
680 /* Height in ascending order */
681 if ((ret = a_height - b_height)) return ret;
683 /* Frequency in descending order */
684 if ((ret = b->dmDisplayFrequency - a->dmDisplayFrequency)) return ret;
686 /* Orientation in ascending order */
687 if ((ret = a->dmDisplayOrientation - b->dmDisplayOrientation)) return ret;
689 if (!(a->dmFields & DM_DISPLAYFLAGS)) a_interlaced = FALSE;
690 else a_interlaced = !!(a->dmDisplayFlags & DM_INTERLACED);
691 if (!(b->dmFields & DM_DISPLAYFLAGS)) b_interlaced = FALSE;
692 else b_interlaced = !!(b->dmDisplayFlags & DM_INTERLACED);
694 /* Interlaced in ascending order */
695 if ((ret = a_interlaced - b_interlaced)) return ret;
697 if (!(a->dmFields & DM_DISPLAYFIXEDOUTPUT)) a_stretched = FALSE;
698 else a_stretched = a->dmDisplayFixedOutput == DMDFO_STRETCH;
699 if (!(b->dmFields & DM_DISPLAYFIXEDOUTPUT)) b_stretched = FALSE;
700 else b_stretched = b->dmDisplayFixedOutput == DMDFO_STRETCH;
702 /* Stretched in ascending order */
703 if ((ret = a_stretched - b_stretched)) return ret;
705 return 0;
708 static unsigned int query_reg_subkey_value( HKEY hkey, const WCHAR *name, unsigned int name_size,
709 KEY_VALUE_PARTIAL_INFORMATION *value, unsigned int size )
711 HKEY subkey;
713 if (!(subkey = reg_open_key( hkey, name, name_size ))) return 0;
714 size = query_reg_value( subkey, NULL, value, size );
715 NtClose( subkey );
716 return size;
719 static BOOL read_display_adapter_settings( unsigned int index, struct adapter *info )
721 char buffer[4096];
722 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
723 WCHAR *value_str = (WCHAR *)value->Data;
724 DEVMODEW *mode;
725 DWORD i, size;
726 HKEY hkey;
728 if (!enum_key && !(enum_key = reg_open_key( NULL, enum_keyW, sizeof(enum_keyW) )))
729 return FALSE;
731 /* Find adapter */
732 sprintf( buffer, "\\Device\\Video%d", index );
733 size = query_reg_ascii_value( video_key, buffer, value, sizeof(buffer) );
734 if (!size || value->Type != REG_SZ ||
735 value->DataLength <= sizeof("\\Registry\\Machine\\") * sizeof(WCHAR))
736 return FALSE;
738 /* DeviceKey */
739 memcpy( info->dev.device_key, value_str, value->DataLength );
740 info->config_key = info->dev.device_key + sizeof("\\Registry\\Machine\\") - 1;
742 if (!(hkey = reg_open_key( NULL, value_str, value->DataLength - sizeof(WCHAR) )))
743 return FALSE;
745 /* DeviceString */
746 if (query_reg_value( hkey, driver_descW, value, sizeof(buffer) ) && value->Type == REG_SZ)
747 memcpy( info->dev.device_string, value_str, value->DataLength );
748 NtClose( hkey );
750 /* DeviceName */
751 sprintf( buffer, "\\\\.\\DISPLAY%d", index + 1 );
752 asciiz_to_unicode( info->dev.device_name, buffer );
754 if (!(hkey = reg_open_key( config_key, info->config_key,
755 lstrlenW( info->config_key ) * sizeof(WCHAR) )))
756 return FALSE;
758 /* StateFlags */
759 if (query_reg_value( hkey, state_flagsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
760 info->dev.state_flags = *(const DWORD *)value->Data;
762 /* Interface name */
763 info->dev.interface_name[0] = 0;
765 /* ModeCount */
766 if (query_reg_value( hkey, mode_countW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
767 info->mode_count = *(const DWORD *)value->Data;
769 /* Modes, allocate an extra mode for easier iteration */
770 if ((info->modes = calloc( info->mode_count + 1, sizeof(DEVMODEW) )))
772 for (i = 0, mode = info->modes; i < info->mode_count; i++)
774 mode->dmSize = offsetof(DEVMODEW, dmICMMethod);
775 if (!read_adapter_mode( hkey, i, mode )) break;
776 mode = NEXT_DEVMODEW(mode);
778 info->mode_count = i;
780 qsort(info->modes, info->mode_count, sizeof(*info->modes) + info->modes->dmDriverExtra, mode_compare);
783 /* DeviceID */
784 size = query_reg_value( hkey, gpu_idW, value, sizeof(buffer) );
785 NtClose( hkey );
786 if (!size || value->Type != REG_SZ || !info->mode_count || !info->modes) return FALSE;
788 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
789 return FALSE;
791 size = query_reg_subkey_value( hkey, devpropkey_gpu_luidW, sizeof(devpropkey_gpu_luidW), value, sizeof(buffer) );
792 if (size != sizeof(info->gpu_luid))
794 NtClose( hkey );
795 return FALSE;
797 memcpy( &info->gpu_luid, value->Data, sizeof(info->gpu_luid) );
799 size = query_reg_value( hkey, hardware_idW, value, sizeof(buffer) );
800 NtClose( hkey );
801 if (!size || (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
802 return FALSE;
804 lstrcpyW( info->dev.device_id, value_str );
805 return TRUE;
808 static BOOL read_monitor_settings( struct adapter *adapter, UINT index, struct monitor *monitor )
810 char buffer[4096];
811 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
812 WCHAR *device_name, *value_str = (WCHAR *)value->Data, *ptr;
813 HKEY hkey, subkey;
814 DWORD size, len;
816 monitor->flags = adapter->id ? 0 : MONITORINFOF_PRIMARY;
818 /* DeviceName */
819 sprintf( buffer, "\\\\.\\DISPLAY%d\\Monitor%d", adapter->id + 1, index );
820 asciiz_to_unicode( monitor->dev.device_name, buffer );
822 if (!(hkey = reg_open_key( config_key, adapter->config_key,
823 lstrlenW( adapter->config_key ) * sizeof(WCHAR) )))
824 return FALSE;
826 /* Interface name */
827 sprintf( buffer, "MonitorID%u", index );
828 size = query_reg_ascii_value( hkey, buffer, value, sizeof(buffer) );
829 NtClose( hkey );
830 if (!size || value->Type != REG_SZ) return FALSE;
831 len = asciiz_to_unicode( monitor->dev.interface_name, "\\\\\?\\" ) / sizeof(WCHAR) - 1;
832 memcpy( monitor->dev.interface_name + len, value_str, value->DataLength - sizeof(WCHAR) );
833 len += value->DataLength / sizeof(WCHAR) - 1;
834 monitor->dev.interface_name[len++] = '#';
835 memcpy( monitor->dev.interface_name + len, guid_devinterface_monitorW,
836 sizeof(guid_devinterface_monitorW) );
838 /* Replace '\\' with '#' after prefix */
839 for (ptr = monitor->dev.interface_name + ARRAYSIZE("\\\\\?\\") - 1; *ptr; ptr++)
840 if (*ptr == '\\') *ptr = '#';
842 if (!(hkey = reg_open_key( enum_key, value_str, value->DataLength - sizeof(WCHAR) )))
843 return FALSE;
845 /* StateFlags, WINE_DEVPROPKEY_MONITOR_STATEFLAGS */
846 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_stateflagsW,
847 sizeof(wine_devpropkey_monitor_stateflagsW),
848 value, sizeof(buffer) );
849 if (size != sizeof(monitor->dev.state_flags))
851 NtClose( hkey );
852 return FALSE;
854 monitor->dev.state_flags = *(const DWORD *)value->Data;
856 /* Output ID */
857 size = query_reg_subkey_value( hkey, devpropkey_monitor_output_idW,
858 sizeof(devpropkey_monitor_output_idW),
859 value, sizeof(buffer) );
860 if (size != sizeof(monitor->output_id))
862 NtClose( hkey );
863 return FALSE;
865 monitor->output_id = *(const unsigned int *)value->Data;
867 /* rc_monitor, WINE_DEVPROPKEY_MONITOR_RCMONITOR */
868 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcmonitorW,
869 sizeof(wine_devpropkey_monitor_rcmonitorW),
870 value, sizeof(buffer) );
871 if (size != sizeof(monitor->rc_monitor))
873 NtClose( hkey );
874 return FALSE;
876 monitor->rc_monitor = *(const RECT *)value->Data;
878 /* rc_work, WINE_DEVPROPKEY_MONITOR_RCWORK */
879 size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcworkW,
880 sizeof(wine_devpropkey_monitor_rcworkW),
881 value, sizeof(buffer) );
882 if (size != sizeof(monitor->rc_work))
884 NtClose( hkey );
885 return FALSE;
887 monitor->rc_work = *(const RECT *)value->Data;
889 /* DeviceString */
890 if (!query_reg_value( hkey, device_descW, value, sizeof(buffer) ) || value->Type != REG_SZ)
892 NtClose( hkey );
893 return FALSE;
895 memcpy( monitor->dev.device_string, value->Data, value->DataLength );
897 /* DeviceKey */
898 if (!query_reg_value( hkey, driverW, value, sizeof(buffer) ) || value->Type != REG_SZ)
900 NtClose( hkey );
901 return FALSE;
903 size = asciiz_to_unicode( monitor->dev.device_key,
904 "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\" );
905 device_name = &monitor->dev.device_key[size / sizeof(WCHAR) - 1];
906 memcpy( device_name, value_str, value->DataLength );
908 /* DeviceID */
909 if (!query_reg_value( hkey, hardware_idW, value, sizeof(buffer) ) ||
910 (value->Type != REG_SZ && value->Type != REG_MULTI_SZ))
912 NtClose( hkey );
913 return FALSE;
915 size = lstrlenW( value_str );
916 memcpy( monitor->dev.device_id, value_str, size * sizeof(WCHAR) );
917 monitor->dev.device_id[size++] = '\\';
918 lstrcpyW( monitor->dev.device_id + size, device_name );
920 /* EDID */
921 if ((subkey = reg_open_key( hkey, device_parametersW, sizeof(device_parametersW) )))
923 if (query_reg_value( subkey, edidW, value, sizeof(buffer) ))
924 get_monitor_info_from_edid( &monitor->edid_info, value->Data, value->DataLength );
925 NtClose( subkey );
928 NtClose( hkey );
929 return TRUE;
932 static void reg_empty_key( HKEY root, const char *key_name )
934 char buffer[4096];
935 KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer;
936 KEY_VALUE_FULL_INFORMATION *value = (KEY_VALUE_FULL_INFORMATION *)buffer;
937 WCHAR bufferW[512];
938 DWORD size;
939 HKEY hkey;
941 if (key_name)
942 hkey = reg_open_key( root, bufferW, asciiz_to_unicode( bufferW, key_name ) - sizeof(WCHAR) );
943 else
944 hkey = root;
946 while (!NtEnumerateKey( hkey, 0, KeyNodeInformation, key, sizeof(buffer), &size ))
947 reg_delete_tree( hkey, key->Name, key->NameLength );
949 while (!NtEnumerateValueKey( hkey, 0, KeyValueFullInformation, value, sizeof(buffer), &size ))
951 UNICODE_STRING name = { value->NameLength, value->NameLength, value->Name };
952 NtDeleteValueKey( hkey, &name );
955 if (hkey != root) NtClose( hkey );
958 static void prepare_devices(void)
960 char buffer[4096];
961 KEY_NODE_INFORMATION *key = (void *)buffer;
962 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
963 WCHAR *value_str = (WCHAR *)value->Data;
964 WCHAR bufferW[128];
965 unsigned i = 0;
966 DWORD size;
967 HKEY hkey, subkey, device_key, prop_key;
969 if (!enum_key) enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL );
970 if (!control_key) control_key = reg_create_key( NULL, control_keyW, sizeof(control_keyW), 0, NULL );
971 if (!video_key) video_key = reg_create_key( NULL, devicemap_video_keyW, sizeof(devicemap_video_keyW),
972 REG_OPTION_VOLATILE, NULL );
974 /* delete monitors */
975 reg_empty_key( enum_key, "DISPLAY" );
976 sprintf( buffer, "Class\\%s", guid_devclass_monitorA );
977 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
978 0, NULL );
979 reg_empty_key( hkey, NULL );
980 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
981 NtClose( hkey );
983 /* delete adapters */
984 reg_empty_key( video_key, NULL );
986 /* clean GPUs */
987 sprintf( buffer, "Class\\%s", guid_devclass_displayA );
988 hkey = reg_create_key( control_key, bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR),
989 0, NULL );
990 reg_empty_key( hkey, NULL );
991 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
992 NtClose( hkey );
994 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
996 /* To preserve GPU GUIDs, mark them as not present and delete them in cleanup_devices if needed. */
997 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
999 unsigned int j = 0;
1001 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
1003 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
1005 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
1006 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1007 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
1009 NtClose( device_key );
1010 continue;
1013 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1014 if (size == sizeof(guid_devclass_displayW) &&
1015 !wcscmp( (const WCHAR *)value->Data, guid_devclass_displayW ) &&
1016 (prop_key = reg_create_key( device_key, devpropkey_device_ispresentW,
1017 sizeof(devpropkey_device_ispresentW), 0, NULL )))
1019 BOOL present = FALSE;
1020 set_reg_value( prop_key, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
1021 &present, sizeof(present) );
1022 NtClose( prop_key );
1025 NtClose( device_key );
1028 NtClose( subkey );
1031 NtClose( hkey );
1034 static void cleanup_devices(void)
1036 char buffer[4096];
1037 KEY_NODE_INFORMATION *key = (void *)buffer;
1038 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1039 WCHAR bufferW[512], *value_str = (WCHAR *)value->Data;
1040 unsigned i = 0;
1041 DWORD size;
1042 HKEY hkey, subkey, device_key, prop_key;
1044 hkey = reg_open_key( enum_key, pciW, sizeof(pciW) );
1046 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
1048 unsigned int j = 0;
1050 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
1052 while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
1054 BOOL present = FALSE;
1056 if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
1057 memcpy( bufferW, key->Name, key->NameLength );
1058 bufferW[key->NameLength / sizeof(WCHAR)] = 0;
1060 size = query_reg_value( device_key, class_guidW, value, sizeof(buffer) );
1061 if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
1063 NtClose( device_key );
1064 continue;
1067 if ((prop_key = reg_open_key( device_key, devpropkey_device_ispresentW,
1068 sizeof(devpropkey_device_ispresentW) )))
1070 if (query_reg_value( prop_key, NULL, value, sizeof(buffer) ) == sizeof(BOOL))
1071 present = *(const BOOL *)value->Data;
1072 NtClose( prop_key );
1075 NtClose( device_key );
1077 if (!present && reg_delete_tree( subkey, bufferW, lstrlenW( bufferW ) * sizeof(WCHAR) ))
1078 j = 0;
1081 NtClose( subkey );
1084 NtClose( hkey );
1087 /* see UuidCreate */
1088 static void uuid_create( GUID *uuid )
1090 char buf[4096];
1091 NtQuerySystemInformation( SystemInterruptInformation, buf, sizeof(buf), NULL );
1092 memcpy( uuid, buf, sizeof(*uuid) );
1093 uuid->Data3 &= 0x0fff;
1094 uuid->Data3 |= (4 << 12);
1095 uuid->Data4[0] &= 0x3f;
1096 uuid->Data4[0] |= 0x80;
1099 #define TICKSPERSEC 10000000
1100 #define SECSPERDAY 86400
1101 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
1102 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
1104 static unsigned int format_date( WCHAR *bufferW, LONGLONG time )
1106 int cleaps, years, yearday, months, days;
1107 unsigned int day, month, year;
1108 char buffer[32];
1110 days = time / TICKSPERSEC / SECSPERDAY;
1112 /* compute year, month and day of month, see RtlTimeToTimeFields */
1113 cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
1114 days += 28188 + cleaps;
1115 years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
1116 yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
1117 months = (64 * yearday) / 1959;
1118 if (months < 14)
1120 month = months - 1;
1121 year = years + 1524;
1123 else
1125 month = months - 13;
1126 year = years + 1525;
1128 day = yearday - (1959 * months) / 64 ;
1130 sprintf( buffer, "%u-%u-%u", month, day, year );
1131 return asciiz_to_unicode( bufferW, buffer );
1134 struct device_manager_ctx
1136 unsigned int gpu_count;
1137 unsigned int adapter_count;
1138 unsigned int video_count;
1139 unsigned int monitor_count;
1140 unsigned int output_count;
1141 unsigned int mode_count;
1142 HANDLE mutex;
1143 WCHAR gpuid[128];
1144 WCHAR gpu_guid[64];
1145 LUID gpu_luid;
1146 HKEY adapter_key;
1149 static void link_device( const WCHAR *instance, const WCHAR *class )
1151 unsigned int instance_len = lstrlenW( instance ), len;
1152 unsigned int class_len = lstrlenW( class );
1153 WCHAR buffer[MAX_PATH], *ptr;
1154 HKEY hkey, subkey;
1156 static const WCHAR symbolic_linkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k',0};
1157 static const WCHAR hashW[] = {'#'};
1159 len = asciiz_to_unicode( buffer, "DeviceClasses\\" ) / sizeof(WCHAR) - 1;
1160 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
1161 len += class_len;
1162 len += asciiz_to_unicode( buffer + len, "\\##?#" ) / sizeof(WCHAR) - 1;
1163 memcpy( buffer + len, instance, instance_len * sizeof(WCHAR) );
1164 for (ptr = buffer + len; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
1165 len += instance_len;
1166 buffer[len++] = '#';
1167 memcpy( buffer + len, class, class_len * sizeof(WCHAR) );
1168 len += class_len;
1169 hkey = reg_create_key( control_key, buffer, len * sizeof(WCHAR), 0, NULL );
1171 set_reg_value( hkey, device_instanceW, REG_SZ, instance, (instance_len + 1) * sizeof(WCHAR) );
1173 subkey = reg_create_key( hkey, hashW, sizeof(hashW), REG_OPTION_VOLATILE, NULL );
1174 NtClose( hkey );
1175 hkey = subkey;
1177 len = asciiz_to_unicode( buffer, "\\\\?\\" ) / sizeof(WCHAR) - 1;
1178 memcpy( buffer + len, instance, (instance_len + 1) * sizeof(WCHAR) );
1179 len += instance_len;
1180 memcpy( buffer + len, class, (class_len + 1) * sizeof(WCHAR) );
1181 len += class_len + 1;
1182 for (ptr = buffer + 4; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
1183 set_reg_value( hkey, symbolic_linkW, REG_SZ, buffer, len * sizeof(WCHAR) );
1185 if ((subkey = reg_create_key( hkey, controlW, sizeof(controlW), REG_OPTION_VOLATILE, NULL )))
1187 const DWORD linked = 1;
1188 set_reg_value( subkey, linkedW, REG_DWORD, &linked, sizeof(linked) );
1189 NtClose( subkey );
1193 static void add_gpu( const struct gdi_gpu *gpu, void *param )
1195 struct device_manager_ctx *ctx = param;
1196 const WCHAR *desc;
1197 char buffer[4096];
1198 WCHAR bufferW[512];
1199 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
1200 unsigned int gpu_index, size;
1201 HKEY hkey, subkey;
1202 LARGE_INTEGER ft;
1204 static const BOOL present = TRUE;
1205 static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0};
1206 static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0};
1207 static const WCHAR driver_date_dataW[] =
1208 {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
1209 static const WCHAR adapter_stringW[] =
1210 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n',
1211 '.','A','d','a','p','t','e','r','S','t','r','i','n','g',0};
1212 static const WCHAR bios_stringW[] =
1213 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1214 'B','i','o','s','S','t','r','i','n','g',0};
1215 static const WCHAR chip_typeW[] =
1216 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1217 'C','h','i','p','T','y','p','e',0};
1218 static const WCHAR dac_typeW[] =
1219 {'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
1220 'D','a','c','T','y','p','e',0};
1221 static const WCHAR ramdacW[] =
1222 {'I','n','t','e','r','g','r','a','t','e','d',' ','R','A','M','D','A','C',0};
1223 static const WCHAR driver_dateW[] = {'D','r','i','v','e','r','D','a','t','e',0};
1225 TRACE( "%s %04X %04X %08X %02X\n", debugstr_w(gpu->name),
1226 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
1228 gpu_index = ctx->gpu_count++;
1229 ctx->adapter_count = 0;
1230 ctx->monitor_count = 0;
1231 ctx->mode_count = 0;
1233 if (!enum_key && !(enum_key = reg_create_key( NULL, enum_keyW, sizeof(enum_keyW), 0, NULL )))
1234 return;
1236 if (!ctx->mutex)
1238 ctx->mutex = get_display_device_init_mutex();
1239 pthread_mutex_lock( &display_lock );
1240 prepare_devices();
1243 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X\\%08X",
1244 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index );
1245 size = asciiz_to_unicode( ctx->gpuid, buffer );
1246 if (!(hkey = reg_create_key( enum_key, ctx->gpuid, size - sizeof(WCHAR), 0, NULL ))) return;
1248 set_reg_value( hkey, classW, REG_SZ, displayW, sizeof(displayW) );
1249 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_displayW,
1250 sizeof(guid_devclass_displayW) );
1251 sprintf( buffer, "%s\\%04X", guid_devclass_displayA, gpu_index );
1252 set_reg_value( hkey, driverW, REG_SZ, bufferW, asciiz_to_unicode( bufferW, buffer ));
1254 sprintf( buffer, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X",
1255 gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
1256 size = asciiz_to_unicode( bufferW, buffer );
1257 bufferW[size / sizeof(WCHAR)] = 0; /* for REG_MULTI_SZ */
1258 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, size + sizeof(WCHAR) );
1260 if ((subkey = reg_create_key( hkey, devpkey_device_matching_device_id,
1261 sizeof(devpkey_device_matching_device_id), 0, NULL )))
1263 if (gpu->vendor_id && gpu->device_id)
1264 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW, size );
1265 else
1266 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
1267 asciiz_to_unicode( bufferW, "ROOT\\BasicRender" ));
1268 NtClose( subkey );
1271 if (gpu->vendor_id && gpu->device_id)
1273 if ((subkey = reg_create_key( hkey, devpkey_device_bus_number,
1274 sizeof(devpkey_device_bus_number), 0, NULL )))
1276 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1277 &gpu_index, sizeof(gpu_index) );
1278 NtClose( subkey );
1282 if ((subkey = reg_create_key( hkey, devpkey_device_removal_policy,
1283 sizeof(devpkey_device_removal_policy), 0, NULL )))
1285 unsigned int removal_policy = CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL;
1287 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1288 &removal_policy, sizeof(removal_policy) );
1289 NtClose( subkey );
1292 desc = gpu->name;
1293 if (!desc[0]) desc = wine_adapterW;
1294 set_reg_value( hkey, device_descW, REG_SZ, desc, (lstrlenW( desc ) + 1) * sizeof(WCHAR) );
1296 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
1298 if (!query_reg_value( subkey, video_idW, value, sizeof(buffer) ))
1300 GUID guid;
1301 uuid_create( &guid );
1302 sprintf( buffer, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
1303 (unsigned int)guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
1304 guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] );
1305 size = asciiz_to_unicode( ctx->gpu_guid, buffer );
1306 TRACE( "created guid %s\n", debugstr_w(ctx->gpu_guid) );
1307 set_reg_value( subkey, video_idW, REG_SZ, ctx->gpu_guid, size );
1309 else
1311 memcpy( ctx->gpu_guid, value->Data, value->DataLength );
1312 TRACE( "got guid %s\n", debugstr_w(ctx->gpu_guid) );
1314 NtClose( subkey );
1317 if ((subkey = reg_create_key( hkey, devpropkey_gpu_vulkan_uuidW,
1318 sizeof(devpropkey_gpu_vulkan_uuidW), 0, NULL )))
1320 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_GUID,
1321 &gpu->vulkan_uuid, sizeof(gpu->vulkan_uuid) );
1322 NtClose( subkey );
1325 if ((subkey = reg_create_key( hkey, devpropkey_device_ispresentW,
1326 sizeof(devpropkey_device_ispresentW), 0, NULL )))
1328 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
1329 &present, sizeof(present) );
1330 NtClose( subkey );
1333 if ((subkey = reg_create_key( hkey, devpropkey_gpu_luidW, sizeof(devpropkey_gpu_luidW), 0, NULL )))
1335 if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) != sizeof(LUID))
1337 NtAllocateLocallyUniqueId( &ctx->gpu_luid );
1338 TRACE("allocated luid %08x%08x\n", (int)ctx->gpu_luid.HighPart, (int)ctx->gpu_luid.LowPart );
1339 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT64,
1340 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
1342 else
1344 memcpy( &ctx->gpu_luid, value->Data, sizeof(ctx->gpu_luid) );
1345 TRACE("got luid %08x%08x\n", (int)ctx->gpu_luid.HighPart, (int)ctx->gpu_luid.LowPart );
1347 NtClose( subkey );
1350 NtClose( hkey );
1352 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_displayA, gpu_index );
1353 hkey = reg_create_key( control_key, bufferW,
1354 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
1356 NtQuerySystemTime( &ft );
1357 set_reg_value( hkey, driver_dateW, REG_SZ, bufferW, format_date( bufferW, ft.QuadPart ));
1359 set_reg_value( hkey, driver_date_dataW, REG_BINARY, &ft, sizeof(ft) );
1361 size = (lstrlenW( desc ) + 1) * sizeof(WCHAR);
1362 set_reg_value( hkey, driver_descW, REG_SZ, desc, size );
1363 set_reg_value( hkey, adapter_stringW, REG_BINARY, desc, size );
1364 set_reg_value( hkey, bios_stringW, REG_BINARY, desc, size );
1365 set_reg_value( hkey, chip_typeW, REG_BINARY, desc, size );
1366 set_reg_value( hkey, dac_typeW, REG_BINARY, ramdacW, sizeof(ramdacW) );
1368 NtClose( hkey );
1370 link_device( ctx->gpuid, guid_devinterface_display_adapterW );
1371 link_device( ctx->gpuid, guid_display_device_arrivalW );
1374 static void add_adapter( const struct gdi_adapter *adapter, void *param )
1376 struct device_manager_ctx *ctx = param;
1377 unsigned int adapter_index, video_index, len;
1378 char name[64], buffer[MAX_PATH];
1379 WCHAR nameW[64], bufferW[MAX_PATH];
1380 HKEY hkey;
1382 TRACE( "\n" );
1384 if (ctx->adapter_key)
1386 NtClose( ctx->adapter_key );
1387 ctx->adapter_key = NULL;
1390 adapter_index = ctx->adapter_count++;
1391 video_index = ctx->video_count++;
1392 ctx->monitor_count = 0;
1393 ctx->mode_count = 0;
1395 len = asciiz_to_unicode( bufferW, "\\Registry\\Machine\\System\\CurrentControlSet\\"
1396 "Control\\Video\\" ) / sizeof(WCHAR) - 1;
1397 lstrcpyW( bufferW + len, ctx->gpu_guid );
1398 len += lstrlenW( bufferW + len );
1399 sprintf( buffer, "\\%04x", adapter_index );
1400 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1401 hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1402 REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, NULL );
1403 if (!hkey) hkey = reg_create_key( NULL, bufferW, len * sizeof(WCHAR),
1404 REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK, NULL );
1406 sprintf( name, "\\Device\\Video%u", video_index );
1407 asciiz_to_unicode( nameW, name );
1408 set_reg_value( video_key, nameW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1410 if (hkey)
1412 sprintf( buffer, "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\"
1413 "%s\\%04X", guid_devclass_displayA, ctx->gpu_count - 1 );
1414 len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR);
1415 set_reg_value( hkey, symbolic_link_valueW, REG_LINK, bufferW, len );
1416 NtClose( hkey );
1418 else ERR( "failed to create link key\n" );
1420 /* Following information is Wine specific, it doesn't really exist on Windows. */
1421 len = asciiz_to_unicode( bufferW, "System\\CurrentControlSet\\Control\\Video\\" )
1422 / sizeof(WCHAR) - 1;
1423 lstrcpyW( bufferW + len, ctx->gpu_guid );
1424 len += lstrlenW( bufferW + len );
1425 sprintf( buffer, "\\%04x", adapter_index );
1426 len += asciiz_to_unicode( bufferW + len, buffer ) / sizeof(WCHAR) - 1;
1427 ctx->adapter_key = reg_create_key( config_key, bufferW, len * sizeof(WCHAR),
1428 REG_OPTION_VOLATILE, NULL );
1430 set_reg_value( ctx->adapter_key, gpu_idW, REG_SZ, ctx->gpuid,
1431 (lstrlenW( ctx->gpuid ) + 1) * sizeof(WCHAR) );
1432 set_reg_value( ctx->adapter_key, state_flagsW, REG_DWORD, &adapter->state_flags,
1433 sizeof(adapter->state_flags) );
1436 static void add_monitor( const struct gdi_monitor *monitor, void *param )
1438 struct device_manager_ctx *ctx = param;
1439 char buffer[MAX_PATH], instance[64];
1440 unsigned int monitor_index, output_index;
1441 struct edid_monitor_info monitor_info;
1442 char monitor_id_string[16];
1443 WCHAR bufferW[MAX_PATH];
1444 HKEY hkey, subkey;
1445 unsigned int len;
1447 monitor_index = ctx->monitor_count++;
1448 output_index = ctx->output_count++;
1450 TRACE( "%u %s %s\n", monitor_index, wine_dbgstr_rect(&monitor->rc_monitor), wine_dbgstr_rect(&monitor->rc_work) );
1452 get_monitor_info_from_edid( &monitor_info, monitor->edid, monitor->edid_len );
1453 if (monitor_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
1454 strcpy( monitor_id_string, monitor_info.monitor_id_string );
1455 else
1456 strcpy( monitor_id_string, "Default_Monitor" );
1458 sprintf( buffer, "MonitorID%u", monitor_index );
1459 sprintf( instance, "DISPLAY\\%s\\%04X&%04X", monitor_id_string, ctx->video_count - 1, monitor_index );
1460 set_reg_ascii_value( ctx->adapter_key, buffer, instance );
1462 hkey = reg_create_key( enum_key, bufferW, asciiz_to_unicode( bufferW, instance ) - sizeof(WCHAR),
1463 0, NULL );
1464 if (!hkey) return;
1466 link_device( bufferW, guid_devinterface_monitorW );
1468 asciiz_to_unicode( bufferW, "Generic Non-PnP Monitor" );
1469 set_reg_value( hkey, device_descW, REG_SZ, bufferW, (lstrlenW( bufferW ) + 1) * sizeof(WCHAR) );
1471 set_reg_value( hkey, classW, REG_SZ, monitorW, sizeof(monitorW) );
1472 sprintf( buffer, "%s\\%04X", guid_devclass_monitorA, output_index );
1473 set_reg_ascii_value( hkey, "Driver", buffer );
1474 set_reg_value( hkey, class_guidW, REG_SZ, guid_devclass_monitorW, sizeof(guid_devclass_monitorW) );
1476 sprintf( buffer, "MONITOR\\%s", monitor_id_string );
1477 len = asciiz_to_unicode( bufferW, buffer );
1478 bufferW[len / sizeof(WCHAR)] = 0;
1479 set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, len + sizeof(WCHAR) );
1481 if ((subkey = reg_create_key( hkey, device_parametersW, sizeof(device_parametersW), 0, NULL )))
1483 static const WCHAR bad_edidW[] = {'B','A','D','_','E','D','I','D',0};
1485 if (monitor->edid_len)
1486 set_reg_value( subkey, edidW, REG_BINARY, monitor->edid, monitor->edid_len );
1487 else
1488 set_reg_value( subkey, bad_edidW, REG_BINARY, NULL, 0 );
1489 NtClose( subkey );
1492 /* StateFlags */
1493 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_stateflagsW,
1494 sizeof(wine_devpropkey_monitor_stateflagsW), 0, NULL )))
1496 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32, &monitor->state_flags,
1497 sizeof(monitor->state_flags) );
1498 NtClose( subkey );
1501 /* WINE_DEVPROPKEY_MONITOR_RCMONITOR */
1502 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcmonitorW,
1503 sizeof(wine_devpropkey_monitor_rcmonitorW), 0, NULL )))
1505 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_monitor,
1506 sizeof(monitor->rc_monitor) );
1507 NtClose( subkey );
1510 /* WINE_DEVPROPKEY_MONITOR_RCWORK */
1511 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_rcworkW,
1512 sizeof(wine_devpropkey_monitor_rcworkW), 0, NULL )))
1514 TRACE( "rc_work %s\n", wine_dbgstr_rect(&monitor->rc_work) );
1515 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_work,
1516 sizeof(monitor->rc_work) );
1517 NtClose( subkey );
1520 /* WINE_DEVPROPKEY_MONITOR_ADAPTERNAME */
1521 if ((subkey = reg_create_key( hkey, wine_devpropkey_monitor_adapternameW,
1522 sizeof(wine_devpropkey_monitor_adapternameW), 0, NULL )))
1524 sprintf( buffer, "\\\\.\\DISPLAY%u", ctx->video_count );
1525 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
1526 asciiz_to_unicode( bufferW, buffer ));
1527 NtClose( subkey );
1530 /* DEVPROPKEY_MONITOR_GPU_LUID */
1531 if ((subkey = reg_create_key( hkey, devpropkey_monitor_gpu_luidW,
1532 sizeof(devpropkey_monitor_gpu_luidW), 0, NULL )))
1534 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_INT64,
1535 &ctx->gpu_luid, sizeof(ctx->gpu_luid) );
1536 NtClose( subkey );
1539 /* DEVPROPKEY_MONITOR_OUTPUT_ID */
1540 if ((subkey = reg_create_key( hkey, devpropkey_monitor_output_idW,
1541 sizeof(devpropkey_monitor_output_idW), 0, NULL )))
1543 set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
1544 &output_index, sizeof(output_index) );
1545 NtClose( subkey );
1548 NtClose( hkey );
1550 sprintf( buffer, "Class\\%s\\%04X", guid_devclass_monitorA, output_index );
1551 hkey = reg_create_key( control_key, bufferW,
1552 asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), 0, NULL );
1553 if (hkey) NtClose( hkey );
1556 static void add_mode( const DEVMODEW *mode, BOOL current, void *param )
1558 struct device_manager_ctx *ctx = param;
1559 DEVMODEW nopos_mode;
1561 if (!ctx->adapter_count)
1563 static const struct gdi_adapter default_adapter =
1565 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1567 TRACE( "adding default fake adapter\n" );
1568 add_adapter( &default_adapter, ctx );
1571 nopos_mode = *mode;
1572 nopos_mode.dmPosition.x = 0;
1573 nopos_mode.dmPosition.y = 0;
1574 nopos_mode.dmFields &= ~DM_POSITION;
1576 if (write_adapter_mode( ctx->adapter_key, ctx->mode_count, &nopos_mode ))
1578 ctx->mode_count++;
1579 set_reg_value( ctx->adapter_key, mode_countW, REG_DWORD, &ctx->mode_count, sizeof(ctx->mode_count) );
1580 if (current)
1582 if (!read_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, &nopos_mode ))
1583 write_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, mode );
1584 write_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, mode );
1589 static const struct gdi_device_manager device_manager =
1591 add_gpu,
1592 add_adapter,
1593 add_monitor,
1594 add_mode,
1597 static void release_display_manager_ctx( struct device_manager_ctx *ctx )
1599 if (ctx->mutex)
1601 pthread_mutex_unlock( &display_lock );
1602 release_display_device_init_mutex( ctx->mutex );
1604 if (ctx->adapter_key)
1606 NtClose( ctx->adapter_key );
1607 last_query_display_time = 0;
1609 if (ctx->gpu_count) cleanup_devices();
1612 static void clear_display_devices(void)
1614 struct adapter *adapter;
1615 struct monitor *monitor;
1617 if (list_head( &monitors ) == &virtual_monitor.entry)
1619 list_init( &monitors );
1620 return;
1623 while (!list_empty( &monitors ))
1625 monitor = LIST_ENTRY( list_head( &monitors ), struct monitor, entry );
1626 adapter_release( monitor->adapter );
1627 list_remove( &monitor->entry );
1628 free( monitor );
1631 while (!list_empty( &adapters ))
1633 adapter = LIST_ENTRY( list_head( &adapters ), struct adapter, entry );
1634 list_remove( &adapter->entry );
1635 adapter_release( adapter );
1639 static BOOL update_display_cache_from_registry(void)
1641 DWORD adapter_id, monitor_id, monitor_count = 0, size;
1642 KEY_BASIC_INFORMATION key;
1643 struct adapter *adapter;
1644 struct monitor *monitor, *monitor2;
1645 HANDLE mutex = NULL;
1646 NTSTATUS status;
1647 BOOL ret;
1649 /* If user driver did initialize the registry, then exit */
1650 if (!video_key && !(video_key = reg_open_key( NULL, devicemap_video_keyW,
1651 sizeof(devicemap_video_keyW) )))
1652 return FALSE;
1654 status = NtQueryKey( video_key, KeyBasicInformation, &key,
1655 offsetof(KEY_BASIC_INFORMATION, Name), &size );
1656 if (status && status != STATUS_BUFFER_OVERFLOW)
1657 return FALSE;
1659 if (key.LastWriteTime.QuadPart <= last_query_display_time) return TRUE;
1661 mutex = get_display_device_init_mutex();
1662 pthread_mutex_lock( &display_lock );
1664 clear_display_devices();
1666 for (adapter_id = 0;; adapter_id++)
1668 if (!(adapter = calloc( 1, sizeof(*adapter) ))) break;
1669 adapter->refcount = 1;
1670 adapter->id = adapter_id;
1672 if (!read_display_adapter_settings( adapter_id, adapter ))
1674 adapter_release( adapter );
1675 break;
1678 list_add_tail( &adapters, &adapter->entry );
1679 for (monitor_id = 0;; monitor_id++)
1681 if (!(monitor = calloc( 1, sizeof(*monitor) ))) break;
1682 if (!read_monitor_settings( adapter, monitor_id, monitor ))
1684 free( monitor );
1685 break;
1688 monitor->id = monitor_id;
1689 monitor->adapter = adapter_acquire( adapter );
1691 LIST_FOR_EACH_ENTRY(monitor2, &monitors, struct monitor, entry)
1693 if (EqualRect(&monitor2->rc_monitor, &monitor->rc_monitor))
1695 monitor->is_clone = TRUE;
1696 break;
1700 monitor->handle = UlongToHandle( ++monitor_count );
1701 list_add_tail( &monitors, &monitor->entry );
1705 if ((ret = !list_empty( &adapters ) && !list_empty( &monitors )))
1706 last_query_display_time = key.LastWriteTime.QuadPart;
1707 pthread_mutex_unlock( &display_lock );
1708 release_display_device_init_mutex( mutex );
1709 return ret;
1712 static BOOL is_same_devmode( const DEVMODEW *a, const DEVMODEW *b )
1714 return a->dmDisplayOrientation == b->dmDisplayOrientation &&
1715 a->dmBitsPerPel == b->dmBitsPerPel &&
1716 a->dmPelsWidth == b->dmPelsWidth &&
1717 a->dmPelsHeight == b->dmPelsHeight &&
1718 a->dmDisplayFrequency == b->dmDisplayFrequency;
1721 static BOOL update_display_cache( BOOL force )
1723 HWINSTA winstation = NtUserGetProcessWindowStation();
1724 struct device_manager_ctx ctx = {0};
1725 USEROBJECTFLAGS flags;
1727 /* services do not have any adapters, only a virtual monitor */
1728 if (NtUserGetObjectInformation( winstation, UOI_FLAGS, &flags, sizeof(flags), NULL )
1729 && !(flags.dwFlags & WSF_VISIBLE))
1731 pthread_mutex_lock( &display_lock );
1732 clear_display_devices();
1733 list_add_tail( &monitors, &virtual_monitor.entry );
1734 pthread_mutex_unlock( &display_lock );
1735 return TRUE;
1738 if (!user_driver->pUpdateDisplayDevices( &device_manager, force, &ctx ) && force)
1740 /* default implementation: expose an adapter and a monitor with a few standard modes,
1741 * and read / write current display settings from / to the registry.
1743 static const DEVMODEW modes[] =
1745 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1746 .dmBitsPerPel = 32, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
1747 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1748 .dmBitsPerPel = 32, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
1749 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1750 .dmBitsPerPel = 32, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
1751 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1752 .dmBitsPerPel = 16, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
1753 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1754 .dmBitsPerPel = 16, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
1755 { .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
1756 .dmBitsPerPel = 16, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
1758 static const struct gdi_gpu gpu;
1759 static const struct gdi_adapter adapter =
1761 .state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
1763 struct gdi_monitor monitor =
1765 .state_flags = DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED,
1767 DEVMODEW mode = {{0}};
1768 UINT i;
1770 add_gpu( &gpu, &ctx );
1771 add_adapter( &adapter, &ctx );
1773 if (!read_adapter_mode( ctx.adapter_key, ENUM_CURRENT_SETTINGS, &mode ))
1775 mode = modes[2];
1776 mode.dmFields |= DM_POSITION;
1778 monitor.rc_monitor.right = mode.dmPelsWidth;
1779 monitor.rc_monitor.bottom = mode.dmPelsHeight;
1780 monitor.rc_work.right = mode.dmPelsWidth;
1781 monitor.rc_work.bottom = mode.dmPelsHeight;
1783 add_monitor( &monitor, &ctx );
1784 for (i = 0; i < ARRAY_SIZE(modes); ++i)
1786 if (is_same_devmode( modes + i, &mode )) add_mode( &mode, TRUE, &ctx );
1787 else add_mode( modes + i, FALSE, &ctx );
1790 release_display_manager_ctx( &ctx );
1792 if (!update_display_cache_from_registry())
1794 if (force)
1796 ERR( "Failed to read display config.\n" );
1797 return FALSE;
1800 if (ctx.gpu_count)
1802 ERR( "Driver reported devices, but we failed to read them.\n" );
1803 return FALSE;
1806 return update_display_cache( TRUE );
1809 return TRUE;
1812 static BOOL lock_display_devices(void)
1814 if (!update_display_cache( FALSE )) return FALSE;
1815 pthread_mutex_lock( &display_lock );
1816 return TRUE;
1819 static void unlock_display_devices(void)
1821 pthread_mutex_unlock( &display_lock );
1824 static HDC get_display_dc(void)
1826 pthread_mutex_lock( &display_dc_lock );
1827 if (!display_dc)
1829 HDC dc;
1831 pthread_mutex_unlock( &display_dc_lock );
1832 dc = NtGdiOpenDCW( NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL );
1833 pthread_mutex_lock( &display_dc_lock );
1834 if (display_dc)
1835 NtGdiDeleteObjectApp( dc );
1836 else
1837 display_dc = dc;
1839 return display_dc;
1842 static void release_display_dc( HDC hdc )
1844 pthread_mutex_unlock( &display_dc_lock );
1847 /**********************************************************************
1848 * get_monitor_dpi
1850 UINT get_monitor_dpi( HMONITOR monitor )
1852 /* FIXME: use the monitor DPI instead */
1853 return system_dpi;
1856 /**********************************************************************
1857 * get_win_monitor_dpi
1859 UINT get_win_monitor_dpi( HWND hwnd )
1861 /* FIXME: use the monitor DPI instead */
1862 return system_dpi;
1865 /**********************************************************************
1866 * get_thread_dpi_awareness
1868 DPI_AWARENESS get_thread_dpi_awareness(void)
1870 struct ntuser_thread_info *info = NtUserGetThreadInfo();
1871 ULONG_PTR context = info->dpi_awareness;
1873 if (!context) context = NtUserGetProcessDpiAwarenessContext( NULL );
1875 switch (context)
1877 case 0x10:
1878 case 0x11:
1879 case 0x12:
1880 case 0x80000010:
1881 case 0x80000011:
1882 case 0x80000012:
1883 return context & 3;
1884 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
1885 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
1886 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
1887 return ~context;
1888 default:
1889 return DPI_AWARENESS_INVALID;
1893 DWORD get_process_layout(void)
1895 return process_layout == ~0u ? 0 : process_layout;
1898 /**********************************************************************
1899 * get_thread_dpi
1901 UINT get_thread_dpi(void)
1903 switch (get_thread_dpi_awareness())
1905 case DPI_AWARENESS_UNAWARE: return USER_DEFAULT_SCREEN_DPI;
1906 case DPI_AWARENESS_SYSTEM_AWARE: return system_dpi;
1907 default: return 0; /* no scaling */
1911 /* see GetDpiForSystem */
1912 UINT get_system_dpi(void)
1914 if (get_thread_dpi_awareness() == DPI_AWARENESS_UNAWARE) return USER_DEFAULT_SCREEN_DPI;
1915 return system_dpi;
1918 /* see GetAwarenessFromDpiAwarenessContext */
1919 static DPI_AWARENESS get_awareness_from_dpi_awareness_context( DPI_AWARENESS_CONTEXT context )
1921 switch ((ULONG_PTR)context)
1923 case 0x10:
1924 case 0x11:
1925 case 0x12:
1926 case 0x80000010:
1927 case 0x80000011:
1928 case 0x80000012:
1929 return (ULONG_PTR)context & 3;
1930 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
1931 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
1932 case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
1933 return ~(ULONG_PTR)context;
1934 default:
1935 return DPI_AWARENESS_INVALID;
1939 /**********************************************************************
1940 * SetThreadDpiAwarenessContext (win32u.so)
1942 DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT context )
1944 struct ntuser_thread_info *info = NtUserGetThreadInfo();
1945 DPI_AWARENESS prev, val = get_awareness_from_dpi_awareness_context( context );
1947 if (val == DPI_AWARENESS_INVALID)
1949 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
1950 return 0;
1952 if (!(prev = info->dpi_awareness))
1954 prev = NtUserGetProcessDpiAwarenessContext( GetCurrentProcess() ) & 3;
1955 prev |= 0x80000010; /* restore to process default */
1957 if (((ULONG_PTR)context & ~(ULONG_PTR)0x13) == 0x80000000) info->dpi_awareness = 0;
1958 else info->dpi_awareness = val | 0x10;
1959 return ULongToHandle( prev );
1962 /**********************************************************************
1963 * map_dpi_rect
1965 RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to )
1967 if (dpi_from && dpi_to && dpi_from != dpi_to)
1969 rect.left = muldiv( rect.left, dpi_to, dpi_from );
1970 rect.top = muldiv( rect.top, dpi_to, dpi_from );
1971 rect.right = muldiv( rect.right, dpi_to, dpi_from );
1972 rect.bottom = muldiv( rect.bottom, dpi_to, dpi_from );
1974 return rect;
1977 /**********************************************************************
1978 * map_dpi_point
1980 POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to )
1982 if (dpi_from && dpi_to && dpi_from != dpi_to)
1984 pt.x = muldiv( pt.x, dpi_to, dpi_from );
1985 pt.y = muldiv( pt.y, dpi_to, dpi_from );
1987 return pt;
1990 /**********************************************************************
1991 * point_win_to_phys_dpi
1993 static POINT point_win_to_phys_dpi( HWND hwnd, POINT pt )
1995 return map_dpi_point( pt, get_dpi_for_window( hwnd ), get_win_monitor_dpi( hwnd ) );
1998 /**********************************************************************
1999 * point_phys_to_win_dpi
2001 POINT point_phys_to_win_dpi( HWND hwnd, POINT pt )
2003 return map_dpi_point( pt, get_win_monitor_dpi( hwnd ), get_dpi_for_window( hwnd ));
2006 /**********************************************************************
2007 * point_thread_to_win_dpi
2009 POINT point_thread_to_win_dpi( HWND hwnd, POINT pt )
2011 UINT dpi = get_thread_dpi();
2012 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
2013 return map_dpi_point( pt, dpi, get_dpi_for_window( hwnd ));
2016 /**********************************************************************
2017 * rect_thread_to_win_dpi
2019 RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect )
2021 UINT dpi = get_thread_dpi();
2022 if (!dpi) dpi = get_win_monitor_dpi( hwnd );
2023 return map_dpi_rect( rect, dpi, get_dpi_for_window( hwnd ) );
2026 /* map value from system dpi to standard 96 dpi for storing in the registry */
2027 static int map_from_system_dpi( int val )
2029 return muldiv( val, USER_DEFAULT_SCREEN_DPI, get_system_dpi() );
2032 /* map value from 96 dpi to system or custom dpi */
2033 static int map_to_dpi( int val, UINT dpi )
2035 if (!dpi) dpi = get_system_dpi();
2036 return muldiv( val, dpi, USER_DEFAULT_SCREEN_DPI );
2039 RECT get_virtual_screen_rect( UINT dpi )
2041 struct monitor *monitor;
2042 RECT rect = {0};
2044 if (!lock_display_devices()) return rect;
2046 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2048 union_rect( &rect, &rect, &monitor->rc_monitor );
2051 unlock_display_devices();
2053 if (dpi) rect = map_dpi_rect( rect, system_dpi, dpi );
2054 return rect;
2057 static BOOL is_window_rect_full_screen( const RECT *rect )
2059 struct monitor *monitor;
2060 BOOL ret = FALSE;
2062 if (!lock_display_devices()) return FALSE;
2064 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2066 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2067 continue;
2069 if (rect->left <= monitor->rc_monitor.left && rect->right >= monitor->rc_monitor.right &&
2070 rect->top <= monitor->rc_monitor.top && rect->bottom >= monitor->rc_monitor.bottom)
2072 ret = TRUE;
2073 break;
2077 unlock_display_devices();
2078 return ret;
2081 RECT get_display_rect( const WCHAR *display )
2083 struct monitor *monitor;
2084 RECT rect = {0};
2086 if (!lock_display_devices()) return rect;
2088 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2090 if (!monitor->adapter || wcsicmp( monitor->adapter->dev.device_name, display )) continue;
2091 rect = monitor->rc_monitor;
2092 break;
2095 unlock_display_devices();
2096 return map_dpi_rect( rect, system_dpi, get_thread_dpi() );
2099 RECT get_primary_monitor_rect( UINT dpi )
2101 struct monitor *monitor;
2102 RECT rect = {0};
2104 if (!lock_display_devices()) return rect;
2106 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2108 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
2109 rect = monitor->rc_monitor;
2110 break;
2113 unlock_display_devices();
2114 return map_dpi_rect( rect, system_dpi, dpi );
2117 /**********************************************************************
2118 * NtUserGetDisplayConfigBufferSizes (win32u.@)
2120 LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_info,
2121 UINT32 *num_mode_info )
2123 struct monitor *monitor;
2124 UINT32 count = 0;
2126 TRACE( "(0x%x %p %p)\n", flags, num_path_info, num_mode_info );
2128 if (!num_path_info || !num_mode_info)
2129 return ERROR_INVALID_PARAMETER;
2131 *num_path_info = 0;
2133 switch (flags)
2135 case QDC_ALL_PATHS:
2136 case QDC_ONLY_ACTIVE_PATHS:
2137 case QDC_DATABASE_CURRENT:
2138 break;
2139 default:
2140 return ERROR_INVALID_PARAMETER;
2143 /* FIXME: semi-stub */
2144 if (flags != QDC_ONLY_ACTIVE_PATHS)
2145 FIXME( "only returning active paths\n" );
2147 if (lock_display_devices())
2149 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2151 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE))
2152 continue;
2153 count++;
2155 unlock_display_devices();
2158 *num_path_info = count;
2159 *num_mode_info = count * 2;
2160 TRACE( "returning %u paths %u modes\n", *num_path_info, *num_mode_info );
2161 return ERROR_SUCCESS;
2164 /* display_lock mutex must be held */
2165 static struct display_device *find_monitor_device( struct display_device *adapter, UINT index )
2167 struct monitor *monitor;
2169 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2170 if (&monitor->adapter->dev == adapter && index == monitor->id)
2171 return &monitor->dev;
2173 WARN( "Failed to find adapter %s monitor with id %u.\n", debugstr_w(adapter->device_name), index );
2174 return NULL;
2177 /* display_lock mutex must be held */
2178 static struct display_device *find_adapter_device_by_id( UINT index )
2180 struct adapter *adapter;
2182 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
2183 if (index == adapter->id) return &adapter->dev;
2185 WARN( "Failed to find adapter with id %u.\n", index );
2186 return NULL;
2189 /* display_lock mutex must be held */
2190 static struct display_device *find_adapter_device_by_name( UNICODE_STRING *name )
2192 SIZE_T len = name->Length / sizeof(WCHAR);
2193 struct adapter *adapter;
2195 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
2196 if (!wcsnicmp( name->Buffer, adapter->dev.device_name, len ) && !adapter->dev.device_name[len])
2197 return &adapter->dev;
2199 WARN( "Failed to find adapter with name %s.\n", debugstr_us(name) );
2200 return NULL;
2203 /* Find and acquire the adapter matching name, or primary adapter if name is NULL.
2204 * If not NULL, the returned adapter needs to be released with adapter_release.
2206 static struct adapter *find_adapter( UNICODE_STRING *name )
2208 struct display_device *device;
2209 struct adapter *adapter;
2211 if (!lock_display_devices()) return NULL;
2213 if (name && name->Length) device = find_adapter_device_by_name( name );
2214 else device = find_adapter_device_by_id( 0 ); /* use primary adapter */
2216 if (!device) adapter = NULL;
2217 else adapter = adapter_acquire( CONTAINING_RECORD( device, struct adapter, dev ) );
2219 unlock_display_devices();
2220 return adapter;
2223 /***********************************************************************
2224 * NtUserEnumDisplayDevices (win32u.@)
2226 NTSTATUS WINAPI NtUserEnumDisplayDevices( UNICODE_STRING *device, DWORD index,
2227 DISPLAY_DEVICEW *info, DWORD flags )
2229 struct display_device *found = NULL;
2231 TRACE( "%s %u %p %#x\n", debugstr_us( device ), (int)index, info, (int)flags );
2233 if (!info || !info->cb) return STATUS_UNSUCCESSFUL;
2235 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
2237 if (!device || !device->Length) found = find_adapter_device_by_id( index );
2238 else if ((found = find_adapter_device_by_name( device ))) found = find_monitor_device( found, index );
2240 if (found)
2242 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceName) + sizeof(info->DeviceName))
2243 lstrcpyW( info->DeviceName, found->device_name );
2244 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceString) + sizeof(info->DeviceString))
2245 lstrcpyW( info->DeviceString, found->device_string );
2246 if (info->cb >= offsetof(DISPLAY_DEVICEW, StateFlags) + sizeof(info->StateFlags))
2247 info->StateFlags = found->state_flags;
2248 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID))
2249 lstrcpyW( info->DeviceID, (flags & EDD_GET_DEVICE_INTERFACE_NAME)
2250 ? found->interface_name : found->device_id );
2251 if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey))
2252 lstrcpyW( info->DeviceKey, found->device_key );
2254 unlock_display_devices();
2255 return found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
2258 #define _X_FIELD(prefix, bits) \
2259 if ((fields) & prefix##_##bits) \
2261 p += sprintf( p, "%s%s", first ? "" : ",", #bits ); \
2262 first = FALSE; \
2265 static const char *_CDS_flags( DWORD fields )
2267 BOOL first = TRUE;
2268 CHAR buf[128];
2269 CHAR *p = buf;
2271 _X_FIELD(CDS, UPDATEREGISTRY)
2272 _X_FIELD(CDS, TEST)
2273 _X_FIELD(CDS, FULLSCREEN)
2274 _X_FIELD(CDS, GLOBAL)
2275 _X_FIELD(CDS, SET_PRIMARY)
2276 _X_FIELD(CDS, VIDEOPARAMETERS)
2277 _X_FIELD(CDS, ENABLE_UNSAFE_MODES)
2278 _X_FIELD(CDS, DISABLE_UNSAFE_MODES)
2279 _X_FIELD(CDS, RESET)
2280 _X_FIELD(CDS, RESET_EX)
2281 _X_FIELD(CDS, NORESET)
2283 *p = 0;
2284 return wine_dbg_sprintf( "%s", buf );
2287 static const char *_DM_fields( DWORD fields )
2289 BOOL first = TRUE;
2290 CHAR buf[128];
2291 CHAR *p = buf;
2293 _X_FIELD(DM, BITSPERPEL)
2294 _X_FIELD(DM, PELSWIDTH)
2295 _X_FIELD(DM, PELSHEIGHT)
2296 _X_FIELD(DM, DISPLAYFLAGS)
2297 _X_FIELD(DM, DISPLAYFREQUENCY)
2298 _X_FIELD(DM, POSITION)
2299 _X_FIELD(DM, DISPLAYORIENTATION)
2301 *p = 0;
2302 return wine_dbg_sprintf( "%s", buf );
2305 #undef _X_FIELD
2307 static void trace_devmode( const DEVMODEW *devmode )
2309 TRACE( "dmFields=%s ", _DM_fields(devmode->dmFields) );
2310 if (devmode->dmFields & DM_BITSPERPEL)
2311 TRACE( "dmBitsPerPel=%u ", (int)devmode->dmBitsPerPel );
2312 if (devmode->dmFields & DM_PELSWIDTH)
2313 TRACE( "dmPelsWidth=%u ", (int)devmode->dmPelsWidth );
2314 if (devmode->dmFields & DM_PELSHEIGHT)
2315 TRACE( "dmPelsHeight=%u ", (int)devmode->dmPelsHeight );
2316 if (devmode->dmFields & DM_DISPLAYFREQUENCY)
2317 TRACE( "dmDisplayFrequency=%u ", (int)devmode->dmDisplayFrequency );
2318 if (devmode->dmFields & DM_POSITION)
2319 TRACE( "dmPosition=(%d,%d) ", (int)devmode->dmPosition.x, (int)devmode->dmPosition.y );
2320 if (devmode->dmFields & DM_DISPLAYFLAGS)
2321 TRACE( "dmDisplayFlags=%#x ", (int)devmode->dmDisplayFlags );
2322 if (devmode->dmFields & DM_DISPLAYORIENTATION)
2323 TRACE( "dmDisplayOrientation=%u ", (int)devmode->dmDisplayOrientation );
2324 TRACE("\n");
2327 static BOOL is_detached_mode( const DEVMODEW *mode )
2329 return mode->dmFields & DM_POSITION &&
2330 mode->dmFields & DM_PELSWIDTH &&
2331 mode->dmFields & DM_PELSHEIGHT &&
2332 mode->dmPelsWidth == 0 &&
2333 mode->dmPelsHeight == 0;
2336 static const DEVMODEW *find_display_mode( const DEVMODEW *modes, DEVMODEW *devmode )
2338 const DEVMODEW *mode;
2340 if (is_detached_mode( devmode )) return devmode;
2342 for (mode = modes; mode && mode->dmSize; mode = NEXT_DEVMODEW(mode))
2344 if ((mode->dmFields & DM_DISPLAYFLAGS) && (mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
2345 continue;
2346 if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel && devmode->dmBitsPerPel != mode->dmBitsPerPel)
2347 continue;
2348 if ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth != mode->dmPelsWidth)
2349 continue;
2350 if ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight != mode->dmPelsHeight)
2351 continue;
2352 if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != mode->dmDisplayFrequency
2353 && devmode->dmDisplayFrequency > 1 && mode->dmDisplayFrequency)
2354 continue;
2355 if ((devmode->dmFields & DM_DISPLAYORIENTATION) && devmode->dmDisplayOrientation != mode->dmDisplayOrientation)
2356 continue;
2357 if ((devmode->dmFields & DM_DISPLAYFLAGS) && (mode->dmFields & DM_DISPLAYFLAGS) &&
2358 (devmode->dmDisplayFlags & DM_INTERLACED) != (mode->dmDisplayFlags & DM_INTERLACED))
2359 continue;
2360 if ((devmode->dmFields & DM_DISPLAYFIXEDOUTPUT) && (mode->dmFields & DM_DISPLAYFIXEDOUTPUT) &&
2361 devmode->dmDisplayFixedOutput != mode->dmDisplayFixedOutput)
2362 continue;
2364 return mode;
2367 return NULL;
2370 static BOOL adapter_get_full_mode( const struct adapter *adapter, const DEVMODEW *devmode, DEVMODEW *full_mode )
2372 const DEVMODEW *adapter_mode;
2374 if (devmode)
2376 trace_devmode( devmode );
2378 if (devmode->dmSize < offsetof(DEVMODEW, dmICMMethod)) return FALSE;
2379 if (!is_detached_mode( devmode ) &&
2380 (!(devmode->dmFields & DM_BITSPERPEL) || !devmode->dmBitsPerPel) &&
2381 (!(devmode->dmFields & DM_PELSWIDTH) || !devmode->dmPelsWidth) &&
2382 (!(devmode->dmFields & DM_PELSHEIGHT) || !devmode->dmPelsHeight) &&
2383 (!(devmode->dmFields & DM_DISPLAYFREQUENCY) || !devmode->dmDisplayFrequency))
2384 devmode = NULL;
2387 if (devmode) memcpy( full_mode, devmode, devmode->dmSize );
2388 else
2390 if (!adapter_get_registry_settings( adapter, full_mode )) return FALSE;
2391 TRACE( "Return to original display mode\n" );
2394 if ((full_mode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
2396 WARN( "devmode doesn't specify the resolution: %#x\n", (int)full_mode->dmFields );
2397 return FALSE;
2400 if (!is_detached_mode( full_mode ) && (!full_mode->dmPelsWidth || !full_mode->dmPelsHeight || !(full_mode->dmFields & DM_POSITION)))
2402 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2404 if (!adapter_get_current_settings( adapter, &current_mode )) return FALSE;
2405 if (!full_mode->dmPelsWidth) full_mode->dmPelsWidth = current_mode.dmPelsWidth;
2406 if (!full_mode->dmPelsHeight) full_mode->dmPelsHeight = current_mode.dmPelsHeight;
2407 if (!(full_mode->dmFields & DM_POSITION))
2409 full_mode->dmPosition = current_mode.dmPosition;
2410 full_mode->dmFields |= DM_POSITION;
2414 if ((adapter_mode = find_display_mode( adapter->modes, full_mode )) && adapter_mode != full_mode)
2416 POINTL position = full_mode->dmPosition;
2417 *full_mode = *adapter_mode;
2418 full_mode->dmFields |= DM_POSITION;
2419 full_mode->dmPosition = position;
2422 return adapter_mode != NULL;
2425 static DEVMODEW *get_display_settings( const WCHAR *devname, const DEVMODEW *devmode )
2427 DEVMODEW *mode, *displays;
2428 struct adapter *adapter;
2429 BOOL ret;
2431 /* allocate an extra mode for easier iteration */
2432 if (!(displays = calloc( list_count( &adapters ) + 1, sizeof(DEVMODEW) ))) return NULL;
2433 mode = displays;
2435 LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
2437 mode->dmSize = sizeof(DEVMODEW);
2438 if (devmode && !wcsicmp( devname, adapter->dev.device_name ))
2439 memcpy( &mode->dmFields, &devmode->dmFields, devmode->dmSize - offsetof(DEVMODEW, dmFields) );
2440 else
2442 if (!devname) ret = adapter_get_registry_settings( adapter, mode );
2443 else ret = adapter_get_current_settings( adapter, mode );
2444 if (!ret)
2446 free( displays );
2447 return NULL;
2451 lstrcpyW( mode->dmDeviceName, adapter->dev.device_name );
2452 mode = NEXT_DEVMODEW(mode);
2455 return displays;
2458 static INT offset_length( POINT offset )
2460 return offset.x * offset.x + offset.y * offset.y;
2463 static void set_rect_from_devmode( RECT *rect, const DEVMODEW *mode )
2465 SetRect( rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth,
2466 mode->dmPosition.y + mode->dmPelsHeight );
2469 /* Check if a rect overlaps with placed display rects */
2470 static BOOL overlap_placed_displays( const RECT *rect, const DEVMODEW *displays )
2472 const DEVMODEW *mode;
2473 RECT intersect;
2475 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2477 set_rect_from_devmode( &intersect, mode );
2478 if ((mode->dmFields & DM_POSITION) && intersect_rect( &intersect, &intersect, rect )) return TRUE;
2481 return FALSE;
2484 /* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */
2485 static POINT get_placement_offset( const DEVMODEW *displays, const DEVMODEW *placing )
2487 POINT points[8], left_top, offset, min_offset = {0, 0};
2488 INT point_idx, point_count, vertex_idx;
2489 BOOL has_placed = FALSE, first = TRUE;
2490 RECT desired_rect, rect;
2491 const DEVMODEW *mode;
2492 INT width, height;
2494 set_rect_from_devmode( &desired_rect, placing );
2496 /* If the display to be placed is detached, no offset is needed to place it */
2497 if (IsRectEmpty( &desired_rect )) return min_offset;
2499 /* If there is no placed and attached display, place this display as it is */
2500 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2502 set_rect_from_devmode( &rect, mode );
2503 if ((mode->dmFields & DM_POSITION) && !IsRectEmpty( &rect ))
2505 has_placed = TRUE;
2506 break;
2510 if (!has_placed) return min_offset;
2512 /* Try to place this display with each of its four vertices at every vertex of the placed
2513 * displays and see which combination has the minimum offset length */
2514 width = desired_rect.right - desired_rect.left;
2515 height = desired_rect.bottom - desired_rect.top;
2517 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2519 set_rect_from_devmode( &rect, mode );
2520 if (!(mode->dmFields & DM_POSITION) || IsRectEmpty( &rect )) continue;
2522 /* Get four vertices of the placed display rectangle */
2523 points[0].x = rect.left;
2524 points[0].y = rect.top;
2525 points[1].x = rect.left;
2526 points[1].y = rect.bottom;
2527 points[2].x = rect.right;
2528 points[2].y = rect.top;
2529 points[3].x = rect.right;
2530 points[3].y = rect.bottom;
2531 point_count = 4;
2533 /* Intersected points when moving the display to be placed horizontally */
2534 if (desired_rect.bottom >= rect.top && desired_rect.top <= rect.bottom)
2536 points[point_count].x = rect.left;
2537 points[point_count++].y = desired_rect.top;
2538 points[point_count].x = rect.right;
2539 points[point_count++].y = desired_rect.top;
2541 /* Intersected points when moving the display to be placed vertically */
2542 if (desired_rect.left <= rect.right && desired_rect.right >= rect.left)
2544 points[point_count].x = desired_rect.left;
2545 points[point_count++].y = rect.top;
2546 points[point_count].x = desired_rect.left;
2547 points[point_count++].y = rect.bottom;
2550 /* Try moving each vertex of the display rectangle to each points */
2551 for (point_idx = 0; point_idx < point_count; ++point_idx)
2553 for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx)
2555 switch (vertex_idx)
2557 /* Move the bottom right vertex to the point */
2558 case 0:
2559 left_top.x = points[point_idx].x - width;
2560 left_top.y = points[point_idx].y - height;
2561 break;
2562 /* Move the bottom left vertex to the point */
2563 case 1:
2564 left_top.x = points[point_idx].x;
2565 left_top.y = points[point_idx].y - height;
2566 break;
2567 /* Move the top right vertex to the point */
2568 case 2:
2569 left_top.x = points[point_idx].x - width;
2570 left_top.y = points[point_idx].y;
2571 break;
2572 /* Move the top left vertex to the point */
2573 case 3:
2574 left_top.x = points[point_idx].x;
2575 left_top.y = points[point_idx].y;
2576 break;
2579 offset.x = left_top.x - desired_rect.left;
2580 offset.y = left_top.y - desired_rect.top;
2581 rect = desired_rect;
2582 OffsetRect( &rect, offset.x, offset.y );
2583 if (!overlap_placed_displays( &rect, displays ))
2585 if (first)
2587 min_offset = offset;
2588 first = FALSE;
2589 continue;
2592 if (offset_length( offset ) < offset_length( min_offset )) min_offset = offset;
2598 return min_offset;
2601 static void place_all_displays( DEVMODEW *displays )
2603 POINT min_offset, offset;
2604 DEVMODEW *mode, *placing;
2606 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2607 mode->dmFields &= ~DM_POSITION;
2609 /* Place all displays with no extra space between them and no overlapping */
2610 while (1)
2612 /* Place the unplaced display with the minimum offset length first */
2613 placing = NULL;
2614 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2616 if (mode->dmFields & DM_POSITION) continue;
2618 offset = get_placement_offset( displays, mode );
2619 if (!placing || offset_length( offset ) < offset_length( min_offset ))
2621 min_offset = offset;
2622 placing = mode;
2626 /* If all displays are placed */
2627 if (!placing) break;
2629 placing->dmPosition.x += min_offset.x;
2630 placing->dmPosition.y += min_offset.y;
2631 placing->dmFields |= DM_POSITION;
2635 static BOOL all_detached_settings( const DEVMODEW *displays )
2637 const DEVMODEW *mode;
2639 for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
2640 if (!is_detached_mode( mode )) return FALSE;
2642 return TRUE;
2645 static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmode,
2646 HWND hwnd, DWORD flags, void *lparam )
2648 WCHAR primary_name[CCHDEVICENAME];
2649 struct display_device *primary;
2650 DEVMODEW *mode, *displays;
2651 struct adapter *adapter;
2652 LONG ret;
2654 if (!lock_display_devices()) return DISP_CHANGE_FAILED;
2655 if (!(displays = get_display_settings( devname, devmode )))
2657 unlock_display_devices();
2658 return DISP_CHANGE_FAILED;
2661 if (all_detached_settings( displays ))
2663 unlock_display_devices();
2664 WARN( "Detaching all modes is not permitted.\n" );
2665 free( displays );
2666 return DISP_CHANGE_SUCCESSFUL;
2669 place_all_displays( displays );
2671 if (!(primary = find_adapter_device_by_id( 0 ))) primary_name[0] = 0;
2672 else wcscpy( primary_name, primary->device_name );
2674 if ((ret = user_driver->pChangeDisplaySettings( displays, primary_name, hwnd, flags, lparam )) == E_NOTIMPL)
2676 /* default implementation: write current display settings to the registry. */
2677 mode = displays;
2678 LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
2680 if (!adapter_set_current_settings( adapter, mode ))
2681 WARN( "Failed to write adapter %s current mode.\n", debugstr_w(adapter->dev.device_name) );
2682 mode = NEXT_DEVMODEW(mode);
2684 ret = DISP_CHANGE_SUCCESSFUL;
2686 unlock_display_devices();
2688 free( displays );
2689 if (ret) return ret;
2691 if (!update_display_cache( TRUE ))
2692 WARN( "Failed to update display cache after mode change.\n" );
2694 if ((adapter = find_adapter( NULL )))
2696 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2698 if (!adapter_get_current_settings( adapter, &current_mode )) WARN( "Failed to get primary adapter current display settings.\n" );
2699 adapter_release( adapter );
2701 send_notify_message( NtUserGetDesktopWindow(), WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
2702 MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), FALSE );
2703 send_message_timeout( HWND_BROADCAST, WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
2704 MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ),
2705 SMTO_ABORTIFHUNG, 2000, FALSE );
2708 return ret;
2711 /***********************************************************************
2712 * NtUserChangeDisplaySettings (win32u.@)
2714 LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devmode, HWND hwnd,
2715 DWORD flags, void *lparam )
2717 DEVMODEW full_mode = {.dmSize = sizeof(DEVMODEW)};
2718 int ret = DISP_CHANGE_SUCCESSFUL;
2719 struct adapter *adapter;
2721 TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, (int)flags, lparam );
2722 TRACE( "flags=%s\n", _CDS_flags(flags) );
2724 if ((!devname || !devname->Length) && !devmode) return apply_display_settings( NULL, NULL, hwnd, flags, lparam );
2726 if (!(adapter = find_adapter( devname ))) return DISP_CHANGE_BADPARAM;
2728 if (!adapter_get_full_mode( adapter, devmode, &full_mode )) ret = DISP_CHANGE_BADMODE;
2729 else if ((flags & CDS_UPDATEREGISTRY) && !adapter_set_registry_settings( adapter, &full_mode )) ret = DISP_CHANGE_NOTUPDATED;
2730 else if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL;
2731 else ret = apply_display_settings( adapter->dev.device_name, &full_mode, hwnd, flags, lparam );
2732 adapter_release( adapter );
2734 if (ret) ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret );
2735 return ret;
2738 static BOOL adapter_enum_display_settings( const struct adapter *adapter, UINT index, DEVMODEW *devmode, UINT flags )
2740 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2741 const DEVMODEW *adapter_mode;
2743 if (!(flags & EDS_ROTATEDMODE) && !adapter_get_current_settings( adapter, &current_mode ))
2745 WARN( "Failed to query current display mode for EDS_ROTATEDMODE flag.\n" );
2746 return FALSE;
2749 for (adapter_mode = adapter->modes; adapter_mode->dmSize; adapter_mode = NEXT_DEVMODEW(adapter_mode))
2751 if (!(flags & EDS_ROTATEDMODE) && (adapter_mode->dmFields & DM_DISPLAYORIENTATION) &&
2752 adapter_mode->dmDisplayOrientation != current_mode.dmDisplayOrientation)
2753 continue;
2754 if (!(flags & EDS_RAWMODE) && (adapter_mode->dmFields & DM_DISPLAYFLAGS) &&
2755 (adapter_mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
2756 continue;
2757 if (!index--)
2759 memcpy( &devmode->dmFields, &adapter_mode->dmFields, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields) );
2760 devmode->dmDisplayFlags &= ~WINE_DM_UNSUPPORTED;
2761 return TRUE;
2765 WARN( "device %s, index %#x, flags %#x display mode not found.\n",
2766 debugstr_w( adapter->dev.device_name ), index, flags );
2767 RtlSetLastWin32Error( ERROR_NO_MORE_FILES );
2768 return FALSE;
2771 /***********************************************************************
2772 * NtUserEnumDisplaySettings (win32u.@)
2774 BOOL WINAPI NtUserEnumDisplaySettings( UNICODE_STRING *device, DWORD index, DEVMODEW *devmode, DWORD flags )
2776 static const WCHAR wine_display_driverW[] = {'W','i','n','e',' ','D','i','s','p','l','a','y',' ','D','r','i','v','e','r',0};
2777 struct adapter *adapter;
2778 BOOL ret;
2780 TRACE( "device %s, index %#x, devmode %p, flags %#x\n",
2781 debugstr_us(device), (int)index, devmode, (int)flags );
2783 if (!(adapter = find_adapter( device ))) return FALSE;
2785 lstrcpynW( devmode->dmDeviceName, wine_display_driverW, ARRAY_SIZE(devmode->dmDeviceName) );
2786 devmode->dmSpecVersion = DM_SPECVERSION;
2787 devmode->dmDriverVersion = DM_SPECVERSION;
2788 devmode->dmSize = offsetof(DEVMODEW, dmICMMethod);
2789 devmode->dmDriverExtra = 0;
2791 if (index == ENUM_REGISTRY_SETTINGS) ret = adapter_get_registry_settings( adapter, devmode );
2792 else if (index == ENUM_CURRENT_SETTINGS) ret = adapter_get_current_settings( adapter, devmode );
2793 else ret = adapter_enum_display_settings( adapter, index, devmode, flags );
2794 adapter_release( adapter );
2796 if (!ret) WARN( "Failed to query %s display settings.\n", debugstr_us(device) );
2797 else TRACE( "position %dx%d, resolution %ux%u, frequency %u, depth %u, orientation %#x.\n",
2798 (int)devmode->dmPosition.x, (int)devmode->dmPosition.y, (int)devmode->dmPelsWidth,
2799 (int)devmode->dmPelsHeight, (int)devmode->dmDisplayFrequency,
2800 (int)devmode->dmBitsPerPel, (int)devmode->dmDisplayOrientation );
2801 return ret;
2804 struct monitor_enum_info
2806 HANDLE handle;
2807 RECT rect;
2810 static unsigned int active_monitor_count(void)
2812 struct monitor *monitor;
2813 unsigned int count = 0;
2815 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2817 if ((monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) count++;
2819 return count;
2822 INT get_display_depth( UNICODE_STRING *name )
2824 struct display_device *device;
2825 BOOL is_primary;
2826 INT depth;
2828 if (!lock_display_devices())
2829 return 32;
2831 if (name && name->Length)
2832 device = find_adapter_device_by_name( name );
2833 else
2834 device = find_adapter_device_by_id( 0 ); /* use primary adapter */
2836 if (!device)
2838 unlock_display_devices();
2839 return 32;
2842 is_primary = !!(device->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
2843 if ((depth = user_driver->pGetDisplayDepth( device->device_name, is_primary )) < 0)
2845 struct adapter *adapter = CONTAINING_RECORD( device, struct adapter, dev );
2846 DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
2848 if (!adapter_get_current_settings( adapter, &current_mode )) depth = 32;
2849 else depth = current_mode.dmBitsPerPel;
2852 unlock_display_devices();
2853 return depth;
2856 /***********************************************************************
2857 * NtUserEnumDisplayMonitors (win32u.@)
2859 BOOL WINAPI NtUserEnumDisplayMonitors( HDC hdc, RECT *rect, MONITORENUMPROC proc, LPARAM lparam )
2861 struct monitor_enum_info enum_buf[8], *enum_info = enum_buf;
2862 struct enum_display_monitor_params params;
2863 struct monitor *monitor;
2864 unsigned int count = 0, i;
2865 POINT origin;
2866 RECT limit;
2867 BOOL ret = TRUE;
2869 if (hdc)
2871 DC *dc;
2872 if (!(dc = get_dc_ptr( hdc ))) return FALSE;
2873 origin.x = dc->attr->vis_rect.left;
2874 origin.y = dc->attr->vis_rect.top;
2875 release_dc_ptr( dc );
2876 if (NtGdiGetAppClipBox( hdc, &limit ) == ERROR) return FALSE;
2878 else
2880 origin.x = origin.y = 0;
2881 limit.left = limit.top = INT_MIN;
2882 limit.right = limit.bottom = INT_MAX;
2884 if (rect && !intersect_rect( &limit, &limit, rect )) return TRUE;
2886 if (!lock_display_devices()) return FALSE;
2888 count = list_count( &monitors );
2889 if (!count || (count > ARRAYSIZE(enum_buf) &&
2890 !(enum_info = malloc( count * sizeof(*enum_info) ))))
2892 unlock_display_devices();
2893 return FALSE;
2896 count = 0;
2897 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2899 RECT monrect;
2901 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) continue;
2903 monrect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
2904 get_thread_dpi() );
2905 OffsetRect( &monrect, -origin.x, -origin.y );
2906 if (!intersect_rect( &monrect, &monrect, &limit )) continue;
2907 if (monitor->is_clone) continue;
2909 enum_info[count].handle = monitor->handle;
2910 enum_info[count].rect = monrect;
2911 count++;
2914 unlock_display_devices();
2916 params.proc = proc;
2917 params.hdc = hdc;
2918 params.lparam = lparam;
2919 for (i = 0; i < count; i++)
2921 void *ret_ptr;
2922 ULONG ret_len;
2923 params.monitor = enum_info[i].handle;
2924 params.rect = enum_info[i].rect;
2925 if (!KeUserModeCallback( NtUserCallEnumDisplayMonitor, &params, sizeof(params),
2926 &ret_ptr, &ret_len ))
2928 ret = FALSE;
2929 break;
2932 if (enum_info != enum_buf) free( enum_info );
2933 return ret;
2936 BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info )
2938 struct monitor *monitor;
2939 UINT dpi_from, dpi_to;
2941 if (info->cbSize != sizeof(MONITORINFOEXW) && info->cbSize != sizeof(MONITORINFO)) return FALSE;
2943 if (!lock_display_devices()) return FALSE;
2945 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
2947 if (monitor->handle != handle) continue;
2948 if (!(monitor->dev.state_flags & DISPLAY_DEVICE_ACTIVE)) break;
2950 /* FIXME: map dpi */
2951 info->rcMonitor = monitor->rc_monitor;
2952 info->rcWork = monitor->rc_work;
2953 info->dwFlags = monitor->flags;
2954 if (info->cbSize >= sizeof(MONITORINFOEXW))
2956 if (monitor->adapter)
2957 lstrcpyW( ((MONITORINFOEXW *)info)->szDevice, monitor->adapter->dev.device_name );
2958 else
2959 asciiz_to_unicode( ((MONITORINFOEXW *)info)->szDevice, "WinDisc" );
2961 unlock_display_devices();
2963 if ((dpi_to = get_thread_dpi()))
2965 dpi_from = get_monitor_dpi( handle );
2966 info->rcMonitor = map_dpi_rect( info->rcMonitor, dpi_from, dpi_to );
2967 info->rcWork = map_dpi_rect( info->rcWork, dpi_from, dpi_to );
2969 TRACE( "flags %04x, monitor %s, work %s\n", (int)info->dwFlags,
2970 wine_dbgstr_rect(&info->rcMonitor), wine_dbgstr_rect(&info->rcWork));
2971 return TRUE;
2974 unlock_display_devices();
2975 WARN( "invalid handle %p\n", handle );
2976 RtlSetLastWin32Error( ERROR_INVALID_MONITOR_HANDLE );
2977 return FALSE;
2980 HMONITOR monitor_from_rect( const RECT *rect, UINT flags, UINT dpi )
2982 HMONITOR primary = 0, nearest = 0, ret = 0;
2983 UINT max_area = 0, min_distance = ~0u;
2984 struct monitor *monitor;
2985 RECT r;
2987 r = map_dpi_rect( *rect, dpi, system_dpi );
2988 if (IsRectEmpty( &r ))
2990 r.right = r.left + 1;
2991 r.bottom = r.top + 1;
2994 if (!lock_display_devices()) return 0;
2996 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
2998 RECT intersect;
2999 RECT monitor_rect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
3000 system_dpi );
3002 if (intersect_rect( &intersect, &monitor_rect, &r ))
3004 /* check for larger intersecting area */
3005 UINT area = (intersect.right - intersect.left) * (intersect.bottom - intersect.top);
3006 if (area > max_area)
3008 max_area = area;
3009 ret = monitor->handle;
3012 else if (!max_area) /* if not intersecting, check for min distance */
3014 UINT distance;
3015 UINT x, y;
3017 if (r.right <= monitor_rect.left) x = monitor_rect.left - r.right;
3018 else if (monitor_rect.right <= r.left) x = r.left - monitor_rect.right;
3019 else x = 0;
3020 if (r.bottom <= monitor_rect.top) y = monitor_rect.top - r.bottom;
3021 else if (monitor_rect.bottom <= r.top) y = r.top - monitor_rect.bottom;
3022 else y = 0;
3023 distance = x * x + y * y;
3024 if (distance < min_distance)
3026 min_distance = distance;
3027 nearest = monitor->handle;
3031 if (monitor->flags & MONITORINFOF_PRIMARY) primary = monitor->handle;
3034 unlock_display_devices();
3036 if (!ret)
3038 if (flags & MONITOR_DEFAULTTOPRIMARY) ret = primary;
3039 else if (flags & MONITOR_DEFAULTTONEAREST) ret = nearest;
3042 TRACE( "%s flags %x returning %p\n", wine_dbgstr_rect(rect), flags, ret );
3043 return ret;
3046 HMONITOR monitor_from_point( POINT pt, UINT flags, UINT dpi )
3048 RECT rect;
3049 SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 );
3050 return monitor_from_rect( &rect, flags, dpi );
3053 /* see MonitorFromWindow */
3054 HMONITOR monitor_from_window( HWND hwnd, UINT flags, UINT dpi )
3056 RECT rect;
3057 WINDOWPLACEMENT wp;
3059 TRACE( "(%p, 0x%08x)\n", hwnd, flags );
3061 wp.length = sizeof(wp);
3062 if (is_iconic( hwnd ) && NtUserGetWindowPlacement( hwnd, &wp ))
3063 return monitor_from_rect( &wp.rcNormalPosition, flags, dpi );
3065 if (get_window_rect( hwnd, &rect, dpi ))
3066 return monitor_from_rect( &rect, flags, dpi );
3068 if (!(flags & (MONITOR_DEFAULTTOPRIMARY|MONITOR_DEFAULTTONEAREST))) return 0;
3069 /* retrieve the primary */
3070 SetRect( &rect, 0, 0, 1, 1 );
3071 return monitor_from_rect( &rect, flags, dpi );
3074 /***********************************************************************
3075 * NtUserGetSystemDpiForProcess (win32u.@)
3077 ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process )
3079 if (process && process != GetCurrentProcess())
3081 FIXME( "not supported on other process %p\n", process );
3082 return 0;
3085 return system_dpi;
3088 /***********************************************************************
3089 * NtUserGetDpiForMonitor (win32u.@)
3091 BOOL WINAPI NtUserGetDpiForMonitor( HMONITOR monitor, UINT type, UINT *x, UINT *y )
3093 if (type > 2)
3095 RtlSetLastWin32Error( ERROR_BAD_ARGUMENTS );
3096 return FALSE;
3098 if (!x || !y)
3100 RtlSetLastWin32Error( ERROR_INVALID_ADDRESS );
3101 return FALSE;
3103 switch (get_thread_dpi_awareness())
3105 case DPI_AWARENESS_UNAWARE: *x = *y = USER_DEFAULT_SCREEN_DPI; break;
3106 case DPI_AWARENESS_SYSTEM_AWARE: *x = *y = system_dpi; break;
3107 default: *x = *y = get_monitor_dpi( monitor ); break;
3109 return TRUE;
3112 /**********************************************************************
3113 * LogicalToPhysicalPointForPerMonitorDPI (win32u.@)
3115 BOOL WINAPI NtUserLogicalToPerMonitorDPIPhysicalPoint( HWND hwnd, POINT *pt )
3117 RECT rect;
3119 if (!get_window_rect( hwnd, &rect, get_thread_dpi() )) return FALSE;
3120 if (pt->x < rect.left || pt->y < rect.top || pt->x > rect.right || pt->y > rect.bottom) return FALSE;
3121 *pt = point_win_to_phys_dpi( hwnd, *pt );
3122 return TRUE;
3125 /**********************************************************************
3126 * NtUserPerMonitorDPIPhysicalToLogicalPoint (win32u.@)
3128 BOOL WINAPI NtUserPerMonitorDPIPhysicalToLogicalPoint( HWND hwnd, POINT *pt )
3130 RECT rect;
3131 BOOL ret = FALSE;
3133 if (get_window_rect( hwnd, &rect, 0 ) &&
3134 pt->x >= rect.left && pt->y >= rect.top && pt->x <= rect.right && pt->y <= rect.bottom)
3136 *pt = point_phys_to_win_dpi( hwnd, *pt );
3137 ret = TRUE;
3139 return ret;
3142 /* retrieve the cached base keys for a given entry */
3143 static BOOL get_base_keys( enum parameter_key index, HKEY *base_key, HKEY *volatile_key )
3145 static HKEY base_keys[NB_PARAM_KEYS];
3146 static HKEY volatile_keys[NB_PARAM_KEYS];
3147 WCHAR bufferW[128];
3148 HKEY key;
3150 if (!base_keys[index] && base_key)
3152 if (!(key = reg_create_key( hkcu_key, bufferW,
3153 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
3154 0, NULL )))
3155 return FALSE;
3156 if (InterlockedCompareExchangePointer( (void **)&base_keys[index], key, 0 ))
3157 NtClose( key );
3159 if (!volatile_keys[index] && volatile_key)
3161 if (!(key = reg_create_key( volatile_base_key, bufferW,
3162 asciiz_to_unicode( bufferW, parameter_key_names[index] ) - sizeof(WCHAR),
3163 REG_OPTION_VOLATILE, NULL )))
3164 return FALSE;
3165 if (InterlockedCompareExchangePointer( (void **)&volatile_keys[index], key, 0 ))
3166 NtClose( key );
3168 if (base_key) *base_key = base_keys[index];
3169 if (volatile_key) *volatile_key = volatile_keys[index];
3170 return TRUE;
3173 /* load a value to a registry entry */
3174 static DWORD load_entry( struct sysparam_entry *entry, void *data, DWORD size )
3176 char buffer[4096];
3177 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
3178 DWORD count;
3179 HKEY base_key, volatile_key;
3181 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
3183 if (!(count = query_reg_ascii_value( volatile_key, entry->regval, value, sizeof(buffer) )))
3184 count = query_reg_ascii_value( base_key, entry->regval, value, sizeof(buffer) );
3185 if (count > size)
3187 count = size;
3188 /* make sure strings are null-terminated */
3189 if (value->Type == REG_SZ) ((WCHAR *)value->Data)[count / sizeof(WCHAR) - 1] = 0;
3191 if (count) memcpy( data, value->Data, count );
3192 entry->loaded = TRUE;
3193 return count;
3196 /* save a value to a registry entry */
3197 static BOOL save_entry( const struct sysparam_entry *entry, const void *data, DWORD size,
3198 DWORD type, UINT flags )
3200 HKEY base_key, volatile_key;
3201 WCHAR nameW[64];
3203 asciiz_to_unicode( nameW, entry->regval );
3204 if (flags & SPIF_UPDATEINIFILE)
3206 if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
3207 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
3208 reg_delete_value( volatile_key, nameW );
3210 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
3212 asciiz_to_unicode( nameW, entry->mirror );
3213 set_reg_value( base_key, nameW, type, data, size );
3216 else
3218 if (!get_base_keys( entry->base_key, NULL, &volatile_key )) return FALSE;
3219 if (!set_reg_value( volatile_key, nameW, type, data, size )) return FALSE;
3221 return TRUE;
3224 /* save a string value to a registry entry */
3225 static BOOL save_entry_string( const struct sysparam_entry *entry, const WCHAR *str, UINT flags )
3227 return save_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ, flags );
3230 /* initialize an entry in the registry if missing */
3231 static BOOL init_entry( struct sysparam_entry *entry, const void *data, DWORD size, DWORD type )
3233 KEY_VALUE_PARTIAL_INFORMATION value;
3234 UNICODE_STRING name;
3235 WCHAR nameW[64];
3236 HKEY base_key;
3237 DWORD count;
3238 NTSTATUS status;
3240 if (!get_base_keys( entry->base_key, &base_key, NULL )) return FALSE;
3242 name.Buffer = nameW;
3243 name.MaximumLength = asciiz_to_unicode( nameW, entry->regval );
3244 name.Length = name.MaximumLength - sizeof(WCHAR);
3245 status = NtQueryValueKey( base_key, &name, KeyValuePartialInformation,
3246 &value, sizeof(value), &count );
3247 if (!status || status == STATUS_BUFFER_OVERFLOW) return TRUE;
3249 if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
3250 if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
3252 asciiz_to_unicode( nameW, entry->mirror );
3253 set_reg_value( base_key, nameW, type, data, size );
3255 entry->loaded = TRUE;
3256 return TRUE;
3259 /* initialize a string value in the registry if missing */
3260 static BOOL init_entry_string( struct sysparam_entry *entry, const WCHAR *str )
3262 return init_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ );
3265 /* set an int parameter in the registry */
3266 static BOOL set_int_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3268 WCHAR bufW[32];
3269 char buf[32];
3271 sprintf( buf, "%d", int_param );
3272 asciiz_to_unicode( bufW, buf );
3273 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3274 entry->uint.val = int_param;
3275 entry->hdr.loaded = TRUE;
3276 return TRUE;
3279 /* initialize an int parameter */
3280 static BOOL init_int_entry( union sysparam_all_entry *entry )
3282 WCHAR bufW[32];
3283 char buf[32];
3285 sprintf( buf, "%d", entry->uint.val );
3286 asciiz_to_unicode( bufW, buf );
3287 return init_entry_string( &entry->hdr, bufW );
3290 /* load a uint parameter from the registry */
3291 static BOOL get_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3293 if (!ptr_param) return FALSE;
3295 if (!entry->hdr.loaded)
3297 WCHAR buf[32];
3298 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
3300 *(UINT *)ptr_param = entry->uint.val;
3301 return TRUE;
3304 /* set a uint parameter in the registry */
3305 static BOOL set_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3307 WCHAR bufW[32];
3308 char buf[32];
3310 sprintf( buf, "%u", int_param );
3311 asciiz_to_unicode( bufW, buf );
3312 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3313 entry->uint.val = int_param;
3314 entry->hdr.loaded = TRUE;
3315 return TRUE;
3318 /* initialize a uint parameter */
3319 static BOOL init_uint_entry( union sysparam_all_entry *entry )
3321 WCHAR bufW[32];
3322 char buf[32];
3324 sprintf( buf, "%u", entry->uint.val );
3325 asciiz_to_unicode( bufW, buf );
3326 return init_entry_string( &entry->hdr, bufW );
3329 /* load a twips parameter from the registry */
3330 static BOOL get_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3332 int val;
3334 if (!ptr_param) return FALSE;
3336 if (!entry->hdr.loaded)
3338 WCHAR buf[32];
3339 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
3342 /* Dimensions are quoted as being "twips" values if negative and pixels if positive.
3343 * One inch is 1440 twips.
3344 * See for example
3345 * Technical Reference to the Windows 2000 Registry ->
3346 * HKEY_CURRENT_USER -> Control Panel -> Desktop -> WindowMetrics
3348 val = entry->uint.val;
3349 if (val < 0)
3350 val = muldiv( -val, dpi, 1440 );
3351 else
3352 val = map_to_dpi( val, dpi );
3354 *(int *)ptr_param = val;
3355 return TRUE;
3358 /* set a twips parameter in the registry */
3359 static BOOL set_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3361 int val = int_param;
3362 if (val > 0) val = map_from_system_dpi( val );
3363 return set_int_entry( entry, val, ptr_param, flags );
3366 /* load a bool parameter from the registry */
3367 static BOOL get_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3369 if (!ptr_param) return FALSE;
3371 if (!entry->hdr.loaded)
3373 WCHAR buf[32];
3374 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = wcstol( buf, NULL, 10 ) != 0;
3376 *(UINT *)ptr_param = entry->bool.val;
3377 return TRUE;
3380 /* set a bool parameter in the registry */
3381 static BOOL set_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3383 WCHAR buf[] = { int_param ? '1' : '0', 0 };
3385 if (!save_entry_string( &entry->hdr, buf, flags )) return FALSE;
3386 entry->bool.val = int_param != 0;
3387 entry->hdr.loaded = TRUE;
3388 return TRUE;
3391 /* initialize a bool parameter */
3392 static BOOL init_bool_entry( union sysparam_all_entry *entry )
3394 WCHAR buf[] = { entry->bool.val ? '1' : '0', 0 };
3396 return init_entry_string( &entry->hdr, buf );
3399 /* load a bool parameter using Yes/No strings from the registry */
3400 static BOOL get_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3402 if (!ptr_param) return FALSE;
3404 if (!entry->hdr.loaded)
3406 WCHAR buf[32];
3407 if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = !wcsicmp( yesW, buf );
3409 *(UINT *)ptr_param = entry->bool.val;
3410 return TRUE;
3413 /* set a bool parameter using Yes/No strings from the registry */
3414 static BOOL set_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3416 const WCHAR *str = int_param ? yesW : noW;
3418 if (!save_entry_string( &entry->hdr, str, flags )) return FALSE;
3419 entry->bool.val = int_param != 0;
3420 entry->hdr.loaded = TRUE;
3421 return TRUE;
3424 /* initialize a bool parameter using Yes/No strings */
3425 static BOOL init_yesno_entry( union sysparam_all_entry *entry )
3427 return init_entry_string( &entry->hdr, entry->bool.val ? yesW : noW );
3430 /* load a dword (binary) parameter from the registry */
3431 static BOOL get_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3433 if (!ptr_param) return FALSE;
3435 if (!entry->hdr.loaded)
3437 DWORD val;
3438 if (load_entry( &entry->hdr, &val, sizeof(val) ) == sizeof(DWORD)) entry->dword.val = val;
3440 *(DWORD *)ptr_param = entry->dword.val;
3441 return TRUE;
3444 /* set a dword (binary) parameter in the registry */
3445 static BOOL set_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3447 DWORD val = PtrToUlong( ptr_param );
3449 if (!save_entry( &entry->hdr, &val, sizeof(val), REG_DWORD, flags )) return FALSE;
3450 entry->dword.val = val;
3451 entry->hdr.loaded = TRUE;
3452 return TRUE;
3455 /* initialize a dword parameter */
3456 static BOOL init_dword_entry( union sysparam_all_entry *entry )
3458 return init_entry( &entry->hdr, &entry->dword.val, sizeof(entry->dword.val), REG_DWORD );
3461 /* load an RGB parameter from the registry */
3462 static BOOL get_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3464 if (!ptr_param) return FALSE;
3466 if (!entry->hdr.loaded)
3468 WCHAR buf[32];
3470 if (load_entry( &entry->hdr, buf, sizeof(buf) ))
3472 DWORD r, g, b;
3473 WCHAR *end, *str = buf;
3475 r = wcstoul( str, &end, 10 );
3476 if (end == str || !*end) goto done;
3477 str = end + 1;
3478 g = wcstoul( str, &end, 10 );
3479 if (end == str || !*end) goto done;
3480 str = end + 1;
3481 b = wcstoul( str, &end, 10 );
3482 if (end == str) goto done;
3483 if (r > 255 || g > 255 || b > 255) goto done;
3484 entry->rgb.val = RGB( r, g, b );
3487 done:
3488 *(COLORREF *)ptr_param = entry->rgb.val;
3489 return TRUE;
3492 /* set an RGB parameter in the registry */
3493 static BOOL set_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3495 WCHAR bufW[32];
3496 char buf[32];
3497 HBRUSH brush;
3498 HPEN pen;
3500 sprintf( buf, "%u %u %u", GetRValue(int_param), GetGValue(int_param), GetBValue(int_param) );
3501 asciiz_to_unicode( bufW, buf );
3502 if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
3503 entry->rgb.val = int_param;
3504 entry->hdr.loaded = TRUE;
3505 if ((brush = InterlockedExchangePointer( (void **)&entry->rgb.brush, 0 )))
3507 make_gdi_object_system( brush, FALSE );
3508 NtGdiDeleteObjectApp( brush );
3510 if ((pen = InterlockedExchangePointer( (void **)&entry->rgb.pen, 0 )))
3512 make_gdi_object_system( pen, FALSE );
3513 NtGdiDeleteObjectApp( pen );
3515 return TRUE;
3518 /* initialize an RGB parameter */
3519 static BOOL init_rgb_entry( union sysparam_all_entry *entry )
3521 WCHAR bufW[32];
3522 char buf[32];
3524 sprintf( buf, "%u %u %u", GetRValue(entry->rgb.val), GetGValue(entry->rgb.val),
3525 GetBValue(entry->rgb.val) );
3526 asciiz_to_unicode( bufW, buf );
3527 return init_entry_string( &entry->hdr, bufW );
3530 /* get a path parameter in the registry */
3531 static BOOL get_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3533 if (!ptr_param) return FALSE;
3535 if (!entry->hdr.loaded)
3537 WCHAR buffer[MAX_PATH];
3539 if (load_entry( &entry->hdr, buffer, sizeof(buffer) ))
3540 lstrcpynW( entry->path.path, buffer, MAX_PATH );
3542 lstrcpynW( ptr_param, entry->path.path, int_param );
3543 return TRUE;
3546 /* set a path parameter in the registry */
3547 static BOOL set_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3549 WCHAR buffer[MAX_PATH];
3550 BOOL ret;
3552 lstrcpynW( buffer, ptr_param, MAX_PATH );
3553 ret = save_entry_string( &entry->hdr, buffer, flags );
3554 if (ret)
3556 lstrcpyW( entry->path.path, buffer );
3557 entry->hdr.loaded = TRUE;
3559 return ret;
3562 /* initialize a path parameter */
3563 static BOOL init_path_entry( union sysparam_all_entry *entry )
3565 return init_entry_string( &entry->hdr, entry->path.path );
3568 /* get a binary parameter in the registry */
3569 static BOOL get_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3571 if (!ptr_param) return FALSE;
3573 if (!entry->hdr.loaded)
3575 void *buffer = malloc( entry->bin.size );
3576 DWORD len = load_entry( &entry->hdr, buffer, entry->bin.size );
3578 if (len)
3580 memcpy( entry->bin.ptr, buffer, entry->bin.size );
3581 memset( (char *)entry->bin.ptr + len, 0, entry->bin.size - len );
3583 free( buffer );
3585 memcpy( ptr_param, entry->bin.ptr, min( int_param, entry->bin.size ) );
3586 return TRUE;
3589 /* set a binary parameter in the registry */
3590 static BOOL set_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3592 BOOL ret;
3593 void *buffer = malloc( entry->bin.size );
3595 memcpy( buffer, entry->bin.ptr, entry->bin.size );
3596 memcpy( buffer, ptr_param, min( int_param, entry->bin.size ));
3597 ret = save_entry( &entry->hdr, buffer, entry->bin.size, REG_BINARY, flags );
3598 if (ret)
3600 memcpy( entry->bin.ptr, buffer, entry->bin.size );
3601 entry->hdr.loaded = TRUE;
3603 free( buffer );
3604 return ret;
3607 /* initialize a binary parameter */
3608 static BOOL init_binary_entry( union sysparam_all_entry *entry )
3610 return init_entry( &entry->hdr, entry->bin.ptr, entry->bin.size, REG_BINARY );
3613 static void logfont16to32( const LOGFONT16 *font16, LPLOGFONTW font32 )
3615 font32->lfHeight = font16->lfHeight;
3616 font32->lfWidth = font16->lfWidth;
3617 font32->lfEscapement = font16->lfEscapement;
3618 font32->lfOrientation = font16->lfOrientation;
3619 font32->lfWeight = font16->lfWeight;
3620 font32->lfItalic = font16->lfItalic;
3621 font32->lfUnderline = font16->lfUnderline;
3622 font32->lfStrikeOut = font16->lfStrikeOut;
3623 font32->lfCharSet = font16->lfCharSet;
3624 font32->lfOutPrecision = font16->lfOutPrecision;
3625 font32->lfClipPrecision = font16->lfClipPrecision;
3626 font32->lfQuality = font16->lfQuality;
3627 font32->lfPitchAndFamily = font16->lfPitchAndFamily;
3628 win32u_mbtowc( &ansi_cp, font32->lfFaceName, LF_FACESIZE, font16->lfFaceName,
3629 strlen( font16->lfFaceName ));
3630 font32->lfFaceName[LF_FACESIZE-1] = 0;
3633 static void get_real_fontname( LOGFONTW *lf, WCHAR fullname[LF_FACESIZE] )
3635 struct font_enum_entry enum_entry;
3636 ULONG count = sizeof(enum_entry);
3637 HDC hdc;
3639 hdc = get_display_dc();
3640 NtGdiEnumFonts( hdc, 0, 0, lstrlenW( lf->lfFaceName ), lf->lfFaceName, lf->lfCharSet,
3641 &count, &enum_entry );
3642 release_display_dc( hdc );
3644 if (count)
3645 lstrcpyW( fullname, enum_entry.lf.elfFullName );
3646 else
3647 lstrcpyW( fullname, lf->lfFaceName );
3650 LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, int *height )
3652 SIZE sz;
3653 static const WCHAR abcdW[] =
3654 {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
3655 'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H',
3656 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
3658 if (metric && !NtGdiGetTextMetricsW( hdc, metric, 0 )) return 0;
3660 if (!NtGdiGetTextExtentExW( hdc, abcdW, ARRAYSIZE(abcdW), 0, NULL, NULL, &sz, 0 ))
3661 return 0;
3663 if (height) *height = sz.cy;
3664 return (sz.cx / 26 + 1) / 2;
3667 /* get text metrics and/or "average" char width of the specified logfont
3668 * for the specified dc */
3669 static void get_text_metr_size( HDC hdc, LOGFONTW *lf, TEXTMETRICW *metric, UINT *psz )
3671 HFONT hfont, hfontsav;
3672 TEXTMETRICW tm;
3673 UINT ret;
3674 if (!metric) metric = &tm;
3675 hfont = NtGdiHfontCreate( lf, sizeof(*lf), 0, 0, NULL );
3676 if (!hfont || !(hfontsav = NtGdiSelectFont( hdc, hfont )))
3678 metric->tmHeight = -1;
3679 if (psz) *psz = 10;
3680 if (hfont) NtGdiDeleteObjectApp( hfont );
3681 return;
3683 ret = get_char_dimensions( hdc, metric, NULL );
3684 if (psz) *psz = ret ? ret : 10;
3685 NtGdiSelectFont( hdc, hfontsav );
3686 NtGdiDeleteObjectApp( hfont );
3689 DWORD get_dialog_base_units(void)
3691 static int cx, cy;
3693 if (!cx)
3695 HDC hdc;
3697 if ((hdc = NtUserGetDC( 0 )))
3699 cx = get_char_dimensions( hdc, NULL, &cy );
3700 NtUserReleaseDC( 0, hdc );
3702 TRACE( "base units = %d,%d\n", cx, cy );
3705 return MAKELONG( muldiv( cx, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ),
3706 muldiv( cy, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ));
3709 /* adjust some of the raw values found in the registry */
3710 static void normalize_nonclientmetrics( NONCLIENTMETRICSW *pncm)
3712 TEXTMETRICW tm;
3713 HDC hdc = get_display_dc();
3715 if( pncm->iBorderWidth < 1) pncm->iBorderWidth = 1;
3716 if( pncm->iCaptionWidth < 8) pncm->iCaptionWidth = 8;
3717 if( pncm->iScrollWidth < 8) pncm->iScrollWidth = 8;
3718 if( pncm->iScrollHeight < 8) pncm->iScrollHeight = 8;
3720 /* adjust some heights to the corresponding font */
3721 get_text_metr_size( hdc, &pncm->lfMenuFont, &tm, NULL);
3722 pncm->iMenuHeight = max( pncm->iMenuHeight, 2 + tm.tmHeight + tm.tmExternalLeading );
3723 get_text_metr_size( hdc, &pncm->lfCaptionFont, &tm, NULL);
3724 pncm->iCaptionHeight = max( pncm->iCaptionHeight, 2 + tm.tmHeight);
3725 get_text_metr_size( hdc, &pncm->lfSmCaptionFont, &tm, NULL);
3726 pncm->iSmCaptionHeight = max( pncm->iSmCaptionHeight, 2 + tm.tmHeight);
3727 release_display_dc( hdc );
3730 /* load a font (binary) parameter from the registry */
3731 static BOOL get_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3733 LOGFONTW font;
3735 if (!ptr_param) return FALSE;
3737 if (!entry->hdr.loaded)
3739 switch (load_entry( &entry->hdr, &font, sizeof(font) ))
3741 case sizeof(font):
3742 if (font.lfHeight > 0) /* positive height value means points ( inch/72 ) */
3743 font.lfHeight = -muldiv( font.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
3744 entry->font.val = font;
3745 break;
3746 case sizeof(LOGFONT16): /* win9x-winME format */
3747 logfont16to32( (LOGFONT16 *)&font, &entry->font.val );
3748 if (entry->font.val.lfHeight > 0)
3749 entry->font.val.lfHeight = -muldiv( entry->font.val.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
3750 break;
3751 default:
3752 WARN( "Unknown format in key %s value %s\n",
3753 debugstr_a( parameter_key_names[entry->hdr.base_key] ),
3754 debugstr_a( entry->hdr.regval ));
3755 /* fall through */
3756 case 0: /* use the default GUI font */
3757 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(font), &font );
3758 font.lfHeight = map_from_system_dpi( font.lfHeight );
3759 font.lfWeight = entry->font.weight;
3760 entry->font.val = font;
3761 break;
3763 get_real_fontname( &entry->font.val, entry->font.fullname );
3764 entry->hdr.loaded = TRUE;
3766 font = entry->font.val;
3767 font.lfHeight = map_to_dpi( font.lfHeight, dpi );
3768 lstrcpyW( font.lfFaceName, entry->font.fullname );
3769 *(LOGFONTW *)ptr_param = font;
3770 return TRUE;
3773 /* set a font (binary) parameter in the registry */
3774 static BOOL set_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3776 LOGFONTW font;
3777 WCHAR *ptr;
3779 memcpy( &font, ptr_param, sizeof(font) );
3780 /* zero pad the end of lfFaceName so we don't save uninitialised data */
3781 for (ptr = font.lfFaceName; ptr < font.lfFaceName + LF_FACESIZE && *ptr; ptr++);
3782 if (ptr < font.lfFaceName + LF_FACESIZE)
3783 memset( ptr, 0, (font.lfFaceName + LF_FACESIZE - ptr) * sizeof(WCHAR) );
3784 if (font.lfHeight < 0) font.lfHeight = map_from_system_dpi( font.lfHeight );
3786 if (!save_entry( &entry->hdr, &font, sizeof(font), REG_BINARY, flags )) return FALSE;
3787 entry->font.val = font;
3788 get_real_fontname( &entry->font.val, entry->font.fullname );
3789 entry->hdr.loaded = TRUE;
3790 return TRUE;
3793 /* initialize a font (binary) parameter */
3794 static BOOL init_font_entry( union sysparam_all_entry *entry )
3796 NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(entry->font.val), &entry->font.val );
3797 entry->font.val.lfHeight = map_from_system_dpi( entry->font.val.lfHeight );
3798 entry->font.val.lfWeight = entry->font.weight;
3799 get_real_fontname( &entry->font.val, entry->font.fullname );
3800 return init_entry( &entry->hdr, &entry->font.val, sizeof(entry->font.val), REG_BINARY );
3803 /* get a user pref parameter in the registry */
3804 static BOOL get_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
3806 union sysparam_all_entry *parent_entry = entry->pref.parent;
3807 BYTE prefs[8];
3809 if (!ptr_param) return FALSE;
3811 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, dpi )) return FALSE;
3812 *(BOOL *)ptr_param = (prefs[entry->pref.offset] & entry->pref.mask) != 0;
3813 return TRUE;
3816 /* set a user pref parameter in the registry */
3817 static BOOL set_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
3819 union sysparam_all_entry *parent_entry = entry->pref.parent;
3820 BYTE prefs[8];
3822 parent_entry->hdr.loaded = FALSE; /* force loading it again */
3823 if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, get_system_dpi() )) return FALSE;
3825 if (PtrToUlong( ptr_param )) prefs[entry->pref.offset] |= entry->pref.mask;
3826 else prefs[entry->pref.offset] &= ~entry->pref.mask;
3828 return parent_entry->hdr.set( parent_entry, sizeof(prefs), prefs, flags );
3831 static BOOL get_entry_dpi( void *ptr, UINT int_param, void *ptr_param, UINT dpi )
3833 union sysparam_all_entry *entry = ptr;
3834 return entry->hdr.get( entry, int_param, ptr_param, dpi );
3837 static BOOL get_entry( void *ptr, UINT int_param, void *ptr_param )
3839 return get_entry_dpi( ptr, int_param, ptr_param, get_system_dpi() );
3842 static BOOL set_entry( void *ptr, UINT int_param, void *ptr_param, UINT flags )
3844 union sysparam_all_entry *entry = ptr;
3845 return entry->hdr.set( entry, int_param, ptr_param, flags );
3848 #define UINT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3849 { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg }, (val) } }
3851 #define UINT_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
3852 { .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg, mirror_base, reg }, (val) } }
3854 #define INT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3855 { .uint = { { get_uint_entry, set_int_entry, init_int_entry, base, reg }, (val) } }
3857 #define BOOL_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3858 { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg }, (val) } }
3860 #define BOOL_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
3861 { .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg, mirror_base, reg }, (val) } }
3863 #define TWIPS_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3864 { .uint = { { get_twips_entry, set_twips_entry, init_int_entry, base, reg }, (val) } }
3866 #define YESNO_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3867 { .bool = { { get_yesno_entry, set_yesno_entry, init_yesno_entry, base, reg }, (val) } }
3869 #define DWORD_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
3870 { .dword = { { get_dword_entry, set_dword_entry, init_dword_entry, base, reg }, (val) } }
3872 #define BINARY_ENTRY(name,data,base,reg) union sysparam_all_entry entry_##name = \
3873 { .bin = { { get_binary_entry, set_binary_entry, init_binary_entry, base, reg }, data, sizeof(data) } }
3875 #define PATH_ENTRY(name,base,reg,buffer) union sysparam_all_entry entry_##name = \
3876 { .path = { { get_path_entry, set_path_entry, init_path_entry, base, reg }, buffer } }
3878 #define FONT_ENTRY(name,weight,base,reg) union sysparam_all_entry entry_##name = \
3879 { .font = { { get_font_entry, set_font_entry, init_font_entry, base, reg }, (weight) } }
3881 #define USERPREF_ENTRY(name,offset,mask) union sysparam_all_entry entry_##name = \
3882 { .pref = { { get_userpref_entry, set_userpref_entry }, &entry_USERPREFERENCESMASK, (offset), (mask) } }
3884 static UINT_ENTRY( DRAGWIDTH, 4, DESKTOP_KEY, "DragWidth" );
3885 static UINT_ENTRY( DRAGHEIGHT, 4, DESKTOP_KEY, "DragHeight" );
3886 static UINT_ENTRY( DOUBLECLICKTIME, 500, MOUSE_KEY, "DoubleClickSpeed" );
3887 static UINT_ENTRY( FONTSMOOTHING, 2, DESKTOP_KEY, "FontSmoothing" );
3888 static UINT_ENTRY( GRIDGRANULARITY, 0, DESKTOP_KEY, "GridGranularity" );
3889 static UINT_ENTRY( KEYBOARDDELAY, 1, KEYBOARD_KEY, "KeyboardDelay" );
3890 static UINT_ENTRY( KEYBOARDSPEED, 31, KEYBOARD_KEY, "KeyboardSpeed" );
3891 static UINT_ENTRY( MENUSHOWDELAY, 400, DESKTOP_KEY, "MenuShowDelay" );
3892 static UINT_ENTRY( MINARRANGE, ARW_HIDE, METRICS_KEY, "MinArrange" );
3893 static UINT_ENTRY( MINHORZGAP, 0, METRICS_KEY, "MinHorzGap" );
3894 static UINT_ENTRY( MINVERTGAP, 0, METRICS_KEY, "MinVertGap" );
3895 static UINT_ENTRY( MINWIDTH, 154, METRICS_KEY, "MinWidth" );
3896 static UINT_ENTRY( MOUSEHOVERHEIGHT, 4, MOUSE_KEY, "MouseHoverHeight" );
3897 static UINT_ENTRY( MOUSEHOVERTIME, 400, MOUSE_KEY, "MouseHoverTime" );
3898 static UINT_ENTRY( MOUSEHOVERWIDTH, 4, MOUSE_KEY, "MouseHoverWidth" );
3899 static UINT_ENTRY( MOUSESPEED, 10, MOUSE_KEY, "MouseSensitivity" );
3900 static UINT_ENTRY( MOUSETRAILS, 0, MOUSE_KEY, "MouseTrails" );
3901 static UINT_ENTRY( SCREENSAVETIMEOUT, 300, DESKTOP_KEY, "ScreenSaveTimeOut" );
3902 static UINT_ENTRY( WHEELSCROLLCHARS, 3, DESKTOP_KEY, "WheelScrollChars" );
3903 static UINT_ENTRY( WHEELSCROLLLINES, 3, DESKTOP_KEY, "WheelScrollLines" );
3904 static UINT_ENTRY_MIRROR( DOUBLECLKHEIGHT, 4, MOUSE_KEY, "DoubleClickHeight", DESKTOP_KEY );
3905 static UINT_ENTRY_MIRROR( DOUBLECLKWIDTH, 4, MOUSE_KEY, "DoubleClickWidth", DESKTOP_KEY );
3906 static UINT_ENTRY_MIRROR( MENUDROPALIGNMENT, 0, DESKTOP_KEY, "MenuDropAlignment", VERSION_KEY );
3908 static INT_ENTRY( MOUSETHRESHOLD1, 6, MOUSE_KEY, "MouseThreshold1" );
3909 static INT_ENTRY( MOUSETHRESHOLD2, 10, MOUSE_KEY, "MouseThreshold2" );
3910 static INT_ENTRY( MOUSEACCELERATION, 1, MOUSE_KEY, "MouseSpeed" );
3912 static BOOL_ENTRY( BLOCKSENDINPUTRESETS, FALSE, DESKTOP_KEY, "BlockSendInputResets" );
3913 static BOOL_ENTRY( DRAGFULLWINDOWS, FALSE, DESKTOP_KEY, "DragFullWindows" );
3914 static BOOL_ENTRY( KEYBOARDPREF, TRUE, KEYBOARDPREF_KEY, "On" );
3915 static BOOL_ENTRY( LOWPOWERACTIVE, FALSE, DESKTOP_KEY, "LowPowerActive" );
3916 static BOOL_ENTRY( MOUSEBUTTONSWAP, FALSE, MOUSE_KEY, "SwapMouseButtons" );
3917 static BOOL_ENTRY( POWEROFFACTIVE, FALSE, DESKTOP_KEY, "PowerOffActive" );
3918 static BOOL_ENTRY( SCREENREADER, FALSE, SCREENREADER_KEY, "On" );
3919 static BOOL_ENTRY( SCREENSAVEACTIVE, TRUE, DESKTOP_KEY, "ScreenSaveActive" );
3920 static BOOL_ENTRY( SCREENSAVERRUNNING, FALSE, DESKTOP_KEY, "WINE_ScreenSaverRunning" ); /* FIXME - real value */
3921 static BOOL_ENTRY( SHOWSOUNDS, FALSE, SHOWSOUNDS_KEY, "On" );
3922 static BOOL_ENTRY( SNAPTODEFBUTTON, FALSE, MOUSE_KEY, "SnapToDefaultButton" );
3923 static BOOL_ENTRY_MIRROR( ICONTITLEWRAP, TRUE, DESKTOP_KEY, "IconTitleWrap", METRICS_KEY );
3924 static BOOL_ENTRY( AUDIODESC_ON, FALSE, AUDIODESC_KEY, "On" );
3926 static TWIPS_ENTRY( BORDER, -15, METRICS_KEY, "BorderWidth" );
3927 static TWIPS_ENTRY( CAPTIONHEIGHT, -270, METRICS_KEY, "CaptionHeight" );
3928 static TWIPS_ENTRY( CAPTIONWIDTH, -270, METRICS_KEY, "CaptionWidth" );
3929 static TWIPS_ENTRY( ICONHORIZONTALSPACING, -1125, METRICS_KEY, "IconSpacing" );
3930 static TWIPS_ENTRY( ICONVERTICALSPACING, -1125, METRICS_KEY, "IconVerticalSpacing" );
3931 static TWIPS_ENTRY( MENUHEIGHT, -270, METRICS_KEY, "MenuHeight" );
3932 static TWIPS_ENTRY( MENUWIDTH, -270, METRICS_KEY, "MenuWidth" );
3933 static TWIPS_ENTRY( PADDEDBORDERWIDTH, 0, METRICS_KEY, "PaddedBorderWidth" );
3934 static TWIPS_ENTRY( SCROLLHEIGHT, -240, METRICS_KEY, "ScrollHeight" );
3935 static TWIPS_ENTRY( SCROLLWIDTH, -240, METRICS_KEY, "ScrollWidth" );
3936 static TWIPS_ENTRY( SMCAPTIONHEIGHT, -225, METRICS_KEY, "SmCaptionHeight" );
3937 static TWIPS_ENTRY( SMCAPTIONWIDTH, -225, METRICS_KEY, "SmCaptionWidth" );
3939 static YESNO_ENTRY( BEEP, TRUE, SOUND_KEY, "Beep" );
3941 static DWORD_ENTRY( ACTIVEWINDOWTRACKING, 0, MOUSE_KEY, "ActiveWindowTracking" );
3942 static DWORD_ENTRY( ACTIVEWNDTRKTIMEOUT, 0, DESKTOP_KEY, "ActiveWndTrackTimeout" );
3943 static DWORD_ENTRY( CARETWIDTH, 1, DESKTOP_KEY, "CaretWidth" );
3944 static DWORD_ENTRY( DPISCALINGVER, 0, DESKTOP_KEY, "DpiScalingVer" );
3945 static DWORD_ENTRY( FOCUSBORDERHEIGHT, 1, DESKTOP_KEY, "FocusBorderHeight" );
3946 static DWORD_ENTRY( FOCUSBORDERWIDTH, 1, DESKTOP_KEY, "FocusBorderWidth" );
3947 static DWORD_ENTRY( FONTSMOOTHINGCONTRAST, 0, DESKTOP_KEY, "FontSmoothingGamma" );
3948 static DWORD_ENTRY( FONTSMOOTHINGORIENTATION, FE_FONTSMOOTHINGORIENTATIONRGB, DESKTOP_KEY, "FontSmoothingOrientation" );
3949 static DWORD_ENTRY( FONTSMOOTHINGTYPE, FE_FONTSMOOTHINGSTANDARD, DESKTOP_KEY, "FontSmoothingType" );
3950 static DWORD_ENTRY( FOREGROUNDFLASHCOUNT, 3, DESKTOP_KEY, "ForegroundFlashCount" );
3951 static DWORD_ENTRY( FOREGROUNDLOCKTIMEOUT, 0, DESKTOP_KEY, "ForegroundLockTimeout" );
3952 static DWORD_ENTRY( LOGPIXELS, 0, DESKTOP_KEY, "LogPixels" );
3953 static DWORD_ENTRY( MOUSECLICKLOCKTIME, 1200, DESKTOP_KEY, "ClickLockTime" );
3954 static DWORD_ENTRY( AUDIODESC_LOCALE, 0, AUDIODESC_KEY, "Locale" );
3956 static WCHAR desk_pattern_path[MAX_PATH];
3957 static WCHAR desk_wallpaper_path[MAX_PATH];
3958 static PATH_ENTRY( DESKPATTERN, DESKTOP_KEY, "Pattern", desk_pattern_path );
3959 static PATH_ENTRY( DESKWALLPAPER, DESKTOP_KEY, "Wallpaper", desk_wallpaper_path );
3961 static BYTE user_prefs[8] = { 0x30, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, 0x00 };
3962 static BINARY_ENTRY( USERPREFERENCESMASK, user_prefs, DESKTOP_KEY, "UserPreferencesMask" );
3964 static FONT_ENTRY( CAPTIONLOGFONT, FW_BOLD, METRICS_KEY, "CaptionFont" );
3965 static FONT_ENTRY( ICONTITLELOGFONT, FW_NORMAL, METRICS_KEY, "IconFont" );
3966 static FONT_ENTRY( MENULOGFONT, FW_NORMAL, METRICS_KEY, "MenuFont" );
3967 static FONT_ENTRY( MESSAGELOGFONT, FW_NORMAL, METRICS_KEY, "MessageFont" );
3968 static FONT_ENTRY( SMCAPTIONLOGFONT, FW_NORMAL, METRICS_KEY, "SmCaptionFont" );
3969 static FONT_ENTRY( STATUSLOGFONT, FW_NORMAL, METRICS_KEY, "StatusFont" );
3971 static USERPREF_ENTRY( MENUANIMATION, 0, 0x02 );
3972 static USERPREF_ENTRY( COMBOBOXANIMATION, 0, 0x04 );
3973 static USERPREF_ENTRY( LISTBOXSMOOTHSCROLLING, 0, 0x08 );
3974 static USERPREF_ENTRY( GRADIENTCAPTIONS, 0, 0x10 );
3975 static USERPREF_ENTRY( KEYBOARDCUES, 0, 0x20 );
3976 static USERPREF_ENTRY( ACTIVEWNDTRKZORDER, 0, 0x40 );
3977 static USERPREF_ENTRY( HOTTRACKING, 0, 0x80 );
3978 static USERPREF_ENTRY( MENUFADE, 1, 0x02 );
3979 static USERPREF_ENTRY( SELECTIONFADE, 1, 0x04 );
3980 static USERPREF_ENTRY( TOOLTIPANIMATION, 1, 0x08 );
3981 static USERPREF_ENTRY( TOOLTIPFADE, 1, 0x10 );
3982 static USERPREF_ENTRY( CURSORSHADOW, 1, 0x20 );
3983 static USERPREF_ENTRY( MOUSESONAR, 1, 0x40 );
3984 static USERPREF_ENTRY( MOUSECLICKLOCK, 1, 0x80 );
3985 static USERPREF_ENTRY( MOUSEVANISH, 2, 0x01 );
3986 static USERPREF_ENTRY( FLATMENU, 2, 0x02 );
3987 static USERPREF_ENTRY( DROPSHADOW, 2, 0x04 );
3988 static USERPREF_ENTRY( UIEFFECTS, 3, 0x80 );
3989 static USERPREF_ENTRY( DISABLEOVERLAPPEDCONTENT, 4, 0x01 );
3990 static USERPREF_ENTRY( CLIENTAREAANIMATION, 4, 0x02 );
3991 static USERPREF_ENTRY( CLEARTYPE, 4, 0x10 );
3992 static USERPREF_ENTRY( SPEECHRECOGNITION, 4, 0x20 );
3994 /* System parameter indexes */
3995 enum spi_index
3997 SPI_SETWORKAREA_IDX,
3998 SPI_INDEX_COUNT
4001 /* indicators whether system parameter value is loaded */
4002 static char spi_loaded[SPI_INDEX_COUNT];
4004 static struct sysparam_rgb_entry system_colors[] =
4006 #define RGB_ENTRY(name,val,reg) { { get_rgb_entry, set_rgb_entry, init_rgb_entry, COLORS_KEY, reg }, (val) }
4007 RGB_ENTRY( COLOR_SCROLLBAR, RGB(212, 208, 200), "Scrollbar" ),
4008 RGB_ENTRY( COLOR_BACKGROUND, RGB(58, 110, 165), "Background" ),
4009 RGB_ENTRY( COLOR_ACTIVECAPTION, RGB(10, 36, 106), "ActiveTitle" ),
4010 RGB_ENTRY( COLOR_INACTIVECAPTION, RGB(128, 128, 128), "InactiveTitle" ),
4011 RGB_ENTRY( COLOR_MENU, RGB(212, 208, 200), "Menu" ),
4012 RGB_ENTRY( COLOR_WINDOW, RGB(255, 255, 255), "Window" ),
4013 RGB_ENTRY( COLOR_WINDOWFRAME, RGB(0, 0, 0), "WindowFrame" ),
4014 RGB_ENTRY( COLOR_MENUTEXT, RGB(0, 0, 0), "MenuText" ),
4015 RGB_ENTRY( COLOR_WINDOWTEXT, RGB(0, 0, 0), "WindowText" ),
4016 RGB_ENTRY( COLOR_CAPTIONTEXT, RGB(255, 255, 255), "TitleText" ),
4017 RGB_ENTRY( COLOR_ACTIVEBORDER, RGB(212, 208, 200), "ActiveBorder" ),
4018 RGB_ENTRY( COLOR_INACTIVEBORDER, RGB(212, 208, 200), "InactiveBorder" ),
4019 RGB_ENTRY( COLOR_APPWORKSPACE, RGB(128, 128, 128), "AppWorkSpace" ),
4020 RGB_ENTRY( COLOR_HIGHLIGHT, RGB(10, 36, 106), "Hilight" ),
4021 RGB_ENTRY( COLOR_HIGHLIGHTTEXT, RGB(255, 255, 255), "HilightText" ),
4022 RGB_ENTRY( COLOR_BTNFACE, RGB(212, 208, 200), "ButtonFace" ),
4023 RGB_ENTRY( COLOR_BTNSHADOW, RGB(128, 128, 128), "ButtonShadow" ),
4024 RGB_ENTRY( COLOR_GRAYTEXT, RGB(128, 128, 128), "GrayText" ),
4025 RGB_ENTRY( COLOR_BTNTEXT, RGB(0, 0, 0), "ButtonText" ),
4026 RGB_ENTRY( COLOR_INACTIVECAPTIONTEXT, RGB(212, 208, 200), "InactiveTitleText" ),
4027 RGB_ENTRY( COLOR_BTNHIGHLIGHT, RGB(255, 255, 255), "ButtonHilight" ),
4028 RGB_ENTRY( COLOR_3DDKSHADOW, RGB(64, 64, 64), "ButtonDkShadow" ),
4029 RGB_ENTRY( COLOR_3DLIGHT, RGB(212, 208, 200), "ButtonLight" ),
4030 RGB_ENTRY( COLOR_INFOTEXT, RGB(0, 0, 0), "InfoText" ),
4031 RGB_ENTRY( COLOR_INFOBK, RGB(255, 255, 225), "InfoWindow" ),
4032 RGB_ENTRY( COLOR_ALTERNATEBTNFACE, RGB(181, 181, 181), "ButtonAlternateFace" ),
4033 RGB_ENTRY( COLOR_HOTLIGHT, RGB(0, 0, 200), "HotTrackingColor" ),
4034 RGB_ENTRY( COLOR_GRADIENTACTIVECAPTION, RGB(166, 202, 240), "GradientActiveTitle" ),
4035 RGB_ENTRY( COLOR_GRADIENTINACTIVECAPTION, RGB(192, 192, 192), "GradientInactiveTitle" ),
4036 RGB_ENTRY( COLOR_MENUHILIGHT, RGB(10, 36, 106), "MenuHilight" ),
4037 RGB_ENTRY( COLOR_MENUBAR, RGB(212, 208, 200), "MenuBar" )
4038 #undef RGB_ENTRY
4041 /* entries that are initialized by default in the registry */
4042 static union sysparam_all_entry * const default_entries[] =
4044 (union sysparam_all_entry *)&entry_ACTIVEWINDOWTRACKING,
4045 (union sysparam_all_entry *)&entry_ACTIVEWNDTRKTIMEOUT,
4046 (union sysparam_all_entry *)&entry_BEEP,
4047 (union sysparam_all_entry *)&entry_BLOCKSENDINPUTRESETS,
4048 (union sysparam_all_entry *)&entry_BORDER,
4049 (union sysparam_all_entry *)&entry_CAPTIONHEIGHT,
4050 (union sysparam_all_entry *)&entry_CAPTIONWIDTH,
4051 (union sysparam_all_entry *)&entry_CARETWIDTH,
4052 (union sysparam_all_entry *)&entry_DESKWALLPAPER,
4053 (union sysparam_all_entry *)&entry_DOUBLECLICKTIME,
4054 (union sysparam_all_entry *)&entry_DOUBLECLKHEIGHT,
4055 (union sysparam_all_entry *)&entry_DOUBLECLKWIDTH,
4056 (union sysparam_all_entry *)&entry_DRAGFULLWINDOWS,
4057 (union sysparam_all_entry *)&entry_DRAGHEIGHT,
4058 (union sysparam_all_entry *)&entry_DRAGWIDTH,
4059 (union sysparam_all_entry *)&entry_FOCUSBORDERHEIGHT,
4060 (union sysparam_all_entry *)&entry_FOCUSBORDERWIDTH,
4061 (union sysparam_all_entry *)&entry_FONTSMOOTHING,
4062 (union sysparam_all_entry *)&entry_FONTSMOOTHINGCONTRAST,
4063 (union sysparam_all_entry *)&entry_FONTSMOOTHINGORIENTATION,
4064 (union sysparam_all_entry *)&entry_FONTSMOOTHINGTYPE,
4065 (union sysparam_all_entry *)&entry_FOREGROUNDFLASHCOUNT,
4066 (union sysparam_all_entry *)&entry_FOREGROUNDLOCKTIMEOUT,
4067 (union sysparam_all_entry *)&entry_ICONHORIZONTALSPACING,
4068 (union sysparam_all_entry *)&entry_ICONTITLEWRAP,
4069 (union sysparam_all_entry *)&entry_ICONVERTICALSPACING,
4070 (union sysparam_all_entry *)&entry_KEYBOARDDELAY,
4071 (union sysparam_all_entry *)&entry_KEYBOARDPREF,
4072 (union sysparam_all_entry *)&entry_KEYBOARDSPEED,
4073 (union sysparam_all_entry *)&entry_LOWPOWERACTIVE,
4074 (union sysparam_all_entry *)&entry_MENUHEIGHT,
4075 (union sysparam_all_entry *)&entry_MENUSHOWDELAY,
4076 (union sysparam_all_entry *)&entry_MENUWIDTH,
4077 (union sysparam_all_entry *)&entry_MOUSEACCELERATION,
4078 (union sysparam_all_entry *)&entry_MOUSEBUTTONSWAP,
4079 (union sysparam_all_entry *)&entry_MOUSECLICKLOCKTIME,
4080 (union sysparam_all_entry *)&entry_MOUSEHOVERHEIGHT,
4081 (union sysparam_all_entry *)&entry_MOUSEHOVERTIME,
4082 (union sysparam_all_entry *)&entry_MOUSEHOVERWIDTH,
4083 (union sysparam_all_entry *)&entry_MOUSESPEED,
4084 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD1,
4085 (union sysparam_all_entry *)&entry_MOUSETHRESHOLD2,
4086 (union sysparam_all_entry *)&entry_PADDEDBORDERWIDTH,
4087 (union sysparam_all_entry *)&entry_SCREENREADER,
4088 (union sysparam_all_entry *)&entry_SCROLLHEIGHT,
4089 (union sysparam_all_entry *)&entry_SCROLLWIDTH,
4090 (union sysparam_all_entry *)&entry_SHOWSOUNDS,
4091 (union sysparam_all_entry *)&entry_SMCAPTIONHEIGHT,
4092 (union sysparam_all_entry *)&entry_SMCAPTIONWIDTH,
4093 (union sysparam_all_entry *)&entry_SNAPTODEFBUTTON,
4094 (union sysparam_all_entry *)&entry_USERPREFERENCESMASK,
4095 (union sysparam_all_entry *)&entry_WHEELSCROLLCHARS,
4096 (union sysparam_all_entry *)&entry_WHEELSCROLLLINES,
4097 (union sysparam_all_entry *)&entry_AUDIODESC_LOCALE,
4098 (union sysparam_all_entry *)&entry_AUDIODESC_ON,
4101 void sysparams_init(void)
4104 DWORD i, dispos, dpi_scaling;
4105 WCHAR layout[KL_NAMELENGTH];
4106 pthread_mutexattr_t attr;
4107 HKEY hkey;
4109 static const WCHAR software_wineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e'};
4110 static const WCHAR temporary_system_parametersW[] =
4111 {'T','e','m','p','o','r','a','r','y',' ','S','y','s','t','e','m',' ',
4112 'P','a','r','a','m','e','t','e','r','s'};
4113 static const WCHAR oneW[] = {'1',0};
4114 static const WCHAR kl_preloadW[] =
4115 {'K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','\\','P','r','e','l','o','a','d'};
4117 pthread_mutexattr_init( &attr );
4118 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
4119 pthread_mutex_init( &user_mutex, &attr );
4120 pthread_mutexattr_destroy( &attr );
4122 if ((hkey = reg_create_key( hkcu_key, kl_preloadW, sizeof(kl_preloadW), 0, NULL )))
4124 if (NtUserGetKeyboardLayoutName( layout ))
4125 set_reg_value( hkey, oneW, REG_SZ, (const BYTE *)layout,
4126 (lstrlenW(layout) + 1) * sizeof(WCHAR) );
4127 NtClose( hkey );
4130 /* this one must be non-volatile */
4131 if (!(hkey = reg_create_key( hkcu_key, software_wineW, sizeof(software_wineW), 0, NULL )))
4133 ERR("Can't create wine registry branch\n");
4134 return;
4137 /* @@ Wine registry key: HKCU\Software\Wine\Temporary System Parameters */
4138 if (!(volatile_base_key = reg_create_key( hkey, temporary_system_parametersW,
4139 sizeof(temporary_system_parametersW),
4140 REG_OPTION_VOLATILE, &dispos )))
4141 ERR("Can't create non-permanent wine registry branch\n");
4143 NtClose( hkey );
4145 config_key = reg_create_key( NULL, config_keyW, sizeof(config_keyW), 0, NULL );
4147 get_dword_entry( (union sysparam_all_entry *)&entry_LOGPIXELS, 0, &system_dpi, 0 );
4148 if (!system_dpi) /* check fallback key */
4150 static const WCHAR log_pixelsW[] = {'L','o','g','P','i','x','e','l','s',0};
4151 static const WCHAR software_fontsW[] =
4152 {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s'};
4154 if ((hkey = reg_open_key( config_key, software_fontsW, sizeof(software_fontsW) )))
4156 char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(DWORD)])];
4157 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
4159 if (query_reg_value( hkey, log_pixelsW, value, sizeof(buffer) ) && value->Type == REG_DWORD)
4160 system_dpi = *(const DWORD *)value->Data;
4161 NtClose( hkey );
4164 if (!system_dpi) system_dpi = USER_DEFAULT_SCREEN_DPI;
4166 /* FIXME: what do the DpiScalingVer flags mean? */
4167 get_dword_entry( (union sysparam_all_entry *)&entry_DPISCALINGVER, 0, &dpi_scaling, 0 );
4168 if (!dpi_scaling) NtUserSetProcessDpiAwarenessContext( NTUSER_DPI_PER_MONITOR_AWARE, 0 );
4170 if (volatile_base_key && dispos == REG_CREATED_NEW_KEY) /* first process, initialize entries */
4172 for (i = 0; i < ARRAY_SIZE( default_entries ); i++)
4173 default_entries[i]->hdr.init( default_entries[i] );
4177 static BOOL update_desktop_wallpaper(void)
4179 /* FIXME: move implementation from user32 */
4180 entry_DESKWALLPAPER.hdr.loaded = entry_DESKPATTERN.hdr.loaded = FALSE;
4181 return TRUE;
4184 /***********************************************************************
4185 * NtUserSystemParametersInfoForDpi (win32u.@)
4187 BOOL WINAPI NtUserSystemParametersInfoForDpi( UINT action, UINT val, PVOID ptr, UINT winini, UINT dpi )
4189 BOOL ret = FALSE;
4191 switch (action)
4193 case SPI_GETICONTITLELOGFONT:
4194 ret = get_entry_dpi( &entry_ICONTITLELOGFONT, val, ptr, dpi );
4195 break;
4196 case SPI_GETNONCLIENTMETRICS:
4198 NONCLIENTMETRICSW *ncm = ptr;
4200 if (!ncm) break;
4201 ret = get_entry_dpi( &entry_BORDER, 0, &ncm->iBorderWidth, dpi ) &&
4202 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ncm->iScrollWidth, dpi ) &&
4203 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ncm->iScrollHeight, dpi ) &&
4204 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ncm->iCaptionWidth, dpi ) &&
4205 get_entry_dpi( &entry_CAPTIONHEIGHT, 0, &ncm->iCaptionHeight, dpi ) &&
4206 get_entry_dpi( &entry_CAPTIONLOGFONT, 0, &ncm->lfCaptionFont, dpi ) &&
4207 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ncm->iSmCaptionWidth, dpi ) &&
4208 get_entry_dpi( &entry_SMCAPTIONHEIGHT, 0, &ncm->iSmCaptionHeight, dpi ) &&
4209 get_entry_dpi( &entry_SMCAPTIONLOGFONT, 0, &ncm->lfSmCaptionFont, dpi ) &&
4210 get_entry_dpi( &entry_MENUWIDTH, 0, &ncm->iMenuWidth, dpi ) &&
4211 get_entry_dpi( &entry_MENUHEIGHT, 0, &ncm->iMenuHeight, dpi ) &&
4212 get_entry_dpi( &entry_MENULOGFONT, 0, &ncm->lfMenuFont, dpi ) &&
4213 get_entry_dpi( &entry_STATUSLOGFONT, 0, &ncm->lfStatusFont, dpi ) &&
4214 get_entry_dpi( &entry_MESSAGELOGFONT, 0, &ncm->lfMessageFont, dpi );
4215 if (ret && ncm->cbSize == sizeof(NONCLIENTMETRICSW))
4216 ret = get_entry_dpi( &entry_PADDEDBORDERWIDTH, 0, &ncm->iPaddedBorderWidth, dpi );
4217 normalize_nonclientmetrics( ncm );
4218 break;
4220 case SPI_GETICONMETRICS:
4222 ICONMETRICSW *im = ptr;
4223 if (im && im->cbSize == sizeof(*im))
4224 ret = get_entry_dpi( &entry_ICONHORIZONTALSPACING, 0, &im->iHorzSpacing, dpi ) &&
4225 get_entry_dpi( &entry_ICONVERTICALSPACING, 0, &im->iVertSpacing, dpi ) &&
4226 get_entry_dpi( &entry_ICONTITLEWRAP, 0, &im->iTitleWrap, dpi ) &&
4227 get_entry_dpi( &entry_ICONTITLELOGFONT, 0, &im->lfFont, dpi );
4228 break;
4230 default:
4231 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4232 break;
4234 return ret;
4237 /***********************************************************************
4238 * NtUserSystemParametersInfo (win32u.@)
4240 * Each system parameter has flag which shows whether the parameter
4241 * is loaded or not. Parameters, stored directly in SysParametersInfo are
4242 * loaded from registry only when they are requested and the flag is
4243 * "false", after the loading the flag is set to "true". On interprocess
4244 * notification of the parameter change the corresponding parameter flag is
4245 * set to "false". The parameter value will be reloaded when it is requested
4246 * the next time.
4247 * Parameters, backed by or depend on GetSystemMetrics are processed
4248 * differently. These parameters are always loaded. They are reloaded right
4249 * away on interprocess change notification. We can't do lazy loading because
4250 * we don't want to complicate GetSystemMetrics.
4251 * Parameters backed by driver settings are read from corresponding setting.
4252 * On the parameter change request the setting is changed. Interprocess change
4253 * notifications are ignored.
4254 * When parameter value is updated the changed value is stored in permanent
4255 * registry branch if saving is requested. Otherwise it is stored
4256 * in temporary branch
4258 * Some SPI values can also be stored as Twips values in the registry,
4259 * don't forget the conversion!
4261 BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT winini )
4263 #define WINE_SPI_FIXME(x) \
4264 case x: \
4266 static BOOL warn = TRUE; \
4267 if (warn) \
4269 warn = FALSE; \
4270 FIXME( "Unimplemented action: %u (%s)\n", x, #x ); \
4273 RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE ); \
4274 ret = FALSE; \
4275 break
4276 #define WINE_SPI_WARN(x) \
4277 case x: \
4278 WARN( "Ignored action: %u (%s)\n", x, #x ); \
4279 ret = TRUE; \
4280 break
4282 BOOL ret = user_driver->pSystemParametersInfo( action, val, ptr, winini );
4283 unsigned spi_idx = 0;
4285 if (!ret) switch (action)
4287 case SPI_GETBEEP:
4288 ret = get_entry( &entry_BEEP, val, ptr );
4289 break;
4290 case SPI_SETBEEP:
4291 ret = set_entry( &entry_BEEP, val, ptr, winini );
4292 break;
4293 case SPI_GETMOUSE:
4294 ret = get_entry( &entry_MOUSETHRESHOLD1, val, (INT *)ptr ) &&
4295 get_entry( &entry_MOUSETHRESHOLD2, val, (INT *)ptr + 1 ) &&
4296 get_entry( &entry_MOUSEACCELERATION, val, (INT *)ptr + 2 );
4297 break;
4298 case SPI_SETMOUSE:
4299 ret = set_entry( &entry_MOUSETHRESHOLD1, ((INT *)ptr)[0], ptr, winini ) &&
4300 set_entry( &entry_MOUSETHRESHOLD2, ((INT *)ptr)[1], ptr, winini ) &&
4301 set_entry( &entry_MOUSEACCELERATION, ((INT *)ptr)[2], ptr, winini );
4302 break;
4303 case SPI_GETBORDER:
4304 ret = get_entry( &entry_BORDER, val, ptr );
4305 if (*(INT*)ptr < 1) *(INT*)ptr = 1;
4306 break;
4307 case SPI_SETBORDER:
4308 ret = set_entry( &entry_BORDER, val, ptr, winini );
4309 break;
4310 case SPI_GETKEYBOARDSPEED:
4311 ret = get_entry( &entry_KEYBOARDSPEED, val, ptr );
4312 break;
4313 case SPI_SETKEYBOARDSPEED:
4314 if (val > 31) val = 31;
4315 ret = set_entry( &entry_KEYBOARDSPEED, val, ptr, winini );
4316 break;
4318 WINE_SPI_WARN(SPI_LANGDRIVER); /* not implemented in Windows */
4320 case SPI_ICONHORIZONTALSPACING:
4321 if (ptr != NULL)
4322 ret = get_entry( &entry_ICONHORIZONTALSPACING, val, ptr );
4323 else
4325 int min_val = map_to_dpi( 32, get_system_dpi() );
4326 ret = set_entry( &entry_ICONHORIZONTALSPACING, max( min_val, val ), ptr, winini );
4328 break;
4329 case SPI_GETSCREENSAVETIMEOUT:
4330 ret = get_entry( &entry_SCREENSAVETIMEOUT, val, ptr );
4331 break;
4332 case SPI_SETSCREENSAVETIMEOUT:
4333 ret = set_entry( &entry_SCREENSAVETIMEOUT, val, ptr, winini );
4334 break;
4335 case SPI_GETSCREENSAVEACTIVE:
4336 ret = get_entry( &entry_SCREENSAVEACTIVE, val, ptr );
4337 break;
4338 case SPI_SETSCREENSAVEACTIVE:
4339 ret = set_entry( &entry_SCREENSAVEACTIVE, val, ptr, winini );
4340 break;
4341 case SPI_GETGRIDGRANULARITY:
4342 ret = get_entry( &entry_GRIDGRANULARITY, val, ptr );
4343 break;
4344 case SPI_SETGRIDGRANULARITY:
4345 ret = set_entry( &entry_GRIDGRANULARITY, val, ptr, winini );
4346 break;
4347 case SPI_SETDESKWALLPAPER:
4348 if (!ptr || set_entry( &entry_DESKWALLPAPER, val, ptr, winini ))
4349 ret = update_desktop_wallpaper();
4350 break;
4351 case SPI_SETDESKPATTERN:
4352 if (!ptr || set_entry( &entry_DESKPATTERN, val, ptr, winini ))
4353 ret = update_desktop_wallpaper();
4354 break;
4355 case SPI_GETKEYBOARDDELAY:
4356 ret = get_entry( &entry_KEYBOARDDELAY, val, ptr );
4357 break;
4358 case SPI_SETKEYBOARDDELAY:
4359 ret = set_entry( &entry_KEYBOARDDELAY, val, ptr, winini );
4360 break;
4361 case SPI_ICONVERTICALSPACING:
4362 if (ptr != NULL)
4363 ret = get_entry( &entry_ICONVERTICALSPACING, val, ptr );
4364 else
4366 int min_val = map_to_dpi( 32, get_system_dpi() );
4367 ret = set_entry( &entry_ICONVERTICALSPACING, max( min_val, val ), ptr, winini );
4369 break;
4370 case SPI_GETICONTITLEWRAP:
4371 ret = get_entry( &entry_ICONTITLEWRAP, val, ptr );
4372 break;
4373 case SPI_SETICONTITLEWRAP:
4374 ret = set_entry( &entry_ICONTITLEWRAP, val, ptr, winini );
4375 break;
4376 case SPI_GETMENUDROPALIGNMENT:
4377 ret = get_entry( &entry_MENUDROPALIGNMENT, val, ptr );
4378 break;
4379 case SPI_SETMENUDROPALIGNMENT:
4380 ret = set_entry( &entry_MENUDROPALIGNMENT, val, ptr, winini );
4381 break;
4382 case SPI_SETDOUBLECLKWIDTH:
4383 ret = set_entry( &entry_DOUBLECLKWIDTH, val, ptr, winini );
4384 break;
4385 case SPI_SETDOUBLECLKHEIGHT:
4386 ret = set_entry( &entry_DOUBLECLKHEIGHT, val, ptr, winini );
4387 break;
4388 case SPI_GETICONTITLELOGFONT:
4389 ret = get_entry( &entry_ICONTITLELOGFONT, val, ptr );
4390 break;
4391 case SPI_SETDOUBLECLICKTIME:
4392 ret = set_entry( &entry_DOUBLECLICKTIME, val, ptr, winini );
4393 break;
4394 case SPI_SETMOUSEBUTTONSWAP:
4395 ret = set_entry( &entry_MOUSEBUTTONSWAP, val, ptr, winini );
4396 break;
4397 case SPI_SETICONTITLELOGFONT:
4398 ret = set_entry( &entry_ICONTITLELOGFONT, val, ptr, winini );
4399 break;
4400 case SPI_GETFASTTASKSWITCH:
4401 if (!ptr) return FALSE;
4402 *(BOOL *)ptr = TRUE;
4403 ret = TRUE;
4404 break;
4405 case SPI_SETFASTTASKSWITCH:
4406 /* the action is disabled */
4407 ret = FALSE;
4408 break;
4409 case SPI_SETDRAGFULLWINDOWS:
4410 ret = set_entry( &entry_DRAGFULLWINDOWS, val, ptr, winini );
4411 break;
4412 case SPI_GETDRAGFULLWINDOWS:
4413 ret = get_entry( &entry_DRAGFULLWINDOWS, val, ptr );
4414 break;
4415 case SPI_GETNONCLIENTMETRICS:
4417 NONCLIENTMETRICSW *nm = ptr;
4418 int padded_border;
4420 if (!ptr) return FALSE;
4422 ret = get_entry( &entry_BORDER, 0, &nm->iBorderWidth ) &&
4423 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border ) &&
4424 get_entry( &entry_SCROLLWIDTH, 0, &nm->iScrollWidth ) &&
4425 get_entry( &entry_SCROLLHEIGHT, 0, &nm->iScrollHeight ) &&
4426 get_entry( &entry_CAPTIONWIDTH, 0, &nm->iCaptionWidth ) &&
4427 get_entry( &entry_CAPTIONHEIGHT, 0, &nm->iCaptionHeight ) &&
4428 get_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont ) &&
4429 get_entry( &entry_SMCAPTIONWIDTH, 0, &nm->iSmCaptionWidth ) &&
4430 get_entry( &entry_SMCAPTIONHEIGHT, 0, &nm->iSmCaptionHeight ) &&
4431 get_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont ) &&
4432 get_entry( &entry_MENUWIDTH, 0, &nm->iMenuWidth ) &&
4433 get_entry( &entry_MENUHEIGHT, 0, &nm->iMenuHeight ) &&
4434 get_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont ) &&
4435 get_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont ) &&
4436 get_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont );
4437 if (ret)
4439 nm->iBorderWidth += padded_border;
4440 if (nm->cbSize == sizeof(NONCLIENTMETRICSW)) nm->iPaddedBorderWidth = 0;
4442 normalize_nonclientmetrics( nm );
4443 break;
4445 case SPI_SETNONCLIENTMETRICS:
4447 LPNONCLIENTMETRICSW nm = ptr;
4448 int padded_border;
4450 if (nm && (nm->cbSize == sizeof(NONCLIENTMETRICSW) ||
4451 nm->cbSize == FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth)))
4453 get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border );
4455 ret = set_entry( &entry_BORDER, nm->iBorderWidth - padded_border, NULL, winini ) &&
4456 set_entry( &entry_SCROLLWIDTH, nm->iScrollWidth, NULL, winini ) &&
4457 set_entry( &entry_SCROLLHEIGHT, nm->iScrollHeight, NULL, winini ) &&
4458 set_entry( &entry_CAPTIONWIDTH, nm->iCaptionWidth, NULL, winini ) &&
4459 set_entry( &entry_CAPTIONHEIGHT, nm->iCaptionHeight, NULL, winini ) &&
4460 set_entry( &entry_SMCAPTIONWIDTH, nm->iSmCaptionWidth, NULL, winini ) &&
4461 set_entry( &entry_SMCAPTIONHEIGHT, nm->iSmCaptionHeight, NULL, winini ) &&
4462 set_entry( &entry_MENUWIDTH, nm->iMenuWidth, NULL, winini ) &&
4463 set_entry( &entry_MENUHEIGHT, nm->iMenuHeight, NULL, winini ) &&
4464 set_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont, winini ) &&
4465 set_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont, winini ) &&
4466 set_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont, winini ) &&
4467 set_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont, winini ) &&
4468 set_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont, winini );
4470 break;
4472 case SPI_GETMINIMIZEDMETRICS:
4474 MINIMIZEDMETRICS *mm = ptr;
4475 if (mm && mm->cbSize == sizeof(*mm)) {
4476 ret = get_entry( &entry_MINWIDTH, 0, &mm->iWidth ) &&
4477 get_entry( &entry_MINHORZGAP, 0, &mm->iHorzGap ) &&
4478 get_entry( &entry_MINVERTGAP, 0, &mm->iVertGap ) &&
4479 get_entry( &entry_MINARRANGE, 0, &mm->iArrange );
4480 mm->iWidth = max( 0, mm->iWidth );
4481 mm->iHorzGap = max( 0, mm->iHorzGap );
4482 mm->iVertGap = max( 0, mm->iVertGap );
4483 mm->iArrange &= 0x0f;
4485 break;
4487 case SPI_SETMINIMIZEDMETRICS:
4489 MINIMIZEDMETRICS *mm = ptr;
4490 if (mm && mm->cbSize == sizeof(*mm))
4491 ret = set_entry( &entry_MINWIDTH, max( 0, mm->iWidth ), NULL, winini ) &&
4492 set_entry( &entry_MINHORZGAP, max( 0, mm->iHorzGap ), NULL, winini ) &&
4493 set_entry( &entry_MINVERTGAP, max( 0, mm->iVertGap ), NULL, winini ) &&
4494 set_entry( &entry_MINARRANGE, mm->iArrange & 0x0f, NULL, winini );
4495 break;
4497 case SPI_GETICONMETRICS:
4499 ICONMETRICSW *icon = ptr;
4500 if(icon && icon->cbSize == sizeof(*icon))
4502 ret = get_entry( &entry_ICONHORIZONTALSPACING, 0, &icon->iHorzSpacing ) &&
4503 get_entry( &entry_ICONVERTICALSPACING, 0, &icon->iVertSpacing ) &&
4504 get_entry( &entry_ICONTITLEWRAP, 0, &icon->iTitleWrap ) &&
4505 get_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont );
4507 break;
4509 case SPI_SETICONMETRICS:
4511 ICONMETRICSW *icon = ptr;
4512 if (icon && icon->cbSize == sizeof(*icon))
4513 ret = set_entry( &entry_ICONVERTICALSPACING, max(32,icon->iVertSpacing), NULL, winini ) &&
4514 set_entry( &entry_ICONHORIZONTALSPACING, max(32,icon->iHorzSpacing), NULL, winini ) &&
4515 set_entry( &entry_ICONTITLEWRAP, icon->iTitleWrap, NULL, winini ) &&
4516 set_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont, winini );
4517 break;
4519 case SPI_SETWORKAREA:
4521 if (!ptr) return FALSE;
4522 spi_idx = SPI_SETWORKAREA_IDX;
4523 work_area = *(RECT*)ptr;
4524 spi_loaded[spi_idx] = TRUE;
4525 ret = TRUE;
4526 break;
4528 case SPI_GETWORKAREA:
4530 if (!ptr) return FALSE;
4532 spi_idx = SPI_SETWORKAREA_IDX;
4533 if (!spi_loaded[spi_idx])
4535 struct monitor *monitor;
4537 if (!lock_display_devices()) return FALSE;
4539 LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
4541 if (!(monitor->flags & MONITORINFOF_PRIMARY)) continue;
4542 work_area = monitor->rc_work;
4543 break;
4546 unlock_display_devices();
4547 spi_loaded[spi_idx] = TRUE;
4549 *(RECT *)ptr = map_dpi_rect( work_area, system_dpi, get_thread_dpi() );
4550 ret = TRUE;
4551 TRACE("work area %s\n", wine_dbgstr_rect( &work_area ));
4552 break;
4555 WINE_SPI_FIXME(SPI_SETPENWINDOWS);
4557 case SPI_GETFILTERKEYS:
4559 LPFILTERKEYS filter_keys = ptr;
4560 WARN("SPI_GETFILTERKEYS not fully implemented\n");
4561 if (filter_keys && filter_keys->cbSize == sizeof(FILTERKEYS))
4563 /* Indicate that no FilterKeys feature available */
4564 filter_keys->dwFlags = 0;
4565 filter_keys->iWaitMSec = 0;
4566 filter_keys->iDelayMSec = 0;
4567 filter_keys->iRepeatMSec = 0;
4568 filter_keys->iBounceMSec = 0;
4569 ret = TRUE;
4571 break;
4573 WINE_SPI_FIXME(SPI_SETFILTERKEYS);
4575 case SPI_GETTOGGLEKEYS:
4577 LPTOGGLEKEYS toggle_keys = ptr;
4578 WARN("SPI_GETTOGGLEKEYS not fully implemented\n");
4579 if (toggle_keys && toggle_keys->cbSize == sizeof(TOGGLEKEYS))
4581 /* Indicate that no ToggleKeys feature available */
4582 toggle_keys->dwFlags = 0;
4583 ret = TRUE;
4585 break;
4588 WINE_SPI_FIXME(SPI_SETTOGGLEKEYS);
4590 case SPI_GETMOUSEKEYS:
4592 MOUSEKEYS *mouse_keys = ptr;
4593 WARN("SPI_GETMOUSEKEYS not fully implemented\n");
4594 if (mouse_keys && mouse_keys->cbSize == sizeof(MOUSEKEYS))
4596 /* Indicate that no MouseKeys feature available */
4597 mouse_keys->dwFlags = 0;
4598 mouse_keys->iMaxSpeed = 360;
4599 mouse_keys->iTimeToMaxSpeed = 1000;
4600 mouse_keys->iCtrlSpeed = 0;
4601 mouse_keys->dwReserved1 = 0;
4602 mouse_keys->dwReserved2 = 0;
4603 ret = TRUE;
4605 break;
4608 WINE_SPI_FIXME(SPI_SETMOUSEKEYS);
4610 case SPI_GETSHOWSOUNDS:
4611 ret = get_entry( &entry_SHOWSOUNDS, val, ptr );
4612 break;
4613 case SPI_SETSHOWSOUNDS:
4614 ret = set_entry( &entry_SHOWSOUNDS, val, ptr, winini );
4615 break;
4616 case SPI_GETSTICKYKEYS:
4618 STICKYKEYS *sticky_keys = ptr;
4619 WARN("SPI_GETSTICKYKEYS not fully implemented\n");
4620 if (sticky_keys && sticky_keys->cbSize == sizeof(STICKYKEYS))
4622 /* Indicate that no StickyKeys feature available */
4623 sticky_keys->dwFlags = 0;
4624 ret = TRUE;
4626 break;
4629 WINE_SPI_FIXME(SPI_SETSTICKYKEYS);
4631 case SPI_GETACCESSTIMEOUT:
4633 ACCESSTIMEOUT *access_timeout = ptr;
4634 WARN("SPI_GETACCESSTIMEOUT not fully implemented\n");
4635 if (access_timeout && access_timeout->cbSize == sizeof(ACCESSTIMEOUT))
4637 /* Indicate that no accessibility features timeout is available */
4638 access_timeout->dwFlags = 0;
4639 access_timeout->iTimeOutMSec = 0;
4640 ret = TRUE;
4642 break;
4645 WINE_SPI_FIXME(SPI_SETACCESSTIMEOUT);
4647 case SPI_GETSERIALKEYS:
4649 LPSERIALKEYSW serial_keys = ptr;
4650 WARN("SPI_GETSERIALKEYS not fully implemented\n");
4651 if (serial_keys && serial_keys->cbSize == sizeof(SERIALKEYSW))
4653 /* Indicate that no SerialKeys feature available */
4654 serial_keys->dwFlags = 0;
4655 serial_keys->lpszActivePort = NULL;
4656 serial_keys->lpszPort = NULL;
4657 serial_keys->iBaudRate = 0;
4658 serial_keys->iPortState = 0;
4659 ret = TRUE;
4661 break;
4664 WINE_SPI_FIXME(SPI_SETSERIALKEYS);
4666 case SPI_GETSOUNDSENTRY:
4668 SOUNDSENTRYW *sound_sentry = ptr;
4669 WARN("SPI_GETSOUNDSENTRY not fully implemented\n");
4670 if (sound_sentry && sound_sentry->cbSize == sizeof(SOUNDSENTRYW))
4672 /* Indicate that no SoundSentry feature available */
4673 sound_sentry->dwFlags = 0;
4674 sound_sentry->iFSTextEffect = 0;
4675 sound_sentry->iFSTextEffectMSec = 0;
4676 sound_sentry->iFSTextEffectColorBits = 0;
4677 sound_sentry->iFSGrafEffect = 0;
4678 sound_sentry->iFSGrafEffectMSec = 0;
4679 sound_sentry->iFSGrafEffectColor = 0;
4680 sound_sentry->iWindowsEffect = 0;
4681 sound_sentry->iWindowsEffectMSec = 0;
4682 sound_sentry->lpszWindowsEffectDLL = 0;
4683 sound_sentry->iWindowsEffectOrdinal = 0;
4684 ret = TRUE;
4686 break;
4689 WINE_SPI_FIXME(SPI_SETSOUNDSENTRY);
4691 case SPI_GETHIGHCONTRAST:
4693 HIGHCONTRASTW *high_contrast = ptr;
4694 WARN("SPI_GETHIGHCONTRAST not fully implemented\n");
4695 if (high_contrast && high_contrast->cbSize == sizeof(HIGHCONTRASTW))
4697 /* Indicate that no high contrast feature available */
4698 high_contrast->dwFlags = 0;
4699 high_contrast->lpszDefaultScheme = NULL;
4700 ret = TRUE;
4702 break;
4705 WINE_SPI_FIXME(SPI_SETHIGHCONTRAST);
4707 case SPI_GETKEYBOARDPREF:
4708 ret = get_entry( &entry_KEYBOARDPREF, val, ptr );
4709 break;
4710 case SPI_SETKEYBOARDPREF:
4711 ret = set_entry( &entry_KEYBOARDPREF, val, ptr, winini );
4712 break;
4713 case SPI_GETSCREENREADER:
4714 ret = get_entry( &entry_SCREENREADER, val, ptr );
4715 break;
4716 case SPI_SETSCREENREADER:
4717 ret = set_entry( &entry_SCREENREADER, val, ptr, winini );
4718 break;
4720 case SPI_GETANIMATION:
4722 ANIMATIONINFO *anim_info = ptr;
4724 /* Tell it "disabled" */
4725 if (anim_info && anim_info->cbSize == sizeof(ANIMATIONINFO))
4727 /* Minimize and restore animation is disabled (nonzero == enabled) */
4728 anim_info->iMinAnimate = 0;
4729 ret = TRUE;
4731 break;
4734 WINE_SPI_WARN(SPI_SETANIMATION);
4736 case SPI_GETFONTSMOOTHING:
4737 ret = get_entry( &entry_FONTSMOOTHING, val, ptr );
4738 if (ret) *(UINT *)ptr = (*(UINT *)ptr != 0);
4739 break;
4740 case SPI_SETFONTSMOOTHING:
4741 val = val ? 2 : 0; /* Win NT4/2k/XP behavior */
4742 ret = set_entry( &entry_FONTSMOOTHING, val, ptr, winini );
4743 break;
4744 case SPI_SETDRAGWIDTH:
4745 ret = set_entry( &entry_DRAGWIDTH, val, ptr, winini );
4746 break;
4747 case SPI_SETDRAGHEIGHT:
4748 ret = set_entry( &entry_DRAGHEIGHT, val, ptr, winini );
4749 break;
4751 WINE_SPI_FIXME(SPI_SETHANDHELD);
4752 WINE_SPI_FIXME(SPI_GETLOWPOWERTIMEOUT);
4753 WINE_SPI_FIXME(SPI_GETPOWEROFFTIMEOUT);
4754 WINE_SPI_FIXME(SPI_SETLOWPOWERTIMEOUT);
4755 WINE_SPI_FIXME(SPI_SETPOWEROFFTIMEOUT);
4757 case SPI_GETLOWPOWERACTIVE:
4758 ret = get_entry( &entry_LOWPOWERACTIVE, val, ptr );
4759 break;
4760 case SPI_SETLOWPOWERACTIVE:
4761 ret = set_entry( &entry_LOWPOWERACTIVE, val, ptr, winini );
4762 break;
4763 case SPI_GETPOWEROFFACTIVE:
4764 ret = get_entry( &entry_POWEROFFACTIVE, val, ptr );
4765 break;
4766 case SPI_SETPOWEROFFACTIVE:
4767 ret = set_entry( &entry_POWEROFFACTIVE, val, ptr, winini );
4768 break;
4770 WINE_SPI_FIXME(SPI_SETCURSORS);
4771 WINE_SPI_FIXME(SPI_SETICONS);
4773 case SPI_GETDEFAULTINPUTLANG:
4774 ret = NtUserGetKeyboardLayout(0) != 0;
4775 break;
4777 WINE_SPI_FIXME(SPI_SETDEFAULTINPUTLANG);
4778 WINE_SPI_FIXME(SPI_SETLANGTOGGLE);
4780 case SPI_GETWINDOWSEXTENSION:
4781 WARN( "pretend no support for Win9x Plus! for now.\n" );
4782 ret = FALSE; /* yes, this is the result value */
4783 break;
4784 case SPI_SETMOUSETRAILS:
4785 ret = set_entry( &entry_MOUSETRAILS, val, ptr, winini );
4786 break;
4787 case SPI_GETMOUSETRAILS:
4788 ret = get_entry( &entry_MOUSETRAILS, val, ptr );
4789 break;
4790 case SPI_GETSNAPTODEFBUTTON:
4791 ret = get_entry( &entry_SNAPTODEFBUTTON, val, ptr );
4792 break;
4793 case SPI_SETSNAPTODEFBUTTON:
4794 ret = set_entry( &entry_SNAPTODEFBUTTON, val, ptr, winini );
4795 break;
4796 case SPI_SETSCREENSAVERRUNNING:
4797 ret = set_entry( &entry_SCREENSAVERRUNNING, val, ptr, winini );
4798 break;
4799 case SPI_GETMOUSEHOVERWIDTH:
4800 ret = get_entry( &entry_MOUSEHOVERWIDTH, val, ptr );
4801 break;
4802 case SPI_SETMOUSEHOVERWIDTH:
4803 ret = set_entry( &entry_MOUSEHOVERWIDTH, val, ptr, winini );
4804 break;
4805 case SPI_GETMOUSEHOVERHEIGHT:
4806 ret = get_entry( &entry_MOUSEHOVERHEIGHT, val, ptr );
4807 break;
4808 case SPI_SETMOUSEHOVERHEIGHT:
4809 ret = set_entry( &entry_MOUSEHOVERHEIGHT, val, ptr, winini );
4810 break;
4811 case SPI_GETMOUSEHOVERTIME:
4812 ret = get_entry( &entry_MOUSEHOVERTIME, val, ptr );
4813 break;
4814 case SPI_SETMOUSEHOVERTIME:
4815 ret = set_entry( &entry_MOUSEHOVERTIME, val, ptr, winini );
4816 break;
4817 case SPI_GETWHEELSCROLLLINES:
4818 ret = get_entry( &entry_WHEELSCROLLLINES, val, ptr );
4819 break;
4820 case SPI_SETWHEELSCROLLLINES:
4821 ret = set_entry( &entry_WHEELSCROLLLINES, val, ptr, winini );
4822 break;
4823 case SPI_GETMENUSHOWDELAY:
4824 ret = get_entry( &entry_MENUSHOWDELAY, val, ptr );
4825 break;
4826 case SPI_SETMENUSHOWDELAY:
4827 ret = set_entry( &entry_MENUSHOWDELAY, val, ptr, winini );
4828 break;
4829 case SPI_GETWHEELSCROLLCHARS:
4830 ret = get_entry( &entry_WHEELSCROLLCHARS, val, ptr );
4831 break;
4832 case SPI_SETWHEELSCROLLCHARS:
4833 ret = set_entry( &entry_WHEELSCROLLCHARS, val, ptr, winini );
4834 break;
4836 WINE_SPI_FIXME(SPI_GETSHOWIMEUI);
4837 WINE_SPI_FIXME(SPI_SETSHOWIMEUI);
4839 case SPI_GETMOUSESPEED:
4840 ret = get_entry( &entry_MOUSESPEED, val, ptr );
4841 break;
4842 case SPI_SETMOUSESPEED:
4843 ret = set_entry( &entry_MOUSESPEED, val, ptr, winini );
4844 break;
4845 case SPI_GETSCREENSAVERRUNNING:
4846 ret = get_entry( &entry_SCREENSAVERRUNNING, val, ptr );
4847 break;
4848 case SPI_GETDESKWALLPAPER:
4849 ret = get_entry( &entry_DESKWALLPAPER, val, ptr );
4850 break;
4851 case SPI_GETACTIVEWINDOWTRACKING:
4852 ret = get_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr );
4853 break;
4854 case SPI_SETACTIVEWINDOWTRACKING:
4855 ret = set_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr, winini );
4856 break;
4857 case SPI_GETMENUANIMATION:
4858 ret = get_entry( &entry_MENUANIMATION, val, ptr );
4859 break;
4860 case SPI_SETMENUANIMATION:
4861 ret = set_entry( &entry_MENUANIMATION, val, ptr, winini );
4862 break;
4863 case SPI_GETCOMBOBOXANIMATION:
4864 ret = get_entry( &entry_COMBOBOXANIMATION, val, ptr );
4865 break;
4866 case SPI_SETCOMBOBOXANIMATION:
4867 ret = set_entry( &entry_COMBOBOXANIMATION, val, ptr, winini );
4868 break;
4869 case SPI_GETLISTBOXSMOOTHSCROLLING:
4870 ret = get_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr );
4871 break;
4872 case SPI_SETLISTBOXSMOOTHSCROLLING:
4873 ret = set_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr, winini );
4874 break;
4875 case SPI_GETGRADIENTCAPTIONS:
4876 ret = get_entry( &entry_GRADIENTCAPTIONS, val, ptr );
4877 break;
4878 case SPI_SETGRADIENTCAPTIONS:
4879 ret = set_entry( &entry_GRADIENTCAPTIONS, val, ptr, winini );
4880 break;
4881 case SPI_GETKEYBOARDCUES:
4882 ret = get_entry( &entry_KEYBOARDCUES, val, ptr );
4883 break;
4884 case SPI_SETKEYBOARDCUES:
4885 ret = set_entry( &entry_KEYBOARDCUES, val, ptr, winini );
4886 break;
4887 case SPI_GETACTIVEWNDTRKZORDER:
4888 ret = get_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr );
4889 break;
4890 case SPI_SETACTIVEWNDTRKZORDER:
4891 ret = set_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr, winini );
4892 break;
4893 case SPI_GETHOTTRACKING:
4894 ret = get_entry( &entry_HOTTRACKING, val, ptr );
4895 break;
4896 case SPI_SETHOTTRACKING:
4897 ret = set_entry( &entry_HOTTRACKING, val, ptr, winini );
4898 break;
4899 case SPI_GETMENUFADE:
4900 ret = get_entry( &entry_MENUFADE, val, ptr );
4901 break;
4902 case SPI_SETMENUFADE:
4903 ret = set_entry( &entry_MENUFADE, val, ptr, winini );
4904 break;
4905 case SPI_GETSELECTIONFADE:
4906 ret = get_entry( &entry_SELECTIONFADE, val, ptr );
4907 break;
4908 case SPI_SETSELECTIONFADE:
4909 ret = set_entry( &entry_SELECTIONFADE, val, ptr, winini );
4910 break;
4911 case SPI_GETTOOLTIPANIMATION:
4912 ret = get_entry( &entry_TOOLTIPANIMATION, val, ptr );
4913 break;
4914 case SPI_SETTOOLTIPANIMATION:
4915 ret = set_entry( &entry_TOOLTIPANIMATION, val, ptr, winini );
4916 break;
4917 case SPI_GETTOOLTIPFADE:
4918 ret = get_entry( &entry_TOOLTIPFADE, val, ptr );
4919 break;
4920 case SPI_SETTOOLTIPFADE:
4921 ret = set_entry( &entry_TOOLTIPFADE, val, ptr, winini );
4922 break;
4923 case SPI_GETCURSORSHADOW:
4924 ret = get_entry( &entry_CURSORSHADOW, val, ptr );
4925 break;
4926 case SPI_SETCURSORSHADOW:
4927 ret = set_entry( &entry_CURSORSHADOW, val, ptr, winini );
4928 break;
4929 case SPI_GETMOUSESONAR:
4930 ret = get_entry( &entry_MOUSESONAR, val, ptr );
4931 break;
4932 case SPI_SETMOUSESONAR:
4933 ret = set_entry( &entry_MOUSESONAR, val, ptr, winini );
4934 break;
4935 case SPI_GETMOUSECLICKLOCK:
4936 ret = get_entry( &entry_MOUSECLICKLOCK, val, ptr );
4937 break;
4938 case SPI_SETMOUSECLICKLOCK:
4939 ret = set_entry( &entry_MOUSECLICKLOCK, val, ptr, winini );
4940 break;
4941 case SPI_GETMOUSEVANISH:
4942 ret = get_entry( &entry_MOUSEVANISH, val, ptr );
4943 break;
4944 case SPI_SETMOUSEVANISH:
4945 ret = set_entry( &entry_MOUSEVANISH, val, ptr, winini );
4946 break;
4947 case SPI_GETFLATMENU:
4948 ret = get_entry( &entry_FLATMENU, val, ptr );
4949 break;
4950 case SPI_SETFLATMENU:
4951 ret = set_entry( &entry_FLATMENU, val, ptr, winini );
4952 break;
4953 case SPI_GETDROPSHADOW:
4954 ret = get_entry( &entry_DROPSHADOW, val, ptr );
4955 break;
4956 case SPI_SETDROPSHADOW:
4957 ret = set_entry( &entry_DROPSHADOW, val, ptr, winini );
4958 break;
4959 case SPI_GETBLOCKSENDINPUTRESETS:
4960 ret = get_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr );
4961 break;
4962 case SPI_SETBLOCKSENDINPUTRESETS:
4963 ret = set_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr, winini );
4964 break;
4965 case SPI_GETUIEFFECTS:
4966 ret = get_entry( &entry_UIEFFECTS, val, ptr );
4967 break;
4968 case SPI_SETUIEFFECTS:
4969 /* FIXME: this probably should mask other UI effect values when unset */
4970 ret = set_entry( &entry_UIEFFECTS, val, ptr, winini );
4971 break;
4972 case SPI_GETDISABLEOVERLAPPEDCONTENT:
4973 ret = get_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr );
4974 break;
4975 case SPI_SETDISABLEOVERLAPPEDCONTENT:
4976 ret = set_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr, winini );
4977 break;
4978 case SPI_GETCLIENTAREAANIMATION:
4979 ret = get_entry( &entry_CLIENTAREAANIMATION, val, ptr );
4980 break;
4981 case SPI_SETCLIENTAREAANIMATION:
4982 ret = set_entry( &entry_CLIENTAREAANIMATION, val, ptr, winini );
4983 break;
4984 case SPI_GETCLEARTYPE:
4985 ret = get_entry( &entry_CLEARTYPE, val, ptr );
4986 break;
4987 case SPI_SETCLEARTYPE:
4988 ret = set_entry( &entry_CLEARTYPE, val, ptr, winini );
4989 break;
4990 case SPI_GETSPEECHRECOGNITION:
4991 ret = get_entry( &entry_SPEECHRECOGNITION, val, ptr );
4992 break;
4993 case SPI_SETSPEECHRECOGNITION:
4994 ret = set_entry( &entry_SPEECHRECOGNITION, val, ptr, winini );
4995 break;
4996 case SPI_GETFOREGROUNDLOCKTIMEOUT:
4997 ret = get_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr );
4998 break;
4999 case SPI_SETFOREGROUNDLOCKTIMEOUT:
5000 /* FIXME: this should check that the calling thread
5001 * is able to change the foreground window */
5002 ret = set_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr, winini );
5003 break;
5004 case SPI_GETACTIVEWNDTRKTIMEOUT:
5005 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
5006 break;
5007 case SPI_SETACTIVEWNDTRKTIMEOUT:
5008 ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
5009 break;
5010 case SPI_GETFOREGROUNDFLASHCOUNT:
5011 ret = get_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr );
5012 break;
5013 case SPI_SETFOREGROUNDFLASHCOUNT:
5014 ret = set_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr, winini );
5015 break;
5016 case SPI_GETCARETWIDTH:
5017 ret = get_entry( &entry_CARETWIDTH, val, ptr );
5018 break;
5019 case SPI_SETCARETWIDTH:
5020 ret = set_entry( &entry_CARETWIDTH, val, ptr, winini );
5021 break;
5022 case SPI_GETMOUSECLICKLOCKTIME:
5023 ret = get_entry( &entry_MOUSECLICKLOCKTIME, val, ptr );
5024 break;
5025 case SPI_SETMOUSECLICKLOCKTIME:
5026 ret = set_entry( &entry_MOUSECLICKLOCKTIME, val, ptr, winini );
5027 break;
5028 case SPI_GETFONTSMOOTHINGTYPE:
5029 ret = get_entry( &entry_FONTSMOOTHINGTYPE, val, ptr );
5030 break;
5031 case SPI_SETFONTSMOOTHINGTYPE:
5032 ret = set_entry( &entry_FONTSMOOTHINGTYPE, val, ptr, winini );
5033 break;
5034 case SPI_GETFONTSMOOTHINGCONTRAST:
5035 ret = get_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr );
5036 break;
5037 case SPI_SETFONTSMOOTHINGCONTRAST:
5038 ret = set_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr, winini );
5039 break;
5040 case SPI_GETFOCUSBORDERWIDTH:
5041 ret = get_entry( &entry_FOCUSBORDERWIDTH, val, ptr );
5042 break;
5043 case SPI_GETFOCUSBORDERHEIGHT:
5044 ret = get_entry( &entry_FOCUSBORDERHEIGHT, val, ptr );
5045 break;
5046 case SPI_SETFOCUSBORDERWIDTH:
5047 ret = set_entry( &entry_FOCUSBORDERWIDTH, val, ptr, winini );
5048 break;
5049 case SPI_SETFOCUSBORDERHEIGHT:
5050 ret = set_entry( &entry_FOCUSBORDERHEIGHT, val, ptr, winini );
5051 break;
5052 case SPI_GETFONTSMOOTHINGORIENTATION:
5053 ret = get_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr );
5054 break;
5055 case SPI_SETFONTSMOOTHINGORIENTATION:
5056 ret = set_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr, winini );
5057 break;
5058 case SPI_GETAUDIODESCRIPTION:
5060 AUDIODESCRIPTION *audio = ptr;
5061 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
5063 ret = get_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled ) &&
5064 get_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale );
5066 break;
5068 case SPI_SETAUDIODESCRIPTION:
5070 AUDIODESCRIPTION *audio = ptr;
5071 if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
5073 ret = set_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled, winini) &&
5074 set_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale, winini );
5076 break;
5078 default:
5079 FIXME( "Unknown action: %u\n", action );
5080 RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE );
5081 ret = FALSE;
5082 break;
5085 if (ret && (winini & SPIF_UPDATEINIFILE))
5087 static const WCHAR emptyW[1];
5088 if (winini & (SPIF_SENDWININICHANGE | SPIF_SENDCHANGE))
5089 send_message_timeout( HWND_BROADCAST, WM_SETTINGCHANGE, action, (LPARAM) emptyW,
5090 SMTO_ABORTIFHUNG, 2000, FALSE );
5092 TRACE( "(%u, %u, %p, %u) ret %d\n", action, val, ptr, winini, ret );
5093 return ret;
5095 #undef WINE_SPI_FIXME
5096 #undef WINE_SPI_WARN
5099 int get_system_metrics( int index )
5101 NONCLIENTMETRICSW ncm;
5102 MINIMIZEDMETRICS mm;
5103 ICONMETRICSW im;
5104 RECT rect;
5105 UINT ret;
5106 HDC hdc;
5108 /* some metrics are dynamic */
5109 switch (index)
5111 case SM_CXVSCROLL:
5112 case SM_CYHSCROLL:
5113 get_entry( &entry_SCROLLWIDTH, 0, &ret );
5114 return max( ret, 8 );
5115 case SM_CYCAPTION:
5116 ncm.cbSize = sizeof(ncm);
5117 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5118 return ncm.iCaptionHeight + 1;
5119 case SM_CXBORDER:
5120 case SM_CYBORDER:
5121 /* SM_C{X,Y}BORDER always returns 1 regardless of 'BorderWidth' value in registry */
5122 return 1;
5123 case SM_CXDLGFRAME:
5124 case SM_CYDLGFRAME:
5125 return 3;
5126 case SM_CYVTHUMB:
5127 case SM_CXHTHUMB:
5128 case SM_CYVSCROLL:
5129 case SM_CXHSCROLL:
5130 get_entry( &entry_SCROLLHEIGHT, 0, &ret );
5131 return max( ret, 8 );
5132 case SM_CXICON:
5133 case SM_CYICON:
5134 return map_to_dpi( 32, get_system_dpi() );
5135 case SM_CXCURSOR:
5136 case SM_CYCURSOR:
5137 ret = map_to_dpi( 32, get_system_dpi() );
5138 if (ret >= 64) return 64;
5139 if (ret >= 48) return 48;
5140 return 32;
5141 case SM_CYMENU:
5142 ncm.cbSize = sizeof(ncm);
5143 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5144 return ncm.iMenuHeight + 1;
5145 case SM_CXFULLSCREEN:
5146 /* see the remark for SM_CXMAXIMIZED, at least this formulation is correct */
5147 return get_system_metrics( SM_CXMAXIMIZED ) - 2 * get_system_metrics( SM_CXFRAME );
5148 case SM_CYFULLSCREEN:
5149 /* see the remark for SM_CYMAXIMIZED, at least this formulation is
5150 * correct */
5151 return get_system_metrics( SM_CYMAXIMIZED ) - get_system_metrics( SM_CYMIN );
5152 case SM_CYKANJIWINDOW:
5153 return 0;
5154 case SM_MOUSEPRESENT:
5155 return 1;
5156 case SM_DEBUG:
5157 return 0;
5158 case SM_SWAPBUTTON:
5159 get_entry( &entry_MOUSEBUTTONSWAP, 0, &ret );
5160 return ret;
5161 case SM_RESERVED1:
5162 case SM_RESERVED2:
5163 case SM_RESERVED3:
5164 case SM_RESERVED4:
5165 return 0;
5166 case SM_CXMIN:
5167 ncm.cbSize = sizeof(ncm);
5168 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5169 hdc = get_display_dc();
5170 get_text_metr_size( hdc, &ncm.lfCaptionFont, NULL, &ret );
5171 release_display_dc( hdc );
5172 return 3 * ncm.iCaptionWidth + ncm.iCaptionHeight + 4 * ret +
5173 2 * get_system_metrics( SM_CXFRAME ) + 4;
5174 case SM_CYMIN:
5175 return get_system_metrics( SM_CYCAPTION ) + 2 * get_system_metrics( SM_CYFRAME );
5176 case SM_CXSIZE:
5177 get_entry( &entry_CAPTIONWIDTH, 0, &ret );
5178 return max( ret, 8 );
5179 case SM_CYSIZE:
5180 ncm.cbSize = sizeof(ncm);
5181 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5182 return ncm.iCaptionHeight;
5183 case SM_CXFRAME:
5184 get_entry( &entry_BORDER, 0, &ret );
5185 ret = max( ret, 1 );
5186 return get_system_metrics( SM_CXDLGFRAME ) + ret;
5187 case SM_CYFRAME:
5188 get_entry( &entry_BORDER, 0, &ret );
5189 ret = max( ret, 1 );
5190 return get_system_metrics( SM_CYDLGFRAME ) + ret;
5191 case SM_CXMINTRACK:
5192 return get_system_metrics( SM_CXMIN );
5193 case SM_CYMINTRACK:
5194 return get_system_metrics( SM_CYMIN );
5195 case SM_CXDOUBLECLK:
5196 get_entry( &entry_DOUBLECLKWIDTH, 0, &ret );
5197 return ret;
5198 case SM_CYDOUBLECLK:
5199 get_entry( &entry_DOUBLECLKHEIGHT, 0, &ret );
5200 return ret;
5201 case SM_CXICONSPACING:
5202 im.cbSize = sizeof(im);
5203 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
5204 return im.iHorzSpacing;
5205 case SM_CYICONSPACING:
5206 im.cbSize = sizeof(im);
5207 NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
5208 return im.iVertSpacing;
5209 case SM_MENUDROPALIGNMENT:
5210 NtUserSystemParametersInfo( SPI_GETMENUDROPALIGNMENT, 0, &ret, 0 );
5211 return ret;
5212 case SM_PENWINDOWS:
5213 return 0;
5214 case SM_DBCSENABLED:
5215 return ansi_cp.MaximumCharacterSize > 1;
5216 case SM_CMOUSEBUTTONS:
5217 return 3;
5218 case SM_SECURE:
5219 return 0;
5220 case SM_CXEDGE:
5221 return get_system_metrics( SM_CXBORDER ) + 1;
5222 case SM_CYEDGE:
5223 return get_system_metrics( SM_CYBORDER ) + 1;
5224 case SM_CXMINSPACING:
5225 mm.cbSize = sizeof(mm);
5226 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5227 return get_system_metrics( SM_CXMINIMIZED ) + mm.iHorzGap;
5228 case SM_CYMINSPACING:
5229 mm.cbSize = sizeof(mm);
5230 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5231 return get_system_metrics( SM_CYMINIMIZED ) + mm.iVertGap;
5232 case SM_CXSMICON:
5233 case SM_CYSMICON:
5234 return map_to_dpi( 16, get_system_dpi() ) & ~1;
5235 case SM_CYSMCAPTION:
5236 ncm.cbSize = sizeof(ncm);
5237 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5238 return ncm.iSmCaptionHeight + 1;
5239 case SM_CXSMSIZE:
5240 get_entry( &entry_SMCAPTIONWIDTH, 0, &ret );
5241 return ret;
5242 case SM_CYSMSIZE:
5243 ncm.cbSize = sizeof(ncm);
5244 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5245 return ncm.iSmCaptionHeight;
5246 case SM_CXMENUSIZE:
5247 get_entry( &entry_MENUWIDTH, 0, &ret );
5248 return ret;
5249 case SM_CYMENUSIZE:
5250 ncm.cbSize = sizeof(ncm);
5251 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5252 return ncm.iMenuHeight;
5253 case SM_ARRANGE:
5254 mm.cbSize = sizeof(mm);
5255 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5256 return mm.iArrange;
5257 case SM_CXMINIMIZED:
5258 mm.cbSize = sizeof(mm);
5259 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
5260 return mm.iWidth + 6;
5261 case SM_CYMINIMIZED:
5262 ncm.cbSize = sizeof(ncm);
5263 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5264 return ncm.iCaptionHeight + 6;
5265 case SM_CXMAXTRACK:
5266 return get_system_metrics( SM_CXVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CXFRAME );
5267 case SM_CYMAXTRACK:
5268 return get_system_metrics( SM_CYVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CYFRAME );
5269 case SM_CXMAXIMIZED:
5270 /* FIXME: subtract the width of any vertical application toolbars*/
5271 return get_system_metrics( SM_CXSCREEN ) + 2 * get_system_metrics( SM_CXFRAME );
5272 case SM_CYMAXIMIZED:
5273 /* FIXME: subtract the width of any horizontal application toolbars*/
5274 return get_system_metrics( SM_CYSCREEN ) + 2 * get_system_metrics( SM_CYCAPTION );
5275 case SM_NETWORK:
5276 return 3; /* FIXME */
5277 case SM_CLEANBOOT:
5278 return 0; /* 0 = ok, 1 = failsafe, 2 = failsafe + network */
5279 case SM_CXDRAG:
5280 get_entry( &entry_DRAGWIDTH, 0, &ret );
5281 return ret;
5282 case SM_CYDRAG:
5283 get_entry( &entry_DRAGHEIGHT, 0, &ret );
5284 return ret;
5285 case SM_SHOWSOUNDS:
5286 get_entry( &entry_SHOWSOUNDS, 0, &ret );
5287 return ret;
5288 case SM_CXMENUCHECK:
5289 case SM_CYMENUCHECK:
5291 TEXTMETRICW tm;
5292 ncm.cbSize = sizeof(ncm);
5293 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
5294 hdc = get_display_dc();
5295 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL );
5296 release_display_dc( hdc );
5297 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading + 1) / 2) * 2 - 1;
5299 case SM_SLOWMACHINE:
5300 return 0; /* Never true */
5301 case SM_MIDEASTENABLED:
5302 return 0; /* FIXME */
5303 case SM_MOUSEWHEELPRESENT:
5304 return 1;
5305 case SM_CXSCREEN:
5306 rect = get_primary_monitor_rect( get_thread_dpi() );
5307 return rect.right - rect.left;
5308 case SM_CYSCREEN:
5309 rect = get_primary_monitor_rect( get_thread_dpi() );
5310 return rect.bottom - rect.top;
5311 case SM_XVIRTUALSCREEN:
5312 rect = get_virtual_screen_rect( get_thread_dpi() );
5313 return rect.left;
5314 case SM_YVIRTUALSCREEN:
5315 rect = get_virtual_screen_rect( get_thread_dpi() );
5316 return rect.top;
5317 case SM_CXVIRTUALSCREEN:
5318 rect = get_virtual_screen_rect( get_thread_dpi() );
5319 return rect.right - rect.left;
5320 case SM_CYVIRTUALSCREEN:
5321 rect = get_virtual_screen_rect( get_thread_dpi() );
5322 return rect.bottom - rect.top;
5323 case SM_CMONITORS:
5324 if (!lock_display_devices()) return FALSE;
5325 ret = active_monitor_count();
5326 unlock_display_devices();
5327 return ret;
5328 case SM_SAMEDISPLAYFORMAT:
5329 return 1;
5330 case SM_IMMENABLED:
5331 return 0; /* FIXME */
5332 case SM_CXFOCUSBORDER:
5333 case SM_CYFOCUSBORDER:
5334 return 1;
5335 case SM_TABLETPC:
5336 case SM_MEDIACENTER:
5337 return 0;
5338 case SM_CMETRICS:
5339 return SM_CMETRICS;
5340 default:
5341 return 0;
5345 static int get_system_metrics_for_dpi( int index, unsigned int dpi )
5347 NONCLIENTMETRICSW ncm;
5348 ICONMETRICSW im;
5349 UINT ret;
5350 HDC hdc;
5352 /* some metrics are dynamic */
5353 switch (index)
5355 case SM_CXVSCROLL:
5356 case SM_CYHSCROLL:
5357 get_entry_dpi( &entry_SCROLLWIDTH, 0, &ret, dpi );
5358 return max( ret, 8 );
5359 case SM_CYCAPTION:
5360 ncm.cbSize = sizeof(ncm);
5361 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5362 return ncm.iCaptionHeight + 1;
5363 case SM_CYVTHUMB:
5364 case SM_CXHTHUMB:
5365 case SM_CYVSCROLL:
5366 case SM_CXHSCROLL:
5367 get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ret, dpi );
5368 return max( ret, 8 );
5369 case SM_CXICON:
5370 case SM_CYICON:
5371 return map_to_dpi( 32, dpi );
5372 case SM_CXCURSOR:
5373 case SM_CYCURSOR:
5374 ret = map_to_dpi( 32, dpi );
5375 if (ret >= 64) return 64;
5376 if (ret >= 48) return 48;
5377 return 32;
5378 case SM_CYMENU:
5379 ncm.cbSize = sizeof(ncm);
5380 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5381 return ncm.iMenuHeight + 1;
5382 case SM_CXSIZE:
5383 get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ret, dpi );
5384 return max( ret, 8 );
5385 case SM_CYSIZE:
5386 ncm.cbSize = sizeof(ncm);
5387 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5388 return ncm.iCaptionHeight;
5389 case SM_CXFRAME:
5390 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
5391 ret = max( ret, 1 );
5392 return get_system_metrics_for_dpi( SM_CXDLGFRAME, dpi ) + ret;
5393 case SM_CYFRAME:
5394 get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
5395 ret = max( ret, 1 );
5396 return get_system_metrics_for_dpi( SM_CYDLGFRAME, dpi ) + ret;
5397 case SM_CXICONSPACING:
5398 im.cbSize = sizeof(im);
5399 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
5400 return im.iHorzSpacing;
5401 case SM_CYICONSPACING:
5402 im.cbSize = sizeof(im);
5403 NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
5404 return im.iVertSpacing;
5405 case SM_CXSMICON:
5406 case SM_CYSMICON:
5407 return map_to_dpi( 16, dpi ) & ~1;
5408 case SM_CYSMCAPTION:
5409 ncm.cbSize = sizeof(ncm);
5410 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5411 return ncm.iSmCaptionHeight + 1;
5412 case SM_CXSMSIZE:
5413 get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ret, dpi );
5414 return ret;
5415 case SM_CYSMSIZE:
5416 ncm.cbSize = sizeof(ncm);
5417 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5418 return ncm.iSmCaptionHeight;
5419 case SM_CXMENUSIZE:
5420 get_entry_dpi( &entry_MENUWIDTH, 0, &ret, dpi );
5421 return ret;
5422 case SM_CYMENUSIZE:
5423 ncm.cbSize = sizeof(ncm);
5424 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5425 return ncm.iMenuHeight;
5426 case SM_CXMENUCHECK:
5427 case SM_CYMENUCHECK:
5429 TEXTMETRICW tm;
5430 ncm.cbSize = sizeof(ncm);
5431 NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
5432 hdc = get_display_dc();
5433 get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL);
5434 release_display_dc( hdc );
5435 return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading - 1) | 1);
5437 default:
5438 return get_system_metrics( index );
5442 COLORREF get_sys_color( int index )
5444 COLORREF ret = 0;
5446 if (index >= 0 && index < ARRAY_SIZE( system_colors ))
5447 get_entry( &system_colors[index], 0, &ret );
5448 return ret;
5451 HBRUSH get_55aa_brush(void)
5453 static const WORD pattern[] = { 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa };
5454 static HBRUSH brush_55aa;
5456 if (!brush_55aa)
5458 HBITMAP bitmap = NtGdiCreateBitmap( 8, 8, 1, 1, pattern );
5459 HBRUSH brush = NtGdiCreatePatternBrushInternal( bitmap, FALSE, FALSE );
5460 NtGdiDeleteObjectApp( bitmap );
5461 make_gdi_object_system( brush, TRUE );
5462 if (InterlockedCompareExchangePointer( (void **)&brush_55aa, brush, 0 ))
5464 make_gdi_object_system( brush, FALSE );
5465 NtGdiDeleteObjectApp( brush );
5468 return brush_55aa;
5471 HBRUSH get_sys_color_brush( unsigned int index )
5473 if (index == COLOR_55AA_BRUSH) return get_55aa_brush();
5474 if (index >= ARRAY_SIZE( system_colors )) return 0;
5476 if (!system_colors[index].brush)
5478 HBRUSH brush = NtGdiCreateSolidBrush( get_sys_color( index ), NULL );
5479 make_gdi_object_system( brush, TRUE );
5480 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].brush, brush, 0 ))
5482 make_gdi_object_system( brush, FALSE );
5483 NtGdiDeleteObjectApp( brush );
5486 return system_colors[index].brush;
5489 HPEN get_sys_color_pen( unsigned int index )
5491 if (index >= ARRAY_SIZE( system_colors )) return 0;
5493 if (!system_colors[index].pen)
5495 HPEN pen = NtGdiCreatePen( PS_SOLID, 1, get_sys_color( index ), NULL );
5496 make_gdi_object_system( pen, TRUE );
5497 if (InterlockedCompareExchangePointer( (void **)&system_colors[index].pen, pen, 0 ))
5499 make_gdi_object_system( pen, FALSE );
5500 NtGdiDeleteObjectApp( pen );
5503 return system_colors[index].pen;
5506 /**********************************************************************
5507 * NtUserGetDoubleClickTime (win32u.@)
5509 UINT WINAPI NtUserGetDoubleClickTime(void)
5511 UINT time = 0;
5513 get_entry( &entry_DOUBLECLICKTIME, 0, &time );
5514 if (!time) time = 500;
5515 return time;
5518 /*************************************************************************
5519 * NtUserSetSysColors (win32u.@)
5521 BOOL WINAPI NtUserSetSysColors( INT count, const INT *colors, const COLORREF *values )
5523 int i;
5525 if (IS_INTRESOURCE(colors)) return FALSE; /* stupid app passes a color instead of an array */
5527 for (i = 0; i < count; i++)
5528 if (colors[i] >= 0 && colors[i] <= ARRAY_SIZE( system_colors ))
5529 set_entry( &system_colors[colors[i]], values[i], 0, 0 );
5531 /* Send WM_SYSCOLORCHANGE message to all windows */
5532 send_message_timeout( HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0,
5533 SMTO_ABORTIFHUNG, 2000, FALSE );
5534 /* Repaint affected portions of all visible windows */
5535 NtUserRedrawWindow( 0, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN );
5536 return TRUE;
5540 static LONG dpi_awareness;
5542 /***********************************************************************
5543 * NtUserSetProcessDpiAwarenessContext (win32u.@)
5545 BOOL WINAPI NtUserSetProcessDpiAwarenessContext( ULONG awareness, ULONG unknown )
5547 switch (awareness)
5549 case NTUSER_DPI_UNAWARE:
5550 case NTUSER_DPI_SYSTEM_AWARE:
5551 case NTUSER_DPI_PER_MONITOR_AWARE:
5552 case NTUSER_DPI_PER_MONITOR_AWARE_V2:
5553 case NTUSER_DPI_PER_UNAWARE_GDISCALED:
5554 break;
5555 default:
5556 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
5557 return FALSE;
5560 return !InterlockedCompareExchange( &dpi_awareness, awareness, 0 );
5563 /***********************************************************************
5564 * NtUserGetProcessDpiAwarenessContext (win32u.@)
5566 ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process )
5568 DPI_AWARENESS val;
5570 if (process && process != GetCurrentProcess())
5572 WARN( "not supported on other process %p\n", process );
5573 return NTUSER_DPI_UNAWARE;
5576 val = ReadNoFence( &dpi_awareness );
5577 if (!val) return NTUSER_DPI_UNAWARE;
5578 return val;
5581 BOOL message_beep( UINT i )
5583 BOOL active = TRUE;
5584 NtUserSystemParametersInfo( SPI_GETBEEP, 0, &active, FALSE );
5585 if (active) user_driver->pBeep();
5586 return TRUE;
5589 static DWORD exiting_thread_id;
5591 /**********************************************************************
5592 * is_exiting_thread
5594 BOOL is_exiting_thread( DWORD tid )
5596 return tid == exiting_thread_id;
5599 static void thread_detach(void)
5601 struct user_thread_info *thread_info = get_user_thread_info();
5603 user_driver->pThreadDetach();
5605 free( thread_info->key_state );
5606 thread_info->key_state = 0;
5607 free( thread_info->rawinput );
5609 destroy_thread_windows();
5610 cleanup_imm_thread();
5611 NtClose( thread_info->server_queue );
5613 exiting_thread_id = 0;
5616 /***********************************************************************
5617 * NtUserCallNoParam (win32u.@)
5619 ULONG_PTR WINAPI NtUserCallNoParam( ULONG code )
5621 switch(code)
5623 case NtUserCallNoParam_DestroyCaret:
5624 return destroy_caret();
5626 case NtUserCallNoParam_GetDesktopWindow:
5627 return HandleToUlong( get_desktop_window() );
5629 case NtUserCallNoParam_GetDialogBaseUnits:
5630 return get_dialog_base_units();
5632 case NtUserCallNoParam_GetInputState:
5633 return get_input_state();
5635 case NtUserCallNoParam_GetProcessDefaultLayout:
5636 return process_layout;
5638 case NtUserCallNoParam_GetProgmanWindow:
5639 return HandleToUlong( get_progman_window() );
5641 case NtUserCallNoParam_GetShellWindow:
5642 return HandleToUlong( get_shell_window() );
5644 case NtUserCallNoParam_GetTaskmanWindow:
5645 return HandleToUlong( get_taskman_window() );
5647 case NtUserCallNoParam_ReleaseCapture:
5648 return release_capture();
5650 /* temporary exports */
5651 case NtUserExitingThread:
5652 exiting_thread_id = GetCurrentThreadId();
5653 return 0;
5655 case NtUserThreadDetach:
5656 thread_detach();
5657 return 0;
5659 default:
5660 FIXME( "invalid code %u\n", (int)code );
5661 return 0;
5665 /***********************************************************************
5666 * NtUserCallOneParam (win32u.@)
5668 ULONG_PTR WINAPI NtUserCallOneParam( ULONG_PTR arg, ULONG code )
5670 switch(code)
5672 case NtUserCallOneParam_BeginDeferWindowPos:
5673 return HandleToUlong( begin_defer_window_pos( arg ));
5675 case NtUserCallOneParam_CreateCursorIcon:
5676 return HandleToUlong( alloc_cursoricon_handle( arg ));
5678 case NtUserCallOneParam_CreateMenu:
5679 return HandleToUlong( create_menu( arg ) );
5681 case NtUserCallOneParam_EnableDC:
5682 return set_dce_flags( UlongToHandle(arg), DCHF_ENABLEDC );
5684 case NtUserCallOneParam_EnableThunkLock:
5685 enable_thunk_lock = arg;
5686 return 0;
5688 case NtUserCallOneParam_EnumClipboardFormats:
5689 return enum_clipboard_formats( arg );
5691 case NtUserCallOneParam_GetClipCursor:
5692 return get_clip_cursor( (RECT *)arg );
5694 case NtUserCallOneParam_GetCursorPos:
5695 return get_cursor_pos( (POINT *)arg );
5697 case NtUserCallOneParam_GetIconParam:
5698 return get_icon_param( UlongToHandle(arg) );
5700 case NtUserCallOneParam_GetMenuItemCount:
5701 return get_menu_item_count( UlongToHandle(arg) );
5703 case NtUserCallOneParam_GetSysColor:
5704 return get_sys_color( arg );
5706 case NtUserCallOneParam_IsWindowRectFullScreen:
5707 return is_window_rect_full_screen( (const RECT *)arg );
5709 case NtUserCallOneParam_RealizePalette:
5710 return realize_palette( UlongToHandle(arg) );
5712 case NtUserCallOneParam_GetPrimaryMonitorRect:
5713 *(RECT *)arg = get_primary_monitor_rect( 0 );
5714 return 1;
5716 case NtUserCallOneParam_GetSysColorBrush:
5717 return HandleToUlong( get_sys_color_brush(arg) );
5719 case NtUserCallOneParam_GetSysColorPen:
5720 return HandleToUlong( get_sys_color_pen(arg) );
5722 case NtUserCallOneParam_GetSystemMetrics:
5723 return get_system_metrics( arg );
5725 case NtUserCallOneParam_GetVirtualScreenRect:
5726 *(RECT *)arg = get_virtual_screen_rect( 0 );
5727 return 1;
5729 case NtUserCallOneParam_MessageBeep:
5730 return message_beep( arg );
5732 case NtUserCallOneParam_ReplyMessage:
5733 return reply_message_result( arg );
5735 case NtUserCallOneParam_SetCaretBlinkTime:
5736 return set_caret_blink_time( arg );
5738 case NtUserCallOneParam_SetProcessDefaultLayout:
5739 process_layout = arg;
5740 return TRUE;
5742 /* temporary exports */
5743 case NtUserGetDeskPattern:
5744 return get_entry( &entry_DESKPATTERN, 256, (WCHAR *)arg );
5746 default:
5747 FIXME( "invalid code %u\n", (int)code );
5748 return 0;
5752 /***********************************************************************
5753 * NtUserCallTwoParam (win32u.@)
5755 ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code )
5757 switch(code)
5759 case NtUserCallTwoParam_GetDialogProc:
5760 return (ULONG_PTR)get_dialog_proc( (DLGPROC)arg1, arg2 );
5762 case NtUserCallTwoParam_GetMenuInfo:
5763 return get_menu_info( UlongToHandle(arg1), (MENUINFO *)arg2 );
5765 case NtUserCallTwoParam_GetMonitorInfo:
5766 return get_monitor_info( UlongToHandle(arg1), (MONITORINFO *)arg2 );
5768 case NtUserCallTwoParam_GetSystemMetricsForDpi:
5769 return get_system_metrics_for_dpi( arg1, arg2 );
5771 case NtUserCallTwoParam_MonitorFromRect:
5772 return HandleToUlong( monitor_from_rect( (const RECT *)arg1, arg2, get_thread_dpi() ));
5774 case NtUserCallTwoParam_SetCaretPos:
5775 return set_caret_pos( arg1, arg2 );
5777 case NtUserCallTwoParam_SetIconParam:
5778 return set_icon_param( UlongToHandle(arg1), arg2 );
5780 case NtUserCallTwoParam_UnhookWindowsHook:
5781 return unhook_windows_hook( arg1, (HOOKPROC)arg2 );
5783 /* temporary exports */
5784 case NtUserAllocWinProc:
5785 return (UINT_PTR)alloc_winproc( (WNDPROC)arg1, arg2 );
5787 default:
5788 FIXME( "invalid code %u\n", (int)code );
5789 return 0;
5793 /***********************************************************************
5794 * NtUserDisplayConfigGetDeviceInfo (win32u.@)
5796 NTSTATUS WINAPI NtUserDisplayConfigGetDeviceInfo( DISPLAYCONFIG_DEVICE_INFO_HEADER *packet )
5798 NTSTATUS ret = STATUS_UNSUCCESSFUL;
5800 TRACE( "packet %p.\n", packet );
5802 if (!packet || packet->size < sizeof(*packet))
5803 return STATUS_UNSUCCESSFUL;
5805 switch (packet->type)
5807 case DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME:
5809 DISPLAYCONFIG_SOURCE_DEVICE_NAME *source_name = (DISPLAYCONFIG_SOURCE_DEVICE_NAME *)packet;
5810 struct adapter *adapter;
5812 TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME.\n" );
5814 if (packet->size < sizeof(*source_name))
5815 return STATUS_INVALID_PARAMETER;
5817 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
5819 LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
5821 if (source_name->header.id != adapter->id) continue;
5822 if (memcmp( &source_name->header.adapterId, &adapter->gpu_luid, sizeof(adapter->gpu_luid) )) continue;
5824 lstrcpyW( source_name->viewGdiDeviceName, adapter->dev.device_name );
5825 ret = STATUS_SUCCESS;
5826 break;
5829 unlock_display_devices();
5830 return ret;
5832 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME:
5834 DISPLAYCONFIG_TARGET_DEVICE_NAME *target_name = (DISPLAYCONFIG_TARGET_DEVICE_NAME *)packet;
5835 char buffer[ARRAY_SIZE(target_name->monitorFriendlyDeviceName)];
5836 struct monitor *monitor;
5838 TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME.\n" );
5840 if (packet->size < sizeof(*target_name))
5841 return STATUS_INVALID_PARAMETER;
5843 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
5845 memset( &target_name->flags, 0, sizeof(*target_name) - offsetof(DISPLAYCONFIG_TARGET_DEVICE_NAME, flags) );
5847 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
5849 if (target_name->header.id != monitor->output_id) continue;
5850 if (memcmp( &target_name->header.adapterId, &monitor->adapter->gpu_luid,
5851 sizeof(monitor->adapter->gpu_luid) ))
5852 continue;
5854 target_name->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL;
5855 snprintf( buffer, ARRAY_SIZE(buffer), "Display%u", monitor->output_id + 1 );
5856 asciiz_to_unicode( target_name->monitorFriendlyDeviceName, buffer );
5857 lstrcpyW( target_name->monitorDevicePath, monitor->dev.interface_name );
5858 if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
5860 target_name->edidManufactureId = monitor->edid_info.manufacturer;
5861 target_name->edidProductCodeId = monitor->edid_info.product_code;
5862 target_name->flags.edidIdsValid = 1;
5864 if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_NAME)
5866 wcscpy( target_name->monitorFriendlyDeviceName, monitor->edid_info.monitor_name );
5867 target_name->flags.friendlyNameFromEdid = 1;
5869 ret = STATUS_SUCCESS;
5870 break;
5873 unlock_display_devices();
5874 return ret;
5876 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE:
5878 DISPLAYCONFIG_TARGET_PREFERRED_MODE *preferred_mode = (DISPLAYCONFIG_TARGET_PREFERRED_MODE *)packet;
5879 DISPLAYCONFIG_VIDEO_SIGNAL_INFO *signal_info = &preferred_mode->targetMode.targetVideoSignalInfo;
5880 unsigned int i, display_freq;
5881 DEVMODEW *found_mode = NULL;
5882 BOOL have_edid_mode = FALSE;
5883 struct monitor *monitor;
5885 FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE semi-stub.\n" );
5887 if (packet->size < sizeof(*preferred_mode))
5888 return STATUS_INVALID_PARAMETER;
5890 if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
5892 memset( &preferred_mode->width, 0, sizeof(*preferred_mode) - offsetof(DISPLAYCONFIG_TARGET_PREFERRED_MODE, width) );
5894 LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
5896 if (preferred_mode->header.id != monitor->output_id) continue;
5897 if (memcmp( &preferred_mode->header.adapterId, &monitor->adapter->gpu_luid,
5898 sizeof(monitor->adapter->gpu_luid) ))
5899 continue;
5901 for (i = 0; i < monitor->adapter->mode_count; ++i)
5903 DEVMODEW *mode = &monitor->adapter->modes[i];
5905 if (!have_edid_mode && monitor->edid_info.flags & MONITOR_INFO_HAS_PREFERRED_MODE
5906 && mode->dmPelsWidth == monitor->edid_info.preferred_width
5907 && mode->dmPelsHeight == monitor->edid_info.preferred_height)
5909 found_mode = mode;
5910 have_edid_mode = TRUE;
5913 if (!have_edid_mode && (!found_mode
5914 || (mode->dmPelsWidth > found_mode->dmPelsWidth && mode->dmPelsHeight >= found_mode->dmPelsHeight)
5915 || (mode->dmPelsHeight > found_mode->dmPelsHeight && mode->dmPelsWidth >= found_mode->dmPelsWidth)))
5916 found_mode = mode;
5918 if (mode->dmPelsWidth == found_mode->dmPelsWidth
5919 && mode->dmPelsHeight == found_mode->dmPelsHeight
5920 && mode->dmDisplayFrequency > found_mode->dmDisplayFrequency)
5921 found_mode = mode;
5924 if (!found_mode)
5926 ERR( "No mode found.\n" );
5927 break;
5929 preferred_mode->width = found_mode->dmPelsWidth;
5930 preferred_mode->height = found_mode->dmPelsHeight;
5931 display_freq = found_mode->dmDisplayFrequency;
5933 signal_info->pixelRate = display_freq * preferred_mode->width * preferred_mode->height;
5934 signal_info->hSyncFreq.Numerator = display_freq * preferred_mode->width;
5935 signal_info->hSyncFreq.Denominator = 1;
5936 signal_info->vSyncFreq.Numerator = display_freq;
5937 signal_info->vSyncFreq.Denominator = 1;
5938 signal_info->activeSize.cx = preferred_mode->width;
5939 signal_info->activeSize.cy = preferred_mode->height;
5940 signal_info->totalSize.cx = preferred_mode->width;
5941 signal_info->totalSize.cy = preferred_mode->height;
5942 signal_info->videoStandard = D3DKMDT_VSS_OTHER;
5943 if (!(found_mode->dmFields & DM_DISPLAYFLAGS))
5944 signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
5945 else if (found_mode->dmDisplayFlags & DM_INTERLACED)
5946 signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED;
5947 else
5948 signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
5949 ret = STATUS_SUCCESS;
5950 break;
5953 unlock_display_devices();
5954 return ret;
5956 case DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME:
5958 DISPLAYCONFIG_ADAPTER_NAME *adapter_name = (DISPLAYCONFIG_ADAPTER_NAME *)packet;
5960 FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME stub.\n" );
5962 if (packet->size < sizeof(*adapter_name))
5963 return STATUS_INVALID_PARAMETER;
5965 return STATUS_NOT_SUPPORTED;
5967 case DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE:
5968 case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE:
5969 case DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION:
5970 case DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION:
5971 case DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO:
5972 case DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE:
5973 case DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL:
5974 default:
5975 FIXME( "Unimplemented packet type %u.\n", packet->type );
5976 return STATUS_INVALID_PARAMETER;