6704 lines
231 KiB
C
6704 lines
231 KiB
C
/*
|
|
* System parameters functions
|
|
*
|
|
* Copyright 1994 Alexandre Julliard
|
|
* Copyright 2019 Zhiyi Zhang for CodeWeavers
|
|
* Copyright 2021 Jacek Caban for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#if 0
|
|
#pragma makedep unix
|
|
#endif
|
|
|
|
#include <pthread.h>
|
|
#include <assert.h>
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "ntgdi_private.h"
|
|
#include "ntuser_private.h"
|
|
#include "winreg.h"
|
|
#include "cfgmgr32.h"
|
|
#include "d3dkmdt.h"
|
|
#include "wine/wingdi16.h"
|
|
#include "wine/server.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(system);
|
|
|
|
|
|
static HKEY video_key, enum_key, control_key, config_key, volatile_base_key;
|
|
|
|
static const char devicemap_video_keyA[] = "\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO";
|
|
static const char enum_keyA[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Enum";
|
|
static const char control_keyA[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Control";
|
|
static const char config_keyA[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\Current";
|
|
|
|
static const char devpropkey_gpu_vulkan_uuidA[] = "Properties\\{233A9EF3-AFC4-4ABD-B564-C32F21F1535C}\\0002";
|
|
static const char devpropkey_gpu_luidA[] = "Properties\\{60B193CB-5276-4D0F-96FC-F173ABAD3EC6}\\0002";
|
|
static const char devpkey_device_matching_device_id[] = "Properties\\{A8B865DD-2E3D-4094-AD97-E593A70C75D6}\\0008";
|
|
static const char devpkey_device_bus_number[] = "Properties\\{A45C254E-DF1C-4EFD-8020-67D146A850E0}\\0017";
|
|
static const char devpkey_device_removal_policy[] = "Properties\\{A45C254E-DF1C-4EFD-8020-67D146A850E0}\\0021";
|
|
static const char devpropkey_device_ispresentA[] = "Properties\\{540B947E-8B40-45BC-A8A2-6A0B894CBDA2}\\0005";
|
|
static const char devpropkey_monitor_gpu_luidA[] = "Properties\\{CA085853-16CE-48AA-B114-DE9C72334223}\\0001";
|
|
static const char devpropkey_monitor_output_idA[] = "Properties\\{CA085853-16CE-48AA-B114-DE9C72334223}\\0002";
|
|
static const char wine_devpropkey_monitor_rcmonitorA[] = "Properties\\{233a9ef3-afc4-4abd-b564-c32f21f1535b}\\0003";
|
|
static const char wine_devpropkey_monitor_rcworkA[] = "Properties\\{233a9ef3-afc4-4abd-b564-c32f21f1535b}\\0004";
|
|
|
|
static const WCHAR linkedW[] = {'L','i','n','k','e','d',0};
|
|
static const WCHAR symbolic_link_valueW[] =
|
|
{'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
|
|
static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0};
|
|
static const WCHAR hardware_idW[] = {'H','a','r','d','w','a','r','e','I','D',0};
|
|
static const WCHAR device_descW[] = {'D','e','v','i','c','e','D','e','s','c',0};
|
|
static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0};
|
|
static const WCHAR yesW[] = {'Y','e','s',0};
|
|
static const WCHAR noW[] = {'N','o',0};
|
|
static const WCHAR mode_countW[] = {'M','o','d','e','C','o','u','n','t',0};
|
|
|
|
static const char guid_devclass_displayA[] = "{4D36E968-E325-11CE-BFC1-08002BE10318}";
|
|
static const WCHAR guid_devclass_displayW[] =
|
|
{'{','4','D','3','6','E','9','6','8','-','E','3','2','5','-','1','1','C','E','-',
|
|
'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0};
|
|
static const char guid_devclass_monitorA[] = "{4D36E96E-E325-11CE-BFC1-08002BE10318}";
|
|
static const WCHAR guid_devclass_monitorW[] =
|
|
{'{','4','D','3','6','E','9','6','E','-','E','3','2','5','-','1','1','C','E','-',
|
|
'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0};
|
|
|
|
static const char guid_devinterface_display_adapterA[] = "{5B45201D-F2F2-4F3B-85BB-30FF1F953599}";
|
|
static const char guid_display_device_arrivalA[] = "{1CA05180-A699-450A-9A0C-DE4FBE3DDD89}";
|
|
static const char guid_devinterface_monitorA[] = "{E6F07B5F-EE97-4A90-B076-33F57BF4EAA7}";
|
|
|
|
#define NEXT_DEVMODEW(mode) ((DEVMODEW *)((char *)((mode) + 1) + (mode)->dmDriverExtra))
|
|
|
|
struct pci_id
|
|
{
|
|
UINT16 vendor;
|
|
UINT16 device;
|
|
UINT16 subsystem;
|
|
UINT16 revision;
|
|
};
|
|
|
|
struct gpu
|
|
{
|
|
LONG refcount;
|
|
struct list entry;
|
|
char path[MAX_PATH];
|
|
WCHAR name[128];
|
|
char guid[39];
|
|
LUID luid;
|
|
UINT index;
|
|
GUID vulkan_uuid;
|
|
UINT adapter_count;
|
|
};
|
|
|
|
struct adapter
|
|
{
|
|
LONG refcount;
|
|
struct list entry;
|
|
unsigned int id;
|
|
struct gpu *gpu;
|
|
UINT state_flags;
|
|
WCHAR config_key[MAX_PATH];
|
|
UINT monitor_count;
|
|
UINT mode_count;
|
|
DEVMODEW *modes;
|
|
};
|
|
|
|
#define MONITOR_INFO_HAS_MONITOR_ID 0x00000001
|
|
#define MONITOR_INFO_HAS_MONITOR_NAME 0x00000002
|
|
#define MONITOR_INFO_HAS_PREFERRED_MODE 0x00000004
|
|
struct edid_monitor_info
|
|
{
|
|
unsigned int flags;
|
|
/* MONITOR_INFO_HAS_MONITOR_ID */
|
|
unsigned short manufacturer, product_code;
|
|
char monitor_id_string[8];
|
|
/* MONITOR_INFO_HAS_MONITOR_NAME */
|
|
WCHAR monitor_name[14];
|
|
/* MONITOR_INFO_HAS_PREFERRED_MODE */
|
|
unsigned int preferred_width, preferred_height;
|
|
};
|
|
|
|
struct monitor
|
|
{
|
|
struct list entry;
|
|
char path[MAX_PATH];
|
|
struct adapter *adapter;
|
|
HANDLE handle;
|
|
unsigned int id;
|
|
unsigned int output_id;
|
|
RECT rc_monitor;
|
|
RECT rc_work;
|
|
BOOL is_clone;
|
|
struct edid_monitor_info edid_info;
|
|
};
|
|
|
|
static struct list gpus = LIST_INIT(gpus);
|
|
static struct list adapters = LIST_INIT(adapters);
|
|
static struct list monitors = LIST_INIT(monitors);
|
|
static INT64 last_query_display_time;
|
|
static pthread_mutex_t display_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
BOOL enable_thunk_lock = FALSE;
|
|
|
|
#define VIRTUAL_HMONITOR ((HMONITOR)(UINT_PTR)(0x10000 + 1))
|
|
static struct monitor virtual_monitor =
|
|
{
|
|
.handle = VIRTUAL_HMONITOR,
|
|
.rc_monitor.right = 1024,
|
|
.rc_monitor.bottom = 768,
|
|
.rc_work.right = 1024,
|
|
.rc_work.bottom = 768,
|
|
};
|
|
|
|
/* the various registry keys that are used to store parameters */
|
|
enum parameter_key
|
|
{
|
|
COLORS_KEY,
|
|
DESKTOP_KEY,
|
|
KEYBOARD_KEY,
|
|
MOUSE_KEY,
|
|
METRICS_KEY,
|
|
SOUND_KEY,
|
|
VERSION_KEY,
|
|
SHOWSOUNDS_KEY,
|
|
KEYBOARDPREF_KEY,
|
|
SCREENREADER_KEY,
|
|
AUDIODESC_KEY,
|
|
NB_PARAM_KEYS
|
|
};
|
|
|
|
static const char *parameter_key_names[NB_PARAM_KEYS] =
|
|
{
|
|
"Control Panel\\Colors",
|
|
"Control Panel\\Desktop",
|
|
"Control Panel\\Keyboard",
|
|
"Control Panel\\Mouse",
|
|
"Control Panel\\Desktop\\WindowMetrics",
|
|
"Control Panel\\Sound",
|
|
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
|
|
"Control Panel\\Accessibility\\ShowSounds",
|
|
"Control Panel\\Accessibility\\Keyboard Preference",
|
|
"Control Panel\\Accessibility\\Blind Access",
|
|
"Control Panel\\Accessibility\\AudioDescription",
|
|
};
|
|
|
|
/* System parameters storage */
|
|
union sysparam_all_entry;
|
|
|
|
struct sysparam_entry
|
|
{
|
|
BOOL (*get)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi );
|
|
BOOL (*set)( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags );
|
|
BOOL (*init)( union sysparam_all_entry *entry );
|
|
enum parameter_key base_key;
|
|
const char *regval;
|
|
enum parameter_key mirror_key;
|
|
const char *mirror;
|
|
BOOL loaded;
|
|
};
|
|
|
|
struct sysparam_uint_entry
|
|
{
|
|
struct sysparam_entry hdr;
|
|
UINT val;
|
|
};
|
|
|
|
struct sysparam_bool_entry
|
|
{
|
|
struct sysparam_entry hdr;
|
|
BOOL val;
|
|
};
|
|
|
|
struct sysparam_dword_entry
|
|
{
|
|
struct sysparam_entry hdr;
|
|
DWORD val;
|
|
};
|
|
|
|
struct sysparam_rgb_entry
|
|
{
|
|
struct sysparam_entry hdr;
|
|
COLORREF val;
|
|
HBRUSH brush;
|
|
HPEN pen;
|
|
};
|
|
|
|
struct sysparam_binary_entry
|
|
{
|
|
struct sysparam_entry hdr;
|
|
void *ptr;
|
|
size_t size;
|
|
};
|
|
|
|
struct sysparam_path_entry
|
|
{
|
|
struct sysparam_entry hdr;
|
|
WCHAR *path;
|
|
};
|
|
|
|
struct sysparam_font_entry
|
|
{
|
|
struct sysparam_entry hdr;
|
|
UINT weight;
|
|
LOGFONTW val;
|
|
WCHAR fullname[LF_FACESIZE];
|
|
};
|
|
|
|
struct sysparam_pref_entry
|
|
{
|
|
struct sysparam_entry hdr;
|
|
union sysparam_all_entry *parent;
|
|
UINT offset;
|
|
UINT mask;
|
|
};
|
|
|
|
union sysparam_all_entry
|
|
{
|
|
struct sysparam_entry hdr;
|
|
struct sysparam_uint_entry uint;
|
|
struct sysparam_bool_entry bool;
|
|
struct sysparam_dword_entry dword;
|
|
struct sysparam_rgb_entry rgb;
|
|
struct sysparam_binary_entry bin;
|
|
struct sysparam_path_entry path;
|
|
struct sysparam_font_entry font;
|
|
struct sysparam_pref_entry pref;
|
|
};
|
|
|
|
static UINT system_dpi;
|
|
static RECT work_area;
|
|
static DWORD process_layout = ~0u;
|
|
|
|
static HDC display_dc;
|
|
static pthread_mutex_t display_dc_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static pthread_mutex_t user_mutex;
|
|
static unsigned int user_lock_thread, user_lock_rec;
|
|
|
|
void user_lock(void)
|
|
{
|
|
pthread_mutex_lock( &user_mutex );
|
|
if (!user_lock_rec++) user_lock_thread = GetCurrentThreadId();
|
|
}
|
|
|
|
void user_unlock(void)
|
|
{
|
|
if (!--user_lock_rec) user_lock_thread = 0;
|
|
pthread_mutex_unlock( &user_mutex );
|
|
}
|
|
|
|
void user_check_not_lock(void)
|
|
{
|
|
if (user_lock_thread == GetCurrentThreadId())
|
|
{
|
|
ERR( "BUG: holding USER lock\n" );
|
|
assert( 0 );
|
|
}
|
|
}
|
|
|
|
static HANDLE get_display_device_init_mutex( void )
|
|
{
|
|
WCHAR bufferW[256];
|
|
UNICODE_STRING name = {.Buffer = bufferW};
|
|
OBJECT_ATTRIBUTES attr;
|
|
char buffer[256];
|
|
HANDLE mutex;
|
|
|
|
snprintf( buffer, ARRAY_SIZE(buffer), "\\Sessions\\%u\\BaseNamedObjects\\display_device_init",
|
|
(int)NtCurrentTeb()->Peb->SessionId );
|
|
name.MaximumLength = asciiz_to_unicode( bufferW, buffer );
|
|
name.Length = name.MaximumLength - sizeof(WCHAR);
|
|
|
|
InitializeObjectAttributes( &attr, &name, OBJ_OPENIF, NULL, NULL );
|
|
if (NtCreateMutant( &mutex, MUTEX_ALL_ACCESS, &attr, FALSE ) < 0) return 0;
|
|
NtWaitForSingleObject( mutex, FALSE, NULL );
|
|
return mutex;
|
|
}
|
|
|
|
static void release_display_device_init_mutex( HANDLE mutex )
|
|
{
|
|
NtReleaseMutant( mutex, NULL );
|
|
NtClose( mutex );
|
|
}
|
|
|
|
static struct gpu *gpu_acquire( struct gpu *gpu )
|
|
{
|
|
InterlockedIncrement( &gpu->refcount );
|
|
return gpu;
|
|
}
|
|
|
|
static void gpu_release( struct gpu *gpu )
|
|
{
|
|
if (!InterlockedDecrement( &gpu->refcount ))
|
|
free( gpu );
|
|
}
|
|
|
|
static struct adapter *adapter_acquire( struct adapter *adapter )
|
|
{
|
|
InterlockedIncrement( &adapter->refcount );
|
|
return adapter;
|
|
}
|
|
|
|
static void adapter_release( struct adapter *adapter )
|
|
{
|
|
if (!InterlockedDecrement( &adapter->refcount ))
|
|
{
|
|
gpu_release( adapter->gpu );
|
|
free( adapter->modes );
|
|
free( adapter );
|
|
}
|
|
}
|
|
|
|
C_ASSERT(sizeof(DEVMODEW) - offsetof(DEVMODEW, dmFields) == 0x94);
|
|
|
|
static void get_monitor_info_from_edid( struct edid_monitor_info *info, const unsigned char *edid, unsigned int edid_len )
|
|
{
|
|
unsigned int i, j;
|
|
unsigned short w;
|
|
unsigned char d;
|
|
const char *s;
|
|
|
|
info->flags = 0;
|
|
if (!edid || edid_len < 128) return;
|
|
|
|
w = (edid[8] << 8) | edid[9]; /* Manufacturer ID, big endian. */
|
|
for (i = 0; i < 3; ++i)
|
|
{
|
|
d = w & 0x1f;
|
|
if (!d || d - 1 > 'Z' - 'A') return;
|
|
info->monitor_id_string[2 - i] = 'A' + d - 1;
|
|
w >>= 5;
|
|
}
|
|
if (w) return;
|
|
w = edid[10] | (edid[11] << 8); /* Product code, little endian. */
|
|
info->manufacturer = *(unsigned short *)(edid + 8);
|
|
info->product_code = w;
|
|
sprintf( info->monitor_id_string + 3, "%04X", w );
|
|
info->flags = MONITOR_INFO_HAS_MONITOR_ID;
|
|
TRACE( "Monitor id %s.\n", info->monitor_id_string );
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
{
|
|
if (edid[54 + i * 18] || edid[54 + i * 18 + 1])
|
|
{
|
|
/* Detailed timing descriptor. */
|
|
if (info->flags & MONITOR_INFO_HAS_PREFERRED_MODE) continue;
|
|
info->preferred_width = edid[54 + i * 18 + 2] | ((UINT32)(edid[54 + i * 18 + 4] & 0xf0) << 4);
|
|
info->preferred_height = edid[54 + i * 18 + 5] | ((UINT32)(edid[54 + i * 18 + 7] & 0xf0) << 4);
|
|
if (info->preferred_width && info->preferred_height)
|
|
info->flags |= MONITOR_INFO_HAS_PREFERRED_MODE;
|
|
continue;
|
|
}
|
|
if (edid[54 + i * 18 + 3] != 0xfc) continue;
|
|
/* "Display name" ASCII descriptor. */
|
|
s = (const char *)&edid[54 + i * 18 + 5];
|
|
for (j = 0; s[j] && j < 13; ++j)
|
|
info->monitor_name[j] = s[j];
|
|
while (j && isspace(s[j - 1])) --j;
|
|
info->monitor_name[j] = 0;
|
|
info->flags |= MONITOR_INFO_HAS_MONITOR_NAME;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static BOOL write_adapter_mode( HKEY adapter_key, UINT index, const DEVMODEW *mode )
|
|
{
|
|
WCHAR bufferW[MAX_PATH] = {0};
|
|
char buffer[MAX_PATH];
|
|
|
|
sprintf( buffer, "Modes\\%08X", index );
|
|
asciiz_to_unicode( bufferW, buffer );
|
|
return set_reg_value( adapter_key, bufferW, REG_BINARY, &mode->dmFields, sizeof(*mode) - offsetof(DEVMODEW, dmFields) );
|
|
}
|
|
|
|
static BOOL read_adapter_mode( HKEY adapter_key, UINT index, DEVMODEW *mode )
|
|
{
|
|
char value_buf[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(*mode)])];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)value_buf;
|
|
char buffer[MAX_PATH];
|
|
|
|
sprintf( buffer, "Modes\\%08X", index );
|
|
if (!query_reg_ascii_value( adapter_key, buffer, value, sizeof(value_buf) )) return FALSE;
|
|
|
|
memcpy( &mode->dmFields, value->Data, sizeof(*mode) - offsetof(DEVMODEW, dmFields) );
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL adapter_get_registry_settings( const struct adapter *adapter, DEVMODEW *mode )
|
|
{
|
|
BOOL ret = FALSE;
|
|
HANDLE mutex;
|
|
HKEY hkey;
|
|
|
|
mutex = get_display_device_init_mutex();
|
|
|
|
if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
|
|
else
|
|
{
|
|
ret = read_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode );
|
|
NtClose( hkey );
|
|
}
|
|
|
|
release_display_device_init_mutex( mutex );
|
|
return ret;
|
|
}
|
|
|
|
static BOOL adapter_set_registry_settings( const struct adapter *adapter, const DEVMODEW *mode )
|
|
{
|
|
HANDLE mutex;
|
|
HKEY hkey;
|
|
BOOL ret;
|
|
|
|
mutex = get_display_device_init_mutex();
|
|
|
|
if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
|
|
else
|
|
{
|
|
ret = write_adapter_mode( hkey, ENUM_REGISTRY_SETTINGS, mode );
|
|
NtClose( hkey );
|
|
}
|
|
|
|
release_display_device_init_mutex( mutex );
|
|
return ret;
|
|
}
|
|
|
|
static BOOL adapter_get_current_settings( const struct adapter *adapter, DEVMODEW *mode )
|
|
{
|
|
BOOL is_primary = !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
|
|
WCHAR device_nameW[CCHDEVICENAME];
|
|
char device_name[CCHDEVICENAME];
|
|
HANDLE mutex;
|
|
HKEY hkey;
|
|
BOOL ret;
|
|
|
|
sprintf( device_name, "\\\\.\\DISPLAY%d", adapter->id + 1 );
|
|
asciiz_to_unicode( device_nameW, device_name );
|
|
|
|
/* use the default implementation in virtual desktop mode */
|
|
if (is_virtual_desktop()) ret = FALSE;
|
|
else ret = user_driver->pGetCurrentDisplaySettings( device_nameW, is_primary, mode );
|
|
|
|
if (ret) return TRUE;
|
|
|
|
/* default implementation: read current display settings from the registry. */
|
|
|
|
mutex = get_display_device_init_mutex();
|
|
|
|
if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
|
|
else
|
|
{
|
|
ret = read_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
|
|
NtClose( hkey );
|
|
}
|
|
|
|
release_display_device_init_mutex( mutex );
|
|
return ret;
|
|
}
|
|
|
|
static BOOL adapter_set_current_settings( const struct adapter *adapter, const DEVMODEW *mode )
|
|
{
|
|
HANDLE mutex;
|
|
HKEY hkey;
|
|
BOOL ret;
|
|
|
|
mutex = get_display_device_init_mutex();
|
|
|
|
if (!(hkey = reg_open_key( config_key, adapter->config_key, lstrlenW( adapter->config_key ) * sizeof(WCHAR) ))) ret = FALSE;
|
|
else
|
|
{
|
|
ret = write_adapter_mode( hkey, ENUM_CURRENT_SETTINGS, mode );
|
|
NtClose( hkey );
|
|
}
|
|
|
|
release_display_device_init_mutex( mutex );
|
|
return ret;
|
|
}
|
|
|
|
static int mode_compare(const void *p1, const void *p2)
|
|
{
|
|
BOOL a_interlaced, b_interlaced, a_stretched, b_stretched;
|
|
DWORD a_width, a_height, b_width, b_height;
|
|
const DEVMODEW *a = p1, *b = p2;
|
|
int ret;
|
|
|
|
/* Depth in descending order */
|
|
if ((ret = b->dmBitsPerPel - a->dmBitsPerPel)) return ret;
|
|
|
|
/* Use the width and height in landscape mode for comparison */
|
|
if (a->dmDisplayOrientation == DMDO_DEFAULT || a->dmDisplayOrientation == DMDO_180)
|
|
{
|
|
a_width = a->dmPelsWidth;
|
|
a_height = a->dmPelsHeight;
|
|
}
|
|
else
|
|
{
|
|
a_width = a->dmPelsHeight;
|
|
a_height = a->dmPelsWidth;
|
|
}
|
|
|
|
if (b->dmDisplayOrientation == DMDO_DEFAULT || b->dmDisplayOrientation == DMDO_180)
|
|
{
|
|
b_width = b->dmPelsWidth;
|
|
b_height = b->dmPelsHeight;
|
|
}
|
|
else
|
|
{
|
|
b_width = b->dmPelsHeight;
|
|
b_height = b->dmPelsWidth;
|
|
}
|
|
|
|
/* Width in ascending order */
|
|
if ((ret = a_width - b_width)) return ret;
|
|
|
|
/* Height in ascending order */
|
|
if ((ret = a_height - b_height)) return ret;
|
|
|
|
/* Frequency in descending order */
|
|
if ((ret = b->dmDisplayFrequency - a->dmDisplayFrequency)) return ret;
|
|
|
|
/* Orientation in ascending order */
|
|
if ((ret = a->dmDisplayOrientation - b->dmDisplayOrientation)) return ret;
|
|
|
|
if (!(a->dmFields & DM_DISPLAYFLAGS)) a_interlaced = FALSE;
|
|
else a_interlaced = !!(a->dmDisplayFlags & DM_INTERLACED);
|
|
if (!(b->dmFields & DM_DISPLAYFLAGS)) b_interlaced = FALSE;
|
|
else b_interlaced = !!(b->dmDisplayFlags & DM_INTERLACED);
|
|
|
|
/* Interlaced in ascending order */
|
|
if ((ret = a_interlaced - b_interlaced)) return ret;
|
|
|
|
if (!(a->dmFields & DM_DISPLAYFIXEDOUTPUT)) a_stretched = FALSE;
|
|
else a_stretched = a->dmDisplayFixedOutput == DMDFO_STRETCH;
|
|
if (!(b->dmFields & DM_DISPLAYFIXEDOUTPUT)) b_stretched = FALSE;
|
|
else b_stretched = b->dmDisplayFixedOutput == DMDFO_STRETCH;
|
|
|
|
/* Stretched in ascending order */
|
|
if ((ret = a_stretched - b_stretched)) return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int query_reg_subkey_value( HKEY hkey, const char *name, KEY_VALUE_PARTIAL_INFORMATION *value, unsigned int size )
|
|
{
|
|
HKEY subkey;
|
|
|
|
if (!(subkey = reg_open_ascii_key( hkey, name ))) return 0;
|
|
size = query_reg_value( subkey, NULL, value, size );
|
|
NtClose( subkey );
|
|
return size;
|
|
}
|
|
|
|
static BOOL read_display_adapter_settings( unsigned int index, struct adapter *info, char *gpu_path )
|
|
{
|
|
char buffer[4096];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
WCHAR *value_str = (WCHAR *)value->Data;
|
|
DEVMODEW *mode;
|
|
DWORD i, size;
|
|
HKEY hkey;
|
|
|
|
if (!enum_key && !(enum_key = reg_open_ascii_key( NULL, enum_keyA )))
|
|
return FALSE;
|
|
|
|
/* Find adapter */
|
|
sprintf( buffer, "\\Device\\Video%d", index );
|
|
size = query_reg_ascii_value( video_key, buffer, value, sizeof(buffer) );
|
|
if (!size || value->Type != REG_SZ ||
|
|
value->DataLength <= sizeof("\\Registry\\Machine\\") * sizeof(WCHAR))
|
|
return FALSE;
|
|
|
|
/* DeviceKey */
|
|
size = sizeof("\\Registry\\Machine\\") - 1;
|
|
memcpy( info->config_key, value_str + size, value->DataLength - size );
|
|
|
|
if (!(hkey = reg_open_key( config_key, info->config_key,
|
|
lstrlenW( info->config_key ) * sizeof(WCHAR) )))
|
|
return FALSE;
|
|
|
|
/* StateFlags */
|
|
if (query_reg_ascii_value( hkey, "StateFlags", value, sizeof(buffer) ) && value->Type == REG_DWORD)
|
|
info->state_flags = *(const DWORD *)value->Data;
|
|
|
|
/* ModeCount */
|
|
if (query_reg_ascii_value( hkey, "ModeCount", value, sizeof(buffer) ) && value->Type == REG_DWORD)
|
|
info->mode_count = *(const DWORD *)value->Data;
|
|
|
|
/* Modes, allocate an extra mode for easier iteration */
|
|
if ((info->modes = calloc( info->mode_count + 1, sizeof(DEVMODEW) )))
|
|
{
|
|
for (i = 0, mode = info->modes; i < info->mode_count; i++)
|
|
{
|
|
mode->dmSize = offsetof(DEVMODEW, dmICMMethod);
|
|
if (!read_adapter_mode( hkey, i, mode )) break;
|
|
mode = NEXT_DEVMODEW(mode);
|
|
}
|
|
info->mode_count = i;
|
|
|
|
qsort(info->modes, info->mode_count, sizeof(*info->modes) + info->modes->dmDriverExtra, mode_compare);
|
|
}
|
|
|
|
/* DeviceID */
|
|
size = query_reg_ascii_value( hkey, "GPUID", value, sizeof(buffer) );
|
|
NtClose( hkey );
|
|
if (!size || value->Type != REG_SZ || !info->mode_count || !info->modes) return FALSE;
|
|
|
|
for (i = 0; i < value->DataLength / sizeof(WCHAR); i++) gpu_path[i] = value_str[i];
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL read_monitor_from_registry( struct monitor *monitor )
|
|
{
|
|
char buffer[4096];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
HKEY hkey, subkey;
|
|
DWORD size;
|
|
|
|
if (!(hkey = reg_open_ascii_key( enum_key, monitor->path ))) return FALSE;
|
|
|
|
/* Output ID */
|
|
size = query_reg_subkey_value( hkey, devpropkey_monitor_output_idA,
|
|
value, sizeof(buffer) );
|
|
if (size != sizeof(monitor->output_id))
|
|
{
|
|
NtClose( hkey );
|
|
return FALSE;
|
|
}
|
|
monitor->output_id = *(const unsigned int *)value->Data;
|
|
|
|
/* rc_monitor, WINE_DEVPROPKEY_MONITOR_RCMONITOR */
|
|
size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcmonitorA,
|
|
value, sizeof(buffer) );
|
|
if (size != sizeof(monitor->rc_monitor))
|
|
{
|
|
NtClose( hkey );
|
|
return FALSE;
|
|
}
|
|
monitor->rc_monitor = *(const RECT *)value->Data;
|
|
|
|
/* rc_work, WINE_DEVPROPKEY_MONITOR_RCWORK */
|
|
size = query_reg_subkey_value( hkey, wine_devpropkey_monitor_rcworkA,
|
|
value, sizeof(buffer) );
|
|
if (size != sizeof(monitor->rc_work))
|
|
{
|
|
NtClose( hkey );
|
|
return FALSE;
|
|
}
|
|
monitor->rc_work = *(const RECT *)value->Data;
|
|
|
|
/* EDID */
|
|
if ((subkey = reg_open_ascii_key( hkey, "Device Parameters" )))
|
|
{
|
|
if (query_reg_ascii_value( subkey, "EDID", value, sizeof(buffer) ))
|
|
get_monitor_info_from_edid( &monitor->edid_info, value->Data, value->DataLength );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
NtClose( hkey );
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL read_adapter_monitor_path( HKEY hkey, UINT index, char *path )
|
|
{
|
|
char buffer[4096];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
WCHAR *value_str = (WCHAR *)value->Data;
|
|
DWORD size;
|
|
UINT i;
|
|
|
|
sprintf( buffer, "MonitorID%u", index );
|
|
size = query_reg_ascii_value( hkey, buffer, value, sizeof(buffer) );
|
|
if (!size || value->Type != REG_SZ) return FALSE;
|
|
|
|
for (i = 0; i < value->DataLength / sizeof(WCHAR); i++) path[i] = value_str[i];
|
|
return TRUE;
|
|
}
|
|
|
|
static void reg_empty_key( HKEY root, const char *key_name )
|
|
{
|
|
char buffer[4096];
|
|
KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer;
|
|
KEY_VALUE_FULL_INFORMATION *value = (KEY_VALUE_FULL_INFORMATION *)buffer;
|
|
DWORD size;
|
|
HKEY hkey = key_name ? reg_open_ascii_key( root, key_name ) : root;
|
|
|
|
while (!NtEnumerateKey( hkey, 0, KeyNodeInformation, key, sizeof(buffer), &size ))
|
|
reg_delete_tree( hkey, key->Name, key->NameLength );
|
|
|
|
while (!NtEnumerateValueKey( hkey, 0, KeyValueFullInformation, value, sizeof(buffer), &size ))
|
|
{
|
|
UNICODE_STRING name = { value->NameLength, value->NameLength, value->Name };
|
|
NtDeleteValueKey( hkey, &name );
|
|
}
|
|
|
|
if (hkey != root) NtClose( hkey );
|
|
}
|
|
|
|
static void prepare_devices(void)
|
|
{
|
|
char buffer[4096];
|
|
KEY_NODE_INFORMATION *key = (void *)buffer;
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
WCHAR *value_str = (WCHAR *)value->Data;
|
|
unsigned i = 0;
|
|
DWORD size;
|
|
HKEY hkey, subkey, device_key, prop_key;
|
|
|
|
if (!enum_key) enum_key = reg_create_ascii_key( NULL, enum_keyA, 0, NULL );
|
|
if (!control_key) control_key = reg_create_ascii_key( NULL, control_keyA, 0, NULL );
|
|
if (!video_key) video_key = reg_create_ascii_key( NULL, devicemap_video_keyA, REG_OPTION_VOLATILE, NULL );
|
|
|
|
/* delete monitors */
|
|
reg_empty_key( enum_key, "DISPLAY" );
|
|
sprintf( buffer, "Class\\%s", guid_devclass_monitorA );
|
|
hkey = reg_create_ascii_key( control_key, buffer, 0, NULL );
|
|
reg_empty_key( hkey, NULL );
|
|
set_reg_ascii_value( hkey, "Class", "Monitor" );
|
|
NtClose( hkey );
|
|
|
|
/* delete adapters */
|
|
reg_empty_key( video_key, NULL );
|
|
|
|
/* clean GPUs */
|
|
sprintf( buffer, "Class\\%s", guid_devclass_displayA );
|
|
hkey = reg_create_ascii_key( control_key, buffer, 0, NULL );
|
|
reg_empty_key( hkey, NULL );
|
|
set_reg_ascii_value( hkey, "Class", "Display" );
|
|
NtClose( hkey );
|
|
|
|
hkey = reg_open_ascii_key( enum_key, "PCI" );
|
|
|
|
/* To preserve GPU GUIDs, mark them as not present and delete them in cleanup_devices if needed. */
|
|
while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
|
|
{
|
|
unsigned int j = 0;
|
|
|
|
if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
|
|
|
|
while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
|
|
{
|
|
if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
|
|
size = query_reg_ascii_value( device_key, "ClassGUID", value, sizeof(buffer) );
|
|
if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
|
|
{
|
|
NtClose( device_key );
|
|
continue;
|
|
}
|
|
|
|
if ((prop_key = reg_create_ascii_key( device_key, devpropkey_device_ispresentA, 0, NULL )))
|
|
{
|
|
BOOL present = FALSE;
|
|
set_reg_value( prop_key, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
|
|
&present, sizeof(present) );
|
|
NtClose( prop_key );
|
|
}
|
|
|
|
NtClose( device_key );
|
|
}
|
|
|
|
NtClose( subkey );
|
|
}
|
|
|
|
NtClose( hkey );
|
|
}
|
|
|
|
static void cleanup_devices(void)
|
|
{
|
|
char buffer[4096];
|
|
KEY_NODE_INFORMATION *key = (void *)buffer;
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
WCHAR bufferW[512], *value_str = (WCHAR *)value->Data;
|
|
unsigned i = 0;
|
|
DWORD size;
|
|
HKEY hkey, subkey, device_key, prop_key;
|
|
|
|
hkey = reg_open_ascii_key( enum_key, "PCI" );
|
|
|
|
while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key, sizeof(buffer), &size ))
|
|
{
|
|
unsigned int j = 0;
|
|
|
|
if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
|
|
|
|
while (!NtEnumerateKey( subkey, j++, KeyNodeInformation, key, sizeof(buffer), &size ))
|
|
{
|
|
BOOL present = FALSE;
|
|
|
|
if (!(device_key = reg_open_key( subkey, key->Name, key->NameLength ))) continue;
|
|
memcpy( bufferW, key->Name, key->NameLength );
|
|
bufferW[key->NameLength / sizeof(WCHAR)] = 0;
|
|
|
|
size = query_reg_ascii_value( device_key, "ClassGUID", value, sizeof(buffer) );
|
|
if (size != sizeof(guid_devclass_displayW) || wcscmp( value_str, guid_devclass_displayW ))
|
|
{
|
|
NtClose( device_key );
|
|
continue;
|
|
}
|
|
|
|
if ((prop_key = reg_open_ascii_key( device_key, devpropkey_device_ispresentA )))
|
|
{
|
|
if (query_reg_value( prop_key, NULL, value, sizeof(buffer) ) == sizeof(BOOL))
|
|
present = *(const BOOL *)value->Data;
|
|
NtClose( prop_key );
|
|
}
|
|
|
|
NtClose( device_key );
|
|
|
|
if (!present && reg_delete_tree( subkey, bufferW, lstrlenW( bufferW ) * sizeof(WCHAR) ))
|
|
j = 0;
|
|
}
|
|
|
|
NtClose( subkey );
|
|
}
|
|
|
|
NtClose( hkey );
|
|
}
|
|
|
|
/* see UuidCreate */
|
|
static void uuid_create( GUID *uuid )
|
|
{
|
|
char buf[4096];
|
|
NtQuerySystemInformation( SystemInterruptInformation, buf, sizeof(buf), NULL );
|
|
memcpy( uuid, buf, sizeof(*uuid) );
|
|
uuid->Data3 &= 0x0fff;
|
|
uuid->Data3 |= (4 << 12);
|
|
uuid->Data4[0] &= 0x3f;
|
|
uuid->Data4[0] |= 0x80;
|
|
}
|
|
|
|
#define TICKSPERSEC 10000000
|
|
#define SECSPERDAY 86400
|
|
#define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
|
|
#define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
|
|
|
|
static unsigned int format_date( WCHAR *bufferW, LONGLONG time )
|
|
{
|
|
int cleaps, years, yearday, months, days;
|
|
unsigned int day, month, year;
|
|
char buffer[32];
|
|
|
|
days = time / TICKSPERSEC / SECSPERDAY;
|
|
|
|
/* compute year, month and day of month, see RtlTimeToTimeFields */
|
|
cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
|
|
days += 28188 + cleaps;
|
|
years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
|
|
yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
|
|
months = (64 * yearday) / 1959;
|
|
if (months < 14)
|
|
{
|
|
month = months - 1;
|
|
year = years + 1524;
|
|
}
|
|
else
|
|
{
|
|
month = months - 13;
|
|
year = years + 1525;
|
|
}
|
|
day = yearday - (1959 * months) / 64 ;
|
|
|
|
sprintf( buffer, "%u-%u-%u", month, day, year );
|
|
return asciiz_to_unicode( bufferW, buffer );
|
|
}
|
|
|
|
struct device_manager_ctx
|
|
{
|
|
UINT gpu_count;
|
|
UINT adapter_count;
|
|
UINT monitor_count;
|
|
HANDLE mutex;
|
|
struct gpu gpu;
|
|
struct adapter adapter;
|
|
HKEY adapter_key;
|
|
/* for the virtual desktop settings */
|
|
BOOL is_primary;
|
|
UINT primary_bpp;
|
|
UINT primary_width;
|
|
UINT primary_height;
|
|
};
|
|
|
|
static void link_device( const char *instance, const char *class )
|
|
{
|
|
char buffer[MAX_PATH], *ptr;
|
|
HKEY hkey, subkey;
|
|
unsigned int pos;
|
|
|
|
pos = snprintf( buffer, ARRAY_SIZE(buffer), "DeviceClasses\\%s\\", class );
|
|
snprintf( buffer + pos, ARRAY_SIZE(buffer) - pos, "##?#%s#%s", instance, class );
|
|
for (ptr = buffer + pos; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
|
|
|
|
hkey = reg_create_ascii_key( control_key, buffer, 0, NULL );
|
|
set_reg_ascii_value( hkey, "DeviceInstance", instance );
|
|
|
|
subkey = reg_create_ascii_key( hkey, "#", REG_OPTION_VOLATILE, NULL );
|
|
NtClose( hkey );
|
|
hkey = subkey;
|
|
|
|
snprintf( buffer, ARRAY_SIZE(buffer), "\\\\?\\%s#%s", instance, class );
|
|
for (ptr = buffer + 4; *ptr; ptr++) if (*ptr == '\\') *ptr = '#';
|
|
set_reg_ascii_value( hkey, "SymbolicLink", buffer );
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, "Control", REG_OPTION_VOLATILE, NULL )))
|
|
{
|
|
const DWORD linked = 1;
|
|
set_reg_value( subkey, linkedW, REG_DWORD, &linked, sizeof(linked) );
|
|
NtClose( subkey );
|
|
}
|
|
}
|
|
|
|
static BOOL read_gpu_from_registry( struct gpu *gpu )
|
|
{
|
|
char buffer[1024];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
WCHAR *value_str = (WCHAR *)value->Data;
|
|
HKEY hkey, subkey;
|
|
unsigned int i;
|
|
|
|
if (!(hkey = reg_open_ascii_key( enum_key, gpu->path ))) return FALSE;
|
|
|
|
if (query_reg_ascii_value( hkey, "Driver", value, sizeof(buffer) ) && value->Type == REG_SZ)
|
|
gpu->index = wcstoul( wcsrchr( value_str, '\\' ) + 1, NULL, 16 );
|
|
|
|
if (query_reg_ascii_value( hkey, "DeviceDesc", value, sizeof(buffer) ) && value->Type == REG_SZ)
|
|
memcpy( gpu->name, value->Data, value->DataLength );
|
|
|
|
if ((subkey = reg_open_ascii_key( hkey, devpropkey_gpu_luidA )))
|
|
{
|
|
if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) == sizeof(LUID))
|
|
gpu->luid = *(const LUID *)value->Data;
|
|
NtClose( subkey );
|
|
}
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, "Device Parameters", 0, NULL )))
|
|
{
|
|
if (query_reg_ascii_value( subkey, "VideoID", value, sizeof(buffer) ) == sizeof(gpu->guid) * sizeof(WCHAR))
|
|
{
|
|
WCHAR *guidW = (WCHAR *)value->Data;
|
|
for (i = 0; i < sizeof(gpu->guid); i++) gpu->guid[i] = guidW[i];
|
|
TRACE( "got guid %s\n", debugstr_a(gpu->guid) );
|
|
}
|
|
NtClose( subkey );
|
|
}
|
|
|
|
if ((subkey = reg_open_ascii_key( hkey, devpropkey_gpu_vulkan_uuidA )))
|
|
{
|
|
if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) == sizeof(GUID))
|
|
gpu->vulkan_uuid = *(const GUID *)value->Data;
|
|
NtClose( subkey );
|
|
}
|
|
|
|
NtClose( hkey );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL write_gpu_to_registry( const struct gpu *gpu, const struct pci_id *pci,
|
|
ULONGLONG memory_size )
|
|
{
|
|
const WCHAR *desc;
|
|
char buffer[4096], *tmp;
|
|
WCHAR bufferW[512];
|
|
unsigned int size;
|
|
HKEY subkey;
|
|
LARGE_INTEGER ft;
|
|
ULONG value;
|
|
HKEY hkey;
|
|
|
|
static const BOOL present = TRUE;
|
|
static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0};
|
|
static const WCHAR driver_date_dataW[] =
|
|
{'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0};
|
|
static const WCHAR adapter_stringW[] =
|
|
{'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n',
|
|
'.','A','d','a','p','t','e','r','S','t','r','i','n','g',0};
|
|
static const WCHAR bios_stringW[] =
|
|
{'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
|
|
'B','i','o','s','S','t','r','i','n','g',0};
|
|
static const WCHAR chip_typeW[] =
|
|
{'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
|
|
'C','h','i','p','T','y','p','e',0};
|
|
static const WCHAR qw_memory_sizeW[] =
|
|
{'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
|
|
'q','w','M','e','m','o','r','y','S','i','z','e',0};
|
|
static const WCHAR memory_sizeW[] =
|
|
{'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
|
|
'M','e','m','o','r','y','S','i','z','e',0};
|
|
static const WCHAR dac_typeW[] =
|
|
{'H','a','r','d','w','a','r','e','I','n','f','o','r','m','a','t','i','o','n','.',
|
|
'D','a','c','T','y','p','e',0};
|
|
static const WCHAR ramdacW[] =
|
|
{'I','n','t','e','r','g','r','a','t','e','d',' ','R','A','M','D','A','C',0};
|
|
static const WCHAR driver_dateW[] = {'D','r','i','v','e','r','D','a','t','e',0};
|
|
|
|
|
|
if (!(hkey = reg_create_ascii_key( enum_key, gpu->path, 0, NULL ))) return FALSE;
|
|
|
|
set_reg_ascii_value( hkey, "Class", "Display" );
|
|
set_reg_ascii_value( hkey, "ClassGUID", guid_devclass_displayA );
|
|
sprintf( buffer, "%s\\%04X", guid_devclass_displayA, gpu->index );
|
|
set_reg_ascii_value( hkey, "Driver", buffer );
|
|
|
|
strcpy( buffer, gpu->path );
|
|
if ((tmp = strrchr( buffer, '\\' ))) *tmp = 0;
|
|
size = asciiz_to_unicode( bufferW, buffer );
|
|
bufferW[size / sizeof(WCHAR)] = 0; /* for REG_MULTI_SZ */
|
|
set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, size + sizeof(WCHAR) );
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, devpkey_device_matching_device_id, 0, NULL )))
|
|
{
|
|
if (pci->vendor && pci->device)
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW, size );
|
|
else
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_STRING, bufferW,
|
|
asciiz_to_unicode( bufferW, "ROOT\\BasicRender" ));
|
|
NtClose( subkey );
|
|
}
|
|
|
|
if (pci->vendor && pci->device)
|
|
{
|
|
if ((subkey = reg_create_ascii_key( hkey, devpkey_device_bus_number, 0, NULL )))
|
|
{
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
|
|
&gpu->index, sizeof(gpu->index) );
|
|
NtClose( subkey );
|
|
}
|
|
}
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, devpkey_device_removal_policy, 0, NULL )))
|
|
{
|
|
unsigned int removal_policy = CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL;
|
|
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
|
|
&removal_policy, sizeof(removal_policy) );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
desc = gpu->name;
|
|
if (!desc[0]) desc = wine_adapterW;
|
|
set_reg_value( hkey, device_descW, REG_SZ, desc, (lstrlenW( desc ) + 1) * sizeof(WCHAR) );
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, "Device Parameters", 0, NULL )))
|
|
{
|
|
set_reg_ascii_value( subkey, "VideoID", gpu->guid );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, devpropkey_gpu_vulkan_uuidA, 0, NULL )))
|
|
{
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_GUID,
|
|
&gpu->vulkan_uuid, sizeof(gpu->vulkan_uuid) );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, devpropkey_device_ispresentA, 0, NULL )))
|
|
{
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BOOLEAN,
|
|
&present, sizeof(present) );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, devpropkey_gpu_luidA, 0, NULL )))
|
|
{
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT64,
|
|
&gpu->luid, sizeof(gpu->luid) );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
NtClose( hkey );
|
|
|
|
|
|
sprintf( buffer, "Class\\%s\\%04X", guid_devclass_displayA, gpu->index );
|
|
if (!(hkey = reg_create_ascii_key( control_key, buffer, 0, NULL ))) return FALSE;
|
|
|
|
NtQuerySystemTime( &ft );
|
|
set_reg_value( hkey, driver_dateW, REG_SZ, bufferW, format_date( bufferW, ft.QuadPart ));
|
|
|
|
set_reg_value( hkey, driver_date_dataW, REG_BINARY, &ft, sizeof(ft) );
|
|
|
|
size = (lstrlenW( desc ) + 1) * sizeof(WCHAR);
|
|
set_reg_value( hkey, driver_descW, REG_SZ, desc, size );
|
|
set_reg_value( hkey, adapter_stringW, REG_SZ, desc, size );
|
|
set_reg_value( hkey, bios_stringW, REG_SZ, desc, size );
|
|
set_reg_value( hkey, chip_typeW, REG_SZ, desc, size );
|
|
set_reg_value( hkey, dac_typeW, REG_SZ, ramdacW, sizeof(ramdacW) );
|
|
|
|
/* If we failed to retrieve the gpu memory size set a default of 1Gb */
|
|
if (!memory_size) memory_size = 1073741824;
|
|
|
|
set_reg_value( hkey, qw_memory_sizeW, REG_QWORD, &memory_size, sizeof(memory_size) );
|
|
value = (ULONG)min( memory_size, (ULONGLONG)ULONG_MAX );
|
|
set_reg_value( hkey, memory_sizeW, REG_DWORD, &value, sizeof(value) );
|
|
|
|
if (pci->vendor && pci->device)
|
|
{
|
|
/* The last seven digits are the driver number. */
|
|
switch (pci->vendor)
|
|
{
|
|
/* Intel */
|
|
case 0x8086:
|
|
sprintf( buffer, "31.0.101.4576" );
|
|
break;
|
|
/* AMD */
|
|
case 0x1002:
|
|
sprintf( buffer, "31.0.14051.5006" );
|
|
break;
|
|
/* Nvidia */
|
|
case 0x10de:
|
|
sprintf( buffer, "31.0.15.3625" );
|
|
break;
|
|
/* Default value for any other vendor. */
|
|
default:
|
|
sprintf( buffer, "31.0.10.1000" );
|
|
break;
|
|
}
|
|
set_reg_ascii_value( hkey, "DriverVersion", buffer );
|
|
}
|
|
|
|
NtClose( hkey );
|
|
|
|
|
|
link_device( gpu->path, guid_devinterface_display_adapterA );
|
|
link_device( gpu->path, guid_display_device_arrivalA );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void add_gpu( const struct gdi_gpu *gpu, void *param )
|
|
{
|
|
const struct pci_id pci_id =
|
|
{
|
|
.vendor = gpu->vendor_id,
|
|
.device = gpu->device_id,
|
|
.subsystem = gpu->subsys_id,
|
|
.revision = gpu->revision_id,
|
|
};
|
|
struct device_manager_ctx *ctx = param;
|
|
char buffer[4096];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
unsigned int i;
|
|
HKEY hkey, subkey;
|
|
|
|
TRACE( "%s %04X %04X %08X %02X\n", debugstr_w(gpu->name),
|
|
gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id );
|
|
|
|
if (!enum_key && !(enum_key = reg_create_ascii_key( NULL, enum_keyA, 0, NULL )))
|
|
return;
|
|
|
|
if (!ctx->mutex)
|
|
{
|
|
pthread_mutex_lock( &display_lock );
|
|
ctx->mutex = get_display_device_init_mutex();
|
|
prepare_devices();
|
|
}
|
|
|
|
memset( &ctx->gpu, 0, sizeof(ctx->gpu) );
|
|
ctx->gpu.index = ctx->gpu_count;
|
|
lstrcpyW( ctx->gpu.name, gpu->name );
|
|
ctx->gpu.vulkan_uuid = gpu->vulkan_uuid;
|
|
|
|
sprintf( ctx->gpu.path, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X\\%08X",
|
|
gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, ctx->gpu.index );
|
|
if (!(hkey = reg_create_ascii_key( enum_key, ctx->gpu.path, 0, NULL ))) return;
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, "Device Parameters", 0, NULL )))
|
|
{
|
|
if (query_reg_ascii_value( subkey, "VideoID", value, sizeof(buffer) ) != sizeof(ctx->gpu.guid) * sizeof(WCHAR))
|
|
{
|
|
GUID guid;
|
|
uuid_create( &guid );
|
|
sprintf( ctx->gpu.guid, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
|
(unsigned int)guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
|
|
guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] );
|
|
TRACE( "created guid %s\n", debugstr_a(ctx->gpu.guid) );
|
|
}
|
|
else
|
|
{
|
|
WCHAR *guidW = (WCHAR *)value->Data;
|
|
for (i = 0; i < sizeof(ctx->gpu.guid); i++) ctx->gpu.guid[i] = guidW[i];
|
|
TRACE( "got guid %s\n", debugstr_a(ctx->gpu.guid) );
|
|
}
|
|
NtClose( subkey );
|
|
}
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, devpropkey_gpu_luidA, 0, NULL )))
|
|
{
|
|
if (query_reg_value( subkey, NULL, value, sizeof(buffer) ) != sizeof(LUID))
|
|
{
|
|
NtAllocateLocallyUniqueId( &ctx->gpu.luid );
|
|
TRACE("allocated luid %08x%08x\n", (int)ctx->gpu.luid.HighPart, (int)ctx->gpu.luid.LowPart );
|
|
}
|
|
else
|
|
{
|
|
memcpy( &ctx->gpu.luid, value->Data, sizeof(ctx->gpu.luid) );
|
|
TRACE("got luid %08x%08x\n", (int)ctx->gpu.luid.HighPart, (int)ctx->gpu.luid.LowPart );
|
|
}
|
|
NtClose( subkey );
|
|
}
|
|
|
|
NtClose( hkey );
|
|
|
|
if (!write_gpu_to_registry( &ctx->gpu, &pci_id, gpu->memory_size ))
|
|
WARN( "Failed to write gpu to registry\n" );
|
|
else
|
|
ctx->gpu_count++;
|
|
}
|
|
|
|
static void add_adapter( const struct gdi_adapter *adapter, void *param )
|
|
{
|
|
struct device_manager_ctx *ctx = param;
|
|
unsigned int adapter_index, len;
|
|
char name[64], buffer[MAX_PATH];
|
|
WCHAR bufferW[MAX_PATH];
|
|
HKEY hkey;
|
|
|
|
TRACE( "\n" );
|
|
|
|
if (ctx->adapter_key)
|
|
{
|
|
NtClose( ctx->adapter_key );
|
|
ctx->adapter_key = NULL;
|
|
}
|
|
|
|
adapter_index = ctx->gpu.adapter_count++;
|
|
|
|
memset( &ctx->adapter, 0, sizeof(ctx->adapter) );
|
|
ctx->adapter.gpu = &ctx->gpu;
|
|
ctx->adapter.id = ctx->adapter_count++;
|
|
|
|
snprintf( buffer, ARRAY_SIZE(buffer), "%s\\Video\\%s\\%04x", control_keyA, ctx->gpu.guid, adapter_index );
|
|
len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR);
|
|
|
|
hkey = reg_create_ascii_key( NULL, buffer, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, NULL );
|
|
if (!hkey) hkey = reg_create_ascii_key( NULL, buffer, REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK, NULL );
|
|
|
|
sprintf( name, "\\Device\\Video%u", ctx->adapter.id );
|
|
set_reg_ascii_value( video_key, name, buffer );
|
|
|
|
if (hkey)
|
|
{
|
|
sprintf( buffer, "%s\\Class\\%s\\%04X", control_keyA, guid_devclass_displayA, ctx->gpu.index );
|
|
len = asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR);
|
|
set_reg_value( hkey, symbolic_link_valueW, REG_LINK, bufferW, len );
|
|
NtClose( hkey );
|
|
}
|
|
else ERR( "failed to create link key\n" );
|
|
|
|
/* Following information is Wine specific, it doesn't really exist on Windows. */
|
|
snprintf( buffer, ARRAY_SIZE(buffer), "System\\CurrentControlSet\\Control\\Video\\%s\\%04x", ctx->gpu.guid, adapter_index );
|
|
ctx->adapter_key = reg_create_ascii_key( config_key, buffer, REG_OPTION_VOLATILE, NULL );
|
|
|
|
set_reg_ascii_value( ctx->adapter_key, "GPUID", ctx->gpu.path );
|
|
set_reg_value( ctx->adapter_key, state_flagsW, REG_DWORD, &adapter->state_flags,
|
|
sizeof(adapter->state_flags) );
|
|
}
|
|
|
|
static BOOL write_monitor_to_registry( struct monitor *monitor, const BYTE *edid, UINT edid_len )
|
|
{
|
|
char buffer[1024], *tmp;
|
|
WCHAR bufferW[1024];
|
|
HKEY hkey, subkey;
|
|
unsigned int len;
|
|
|
|
if (!(hkey = reg_create_ascii_key( enum_key, monitor->path, 0, NULL ))) return FALSE;
|
|
|
|
set_reg_ascii_value( hkey, "DeviceDesc", "Generic Non-PnP Monitor" );
|
|
|
|
set_reg_ascii_value( hkey, "Class", "Monitor" );
|
|
sprintf( buffer, "%s\\%04X", guid_devclass_monitorA, monitor->output_id );
|
|
set_reg_ascii_value( hkey, "Driver", buffer );
|
|
set_reg_ascii_value( hkey, "ClassGUID", guid_devclass_monitorA );
|
|
|
|
sprintf( buffer, "MONITOR\\%s", monitor->path + 8 );
|
|
if ((tmp = strrchr( buffer, '\\' ))) *tmp = 0;
|
|
len = asciiz_to_unicode( bufferW, buffer );
|
|
bufferW[len / sizeof(WCHAR)] = 0;
|
|
set_reg_value( hkey, hardware_idW, REG_MULTI_SZ, bufferW, len + sizeof(WCHAR) );
|
|
|
|
if ((subkey = reg_create_ascii_key( hkey, "Device Parameters", 0, NULL )))
|
|
{
|
|
static const WCHAR bad_edidW[] = {'B','A','D','_','E','D','I','D',0};
|
|
static const WCHAR edidW[] = {'E','D','I','D',0};
|
|
|
|
if (edid_len)
|
|
set_reg_value( subkey, edidW, REG_BINARY, edid, edid_len );
|
|
else
|
|
set_reg_value( subkey, bad_edidW, REG_BINARY, NULL, 0 );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
/* WINE_DEVPROPKEY_MONITOR_RCMONITOR */
|
|
if ((subkey = reg_create_ascii_key( hkey, wine_devpropkey_monitor_rcmonitorA, 0, NULL )))
|
|
{
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_monitor,
|
|
sizeof(monitor->rc_monitor) );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
/* WINE_DEVPROPKEY_MONITOR_RCWORK */
|
|
if ((subkey = reg_create_ascii_key( hkey, wine_devpropkey_monitor_rcworkA, 0, NULL )))
|
|
{
|
|
TRACE( "rc_work %s\n", wine_dbgstr_rect(&monitor->rc_work) );
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_BINARY, &monitor->rc_work,
|
|
sizeof(monitor->rc_work) );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
/* DEVPROPKEY_MONITOR_GPU_LUID */
|
|
if ((subkey = reg_create_ascii_key( hkey, devpropkey_monitor_gpu_luidA, 0, NULL )))
|
|
{
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_INT64,
|
|
&monitor->adapter->gpu->luid, sizeof(monitor->adapter->gpu->luid) );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
/* DEVPROPKEY_MONITOR_OUTPUT_ID */
|
|
if ((subkey = reg_create_ascii_key( hkey, devpropkey_monitor_output_idA, 0, NULL )))
|
|
{
|
|
set_reg_value( subkey, NULL, 0xffff0000 | DEVPROP_TYPE_UINT32,
|
|
&monitor->output_id, sizeof(monitor->output_id) );
|
|
NtClose( subkey );
|
|
}
|
|
|
|
NtClose( hkey );
|
|
|
|
|
|
sprintf( buffer, "Class\\%s\\%04X", guid_devclass_monitorA, monitor->output_id );
|
|
if (!(hkey = reg_create_ascii_key( control_key, buffer, 0, NULL ))) return FALSE;
|
|
NtClose( hkey );
|
|
|
|
link_device( monitor->path, guid_devinterface_monitorA );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void add_monitor( const struct gdi_monitor *gdi_monitor, void *param )
|
|
{
|
|
struct device_manager_ctx *ctx = param;
|
|
struct monitor monitor = {0};
|
|
char buffer[MAX_PATH];
|
|
char monitor_id_string[16];
|
|
|
|
monitor.adapter = &ctx->adapter;
|
|
monitor.id = ctx->adapter.monitor_count;
|
|
monitor.output_id = ctx->monitor_count;
|
|
monitor.rc_monitor = gdi_monitor->rc_monitor;
|
|
monitor.rc_work = gdi_monitor->rc_work;
|
|
|
|
TRACE( "%u %s %s\n", monitor.id, wine_dbgstr_rect(&gdi_monitor->rc_monitor), wine_dbgstr_rect(&gdi_monitor->rc_work) );
|
|
|
|
get_monitor_info_from_edid( &monitor.edid_info, gdi_monitor->edid, gdi_monitor->edid_len );
|
|
if (monitor.edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
|
|
strcpy( monitor_id_string, monitor.edid_info.monitor_id_string );
|
|
else
|
|
strcpy( monitor_id_string, "Default_Monitor" );
|
|
|
|
sprintf( buffer, "MonitorID%u", monitor.id );
|
|
sprintf( monitor.path, "DISPLAY\\%s\\%04X&%04X", monitor_id_string, ctx->adapter.id, monitor.id );
|
|
set_reg_ascii_value( ctx->adapter_key, buffer, monitor.path );
|
|
|
|
if (!write_monitor_to_registry( &monitor, gdi_monitor->edid, gdi_monitor->edid_len ))
|
|
WARN( "Failed to write monitor to registry\n" );
|
|
else
|
|
{
|
|
ctx->adapter.monitor_count++;
|
|
ctx->monitor_count++;
|
|
}
|
|
}
|
|
|
|
static void add_mode( const DEVMODEW *mode, BOOL current, void *param )
|
|
{
|
|
struct device_manager_ctx *ctx = param;
|
|
DEVMODEW nopos_mode;
|
|
|
|
if (!ctx->gpu.adapter_count)
|
|
{
|
|
static const struct gdi_adapter default_adapter =
|
|
{
|
|
.state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
|
|
};
|
|
TRACE( "adding default fake adapter\n" );
|
|
add_adapter( &default_adapter, ctx );
|
|
}
|
|
|
|
nopos_mode = *mode;
|
|
nopos_mode.dmPosition.x = 0;
|
|
nopos_mode.dmPosition.y = 0;
|
|
nopos_mode.dmFields &= ~DM_POSITION;
|
|
|
|
if (write_adapter_mode( ctx->adapter_key, ctx->adapter.mode_count, &nopos_mode ))
|
|
{
|
|
ctx->adapter.mode_count++;
|
|
set_reg_value( ctx->adapter_key, mode_countW, REG_DWORD, &ctx->adapter.mode_count, sizeof(ctx->adapter.mode_count) );
|
|
if (current)
|
|
{
|
|
if (!read_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, &nopos_mode ))
|
|
write_adapter_mode( ctx->adapter_key, ENUM_REGISTRY_SETTINGS, mode );
|
|
write_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, mode );
|
|
}
|
|
}
|
|
}
|
|
|
|
static const struct gdi_device_manager device_manager =
|
|
{
|
|
add_gpu,
|
|
add_adapter,
|
|
add_monitor,
|
|
add_mode,
|
|
};
|
|
|
|
static void reset_display_manager_ctx( struct device_manager_ctx *ctx )
|
|
{
|
|
HANDLE mutex = ctx->mutex;
|
|
|
|
if (ctx->adapter_key)
|
|
{
|
|
NtClose( ctx->adapter_key );
|
|
last_query_display_time = 0;
|
|
}
|
|
if (ctx->gpu_count) cleanup_devices();
|
|
|
|
memset( ctx, 0, sizeof(*ctx) );
|
|
if ((ctx->mutex = mutex)) prepare_devices();
|
|
}
|
|
|
|
static void release_display_manager_ctx( struct device_manager_ctx *ctx )
|
|
{
|
|
if (ctx->mutex)
|
|
{
|
|
pthread_mutex_unlock( &display_lock );
|
|
release_display_device_init_mutex( ctx->mutex );
|
|
ctx->mutex = 0;
|
|
}
|
|
reset_display_manager_ctx( ctx );
|
|
}
|
|
|
|
static void clear_display_devices(void)
|
|
{
|
|
struct adapter *adapter;
|
|
struct monitor *monitor;
|
|
struct gpu *gpu;
|
|
|
|
if (list_head( &monitors ) == &virtual_monitor.entry)
|
|
{
|
|
list_init( &monitors );
|
|
return;
|
|
}
|
|
|
|
while (!list_empty( &monitors ))
|
|
{
|
|
monitor = LIST_ENTRY( list_head( &monitors ), struct monitor, entry );
|
|
if (monitor->adapter) adapter_release( monitor->adapter );
|
|
list_remove( &monitor->entry );
|
|
free( monitor );
|
|
}
|
|
|
|
while (!list_empty( &adapters ))
|
|
{
|
|
adapter = LIST_ENTRY( list_head( &adapters ), struct adapter, entry );
|
|
list_remove( &adapter->entry );
|
|
adapter_release( adapter );
|
|
}
|
|
|
|
while (!list_empty( &gpus ))
|
|
{
|
|
gpu = LIST_ENTRY( list_head( &gpus ), struct gpu, entry );
|
|
list_remove( &gpu->entry );
|
|
gpu_release( gpu );
|
|
}
|
|
}
|
|
|
|
static BOOL is_monitor_active( struct monitor *monitor )
|
|
{
|
|
struct adapter *adapter;
|
|
/* services do not have any adapters, only a virtual monitor */
|
|
if (!(adapter = monitor->adapter)) return TRUE;
|
|
if (!(adapter->state_flags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) return FALSE;
|
|
return !IsRectEmpty( &monitor->rc_monitor );
|
|
}
|
|
|
|
static BOOL is_monitor_primary( struct monitor *monitor )
|
|
{
|
|
struct adapter *adapter;
|
|
/* services do not have any adapters, only a virtual monitor */
|
|
if (!(adapter = monitor->adapter)) return TRUE;
|
|
return !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
|
|
}
|
|
|
|
static void enum_device_keys( const char *root, const WCHAR *classW, UINT class_size, void (*callback)(const char *) )
|
|
{
|
|
char buffer[1024];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
KEY_BASIC_INFORMATION *key2 = (void *)buffer;
|
|
HKEY root_key, device_key, instance_key;
|
|
DWORD size, root_len, i = 0;
|
|
char path[MAX_PATH];
|
|
|
|
if (!(root_key = reg_open_ascii_key( enum_key, root ))) return;
|
|
root_len = sprintf( path, "%s\\", root );
|
|
|
|
while (!NtEnumerateKey( root_key, i++, KeyBasicInformation, key2, sizeof(buffer), &size ))
|
|
{
|
|
DWORD j = 0, k, len, device_len;
|
|
|
|
if (!(device_key = reg_open_key( root_key, key2->Name, key2->NameLength ))) continue;
|
|
for (k = 0, len = root_len; k < key2->NameLength / sizeof(WCHAR); k++) path[len++] = key2->Name[k];
|
|
path[len++] = '\\';
|
|
device_len = len;
|
|
|
|
while (!NtEnumerateKey( device_key, j++, KeyBasicInformation, key2, sizeof(buffer), &size ))
|
|
{
|
|
if (!(instance_key = reg_open_key( device_key, key2->Name, key2->NameLength ))) continue;
|
|
for (k = 0, len = device_len; k < key2->NameLength / sizeof(WCHAR); k++) path[len++] = key2->Name[k];
|
|
path[len++] = 0;
|
|
|
|
size = query_reg_ascii_value( instance_key, "ClassGUID", value, sizeof(buffer) );
|
|
if (size != class_size || wcscmp( (WCHAR *)value->Data, classW ))
|
|
{
|
|
NtClose( instance_key );
|
|
continue;
|
|
}
|
|
|
|
callback( path );
|
|
|
|
NtClose( instance_key );
|
|
}
|
|
|
|
NtClose( device_key );
|
|
}
|
|
|
|
NtClose( root_key );
|
|
}
|
|
|
|
static void enum_gpus( const char *path )
|
|
{
|
|
struct gpu *gpu;
|
|
if (!(gpu = calloc( 1, sizeof(*gpu) ))) return;
|
|
gpu->refcount = 1;
|
|
strcpy( gpu->path, path );
|
|
list_add_tail( &gpus, &gpu->entry );
|
|
}
|
|
|
|
static struct gpu *find_gpu_from_path( const char *path )
|
|
{
|
|
struct gpu *gpu;
|
|
|
|
LIST_FOR_EACH_ENTRY( gpu, &gpus, struct gpu, entry )
|
|
if (!strcmp( gpu->path, path )) return gpu_acquire( gpu );
|
|
|
|
ERR( "Failed to find gpu with path %s\n", debugstr_a(path) );
|
|
return NULL;
|
|
}
|
|
|
|
static void enum_monitors( const char *path )
|
|
{
|
|
struct monitor *monitor;
|
|
if (!(monitor = calloc( 1, sizeof(*monitor) ))) return;
|
|
strcpy( monitor->path, path );
|
|
list_add_tail( &monitors, &monitor->entry );
|
|
}
|
|
|
|
static struct monitor *find_monitor_from_path( const char *path )
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
if (!strcmp( monitor->path, path )) return monitor;
|
|
|
|
ERR( "Failed to find monitor with path %s\n", debugstr_a(path) );
|
|
return NULL;
|
|
}
|
|
|
|
static BOOL update_display_cache_from_registry(void)
|
|
{
|
|
char path[MAX_PATH];
|
|
DWORD adapter_id, monitor_id, monitor_count = 0, size;
|
|
KEY_BASIC_INFORMATION key;
|
|
struct adapter *adapter;
|
|
struct monitor *monitor;
|
|
HANDLE mutex = NULL;
|
|
NTSTATUS status;
|
|
struct gpu *gpu;
|
|
HKEY hkey;
|
|
BOOL ret;
|
|
|
|
/* If user driver did initialize the registry, then exit */
|
|
if (!enum_key && !(enum_key = reg_open_ascii_key( NULL, enum_keyA )))
|
|
return FALSE;
|
|
if (!video_key && !(video_key = reg_open_ascii_key( NULL, devicemap_video_keyA )))
|
|
return FALSE;
|
|
|
|
status = NtQueryKey( video_key, KeyBasicInformation, &key,
|
|
offsetof(KEY_BASIC_INFORMATION, Name), &size );
|
|
if (status && status != STATUS_BUFFER_OVERFLOW)
|
|
return FALSE;
|
|
|
|
if (key.LastWriteTime.QuadPart <= last_query_display_time) return TRUE;
|
|
|
|
pthread_mutex_lock( &display_lock );
|
|
mutex = get_display_device_init_mutex();
|
|
|
|
clear_display_devices();
|
|
|
|
enum_device_keys( "PCI", guid_devclass_displayW, sizeof(guid_devclass_displayW), enum_gpus );
|
|
enum_device_keys( "DISPLAY", guid_devclass_monitorW, sizeof(guid_devclass_monitorW), enum_monitors );
|
|
|
|
LIST_FOR_EACH_ENTRY( gpu, &gpus, struct gpu, entry )
|
|
{
|
|
if (!read_gpu_from_registry( gpu ))
|
|
WARN( "Failed to read gpu from registry\n" );
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
{
|
|
if (!read_monitor_from_registry( monitor ))
|
|
WARN( "Failed to read monitor from registry\n" );
|
|
}
|
|
|
|
for (adapter_id = 0;; adapter_id++)
|
|
{
|
|
if (!(adapter = calloc( 1, sizeof(*adapter) ))) break;
|
|
adapter->refcount = 1;
|
|
adapter->id = adapter_id;
|
|
|
|
if (!read_display_adapter_settings( adapter_id, adapter, path ) ||
|
|
!(adapter->gpu = find_gpu_from_path( path )))
|
|
{
|
|
free( adapter->modes );
|
|
free( adapter );
|
|
break;
|
|
}
|
|
|
|
list_add_tail( &adapters, &adapter->entry );
|
|
|
|
size = lstrlenW( adapter->config_key ) * sizeof(WCHAR);
|
|
if (!(hkey = reg_open_key( config_key, adapter->config_key, size ))) continue;
|
|
|
|
for (monitor_id = 0;; monitor_id++)
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
if (!read_adapter_monitor_path( hkey, monitor_id, path )) break;
|
|
if (!(monitor = find_monitor_from_path( path ))) continue;
|
|
|
|
monitor->id = monitor_id;
|
|
monitor->adapter = adapter_acquire( adapter );
|
|
monitor->handle = UlongToHandle( ++monitor_count );
|
|
if (adapter->monitor_count++) monitor->is_clone = TRUE;
|
|
}
|
|
|
|
NtClose( hkey );
|
|
}
|
|
|
|
if ((ret = !list_empty( &adapters ) && !list_empty( &monitors )))
|
|
last_query_display_time = key.LastWriteTime.QuadPart;
|
|
pthread_mutex_unlock( &display_lock );
|
|
release_display_device_init_mutex( mutex );
|
|
return ret;
|
|
}
|
|
|
|
static BOOL is_same_devmode( const DEVMODEW *a, const DEVMODEW *b )
|
|
{
|
|
return a->dmDisplayOrientation == b->dmDisplayOrientation &&
|
|
a->dmBitsPerPel == b->dmBitsPerPel &&
|
|
a->dmPelsWidth == b->dmPelsWidth &&
|
|
a->dmPelsHeight == b->dmPelsHeight &&
|
|
a->dmDisplayFrequency == b->dmDisplayFrequency;
|
|
}
|
|
|
|
static BOOL default_update_display_devices( const struct gdi_device_manager *manager, BOOL force, struct device_manager_ctx *ctx )
|
|
{
|
|
/* default implementation: expose an adapter and a monitor with a few standard modes,
|
|
* and read / write current display settings from / to the registry.
|
|
*/
|
|
static const DEVMODEW modes[] =
|
|
{
|
|
{ .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
|
|
.dmBitsPerPel = 32, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
|
|
{ .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
|
|
.dmBitsPerPel = 32, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
|
|
{ .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
|
|
.dmBitsPerPel = 32, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
|
|
{ .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
|
|
.dmBitsPerPel = 16, .dmPelsWidth = 640, .dmPelsHeight = 480, .dmDisplayFrequency = 60, },
|
|
{ .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
|
|
.dmBitsPerPel = 16, .dmPelsWidth = 800, .dmPelsHeight = 600, .dmDisplayFrequency = 60, },
|
|
{ .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
|
|
.dmBitsPerPel = 16, .dmPelsWidth = 1024, .dmPelsHeight = 768, .dmDisplayFrequency = 60, },
|
|
};
|
|
static const struct gdi_gpu gpu;
|
|
static const struct gdi_adapter adapter =
|
|
{
|
|
.state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
|
|
};
|
|
struct gdi_monitor monitor = {0};
|
|
DEVMODEW mode = {{0}};
|
|
UINT i;
|
|
|
|
if (!force) return TRUE;
|
|
|
|
manager->add_gpu( &gpu, ctx );
|
|
manager->add_adapter( &adapter, ctx );
|
|
|
|
if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, &mode ))
|
|
{
|
|
mode = modes[2];
|
|
mode.dmFields |= DM_POSITION;
|
|
}
|
|
monitor.rc_monitor.right = mode.dmPelsWidth;
|
|
monitor.rc_monitor.bottom = mode.dmPelsHeight;
|
|
monitor.rc_work.right = mode.dmPelsWidth;
|
|
monitor.rc_work.bottom = mode.dmPelsHeight;
|
|
|
|
manager->add_monitor( &monitor, ctx );
|
|
for (i = 0; i < ARRAY_SIZE(modes); ++i)
|
|
{
|
|
if (is_same_devmode( modes + i, &mode )) manager->add_mode( &mode, TRUE, ctx );
|
|
else manager->add_mode( modes + i, FALSE, ctx );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL update_display_devices( const struct gdi_device_manager *manager, BOOL force, struct device_manager_ctx *ctx )
|
|
{
|
|
if (user_driver->pUpdateDisplayDevices( manager, force, ctx )) return TRUE;
|
|
return default_update_display_devices( manager, force, ctx );
|
|
}
|
|
|
|
/* parse the desktop size specification */
|
|
static BOOL parse_size( const WCHAR *size, unsigned int *width, unsigned int *height )
|
|
{
|
|
WCHAR *end;
|
|
|
|
*width = wcstoul( size, &end, 10 );
|
|
if (end == size) return FALSE;
|
|
if (*end != 'x') return FALSE;
|
|
size = end + 1;
|
|
*height = wcstoul( size, &end, 10 );
|
|
return !*end;
|
|
}
|
|
|
|
/* retrieve the default desktop size from the registry */
|
|
static BOOL get_default_desktop_size( unsigned int *width, unsigned int *height )
|
|
{
|
|
WCHAR buffer[4096];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
DWORD size;
|
|
HKEY hkey;
|
|
|
|
/* @@ Wine registry key: HKCU\Software\Wine\Explorer\Desktops */
|
|
if (!(hkey = reg_open_hkcu_key( "Software\\Wine\\Explorer\\Desktops" ))) return FALSE;
|
|
|
|
size = query_reg_ascii_value( hkey, "Default", value, sizeof(buffer) );
|
|
NtClose( hkey );
|
|
if (!size || value->Type != REG_SZ) return FALSE;
|
|
|
|
if (!parse_size( (const WCHAR *)value->Data, width, height )) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static void desktop_add_gpu( const struct gdi_gpu *gpu, void *param )
|
|
{
|
|
}
|
|
|
|
static void desktop_add_adapter( const struct gdi_adapter *adapter, void *param )
|
|
{
|
|
struct device_manager_ctx *ctx = param;
|
|
ctx->is_primary = !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
|
|
}
|
|
|
|
static void desktop_add_monitor( const struct gdi_monitor *monitor, void *param )
|
|
{
|
|
}
|
|
|
|
static void desktop_add_mode( const DEVMODEW *mode, BOOL current, void *param )
|
|
{
|
|
struct device_manager_ctx *ctx = param;
|
|
|
|
if (ctx->is_primary && current)
|
|
{
|
|
ctx->primary_bpp = mode->dmBitsPerPel;
|
|
ctx->primary_width = mode->dmPelsWidth;
|
|
ctx->primary_height = mode->dmPelsHeight;
|
|
}
|
|
}
|
|
|
|
static const struct gdi_device_manager desktop_device_manager =
|
|
{
|
|
desktop_add_gpu,
|
|
desktop_add_adapter,
|
|
desktop_add_monitor,
|
|
desktop_add_mode,
|
|
};
|
|
|
|
static BOOL desktop_update_display_devices( BOOL force, struct device_manager_ctx *ctx )
|
|
{
|
|
static const struct gdi_gpu gpu;
|
|
static const struct gdi_adapter adapter =
|
|
{
|
|
.state_flags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP | DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_VGA_COMPATIBLE,
|
|
};
|
|
struct gdi_monitor monitor = {0};
|
|
static struct screen_size
|
|
{
|
|
unsigned int width;
|
|
unsigned int height;
|
|
} screen_sizes[] = {
|
|
/* 4:3 */
|
|
{ 320, 240},
|
|
{ 400, 300},
|
|
{ 512, 384},
|
|
{ 640, 480},
|
|
{ 768, 576},
|
|
{ 800, 600},
|
|
{1024, 768},
|
|
{1152, 864},
|
|
{1280, 960},
|
|
{1400, 1050},
|
|
{1600, 1200},
|
|
{2048, 1536},
|
|
/* 5:4 */
|
|
{1280, 1024},
|
|
{2560, 2048},
|
|
/* 16:9 */
|
|
{1280, 720},
|
|
{1366, 768},
|
|
{1600, 900},
|
|
{1920, 1080},
|
|
{2560, 1440},
|
|
{3840, 2160},
|
|
/* 16:10 */
|
|
{ 320, 200},
|
|
{ 640, 400},
|
|
{1280, 800},
|
|
{1440, 900},
|
|
{1680, 1050},
|
|
{1920, 1200},
|
|
{2560, 1600}
|
|
};
|
|
|
|
struct device_manager_ctx desktop_ctx = {0};
|
|
UINT screen_width, screen_height, max_width, max_height;
|
|
unsigned int depths[] = {8, 16, 0};
|
|
DEVMODEW current, mode =
|
|
{
|
|
.dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY,
|
|
.dmDisplayFrequency = 60,
|
|
};
|
|
UINT i, j;
|
|
|
|
if (!force) return TRUE;
|
|
/* in virtual desktop mode, read the device list from the user driver but expose virtual devices */
|
|
if (!update_display_devices( &desktop_device_manager, TRUE, &desktop_ctx )) return FALSE;
|
|
|
|
max_width = desktop_ctx.primary_width;
|
|
max_height = desktop_ctx.primary_height;
|
|
depths[ARRAY_SIZE(depths) - 1] = desktop_ctx.primary_bpp;
|
|
|
|
if (!get_default_desktop_size( &screen_width, &screen_height ))
|
|
{
|
|
screen_width = max_width;
|
|
screen_height = max_height;
|
|
}
|
|
|
|
add_gpu( &gpu, ctx );
|
|
add_adapter( &adapter, ctx );
|
|
if (!read_adapter_mode( ctx->adapter_key, ENUM_CURRENT_SETTINGS, ¤t ))
|
|
{
|
|
current = mode;
|
|
current.dmFields |= DM_POSITION;
|
|
current.dmBitsPerPel = desktop_ctx.primary_bpp;
|
|
current.dmPelsWidth = screen_width;
|
|
current.dmPelsHeight = screen_height;
|
|
}
|
|
|
|
monitor.rc_monitor.right = current.dmPelsWidth;
|
|
monitor.rc_monitor.bottom = current.dmPelsHeight;
|
|
monitor.rc_work.right = current.dmPelsWidth;
|
|
monitor.rc_work.bottom = current.dmPelsHeight;
|
|
add_monitor( &monitor, ctx );
|
|
|
|
for (i = 0; i < ARRAY_SIZE(depths); ++i)
|
|
{
|
|
mode.dmBitsPerPel = depths[i];
|
|
|
|
for (j = 0; j < ARRAY_SIZE(screen_sizes); ++j)
|
|
{
|
|
mode.dmPelsWidth = screen_sizes[j].width;
|
|
mode.dmPelsHeight = screen_sizes[j].height;
|
|
|
|
if (mode.dmPelsWidth > max_width || mode.dmPelsHeight > max_height) continue;
|
|
if (mode.dmPelsWidth == max_width && mode.dmPelsHeight == max_height) continue;
|
|
if (mode.dmPelsWidth == screen_width && mode.dmPelsHeight == screen_height) continue;
|
|
|
|
if (is_same_devmode( &mode, ¤t )) add_mode( ¤t, TRUE, ctx );
|
|
else add_mode( &mode, FALSE, ctx );
|
|
}
|
|
|
|
mode.dmPelsWidth = screen_width;
|
|
mode.dmPelsHeight = screen_height;
|
|
if (is_same_devmode( &mode, ¤t )) add_mode( ¤t, TRUE, ctx );
|
|
else add_mode( &mode, FALSE, ctx );
|
|
|
|
if (max_width != screen_width || max_height != screen_height)
|
|
{
|
|
mode.dmPelsWidth = max_width;
|
|
mode.dmPelsHeight = max_height;
|
|
if (is_same_devmode( &mode, ¤t )) add_mode( ¤t, TRUE, ctx );
|
|
else add_mode( &mode, FALSE, ctx );
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL update_display_cache( BOOL force )
|
|
{
|
|
static const WCHAR wine_service_station_name[] =
|
|
{'_','_','w','i','n','e','s','e','r','v','i','c','e','_','w','i','n','s','t','a','t','i','o','n',0};
|
|
HWINSTA winstation = NtUserGetProcessWindowStation();
|
|
struct device_manager_ctx ctx = {0};
|
|
BOOL was_virtual_desktop, ret;
|
|
WCHAR name[MAX_PATH];
|
|
|
|
/* services do not have any adapters, only a virtual monitor */
|
|
if (NtUserGetObjectInformation( winstation, UOI_NAME, name, sizeof(name), NULL )
|
|
&& !wcscmp( name, wine_service_station_name ))
|
|
{
|
|
pthread_mutex_lock( &display_lock );
|
|
clear_display_devices();
|
|
list_add_tail( &monitors, &virtual_monitor.entry );
|
|
pthread_mutex_unlock( &display_lock );
|
|
return TRUE;
|
|
}
|
|
|
|
if ((was_virtual_desktop = is_virtual_desktop())) ret = TRUE;
|
|
else ret = update_display_devices( &device_manager, force, &ctx );
|
|
|
|
/* as update_display_devices calls the user driver, it starts explorer and may change the virtual desktop state */
|
|
if (ret && is_virtual_desktop())
|
|
{
|
|
reset_display_manager_ctx( &ctx );
|
|
ret = desktop_update_display_devices( force || !was_virtual_desktop, &ctx );
|
|
}
|
|
|
|
release_display_manager_ctx( &ctx );
|
|
if (!ret) WARN( "Failed to update display devices\n" );
|
|
|
|
if (!update_display_cache_from_registry())
|
|
{
|
|
if (force)
|
|
{
|
|
ERR( "Failed to read display config.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
if (ctx.gpu_count)
|
|
{
|
|
ERR( "Driver reported devices, but we failed to read them.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
return update_display_cache( TRUE );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL lock_display_devices(void)
|
|
{
|
|
if (!update_display_cache( FALSE )) return FALSE;
|
|
pthread_mutex_lock( &display_lock );
|
|
return TRUE;
|
|
}
|
|
|
|
static void unlock_display_devices(void)
|
|
{
|
|
pthread_mutex_unlock( &display_lock );
|
|
}
|
|
|
|
static HDC get_display_dc(void)
|
|
{
|
|
pthread_mutex_lock( &display_dc_lock );
|
|
if (!display_dc)
|
|
{
|
|
HDC dc;
|
|
|
|
pthread_mutex_unlock( &display_dc_lock );
|
|
dc = NtGdiOpenDCW( NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL );
|
|
pthread_mutex_lock( &display_dc_lock );
|
|
if (display_dc)
|
|
NtGdiDeleteObjectApp( dc );
|
|
else
|
|
display_dc = dc;
|
|
}
|
|
return display_dc;
|
|
}
|
|
|
|
static void release_display_dc( HDC hdc )
|
|
{
|
|
pthread_mutex_unlock( &display_dc_lock );
|
|
}
|
|
|
|
/**********************************************************************
|
|
* get_monitor_dpi
|
|
*/
|
|
UINT get_monitor_dpi( HMONITOR monitor )
|
|
{
|
|
/* FIXME: use the monitor DPI instead */
|
|
return system_dpi;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* get_win_monitor_dpi
|
|
*/
|
|
UINT get_win_monitor_dpi( HWND hwnd )
|
|
{
|
|
/* FIXME: use the monitor DPI instead */
|
|
return system_dpi;
|
|
}
|
|
|
|
/* copied from user32 GetAwarenessFromDpiAwarenessContext, make sure to keep that in sync */
|
|
static DPI_AWARENESS get_awareness_from_dpi_awareness_context( DPI_AWARENESS_CONTEXT context )
|
|
{
|
|
switch ((ULONG_PTR)context)
|
|
{
|
|
case 0x10:
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x22:
|
|
case 0x80000010:
|
|
case 0x80000011:
|
|
case 0x80000012:
|
|
case 0x80000022:
|
|
return (ULONG_PTR)context & 3;
|
|
case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
|
|
case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
|
|
case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
|
|
return ~(ULONG_PTR)context;
|
|
case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2:
|
|
return ~(ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
|
|
default:
|
|
return DPI_AWARENESS_INVALID;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
* get_thread_dpi_awareness
|
|
*/
|
|
DPI_AWARENESS get_thread_dpi_awareness(void)
|
|
{
|
|
struct ntuser_thread_info *info = NtUserGetThreadInfo();
|
|
ULONG_PTR context = info->dpi_awareness;
|
|
|
|
if (context == 0) /* process default */
|
|
return NtUserGetProcessDpiAwarenessContext( NULL ) & 3;
|
|
|
|
return get_awareness_from_dpi_awareness_context((DPI_AWARENESS_CONTEXT)context);
|
|
}
|
|
|
|
DWORD get_process_layout(void)
|
|
{
|
|
return process_layout == ~0u ? 0 : process_layout;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* get_thread_dpi
|
|
*/
|
|
UINT get_thread_dpi(void)
|
|
{
|
|
switch (get_thread_dpi_awareness())
|
|
{
|
|
case DPI_AWARENESS_UNAWARE: return USER_DEFAULT_SCREEN_DPI;
|
|
case DPI_AWARENESS_SYSTEM_AWARE: return system_dpi;
|
|
default: return 0; /* no scaling */
|
|
}
|
|
}
|
|
|
|
/* see GetDpiForSystem */
|
|
UINT get_system_dpi(void)
|
|
{
|
|
if (get_thread_dpi_awareness() == DPI_AWARENESS_UNAWARE) return USER_DEFAULT_SCREEN_DPI;
|
|
return system_dpi;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* SetThreadDpiAwarenessContext (win32u.so)
|
|
* copied from user32, make sure to keep that in sync
|
|
*/
|
|
DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT context )
|
|
{
|
|
struct ntuser_thread_info *info = NtUserGetThreadInfo();
|
|
DPI_AWARENESS prev, val = get_awareness_from_dpi_awareness_context( context );
|
|
|
|
if (val == DPI_AWARENESS_INVALID)
|
|
{
|
|
RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
|
|
return 0;
|
|
}
|
|
if (!(prev = info->dpi_awareness))
|
|
{
|
|
prev = NtUserGetProcessDpiAwarenessContext( GetCurrentProcess() ) & 3;
|
|
prev |= 0x80000010; /* restore to process default */
|
|
}
|
|
if (((ULONG_PTR)context & ~(ULONG_PTR)0x33) == 0x80000000) info->dpi_awareness = 0;
|
|
else if (context == DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 || context == (DPI_AWARENESS_CONTEXT)0x22)
|
|
info->dpi_awareness = 0x22;
|
|
else info->dpi_awareness = val | 0x10;
|
|
return ULongToHandle( prev );
|
|
}
|
|
|
|
/**********************************************************************
|
|
* map_dpi_rect
|
|
*/
|
|
RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to )
|
|
{
|
|
if (dpi_from && dpi_to && dpi_from != dpi_to)
|
|
{
|
|
rect.left = muldiv( rect.left, dpi_to, dpi_from );
|
|
rect.top = muldiv( rect.top, dpi_to, dpi_from );
|
|
rect.right = muldiv( rect.right, dpi_to, dpi_from );
|
|
rect.bottom = muldiv( rect.bottom, dpi_to, dpi_from );
|
|
}
|
|
return rect;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* map_dpi_point
|
|
*/
|
|
POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to )
|
|
{
|
|
if (dpi_from && dpi_to && dpi_from != dpi_to)
|
|
{
|
|
pt.x = muldiv( pt.x, dpi_to, dpi_from );
|
|
pt.y = muldiv( pt.y, dpi_to, dpi_from );
|
|
}
|
|
return pt;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* point_win_to_phys_dpi
|
|
*/
|
|
static POINT point_win_to_phys_dpi( HWND hwnd, POINT pt )
|
|
{
|
|
return map_dpi_point( pt, get_dpi_for_window( hwnd ), get_win_monitor_dpi( hwnd ) );
|
|
}
|
|
|
|
/**********************************************************************
|
|
* point_phys_to_win_dpi
|
|
*/
|
|
POINT point_phys_to_win_dpi( HWND hwnd, POINT pt )
|
|
{
|
|
return map_dpi_point( pt, get_win_monitor_dpi( hwnd ), get_dpi_for_window( hwnd ));
|
|
}
|
|
|
|
/**********************************************************************
|
|
* point_thread_to_win_dpi
|
|
*/
|
|
POINT point_thread_to_win_dpi( HWND hwnd, POINT pt )
|
|
{
|
|
UINT dpi = get_thread_dpi();
|
|
if (!dpi) dpi = get_win_monitor_dpi( hwnd );
|
|
return map_dpi_point( pt, dpi, get_dpi_for_window( hwnd ));
|
|
}
|
|
|
|
/**********************************************************************
|
|
* rect_thread_to_win_dpi
|
|
*/
|
|
RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect )
|
|
{
|
|
UINT dpi = get_thread_dpi();
|
|
if (!dpi) dpi = get_win_monitor_dpi( hwnd );
|
|
return map_dpi_rect( rect, dpi, get_dpi_for_window( hwnd ) );
|
|
}
|
|
|
|
/* map value from system dpi to standard 96 dpi for storing in the registry */
|
|
static int map_from_system_dpi( int val )
|
|
{
|
|
return muldiv( val, USER_DEFAULT_SCREEN_DPI, get_system_dpi() );
|
|
}
|
|
|
|
/* map value from 96 dpi to system or custom dpi */
|
|
static int map_to_dpi( int val, UINT dpi )
|
|
{
|
|
if (!dpi) dpi = get_system_dpi();
|
|
return muldiv( val, dpi, USER_DEFAULT_SCREEN_DPI );
|
|
}
|
|
|
|
RECT get_virtual_screen_rect( UINT dpi )
|
|
{
|
|
struct monitor *monitor;
|
|
RECT rect = {0};
|
|
|
|
if (!lock_display_devices()) return rect;
|
|
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
{
|
|
if (!is_monitor_active( monitor ) || monitor->is_clone) continue;
|
|
union_rect( &rect, &rect, &monitor->rc_monitor );
|
|
}
|
|
|
|
unlock_display_devices();
|
|
|
|
if (dpi) rect = map_dpi_rect( rect, system_dpi, dpi );
|
|
return rect;
|
|
}
|
|
|
|
static BOOL is_window_rect_full_screen( const RECT *rect )
|
|
{
|
|
struct monitor *monitor;
|
|
BOOL ret = FALSE;
|
|
|
|
if (!lock_display_devices()) return FALSE;
|
|
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
{
|
|
RECT monrect;
|
|
|
|
if (!is_monitor_active( monitor ) || monitor->is_clone) continue;
|
|
|
|
monrect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
|
|
get_thread_dpi() );
|
|
|
|
if (rect->left <= monrect.left && rect->right >= monrect.right &&
|
|
rect->top <= monrect.top && rect->bottom >= monrect.bottom)
|
|
{
|
|
ret = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
unlock_display_devices();
|
|
return ret;
|
|
}
|
|
|
|
static UINT get_display_index( const UNICODE_STRING *name )
|
|
{
|
|
static const WCHAR displayW[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y'};
|
|
WCHAR *end, display[64] = {0};
|
|
int index;
|
|
|
|
memcpy( display, name->Buffer, min( name->Length, 63 * sizeof(WCHAR) ) );
|
|
if (wcsnicmp( display, displayW, ARRAY_SIZE(displayW) )) return 0;
|
|
if (!(index = wcstoul( display + ARRAY_SIZE(displayW), &end, 10 )) || *end) return 0;
|
|
return index;
|
|
}
|
|
|
|
RECT get_display_rect( const WCHAR *display )
|
|
{
|
|
struct monitor *monitor;
|
|
UNICODE_STRING name;
|
|
RECT rect = {0};
|
|
UINT index;
|
|
|
|
RtlInitUnicodeString( &name, display );
|
|
if (!(index = get_display_index( &name ))) return rect;
|
|
if (!lock_display_devices()) return rect;
|
|
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
{
|
|
if (!monitor->adapter || monitor->adapter->id + 1 != index) continue;
|
|
rect = monitor->rc_monitor;
|
|
break;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
return map_dpi_rect( rect, system_dpi, get_thread_dpi() );
|
|
}
|
|
|
|
RECT get_primary_monitor_rect( UINT dpi )
|
|
{
|
|
struct monitor *monitor;
|
|
RECT rect = {0};
|
|
|
|
if (!lock_display_devices()) return rect;
|
|
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
{
|
|
if (!is_monitor_primary( monitor )) continue;
|
|
rect = monitor->rc_monitor;
|
|
break;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
return map_dpi_rect( rect, system_dpi, dpi );
|
|
}
|
|
|
|
/**********************************************************************
|
|
* NtUserGetDisplayConfigBufferSizes (win32u.@)
|
|
*/
|
|
LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_info,
|
|
UINT32 *num_mode_info )
|
|
{
|
|
struct monitor *monitor;
|
|
UINT32 count = 0;
|
|
|
|
TRACE( "(0x%x %p %p)\n", flags, num_path_info, num_mode_info );
|
|
|
|
if (!num_path_info || !num_mode_info)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
*num_path_info = 0;
|
|
|
|
switch (flags)
|
|
{
|
|
case QDC_ALL_PATHS:
|
|
case QDC_ONLY_ACTIVE_PATHS:
|
|
case QDC_DATABASE_CURRENT:
|
|
break;
|
|
default:
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* FIXME: semi-stub */
|
|
if (flags != QDC_ONLY_ACTIVE_PATHS)
|
|
FIXME( "only returning active paths\n" );
|
|
|
|
if (lock_display_devices())
|
|
{
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
{
|
|
if (!is_monitor_active( monitor )) continue;
|
|
count++;
|
|
}
|
|
unlock_display_devices();
|
|
}
|
|
|
|
*num_path_info = count;
|
|
*num_mode_info = count * 2;
|
|
TRACE( "returning %u paths %u modes\n", *num_path_info, *num_mode_info );
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static DISPLAYCONFIG_ROTATION get_dc_rotation( const DEVMODEW *devmode )
|
|
{
|
|
if (devmode->dmFields & DM_DISPLAYORIENTATION)
|
|
return devmode->dmDisplayOrientation + 1;
|
|
else
|
|
return DISPLAYCONFIG_ROTATION_IDENTITY;
|
|
}
|
|
|
|
static DISPLAYCONFIG_SCANLINE_ORDERING get_dc_scanline_ordering( const DEVMODEW *devmode )
|
|
{
|
|
if (!(devmode->dmFields & DM_DISPLAYFLAGS))
|
|
return DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
|
|
else if (devmode->dmDisplayFlags & DM_INTERLACED)
|
|
return DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED;
|
|
else
|
|
return DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
|
|
}
|
|
|
|
static DISPLAYCONFIG_PIXELFORMAT get_dc_pixelformat( DWORD dmBitsPerPel )
|
|
{
|
|
if ((dmBitsPerPel == 8) || (dmBitsPerPel == 16) ||
|
|
(dmBitsPerPel == 24) || (dmBitsPerPel == 32))
|
|
return dmBitsPerPel / 8;
|
|
else
|
|
return DISPLAYCONFIG_PIXELFORMAT_NONGDI;
|
|
}
|
|
|
|
static void set_mode_target_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid, UINT32 target_id,
|
|
UINT32 flags, const DEVMODEW *devmode )
|
|
{
|
|
DISPLAYCONFIG_TARGET_MODE *mode = &info->targetMode;
|
|
|
|
info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET;
|
|
info->adapterId = *gpu_luid;
|
|
info->id = target_id;
|
|
|
|
/* FIXME: Populate pixelRate/hSyncFreq/totalSize with real data */
|
|
mode->targetVideoSignalInfo.pixelRate = devmode->dmDisplayFrequency * devmode->dmPelsWidth * devmode->dmPelsHeight;
|
|
mode->targetVideoSignalInfo.hSyncFreq.Numerator = devmode->dmDisplayFrequency * devmode->dmPelsWidth;
|
|
mode->targetVideoSignalInfo.hSyncFreq.Denominator = 1;
|
|
mode->targetVideoSignalInfo.vSyncFreq.Numerator = devmode->dmDisplayFrequency;
|
|
mode->targetVideoSignalInfo.vSyncFreq.Denominator = 1;
|
|
mode->targetVideoSignalInfo.activeSize.cx = devmode->dmPelsWidth;
|
|
mode->targetVideoSignalInfo.activeSize.cy = devmode->dmPelsHeight;
|
|
if (flags & QDC_DATABASE_CURRENT)
|
|
{
|
|
mode->targetVideoSignalInfo.totalSize.cx = 0;
|
|
mode->targetVideoSignalInfo.totalSize.cy = 0;
|
|
}
|
|
else
|
|
{
|
|
mode->targetVideoSignalInfo.totalSize.cx = devmode->dmPelsWidth;
|
|
mode->targetVideoSignalInfo.totalSize.cy = devmode->dmPelsHeight;
|
|
}
|
|
mode->targetVideoSignalInfo.videoStandard = D3DKMDT_VSS_OTHER;
|
|
mode->targetVideoSignalInfo.scanLineOrdering = get_dc_scanline_ordering( devmode );
|
|
}
|
|
|
|
static void set_path_target_info( DISPLAYCONFIG_PATH_TARGET_INFO *info, const LUID *gpu_luid,
|
|
UINT32 target_id, UINT32 mode_index, const DEVMODEW *devmode )
|
|
{
|
|
info->adapterId = *gpu_luid;
|
|
info->id = target_id;
|
|
info->modeInfoIdx = mode_index;
|
|
info->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL;
|
|
info->rotation = get_dc_rotation( devmode );
|
|
info->scaling = DISPLAYCONFIG_SCALING_IDENTITY;
|
|
info->refreshRate.Numerator = devmode->dmDisplayFrequency;
|
|
info->refreshRate.Denominator = 1;
|
|
info->scanLineOrdering = get_dc_scanline_ordering( devmode );
|
|
info->targetAvailable = TRUE;
|
|
info->statusFlags = DISPLAYCONFIG_TARGET_IN_USE;
|
|
}
|
|
|
|
static void set_mode_source_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid,
|
|
UINT32 source_id, const DEVMODEW *devmode )
|
|
{
|
|
DISPLAYCONFIG_SOURCE_MODE *mode = &info->sourceMode;
|
|
|
|
info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE;
|
|
info->adapterId = *gpu_luid;
|
|
info->id = source_id;
|
|
|
|
mode->width = devmode->dmPelsWidth;
|
|
mode->height = devmode->dmPelsHeight;
|
|
mode->pixelFormat = get_dc_pixelformat( devmode->dmBitsPerPel );
|
|
if (devmode->dmFields & DM_POSITION)
|
|
{
|
|
mode->position = devmode->dmPosition;
|
|
}
|
|
else
|
|
{
|
|
mode->position.x = 0;
|
|
mode->position.y = 0;
|
|
}
|
|
}
|
|
|
|
static void set_path_source_info( DISPLAYCONFIG_PATH_SOURCE_INFO *info, const LUID *gpu_luid,
|
|
UINT32 source_id, UINT32 mode_index )
|
|
{
|
|
info->adapterId = *gpu_luid;
|
|
info->id = source_id;
|
|
info->modeInfoIdx = mode_index;
|
|
info->statusFlags = DISPLAYCONFIG_SOURCE_IN_USE;
|
|
}
|
|
|
|
static BOOL source_mode_exists( const DISPLAYCONFIG_MODE_INFO *modes, UINT32 modes_count,
|
|
UINT32 source_id, UINT32 *found_mode_index )
|
|
{
|
|
UINT32 i;
|
|
|
|
for (i = 0; i < modes_count; i++)
|
|
{
|
|
if (modes[i].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE &&
|
|
modes[i].id == source_id)
|
|
{
|
|
*found_mode_index = i;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserQueryDisplayConfig (win32u.@)
|
|
*/
|
|
LONG WINAPI NtUserQueryDisplayConfig( UINT32 flags, UINT32 *paths_count, DISPLAYCONFIG_PATH_INFO *paths,
|
|
UINT32 *modes_count, DISPLAYCONFIG_MODE_INFO *modes,
|
|
DISPLAYCONFIG_TOPOLOGY_ID *topology_id )
|
|
{
|
|
ULONG adapter_index;
|
|
LONG ret;
|
|
UINT32 output_id, source_mode_index, path_index = 0, mode_index = 0;
|
|
const LUID *gpu_luid;
|
|
DEVMODEW devmode;
|
|
struct monitor *monitor;
|
|
|
|
FIXME( "flags %#x, paths_count %p, paths %p, modes_count %p, modes %p, topology_id %p semi-stub\n",
|
|
flags, paths_count, paths, modes_count, modes, topology_id );
|
|
|
|
if (!paths_count || !modes_count)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (!*paths_count || !*modes_count)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (flags != QDC_ALL_PATHS &&
|
|
flags != QDC_ONLY_ACTIVE_PATHS &&
|
|
flags != QDC_DATABASE_CURRENT)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (((flags == QDC_DATABASE_CURRENT) && !topology_id) ||
|
|
((flags != QDC_DATABASE_CURRENT) && topology_id))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (flags != QDC_ONLY_ACTIVE_PATHS)
|
|
FIXME( "only returning active paths\n" );
|
|
|
|
if (topology_id)
|
|
{
|
|
FIXME( "setting toplogyid to DISPLAYCONFIG_TOPOLOGY_INTERNAL\n" );
|
|
*topology_id = DISPLAYCONFIG_TOPOLOGY_INTERNAL;
|
|
}
|
|
|
|
if (!lock_display_devices())
|
|
return ERROR_GEN_FAILURE;
|
|
|
|
ret = ERROR_GEN_FAILURE;
|
|
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
{
|
|
if (!is_monitor_active( monitor )) continue;
|
|
|
|
if (!monitor->adapter)
|
|
continue;
|
|
|
|
adapter_index = monitor->adapter->id;
|
|
gpu_luid = &monitor->adapter->gpu->luid;
|
|
output_id = monitor->output_id;
|
|
|
|
memset( &devmode, 0, sizeof(devmode) );
|
|
devmode.dmSize = sizeof(devmode);
|
|
if (!adapter_get_current_settings( monitor->adapter, &devmode ))
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
if (path_index == *paths_count || mode_index == *modes_count)
|
|
{
|
|
ret = ERROR_INSUFFICIENT_BUFFER;
|
|
goto done;
|
|
}
|
|
|
|
paths[path_index].flags = DISPLAYCONFIG_PATH_ACTIVE;
|
|
set_mode_target_info( &modes[mode_index], gpu_luid, output_id, flags, &devmode );
|
|
set_path_target_info( &paths[path_index].targetInfo, gpu_luid, output_id, mode_index, &devmode );
|
|
|
|
mode_index++;
|
|
if (mode_index == *modes_count)
|
|
{
|
|
ret = ERROR_INSUFFICIENT_BUFFER;
|
|
goto done;
|
|
}
|
|
|
|
/* Multiple targets can be driven by the same source, ensure a mode
|
|
* hasn't already been added for this source.
|
|
*/
|
|
if (!source_mode_exists( modes, mode_index, adapter_index, &source_mode_index ))
|
|
{
|
|
set_mode_source_info( &modes[mode_index], gpu_luid, adapter_index, &devmode );
|
|
source_mode_index = mode_index;
|
|
mode_index++;
|
|
}
|
|
set_path_source_info( &paths[path_index].sourceInfo, gpu_luid, adapter_index, source_mode_index );
|
|
path_index++;
|
|
}
|
|
|
|
*paths_count = path_index;
|
|
*modes_count = mode_index;
|
|
ret = ERROR_SUCCESS;
|
|
|
|
done:
|
|
unlock_display_devices();
|
|
return ret;
|
|
}
|
|
|
|
/* display_lock mutex must be held */
|
|
static struct monitor *find_monitor_by_index( struct adapter *adapter, UINT index )
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
|
|
if (monitor->adapter == adapter && index == monitor->id)
|
|
return monitor;
|
|
|
|
WARN( "Failed to find adapter %u monitor with id %u.\n", adapter->id, index );
|
|
return NULL;
|
|
}
|
|
|
|
/* display_lock mutex must be held */
|
|
static struct adapter *find_adapter_by_index( UINT index )
|
|
{
|
|
struct adapter *adapter;
|
|
|
|
LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
|
|
if (index == adapter->id) return adapter;
|
|
|
|
WARN( "Failed to find adapter with id %u.\n", index );
|
|
return NULL;
|
|
}
|
|
|
|
/* display_lock mutex must be held */
|
|
static struct adapter *find_primary_adapter(void)
|
|
{
|
|
struct adapter *adapter;
|
|
|
|
LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
|
|
if (adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
return adapter;
|
|
|
|
WARN( "Failed to find primary adapter.\n" );
|
|
return NULL;
|
|
}
|
|
|
|
/* display_lock mutex must be held */
|
|
static struct adapter *find_adapter_by_name( UNICODE_STRING *name )
|
|
{
|
|
struct adapter *adapter;
|
|
UINT index;
|
|
|
|
if (!(index = get_display_index( name ))) return NULL;
|
|
|
|
LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
|
|
if (adapter->id + 1 == index)
|
|
return adapter;
|
|
|
|
WARN( "Failed to find adapter with name %s.\n", debugstr_us(name) );
|
|
return NULL;
|
|
}
|
|
|
|
/* Find and acquire the adapter matching name, or primary adapter if name is NULL.
|
|
* If not NULL, the returned adapter needs to be released with adapter_release.
|
|
*/
|
|
static struct adapter *find_adapter( UNICODE_STRING *name )
|
|
{
|
|
struct adapter *adapter;
|
|
|
|
if (!lock_display_devices()) return NULL;
|
|
|
|
if (name && name->Length) adapter = find_adapter_by_name( name );
|
|
else adapter = find_primary_adapter();
|
|
|
|
if (adapter) adapter = adapter_acquire( adapter );
|
|
|
|
unlock_display_devices();
|
|
return adapter;
|
|
}
|
|
|
|
static void monitor_get_interface_name( struct monitor *monitor, WCHAR *interface_name )
|
|
{
|
|
char buffer[MAX_PATH] = {0}, *tmp;
|
|
const char *id;
|
|
|
|
*interface_name = 0;
|
|
if (!monitor->adapter) return;
|
|
|
|
if (!(monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID)) id = "Default_Monitor";
|
|
else id = monitor->edid_info.monitor_id_string;
|
|
|
|
sprintf( buffer, "\\\\?\\DISPLAY\\%s\\%04X&%04X#%s", id, monitor->adapter->id,
|
|
monitor->id, guid_devinterface_monitorA );
|
|
for (tmp = buffer + 4; *tmp; tmp++) if (*tmp == '\\') *tmp = '#';
|
|
|
|
asciiz_to_unicode( interface_name, buffer );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserEnumDisplayDevices (win32u.@)
|
|
*/
|
|
NTSTATUS WINAPI NtUserEnumDisplayDevices( UNICODE_STRING *device, DWORD index,
|
|
DISPLAY_DEVICEW *info, DWORD flags )
|
|
{
|
|
struct monitor *monitor = NULL;
|
|
struct adapter *adapter = NULL;
|
|
BOOL found = FALSE;
|
|
|
|
TRACE( "%s %u %p %#x\n", debugstr_us( device ), (int)index, info, (int)flags );
|
|
|
|
if (!info || !info->cb) return STATUS_UNSUCCESSFUL;
|
|
|
|
if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
|
|
|
|
if (!device || !device->Length)
|
|
{
|
|
if ((adapter = find_adapter_by_index( index ))) found = TRUE;
|
|
}
|
|
else if ((adapter = find_adapter_by_name( device )))
|
|
{
|
|
if ((monitor = find_monitor_by_index( adapter, index ))) found = TRUE;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
char buffer[MAX_PATH], *tmp;
|
|
|
|
if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceName) + sizeof(info->DeviceName))
|
|
{
|
|
if (monitor) sprintf( buffer, "\\\\.\\DISPLAY%d\\Monitor%d", adapter->id + 1, monitor->id );
|
|
else sprintf( buffer, "\\\\.\\DISPLAY%d", adapter->id + 1 );
|
|
asciiz_to_unicode( info->DeviceName, buffer );
|
|
}
|
|
if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceString) + sizeof(info->DeviceString))
|
|
{
|
|
if (monitor) asciiz_to_unicode( info->DeviceString, "Generic Non-PnP Monitor" );
|
|
else lstrcpynW( info->DeviceString, adapter->gpu->name, ARRAY_SIZE(info->DeviceString) );
|
|
}
|
|
if (info->cb >= offsetof(DISPLAY_DEVICEW, StateFlags) + sizeof(info->StateFlags))
|
|
{
|
|
if (!monitor) info->StateFlags = adapter->state_flags;
|
|
else
|
|
{
|
|
info->StateFlags = DISPLAY_DEVICE_ATTACHED;
|
|
if (is_monitor_active( monitor )) info->StateFlags |= DISPLAY_DEVICE_ACTIVE;
|
|
}
|
|
}
|
|
if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceID) + sizeof(info->DeviceID))
|
|
{
|
|
if (flags & EDD_GET_DEVICE_INTERFACE_NAME)
|
|
{
|
|
if (monitor) monitor_get_interface_name( monitor, info->DeviceID );
|
|
else *info->DeviceID = 0;
|
|
}
|
|
else
|
|
{
|
|
if (monitor)
|
|
{
|
|
sprintf( buffer, "MONITOR\\%s", monitor->path + 8 );
|
|
if (!(tmp = strrchr( buffer, '\\' ))) tmp = buffer + strlen( buffer );
|
|
sprintf( tmp, "\\%s\\%04X", guid_devclass_monitorA, monitor->output_id );
|
|
}
|
|
else
|
|
{
|
|
strcpy( buffer, adapter->gpu->path );
|
|
if ((tmp = strrchr( buffer, '\\' ))) *tmp = 0;
|
|
}
|
|
asciiz_to_unicode( info->DeviceID, buffer );
|
|
}
|
|
}
|
|
if (info->cb >= offsetof(DISPLAY_DEVICEW, DeviceKey) + sizeof(info->DeviceKey))
|
|
{
|
|
if (monitor) sprintf( buffer, "%s\\Class\\%s\\%04X", control_keyA, guid_devclass_monitorA, monitor->output_id );
|
|
else sprintf( buffer, "%s\\Video\\%s\\%04x", control_keyA, adapter->gpu->guid, adapter->id );
|
|
asciiz_to_unicode( info->DeviceKey, buffer );
|
|
}
|
|
}
|
|
unlock_display_devices();
|
|
return found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
#define _X_FIELD(prefix, bits) \
|
|
if ((fields) & prefix##_##bits) \
|
|
{ \
|
|
p += sprintf( p, "%s%s", first ? "" : ",", #bits ); \
|
|
first = FALSE; \
|
|
}
|
|
|
|
static const char *_CDS_flags( DWORD fields )
|
|
{
|
|
BOOL first = TRUE;
|
|
CHAR buf[128];
|
|
CHAR *p = buf;
|
|
|
|
_X_FIELD(CDS, UPDATEREGISTRY)
|
|
_X_FIELD(CDS, TEST)
|
|
_X_FIELD(CDS, FULLSCREEN)
|
|
_X_FIELD(CDS, GLOBAL)
|
|
_X_FIELD(CDS, SET_PRIMARY)
|
|
_X_FIELD(CDS, VIDEOPARAMETERS)
|
|
_X_FIELD(CDS, ENABLE_UNSAFE_MODES)
|
|
_X_FIELD(CDS, DISABLE_UNSAFE_MODES)
|
|
_X_FIELD(CDS, RESET)
|
|
_X_FIELD(CDS, RESET_EX)
|
|
_X_FIELD(CDS, NORESET)
|
|
|
|
*p = 0;
|
|
return wine_dbg_sprintf( "%s", buf );
|
|
}
|
|
|
|
static const char *_DM_fields( DWORD fields )
|
|
{
|
|
BOOL first = TRUE;
|
|
CHAR buf[128];
|
|
CHAR *p = buf;
|
|
|
|
_X_FIELD(DM, BITSPERPEL)
|
|
_X_FIELD(DM, PELSWIDTH)
|
|
_X_FIELD(DM, PELSHEIGHT)
|
|
_X_FIELD(DM, DISPLAYFLAGS)
|
|
_X_FIELD(DM, DISPLAYFREQUENCY)
|
|
_X_FIELD(DM, POSITION)
|
|
_X_FIELD(DM, DISPLAYORIENTATION)
|
|
|
|
*p = 0;
|
|
return wine_dbg_sprintf( "%s", buf );
|
|
}
|
|
|
|
#undef _X_FIELD
|
|
|
|
static void trace_devmode( const DEVMODEW *devmode )
|
|
{
|
|
TRACE( "dmFields=%s ", _DM_fields(devmode->dmFields) );
|
|
if (devmode->dmFields & DM_BITSPERPEL)
|
|
TRACE( "dmBitsPerPel=%u ", (int)devmode->dmBitsPerPel );
|
|
if (devmode->dmFields & DM_PELSWIDTH)
|
|
TRACE( "dmPelsWidth=%u ", (int)devmode->dmPelsWidth );
|
|
if (devmode->dmFields & DM_PELSHEIGHT)
|
|
TRACE( "dmPelsHeight=%u ", (int)devmode->dmPelsHeight );
|
|
if (devmode->dmFields & DM_DISPLAYFREQUENCY)
|
|
TRACE( "dmDisplayFrequency=%u ", (int)devmode->dmDisplayFrequency );
|
|
if (devmode->dmFields & DM_POSITION)
|
|
TRACE( "dmPosition=(%d,%d) ", (int)devmode->dmPosition.x, (int)devmode->dmPosition.y );
|
|
if (devmode->dmFields & DM_DISPLAYFLAGS)
|
|
TRACE( "dmDisplayFlags=%#x ", (int)devmode->dmDisplayFlags );
|
|
if (devmode->dmFields & DM_DISPLAYORIENTATION)
|
|
TRACE( "dmDisplayOrientation=%u ", (int)devmode->dmDisplayOrientation );
|
|
TRACE("\n");
|
|
}
|
|
|
|
static BOOL is_detached_mode( const DEVMODEW *mode )
|
|
{
|
|
return mode->dmFields & DM_POSITION &&
|
|
mode->dmFields & DM_PELSWIDTH &&
|
|
mode->dmFields & DM_PELSHEIGHT &&
|
|
mode->dmPelsWidth == 0 &&
|
|
mode->dmPelsHeight == 0;
|
|
}
|
|
|
|
static const DEVMODEW *find_display_mode( const DEVMODEW *modes, DEVMODEW *devmode )
|
|
{
|
|
const DEVMODEW *mode;
|
|
|
|
if (is_detached_mode( devmode )) return devmode;
|
|
|
|
for (mode = modes; mode && mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
|
{
|
|
if ((mode->dmFields & DM_DISPLAYFLAGS) && (mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
|
|
continue;
|
|
if ((devmode->dmFields & DM_BITSPERPEL) && devmode->dmBitsPerPel && devmode->dmBitsPerPel != mode->dmBitsPerPel)
|
|
continue;
|
|
if ((devmode->dmFields & DM_PELSWIDTH) && devmode->dmPelsWidth != mode->dmPelsWidth)
|
|
continue;
|
|
if ((devmode->dmFields & DM_PELSHEIGHT) && devmode->dmPelsHeight != mode->dmPelsHeight)
|
|
continue;
|
|
if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && devmode->dmDisplayFrequency != mode->dmDisplayFrequency
|
|
&& devmode->dmDisplayFrequency > 1 && mode->dmDisplayFrequency)
|
|
continue;
|
|
if ((devmode->dmFields & DM_DISPLAYORIENTATION) && devmode->dmDisplayOrientation != mode->dmDisplayOrientation)
|
|
continue;
|
|
if ((devmode->dmFields & DM_DISPLAYFLAGS) && (mode->dmFields & DM_DISPLAYFLAGS) &&
|
|
(devmode->dmDisplayFlags & DM_INTERLACED) != (mode->dmDisplayFlags & DM_INTERLACED))
|
|
continue;
|
|
if ((devmode->dmFields & DM_DISPLAYFIXEDOUTPUT) && (mode->dmFields & DM_DISPLAYFIXEDOUTPUT) &&
|
|
devmode->dmDisplayFixedOutput != mode->dmDisplayFixedOutput)
|
|
continue;
|
|
|
|
return mode;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static BOOL adapter_get_full_mode( const struct adapter *adapter, const DEVMODEW *devmode, DEVMODEW *full_mode )
|
|
{
|
|
const DEVMODEW *adapter_mode;
|
|
|
|
if (devmode)
|
|
{
|
|
trace_devmode( devmode );
|
|
|
|
if (devmode->dmSize < offsetof(DEVMODEW, dmICMMethod)) return FALSE;
|
|
if (!is_detached_mode( devmode ) &&
|
|
(!(devmode->dmFields & DM_BITSPERPEL) || !devmode->dmBitsPerPel) &&
|
|
(!(devmode->dmFields & DM_PELSWIDTH) || !devmode->dmPelsWidth) &&
|
|
(!(devmode->dmFields & DM_PELSHEIGHT) || !devmode->dmPelsHeight) &&
|
|
(!(devmode->dmFields & DM_DISPLAYFREQUENCY) || !devmode->dmDisplayFrequency))
|
|
devmode = NULL;
|
|
}
|
|
|
|
if (devmode) memcpy( full_mode, devmode, devmode->dmSize );
|
|
else
|
|
{
|
|
if (!adapter_get_registry_settings( adapter, full_mode )) return FALSE;
|
|
TRACE( "Return to original display mode\n" );
|
|
}
|
|
|
|
if ((full_mode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
|
|
{
|
|
WARN( "devmode doesn't specify the resolution: %#x\n", (int)full_mode->dmFields );
|
|
return FALSE;
|
|
}
|
|
|
|
if (!is_detached_mode( full_mode ) && (!full_mode->dmPelsWidth || !full_mode->dmPelsHeight || !(full_mode->dmFields & DM_POSITION)))
|
|
{
|
|
DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
|
|
|
|
if (!adapter_get_current_settings( adapter, ¤t_mode )) return FALSE;
|
|
if (!full_mode->dmPelsWidth) full_mode->dmPelsWidth = current_mode.dmPelsWidth;
|
|
if (!full_mode->dmPelsHeight) full_mode->dmPelsHeight = current_mode.dmPelsHeight;
|
|
if (!(full_mode->dmFields & DM_POSITION))
|
|
{
|
|
full_mode->dmPosition = current_mode.dmPosition;
|
|
full_mode->dmFields |= DM_POSITION;
|
|
}
|
|
}
|
|
|
|
if ((adapter_mode = find_display_mode( adapter->modes, full_mode )) && adapter_mode != full_mode)
|
|
{
|
|
POINTL position = full_mode->dmPosition;
|
|
*full_mode = *adapter_mode;
|
|
full_mode->dmFields |= DM_POSITION;
|
|
full_mode->dmPosition = position;
|
|
}
|
|
|
|
return adapter_mode != NULL;
|
|
}
|
|
|
|
static DEVMODEW *get_display_settings( struct adapter *target, const DEVMODEW *devmode )
|
|
{
|
|
DEVMODEW *mode, *displays;
|
|
struct adapter *adapter;
|
|
BOOL ret;
|
|
|
|
/* allocate an extra mode for easier iteration */
|
|
if (!(displays = calloc( list_count( &adapters ) + 1, sizeof(DEVMODEW) ))) return NULL;
|
|
mode = displays;
|
|
|
|
LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
|
|
{
|
|
char buffer[CCHDEVICENAME];
|
|
|
|
mode->dmSize = sizeof(DEVMODEW);
|
|
if (devmode && adapter->id == target->id)
|
|
memcpy( &mode->dmFields, &devmode->dmFields, devmode->dmSize - offsetof(DEVMODEW, dmFields) );
|
|
else
|
|
{
|
|
if (!target) ret = adapter_get_registry_settings( adapter, mode );
|
|
else ret = adapter_get_current_settings( adapter, mode );
|
|
if (!ret)
|
|
{
|
|
free( displays );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
sprintf( buffer, "\\\\.\\DISPLAY%d", adapter->id + 1 );
|
|
asciiz_to_unicode( mode->dmDeviceName, buffer );
|
|
mode = NEXT_DEVMODEW(mode);
|
|
}
|
|
|
|
return displays;
|
|
}
|
|
|
|
static INT offset_length( POINT offset )
|
|
{
|
|
return offset.x * offset.x + offset.y * offset.y;
|
|
}
|
|
|
|
static void set_rect_from_devmode( RECT *rect, const DEVMODEW *mode )
|
|
{
|
|
SetRect( rect, mode->dmPosition.x, mode->dmPosition.y, mode->dmPosition.x + mode->dmPelsWidth,
|
|
mode->dmPosition.y + mode->dmPelsHeight );
|
|
}
|
|
|
|
/* Check if a rect overlaps with placed display rects */
|
|
static BOOL overlap_placed_displays( const RECT *rect, const DEVMODEW *displays )
|
|
{
|
|
const DEVMODEW *mode;
|
|
RECT intersect;
|
|
|
|
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
|
{
|
|
set_rect_from_devmode( &intersect, mode );
|
|
if ((mode->dmFields & DM_POSITION) && intersect_rect( &intersect, &intersect, rect )) return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */
|
|
static POINT get_placement_offset( const DEVMODEW *displays, const DEVMODEW *placing )
|
|
{
|
|
POINT points[8], left_top, offset, min_offset = {0, 0};
|
|
INT point_idx, point_count, vertex_idx;
|
|
BOOL has_placed = FALSE, first = TRUE;
|
|
RECT desired_rect, rect;
|
|
const DEVMODEW *mode;
|
|
INT width, height;
|
|
|
|
set_rect_from_devmode( &desired_rect, placing );
|
|
|
|
/* If the display to be placed is detached, no offset is needed to place it */
|
|
if (IsRectEmpty( &desired_rect )) return min_offset;
|
|
|
|
/* If there is no placed and attached display, place this display as it is */
|
|
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
|
{
|
|
set_rect_from_devmode( &rect, mode );
|
|
if ((mode->dmFields & DM_POSITION) && !IsRectEmpty( &rect ))
|
|
{
|
|
has_placed = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!has_placed) return min_offset;
|
|
|
|
/* Try to place this display with each of its four vertices at every vertex of the placed
|
|
* displays and see which combination has the minimum offset length */
|
|
width = desired_rect.right - desired_rect.left;
|
|
height = desired_rect.bottom - desired_rect.top;
|
|
|
|
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
|
{
|
|
set_rect_from_devmode( &rect, mode );
|
|
if (!(mode->dmFields & DM_POSITION) || IsRectEmpty( &rect )) continue;
|
|
|
|
/* Get four vertices of the placed display rectangle */
|
|
points[0].x = rect.left;
|
|
points[0].y = rect.top;
|
|
points[1].x = rect.left;
|
|
points[1].y = rect.bottom;
|
|
points[2].x = rect.right;
|
|
points[2].y = rect.top;
|
|
points[3].x = rect.right;
|
|
points[3].y = rect.bottom;
|
|
point_count = 4;
|
|
|
|
/* Intersected points when moving the display to be placed horizontally */
|
|
if (desired_rect.bottom >= rect.top && desired_rect.top <= rect.bottom)
|
|
{
|
|
points[point_count].x = rect.left;
|
|
points[point_count++].y = desired_rect.top;
|
|
points[point_count].x = rect.right;
|
|
points[point_count++].y = desired_rect.top;
|
|
}
|
|
/* Intersected points when moving the display to be placed vertically */
|
|
if (desired_rect.left <= rect.right && desired_rect.right >= rect.left)
|
|
{
|
|
points[point_count].x = desired_rect.left;
|
|
points[point_count++].y = rect.top;
|
|
points[point_count].x = desired_rect.left;
|
|
points[point_count++].y = rect.bottom;
|
|
}
|
|
|
|
/* Try moving each vertex of the display rectangle to each points */
|
|
for (point_idx = 0; point_idx < point_count; ++point_idx)
|
|
{
|
|
for (vertex_idx = 0; vertex_idx < 4; ++vertex_idx)
|
|
{
|
|
switch (vertex_idx)
|
|
{
|
|
/* Move the bottom right vertex to the point */
|
|
case 0:
|
|
left_top.x = points[point_idx].x - width;
|
|
left_top.y = points[point_idx].y - height;
|
|
break;
|
|
/* Move the bottom left vertex to the point */
|
|
case 1:
|
|
left_top.x = points[point_idx].x;
|
|
left_top.y = points[point_idx].y - height;
|
|
break;
|
|
/* Move the top right vertex to the point */
|
|
case 2:
|
|
left_top.x = points[point_idx].x - width;
|
|
left_top.y = points[point_idx].y;
|
|
break;
|
|
/* Move the top left vertex to the point */
|
|
case 3:
|
|
left_top.x = points[point_idx].x;
|
|
left_top.y = points[point_idx].y;
|
|
break;
|
|
}
|
|
|
|
offset.x = left_top.x - desired_rect.left;
|
|
offset.y = left_top.y - desired_rect.top;
|
|
rect = desired_rect;
|
|
OffsetRect( &rect, offset.x, offset.y );
|
|
if (!overlap_placed_displays( &rect, displays ))
|
|
{
|
|
if (first)
|
|
{
|
|
min_offset = offset;
|
|
first = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (offset_length( offset ) < offset_length( min_offset )) min_offset = offset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return min_offset;
|
|
}
|
|
|
|
static void place_all_displays( DEVMODEW *displays )
|
|
{
|
|
POINT min_offset, offset;
|
|
DEVMODEW *mode, *placing;
|
|
|
|
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
|
mode->dmFields &= ~DM_POSITION;
|
|
|
|
/* Place all displays with no extra space between them and no overlapping */
|
|
while (1)
|
|
{
|
|
/* Place the unplaced display with the minimum offset length first */
|
|
placing = NULL;
|
|
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
|
{
|
|
if (mode->dmFields & DM_POSITION) continue;
|
|
|
|
offset = get_placement_offset( displays, mode );
|
|
if (!placing || offset_length( offset ) < offset_length( min_offset ))
|
|
{
|
|
min_offset = offset;
|
|
placing = mode;
|
|
}
|
|
}
|
|
|
|
/* If all displays are placed */
|
|
if (!placing) break;
|
|
|
|
placing->dmPosition.x += min_offset.x;
|
|
placing->dmPosition.y += min_offset.y;
|
|
placing->dmFields |= DM_POSITION;
|
|
}
|
|
}
|
|
|
|
static BOOL all_detached_settings( const DEVMODEW *displays )
|
|
{
|
|
const DEVMODEW *mode;
|
|
|
|
for (mode = displays; mode->dmSize; mode = NEXT_DEVMODEW(mode))
|
|
if (!is_detached_mode( mode )) return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static LONG apply_display_settings( struct adapter *target, const DEVMODEW *devmode,
|
|
HWND hwnd, DWORD flags, void *lparam )
|
|
{
|
|
WCHAR primary_name[CCHDEVICENAME];
|
|
struct adapter *primary, *adapter;
|
|
DEVMODEW *mode, *displays;
|
|
LONG ret;
|
|
|
|
if (!lock_display_devices()) return DISP_CHANGE_FAILED;
|
|
if (!(displays = get_display_settings( target, devmode )))
|
|
{
|
|
unlock_display_devices();
|
|
return DISP_CHANGE_FAILED;
|
|
}
|
|
|
|
if (all_detached_settings( displays ))
|
|
{
|
|
unlock_display_devices();
|
|
WARN( "Detaching all modes is not permitted.\n" );
|
|
free( displays );
|
|
return DISP_CHANGE_SUCCESSFUL;
|
|
}
|
|
|
|
place_all_displays( displays );
|
|
|
|
if (!(primary = find_primary_adapter())) primary_name[0] = 0;
|
|
else
|
|
{
|
|
char device_name[CCHDEVICENAME];
|
|
sprintf( device_name, "\\\\.\\DISPLAY%d", primary->id + 1 );
|
|
asciiz_to_unicode( primary_name, device_name );
|
|
}
|
|
|
|
/* use the default implementation in virtual desktop mode */
|
|
if (is_virtual_desktop()) ret = E_NOTIMPL;
|
|
else ret = user_driver->pChangeDisplaySettings( displays, primary_name, hwnd, flags, lparam );
|
|
|
|
if (ret == E_NOTIMPL)
|
|
{
|
|
/* default implementation: write current display settings to the registry. */
|
|
mode = displays;
|
|
LIST_FOR_EACH_ENTRY( adapter, &adapters, struct adapter, entry )
|
|
{
|
|
if (!adapter_set_current_settings( adapter, mode ))
|
|
WARN( "Failed to write adapter %u current mode.\n", adapter->id );
|
|
mode = NEXT_DEVMODEW(mode);
|
|
}
|
|
ret = DISP_CHANGE_SUCCESSFUL;
|
|
}
|
|
unlock_display_devices();
|
|
|
|
free( displays );
|
|
if (ret) return ret;
|
|
|
|
if (!update_display_cache( TRUE ))
|
|
WARN( "Failed to update display cache after mode change.\n" );
|
|
|
|
if ((adapter = find_adapter( NULL )))
|
|
{
|
|
DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
|
|
|
|
if (!adapter_get_current_settings( adapter, ¤t_mode )) WARN( "Failed to get primary adapter current display settings.\n" );
|
|
adapter_release( adapter );
|
|
|
|
NtUserClipCursor( NULL );
|
|
send_notify_message( NtUserGetDesktopWindow(), WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
|
|
MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), FALSE );
|
|
send_message_timeout( HWND_BROADCAST, WM_DISPLAYCHANGE, current_mode.dmBitsPerPel,
|
|
MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ),
|
|
SMTO_ABORTIFHUNG, 2000, FALSE );
|
|
/* post clip_fullscreen_window request to the foreground window */
|
|
NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_CLIPCURSOR, SET_CURSOR_FSCLIP, 0 );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserChangeDisplaySettings (win32u.@)
|
|
*/
|
|
LONG WINAPI NtUserChangeDisplaySettings( UNICODE_STRING *devname, DEVMODEW *devmode, HWND hwnd,
|
|
DWORD flags, void *lparam )
|
|
{
|
|
DEVMODEW full_mode = {.dmSize = sizeof(DEVMODEW)};
|
|
int ret = DISP_CHANGE_SUCCESSFUL;
|
|
struct adapter *adapter;
|
|
|
|
TRACE( "%s %p %p %#x %p\n", debugstr_us(devname), devmode, hwnd, (int)flags, lparam );
|
|
TRACE( "flags=%s\n", _CDS_flags(flags) );
|
|
|
|
if ((!devname || !devname->Length) && !devmode) return apply_display_settings( NULL, NULL, hwnd, flags, lparam );
|
|
|
|
if (!(adapter = find_adapter( devname ))) return DISP_CHANGE_BADPARAM;
|
|
|
|
if (!adapter_get_full_mode( adapter, devmode, &full_mode )) ret = DISP_CHANGE_BADMODE;
|
|
else if ((flags & CDS_UPDATEREGISTRY) && !adapter_set_registry_settings( adapter, &full_mode )) ret = DISP_CHANGE_NOTUPDATED;
|
|
else if (flags & (CDS_TEST | CDS_NORESET)) ret = DISP_CHANGE_SUCCESSFUL;
|
|
else ret = apply_display_settings( adapter, &full_mode, hwnd, flags, lparam );
|
|
adapter_release( adapter );
|
|
|
|
if (ret) ERR( "Changing %s display settings returned %d.\n", debugstr_us(devname), ret );
|
|
return ret;
|
|
}
|
|
|
|
static BOOL adapter_enum_display_settings( const struct adapter *adapter, UINT index, DEVMODEW *devmode, UINT flags )
|
|
{
|
|
DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
|
|
const DEVMODEW *adapter_mode;
|
|
|
|
if (!(flags & EDS_ROTATEDMODE) && !adapter_get_current_settings( adapter, ¤t_mode ))
|
|
{
|
|
WARN( "Failed to query current display mode for EDS_ROTATEDMODE flag.\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
for (adapter_mode = adapter->modes; adapter_mode->dmSize; adapter_mode = NEXT_DEVMODEW(adapter_mode))
|
|
{
|
|
if (!(flags & EDS_ROTATEDMODE) && (adapter_mode->dmFields & DM_DISPLAYORIENTATION) &&
|
|
adapter_mode->dmDisplayOrientation != current_mode.dmDisplayOrientation)
|
|
continue;
|
|
if (!(flags & EDS_RAWMODE) && (adapter_mode->dmFields & DM_DISPLAYFLAGS) &&
|
|
(adapter_mode->dmDisplayFlags & WINE_DM_UNSUPPORTED))
|
|
continue;
|
|
if (!index--)
|
|
{
|
|
memcpy( &devmode->dmFields, &adapter_mode->dmFields, devmode->dmSize - FIELD_OFFSET(DEVMODEW, dmFields) );
|
|
devmode->dmDisplayFlags &= ~WINE_DM_UNSUPPORTED;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
WARN( "device %d, index %#x, flags %#x display mode not found.\n", adapter->id, index, flags );
|
|
RtlSetLastWin32Error( ERROR_NO_MORE_FILES );
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserEnumDisplaySettings (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserEnumDisplaySettings( UNICODE_STRING *device, DWORD index, DEVMODEW *devmode, DWORD flags )
|
|
{
|
|
static const WCHAR wine_display_driverW[] = {'W','i','n','e',' ','D','i','s','p','l','a','y',' ','D','r','i','v','e','r',0};
|
|
struct adapter *adapter;
|
|
BOOL ret;
|
|
|
|
TRACE( "device %s, index %#x, devmode %p, flags %#x\n",
|
|
debugstr_us(device), (int)index, devmode, (int)flags );
|
|
|
|
if (!(adapter = find_adapter( device ))) return FALSE;
|
|
|
|
lstrcpynW( devmode->dmDeviceName, wine_display_driverW, ARRAY_SIZE(devmode->dmDeviceName) );
|
|
devmode->dmSpecVersion = DM_SPECVERSION;
|
|
devmode->dmDriverVersion = DM_SPECVERSION;
|
|
devmode->dmSize = offsetof(DEVMODEW, dmICMMethod);
|
|
devmode->dmDriverExtra = 0;
|
|
|
|
if (index == ENUM_REGISTRY_SETTINGS) ret = adapter_get_registry_settings( adapter, devmode );
|
|
else if (index == ENUM_CURRENT_SETTINGS) ret = adapter_get_current_settings( adapter, devmode );
|
|
else ret = adapter_enum_display_settings( adapter, index, devmode, flags );
|
|
adapter_release( adapter );
|
|
|
|
if (!ret) WARN( "Failed to query %s display settings.\n", debugstr_us(device) );
|
|
else TRACE( "position %dx%d, resolution %ux%u, frequency %u, depth %u, orientation %#x.\n",
|
|
(int)devmode->dmPosition.x, (int)devmode->dmPosition.y, (int)devmode->dmPelsWidth,
|
|
(int)devmode->dmPelsHeight, (int)devmode->dmDisplayFrequency,
|
|
(int)devmode->dmBitsPerPel, (int)devmode->dmDisplayOrientation );
|
|
return ret;
|
|
}
|
|
|
|
struct monitor_enum_info
|
|
{
|
|
HANDLE handle;
|
|
RECT rect;
|
|
};
|
|
|
|
static unsigned int active_unique_monitor_count(void)
|
|
{
|
|
struct monitor *monitor;
|
|
unsigned int count = 0;
|
|
|
|
LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
|
|
{
|
|
if (is_monitor_active( monitor ) && !monitor->is_clone)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
INT get_display_depth( UNICODE_STRING *name )
|
|
{
|
|
WCHAR device_nameW[CCHDEVICENAME];
|
|
char device_name[CCHDEVICENAME];
|
|
struct adapter *adapter;
|
|
BOOL is_primary;
|
|
INT depth;
|
|
|
|
if (!lock_display_devices())
|
|
return 32;
|
|
|
|
if (name && name->Length) adapter = find_adapter_by_name( name );
|
|
else adapter = find_primary_adapter();
|
|
|
|
if (!adapter)
|
|
{
|
|
unlock_display_devices();
|
|
return 32;
|
|
}
|
|
|
|
is_primary = !!(adapter->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE);
|
|
sprintf( device_name, "\\\\.\\DISPLAY%d", adapter->id + 1 );
|
|
asciiz_to_unicode( device_nameW, device_name );
|
|
|
|
/* use the default implementation in virtual desktop mode */
|
|
if (is_virtual_desktop()) depth = -1;
|
|
else depth = user_driver->pGetDisplayDepth( device_nameW, is_primary );
|
|
|
|
if (depth < 0)
|
|
{
|
|
DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
|
|
if (!adapter_get_current_settings( adapter, ¤t_mode )) depth = 32;
|
|
else depth = current_mode.dmBitsPerPel;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
return depth;
|
|
}
|
|
|
|
static BOOL should_enumerate_monitor( struct monitor *monitor, const POINT *origin,
|
|
const RECT *limit, RECT *rect )
|
|
{
|
|
if (!is_monitor_active( monitor )) return FALSE;
|
|
if (monitor->is_clone) return FALSE;
|
|
|
|
*rect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ),
|
|
get_thread_dpi() );
|
|
OffsetRect( rect, -origin->x, -origin->y );
|
|
return intersect_rect( rect, rect, limit );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserEnumDisplayMonitors (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserEnumDisplayMonitors( HDC hdc, RECT *rect, MONITORENUMPROC proc, LPARAM lparam )
|
|
{
|
|
struct monitor_enum_info enum_buf[8], *enum_info = enum_buf;
|
|
struct enum_display_monitor_params params;
|
|
struct monitor *monitor;
|
|
unsigned int count = 0, i;
|
|
POINT origin;
|
|
RECT limit;
|
|
BOOL ret = TRUE;
|
|
|
|
if (hdc)
|
|
{
|
|
DC *dc;
|
|
if (!(dc = get_dc_ptr( hdc ))) return FALSE;
|
|
origin.x = dc->attr->vis_rect.left;
|
|
origin.y = dc->attr->vis_rect.top;
|
|
release_dc_ptr( dc );
|
|
if (NtGdiGetAppClipBox( hdc, &limit ) == ERROR) return FALSE;
|
|
}
|
|
else
|
|
{
|
|
origin.x = origin.y = 0;
|
|
limit.left = limit.top = INT_MIN;
|
|
limit.right = limit.bottom = INT_MAX;
|
|
}
|
|
if (rect && !intersect_rect( &limit, &limit, rect )) return TRUE;
|
|
|
|
if (!lock_display_devices()) return FALSE;
|
|
|
|
count = list_count( &monitors );
|
|
if (!count || (count > ARRAYSIZE(enum_buf) &&
|
|
!(enum_info = malloc( count * sizeof(*enum_info) ))))
|
|
{
|
|
unlock_display_devices();
|
|
return FALSE;
|
|
}
|
|
|
|
count = 0;
|
|
|
|
/* enumerate primary monitors first */
|
|
LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
|
|
{
|
|
if (!is_monitor_primary( monitor )) continue;
|
|
if (should_enumerate_monitor( monitor, &origin, &limit, &enum_info[count].rect ))
|
|
{
|
|
enum_info[count++].handle = monitor->handle;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* then non-primary monitors */
|
|
LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
|
|
{
|
|
if (is_monitor_primary( monitor )) continue;
|
|
if (should_enumerate_monitor( monitor, &origin, &limit, &enum_info[count].rect ))
|
|
enum_info[count++].handle = monitor->handle;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
|
|
params.proc = proc;
|
|
params.hdc = hdc;
|
|
params.lparam = lparam;
|
|
for (i = 0; i < count && ret; i++)
|
|
{
|
|
void *ret_ptr;
|
|
ULONG ret_len;
|
|
NTSTATUS status;
|
|
params.monitor = enum_info[i].handle;
|
|
params.rect = enum_info[i].rect;
|
|
status = KeUserModeCallback( NtUserCallEnumDisplayMonitor, ¶ms, sizeof(params),
|
|
&ret_ptr, &ret_len );
|
|
if (!status && ret_len == sizeof(ret)) ret = *(BOOL *)ret_ptr;
|
|
else ret = FALSE;
|
|
}
|
|
if (enum_info != enum_buf) free( enum_info );
|
|
return ret;
|
|
}
|
|
|
|
BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info )
|
|
{
|
|
struct monitor *monitor;
|
|
UINT dpi_from, dpi_to;
|
|
|
|
if (info->cbSize != sizeof(MONITORINFOEXW) && info->cbSize != sizeof(MONITORINFO)) return FALSE;
|
|
|
|
if (!lock_display_devices()) return FALSE;
|
|
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
{
|
|
if (monitor->handle != handle) continue;
|
|
if (!is_monitor_active( monitor )) continue;
|
|
|
|
/* FIXME: map dpi */
|
|
info->rcMonitor = monitor->rc_monitor;
|
|
info->rcWork = monitor->rc_work;
|
|
info->dwFlags = is_monitor_primary( monitor ) ? MONITORINFOF_PRIMARY : 0;
|
|
if (info->cbSize >= sizeof(MONITORINFOEXW))
|
|
{
|
|
char buffer[CCHDEVICENAME];
|
|
if (monitor->adapter) sprintf( buffer, "\\\\.\\DISPLAY%d", monitor->adapter->id + 1 );
|
|
else strcpy( buffer, "WinDisc" );
|
|
asciiz_to_unicode( ((MONITORINFOEXW *)info)->szDevice, buffer );
|
|
}
|
|
unlock_display_devices();
|
|
|
|
if ((dpi_to = get_thread_dpi()))
|
|
{
|
|
dpi_from = get_monitor_dpi( handle );
|
|
info->rcMonitor = map_dpi_rect( info->rcMonitor, dpi_from, dpi_to );
|
|
info->rcWork = map_dpi_rect( info->rcWork, dpi_from, dpi_to );
|
|
}
|
|
TRACE( "flags %04x, monitor %s, work %s\n", (int)info->dwFlags,
|
|
wine_dbgstr_rect(&info->rcMonitor), wine_dbgstr_rect(&info->rcWork));
|
|
return TRUE;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
WARN( "invalid handle %p\n", handle );
|
|
RtlSetLastWin32Error( ERROR_INVALID_MONITOR_HANDLE );
|
|
return FALSE;
|
|
}
|
|
|
|
HMONITOR monitor_from_rect( const RECT *rect, UINT flags, UINT dpi )
|
|
{
|
|
HMONITOR primary = 0, nearest = 0, ret = 0;
|
|
UINT max_area = 0, min_distance = ~0u;
|
|
struct monitor *monitor;
|
|
RECT r;
|
|
|
|
r = map_dpi_rect( *rect, dpi, system_dpi );
|
|
if (IsRectEmpty( &r ))
|
|
{
|
|
r.right = r.left + 1;
|
|
r.bottom = r.top + 1;
|
|
}
|
|
|
|
if (!lock_display_devices()) return 0;
|
|
|
|
LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
|
|
{
|
|
RECT intersect, monitor_rect;
|
|
|
|
if (!is_monitor_active( monitor ) || monitor->is_clone) continue;
|
|
|
|
monitor_rect = map_dpi_rect( monitor->rc_monitor, get_monitor_dpi( monitor->handle ), system_dpi );
|
|
if (intersect_rect( &intersect, &monitor_rect, &r ))
|
|
{
|
|
/* check for larger intersecting area */
|
|
UINT area = (intersect.right - intersect.left) * (intersect.bottom - intersect.top);
|
|
if (area > max_area)
|
|
{
|
|
max_area = area;
|
|
ret = monitor->handle;
|
|
}
|
|
}
|
|
else if (!max_area) /* if not intersecting, check for min distance */
|
|
{
|
|
UINT distance;
|
|
UINT x, y;
|
|
|
|
if (r.right <= monitor_rect.left) x = monitor_rect.left - r.right;
|
|
else if (monitor_rect.right <= r.left) x = r.left - monitor_rect.right;
|
|
else x = 0;
|
|
if (r.bottom <= monitor_rect.top) y = monitor_rect.top - r.bottom;
|
|
else if (monitor_rect.bottom <= r.top) y = r.top - monitor_rect.bottom;
|
|
else y = 0;
|
|
distance = x * x + y * y;
|
|
if (distance < min_distance)
|
|
{
|
|
min_distance = distance;
|
|
nearest = monitor->handle;
|
|
}
|
|
}
|
|
|
|
if (is_monitor_primary( monitor )) primary = monitor->handle;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
|
|
if (!ret)
|
|
{
|
|
if (flags & MONITOR_DEFAULTTOPRIMARY) ret = primary;
|
|
else if (flags & MONITOR_DEFAULTTONEAREST) ret = nearest;
|
|
}
|
|
|
|
TRACE( "%s flags %x returning %p\n", wine_dbgstr_rect(rect), flags, ret );
|
|
return ret;
|
|
}
|
|
|
|
HMONITOR monitor_from_point( POINT pt, UINT flags, UINT dpi )
|
|
{
|
|
RECT rect;
|
|
SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 );
|
|
return monitor_from_rect( &rect, flags, dpi );
|
|
}
|
|
|
|
/* see MonitorFromWindow */
|
|
HMONITOR monitor_from_window( HWND hwnd, UINT flags, UINT dpi )
|
|
{
|
|
RECT rect;
|
|
WINDOWPLACEMENT wp;
|
|
|
|
TRACE( "(%p, 0x%08x)\n", hwnd, flags );
|
|
|
|
wp.length = sizeof(wp);
|
|
if (is_iconic( hwnd ) && NtUserGetWindowPlacement( hwnd, &wp ))
|
|
return monitor_from_rect( &wp.rcNormalPosition, flags, dpi );
|
|
|
|
if (get_window_rect( hwnd, &rect, dpi ))
|
|
return monitor_from_rect( &rect, flags, dpi );
|
|
|
|
if (!(flags & (MONITOR_DEFAULTTOPRIMARY|MONITOR_DEFAULTTONEAREST))) return 0;
|
|
/* retrieve the primary */
|
|
SetRect( &rect, 0, 0, 1, 1 );
|
|
return monitor_from_rect( &rect, flags, dpi );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserGetSystemDpiForProcess (win32u.@)
|
|
*/
|
|
ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process )
|
|
{
|
|
if (process && process != GetCurrentProcess())
|
|
{
|
|
FIXME( "not supported on other process %p\n", process );
|
|
return 0;
|
|
}
|
|
|
|
return system_dpi;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserGetDpiForMonitor (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserGetDpiForMonitor( HMONITOR monitor, UINT type, UINT *x, UINT *y )
|
|
{
|
|
if (type > 2)
|
|
{
|
|
RtlSetLastWin32Error( ERROR_BAD_ARGUMENTS );
|
|
return FALSE;
|
|
}
|
|
if (!x || !y)
|
|
{
|
|
RtlSetLastWin32Error( ERROR_INVALID_ADDRESS );
|
|
return FALSE;
|
|
}
|
|
switch (get_thread_dpi_awareness())
|
|
{
|
|
case DPI_AWARENESS_UNAWARE: *x = *y = USER_DEFAULT_SCREEN_DPI; break;
|
|
case DPI_AWARENESS_SYSTEM_AWARE: *x = *y = system_dpi; break;
|
|
default: *x = *y = get_monitor_dpi( monitor ); break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* LogicalToPhysicalPointForPerMonitorDPI (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserLogicalToPerMonitorDPIPhysicalPoint( HWND hwnd, POINT *pt )
|
|
{
|
|
RECT rect;
|
|
|
|
if (!get_window_rect( hwnd, &rect, get_thread_dpi() )) return FALSE;
|
|
if (pt->x < rect.left || pt->y < rect.top || pt->x > rect.right || pt->y > rect.bottom) return FALSE;
|
|
*pt = point_win_to_phys_dpi( hwnd, *pt );
|
|
return TRUE;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* NtUserPerMonitorDPIPhysicalToLogicalPoint (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserPerMonitorDPIPhysicalToLogicalPoint( HWND hwnd, POINT *pt )
|
|
{
|
|
RECT rect;
|
|
BOOL ret = FALSE;
|
|
|
|
if (get_window_rect( hwnd, &rect, 0 ) &&
|
|
pt->x >= rect.left && pt->y >= rect.top && pt->x <= rect.right && pt->y <= rect.bottom)
|
|
{
|
|
*pt = point_phys_to_win_dpi( hwnd, *pt );
|
|
ret = TRUE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* retrieve the cached base keys for a given entry */
|
|
static BOOL get_base_keys( enum parameter_key index, HKEY *base_key, HKEY *volatile_key )
|
|
{
|
|
static HKEY base_keys[NB_PARAM_KEYS];
|
|
static HKEY volatile_keys[NB_PARAM_KEYS];
|
|
HKEY key;
|
|
|
|
if (!base_keys[index] && base_key)
|
|
{
|
|
if (!(key = reg_create_ascii_key( hkcu_key, parameter_key_names[index], 0, NULL )))
|
|
return FALSE;
|
|
if (InterlockedCompareExchangePointer( (void **)&base_keys[index], key, 0 ))
|
|
NtClose( key );
|
|
}
|
|
if (!volatile_keys[index] && volatile_key)
|
|
{
|
|
if (!(key = reg_create_ascii_key( volatile_base_key, parameter_key_names[index], REG_OPTION_VOLATILE, NULL )))
|
|
return FALSE;
|
|
if (InterlockedCompareExchangePointer( (void **)&volatile_keys[index], key, 0 ))
|
|
NtClose( key );
|
|
}
|
|
if (base_key) *base_key = base_keys[index];
|
|
if (volatile_key) *volatile_key = volatile_keys[index];
|
|
return TRUE;
|
|
}
|
|
|
|
/* load a value to a registry entry */
|
|
static DWORD load_entry( struct sysparam_entry *entry, void *data, DWORD size )
|
|
{
|
|
char buffer[4096];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
DWORD count;
|
|
HKEY base_key, volatile_key;
|
|
|
|
if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
|
|
|
|
if (!(count = query_reg_ascii_value( volatile_key, entry->regval, value, sizeof(buffer) )))
|
|
count = query_reg_ascii_value( base_key, entry->regval, value, sizeof(buffer) );
|
|
if (count > size)
|
|
{
|
|
count = size;
|
|
/* make sure strings are null-terminated */
|
|
if (value->Type == REG_SZ) ((WCHAR *)value->Data)[count / sizeof(WCHAR) - 1] = 0;
|
|
}
|
|
if (count) memcpy( data, value->Data, count );
|
|
entry->loaded = TRUE;
|
|
return count;
|
|
}
|
|
|
|
/* save a value to a registry entry */
|
|
static BOOL save_entry( const struct sysparam_entry *entry, const void *data, DWORD size,
|
|
DWORD type, UINT flags )
|
|
{
|
|
HKEY base_key, volatile_key;
|
|
WCHAR nameW[64];
|
|
|
|
asciiz_to_unicode( nameW, entry->regval );
|
|
if (flags & SPIF_UPDATEINIFILE)
|
|
{
|
|
if (!get_base_keys( entry->base_key, &base_key, &volatile_key )) return FALSE;
|
|
if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
|
|
reg_delete_value( volatile_key, nameW );
|
|
|
|
if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
|
|
{
|
|
asciiz_to_unicode( nameW, entry->mirror );
|
|
set_reg_value( base_key, nameW, type, data, size );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!get_base_keys( entry->base_key, NULL, &volatile_key )) return FALSE;
|
|
if (!set_reg_value( volatile_key, nameW, type, data, size )) return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* save a string value to a registry entry */
|
|
static BOOL save_entry_string( const struct sysparam_entry *entry, const WCHAR *str, UINT flags )
|
|
{
|
|
return save_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ, flags );
|
|
}
|
|
|
|
/* initialize an entry in the registry if missing */
|
|
static BOOL init_entry( struct sysparam_entry *entry, const void *data, DWORD size, DWORD type )
|
|
{
|
|
KEY_VALUE_PARTIAL_INFORMATION value;
|
|
UNICODE_STRING name;
|
|
WCHAR nameW[64];
|
|
HKEY base_key;
|
|
DWORD count;
|
|
NTSTATUS status;
|
|
|
|
if (!get_base_keys( entry->base_key, &base_key, NULL )) return FALSE;
|
|
|
|
name.Buffer = nameW;
|
|
name.MaximumLength = asciiz_to_unicode( nameW, entry->regval );
|
|
name.Length = name.MaximumLength - sizeof(WCHAR);
|
|
status = NtQueryValueKey( base_key, &name, KeyValuePartialInformation,
|
|
&value, sizeof(value), &count );
|
|
if (!status || status == STATUS_BUFFER_OVERFLOW) return TRUE;
|
|
|
|
if (!set_reg_value( base_key, nameW, type, data, size )) return FALSE;
|
|
if (entry->mirror && get_base_keys( entry->mirror_key, &base_key, NULL ))
|
|
{
|
|
asciiz_to_unicode( nameW, entry->mirror );
|
|
set_reg_value( base_key, nameW, type, data, size );
|
|
}
|
|
entry->loaded = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* initialize a string value in the registry if missing */
|
|
static BOOL init_entry_string( struct sysparam_entry *entry, const WCHAR *str )
|
|
{
|
|
return init_entry( entry, str, (lstrlenW(str) + 1) * sizeof(WCHAR), REG_SZ );
|
|
}
|
|
|
|
/* set an int parameter in the registry */
|
|
static BOOL set_int_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
WCHAR bufW[32];
|
|
char buf[32];
|
|
|
|
sprintf( buf, "%d", int_param );
|
|
asciiz_to_unicode( bufW, buf );
|
|
if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
|
|
entry->uint.val = int_param;
|
|
entry->hdr.loaded = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* initialize an int parameter */
|
|
static BOOL init_int_entry( union sysparam_all_entry *entry )
|
|
{
|
|
WCHAR bufW[32];
|
|
char buf[32];
|
|
|
|
sprintf( buf, "%d", entry->uint.val );
|
|
asciiz_to_unicode( bufW, buf );
|
|
return init_entry_string( &entry->hdr, bufW );
|
|
}
|
|
|
|
/* load a uint parameter from the registry */
|
|
static BOOL get_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!entry->hdr.loaded)
|
|
{
|
|
WCHAR buf[32];
|
|
if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
|
|
}
|
|
*(UINT *)ptr_param = entry->uint.val;
|
|
return TRUE;
|
|
}
|
|
|
|
/* set a uint parameter in the registry */
|
|
static BOOL set_uint_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
WCHAR bufW[32];
|
|
char buf[32];
|
|
|
|
sprintf( buf, "%u", int_param );
|
|
asciiz_to_unicode( bufW, buf );
|
|
if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
|
|
entry->uint.val = int_param;
|
|
entry->hdr.loaded = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* initialize a uint parameter */
|
|
static BOOL init_uint_entry( union sysparam_all_entry *entry )
|
|
{
|
|
WCHAR bufW[32];
|
|
char buf[32];
|
|
|
|
sprintf( buf, "%u", entry->uint.val );
|
|
asciiz_to_unicode( bufW, buf );
|
|
return init_entry_string( &entry->hdr, bufW );
|
|
}
|
|
|
|
/* load a twips parameter from the registry */
|
|
static BOOL get_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
int val;
|
|
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!entry->hdr.loaded)
|
|
{
|
|
WCHAR buf[32];
|
|
if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->uint.val = wcstol( buf, NULL, 10 );
|
|
}
|
|
|
|
/* Dimensions are quoted as being "twips" values if negative and pixels if positive.
|
|
* One inch is 1440 twips.
|
|
* See for example
|
|
* Technical Reference to the Windows 2000 Registry ->
|
|
* HKEY_CURRENT_USER -> Control Panel -> Desktop -> WindowMetrics
|
|
*/
|
|
val = entry->uint.val;
|
|
if (val < 0)
|
|
val = muldiv( -val, dpi, 1440 );
|
|
else
|
|
val = map_to_dpi( val, dpi );
|
|
|
|
*(int *)ptr_param = val;
|
|
return TRUE;
|
|
}
|
|
|
|
/* set a twips parameter in the registry */
|
|
static BOOL set_twips_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
int val = int_param;
|
|
if (val > 0) val = map_from_system_dpi( val );
|
|
return set_int_entry( entry, val, ptr_param, flags );
|
|
}
|
|
|
|
/* load a bool parameter from the registry */
|
|
static BOOL get_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!entry->hdr.loaded)
|
|
{
|
|
WCHAR buf[32];
|
|
if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = wcstol( buf, NULL, 10 ) != 0;
|
|
}
|
|
*(UINT *)ptr_param = entry->bool.val;
|
|
return TRUE;
|
|
}
|
|
|
|
/* set a bool parameter in the registry */
|
|
static BOOL set_bool_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
WCHAR buf[] = { int_param ? '1' : '0', 0 };
|
|
|
|
if (!save_entry_string( &entry->hdr, buf, flags )) return FALSE;
|
|
entry->bool.val = int_param != 0;
|
|
entry->hdr.loaded = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* initialize a bool parameter */
|
|
static BOOL init_bool_entry( union sysparam_all_entry *entry )
|
|
{
|
|
WCHAR buf[] = { entry->bool.val ? '1' : '0', 0 };
|
|
|
|
return init_entry_string( &entry->hdr, buf );
|
|
}
|
|
|
|
/* load a bool parameter using Yes/No strings from the registry */
|
|
static BOOL get_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!entry->hdr.loaded)
|
|
{
|
|
WCHAR buf[32];
|
|
if (load_entry( &entry->hdr, buf, sizeof(buf) )) entry->bool.val = !wcsicmp( yesW, buf );
|
|
}
|
|
*(UINT *)ptr_param = entry->bool.val;
|
|
return TRUE;
|
|
}
|
|
|
|
/* set a bool parameter using Yes/No strings from the registry */
|
|
static BOOL set_yesno_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
const WCHAR *str = int_param ? yesW : noW;
|
|
|
|
if (!save_entry_string( &entry->hdr, str, flags )) return FALSE;
|
|
entry->bool.val = int_param != 0;
|
|
entry->hdr.loaded = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* initialize a bool parameter using Yes/No strings */
|
|
static BOOL init_yesno_entry( union sysparam_all_entry *entry )
|
|
{
|
|
return init_entry_string( &entry->hdr, entry->bool.val ? yesW : noW );
|
|
}
|
|
|
|
/* load a dword (binary) parameter from the registry */
|
|
static BOOL get_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!entry->hdr.loaded)
|
|
{
|
|
DWORD val;
|
|
if (load_entry( &entry->hdr, &val, sizeof(val) ) == sizeof(DWORD)) entry->dword.val = val;
|
|
}
|
|
*(DWORD *)ptr_param = entry->dword.val;
|
|
return TRUE;
|
|
}
|
|
|
|
/* set a dword (binary) parameter in the registry */
|
|
static BOOL set_dword_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
DWORD val = PtrToUlong( ptr_param );
|
|
|
|
if (!save_entry( &entry->hdr, &val, sizeof(val), REG_DWORD, flags )) return FALSE;
|
|
entry->dword.val = val;
|
|
entry->hdr.loaded = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* initialize a dword parameter */
|
|
static BOOL init_dword_entry( union sysparam_all_entry *entry )
|
|
{
|
|
return init_entry( &entry->hdr, &entry->dword.val, sizeof(entry->dword.val), REG_DWORD );
|
|
}
|
|
|
|
/* load an RGB parameter from the registry */
|
|
static BOOL get_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!entry->hdr.loaded)
|
|
{
|
|
WCHAR buf[32];
|
|
|
|
if (load_entry( &entry->hdr, buf, sizeof(buf) ))
|
|
{
|
|
DWORD r, g, b;
|
|
WCHAR *end, *str = buf;
|
|
|
|
r = wcstoul( str, &end, 10 );
|
|
if (end == str || !*end) goto done;
|
|
str = end + 1;
|
|
g = wcstoul( str, &end, 10 );
|
|
if (end == str || !*end) goto done;
|
|
str = end + 1;
|
|
b = wcstoul( str, &end, 10 );
|
|
if (end == str) goto done;
|
|
if (r > 255 || g > 255 || b > 255) goto done;
|
|
entry->rgb.val = RGB( r, g, b );
|
|
}
|
|
}
|
|
done:
|
|
*(COLORREF *)ptr_param = entry->rgb.val;
|
|
return TRUE;
|
|
}
|
|
|
|
/* set an RGB parameter in the registry */
|
|
static BOOL set_rgb_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
WCHAR bufW[32];
|
|
char buf[32];
|
|
HBRUSH brush;
|
|
HPEN pen;
|
|
|
|
sprintf( buf, "%u %u %u", GetRValue(int_param), GetGValue(int_param), GetBValue(int_param) );
|
|
asciiz_to_unicode( bufW, buf );
|
|
if (!save_entry_string( &entry->hdr, bufW, flags )) return FALSE;
|
|
entry->rgb.val = int_param;
|
|
entry->hdr.loaded = TRUE;
|
|
if ((brush = InterlockedExchangePointer( (void **)&entry->rgb.brush, 0 )))
|
|
{
|
|
make_gdi_object_system( brush, FALSE );
|
|
NtGdiDeleteObjectApp( brush );
|
|
}
|
|
if ((pen = InterlockedExchangePointer( (void **)&entry->rgb.pen, 0 )))
|
|
{
|
|
make_gdi_object_system( pen, FALSE );
|
|
NtGdiDeleteObjectApp( pen );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* initialize an RGB parameter */
|
|
static BOOL init_rgb_entry( union sysparam_all_entry *entry )
|
|
{
|
|
WCHAR bufW[32];
|
|
char buf[32];
|
|
|
|
sprintf( buf, "%u %u %u", GetRValue(entry->rgb.val), GetGValue(entry->rgb.val),
|
|
GetBValue(entry->rgb.val) );
|
|
asciiz_to_unicode( bufW, buf );
|
|
return init_entry_string( &entry->hdr, bufW );
|
|
}
|
|
|
|
/* get a path parameter in the registry */
|
|
static BOOL get_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!entry->hdr.loaded)
|
|
{
|
|
WCHAR buffer[MAX_PATH];
|
|
|
|
if (load_entry( &entry->hdr, buffer, sizeof(buffer) ))
|
|
lstrcpynW( entry->path.path, buffer, MAX_PATH );
|
|
}
|
|
lstrcpynW( ptr_param, entry->path.path, int_param );
|
|
return TRUE;
|
|
}
|
|
|
|
/* set a path parameter in the registry */
|
|
static BOOL set_path_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
WCHAR buffer[MAX_PATH];
|
|
BOOL ret;
|
|
|
|
lstrcpynW( buffer, ptr_param, MAX_PATH );
|
|
ret = save_entry_string( &entry->hdr, buffer, flags );
|
|
if (ret)
|
|
{
|
|
lstrcpyW( entry->path.path, buffer );
|
|
entry->hdr.loaded = TRUE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* initialize a path parameter */
|
|
static BOOL init_path_entry( union sysparam_all_entry *entry )
|
|
{
|
|
return init_entry_string( &entry->hdr, entry->path.path );
|
|
}
|
|
|
|
/* get a binary parameter in the registry */
|
|
static BOOL get_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!entry->hdr.loaded)
|
|
{
|
|
void *buffer = malloc( entry->bin.size );
|
|
DWORD len = load_entry( &entry->hdr, buffer, entry->bin.size );
|
|
|
|
if (len)
|
|
{
|
|
memcpy( entry->bin.ptr, buffer, entry->bin.size );
|
|
memset( (char *)entry->bin.ptr + len, 0, entry->bin.size - len );
|
|
}
|
|
free( buffer );
|
|
}
|
|
memcpy( ptr_param, entry->bin.ptr, min( int_param, entry->bin.size ) );
|
|
return TRUE;
|
|
}
|
|
|
|
/* set a binary parameter in the registry */
|
|
static BOOL set_binary_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
BOOL ret;
|
|
void *buffer = malloc( entry->bin.size );
|
|
|
|
memcpy( buffer, entry->bin.ptr, entry->bin.size );
|
|
memcpy( buffer, ptr_param, min( int_param, entry->bin.size ));
|
|
ret = save_entry( &entry->hdr, buffer, entry->bin.size, REG_BINARY, flags );
|
|
if (ret)
|
|
{
|
|
memcpy( entry->bin.ptr, buffer, entry->bin.size );
|
|
entry->hdr.loaded = TRUE;
|
|
}
|
|
free( buffer );
|
|
return ret;
|
|
}
|
|
|
|
/* initialize a binary parameter */
|
|
static BOOL init_binary_entry( union sysparam_all_entry *entry )
|
|
{
|
|
return init_entry( &entry->hdr, entry->bin.ptr, entry->bin.size, REG_BINARY );
|
|
}
|
|
|
|
static void logfont16to32( const LOGFONT16 *font16, LPLOGFONTW font32 )
|
|
{
|
|
font32->lfHeight = font16->lfHeight;
|
|
font32->lfWidth = font16->lfWidth;
|
|
font32->lfEscapement = font16->lfEscapement;
|
|
font32->lfOrientation = font16->lfOrientation;
|
|
font32->lfWeight = font16->lfWeight;
|
|
font32->lfItalic = font16->lfItalic;
|
|
font32->lfUnderline = font16->lfUnderline;
|
|
font32->lfStrikeOut = font16->lfStrikeOut;
|
|
font32->lfCharSet = font16->lfCharSet;
|
|
font32->lfOutPrecision = font16->lfOutPrecision;
|
|
font32->lfClipPrecision = font16->lfClipPrecision;
|
|
font32->lfQuality = font16->lfQuality;
|
|
font32->lfPitchAndFamily = font16->lfPitchAndFamily;
|
|
win32u_mbtowc( &ansi_cp, font32->lfFaceName, LF_FACESIZE, font16->lfFaceName,
|
|
strlen( font16->lfFaceName ));
|
|
font32->lfFaceName[LF_FACESIZE-1] = 0;
|
|
}
|
|
|
|
static void get_real_fontname( LOGFONTW *lf, WCHAR fullname[LF_FACESIZE] )
|
|
{
|
|
struct font_enum_entry enum_entry;
|
|
ULONG count = sizeof(enum_entry);
|
|
HDC hdc;
|
|
|
|
hdc = get_display_dc();
|
|
NtGdiEnumFonts( hdc, 0, 0, lstrlenW( lf->lfFaceName ), lf->lfFaceName, lf->lfCharSet,
|
|
&count, &enum_entry );
|
|
release_display_dc( hdc );
|
|
|
|
if (count)
|
|
lstrcpyW( fullname, enum_entry.lf.elfFullName );
|
|
else
|
|
lstrcpyW( fullname, lf->lfFaceName );
|
|
}
|
|
|
|
LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, int *height )
|
|
{
|
|
SIZE sz;
|
|
static const WCHAR abcdW[] =
|
|
{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
|
|
'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H',
|
|
'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
|
|
|
|
if (metric && !NtGdiGetTextMetricsW( hdc, metric, 0 )) return 0;
|
|
|
|
if (!NtGdiGetTextExtentExW( hdc, abcdW, ARRAYSIZE(abcdW), 0, NULL, NULL, &sz, 0 ))
|
|
return 0;
|
|
|
|
if (height) *height = sz.cy;
|
|
return (sz.cx / 26 + 1) / 2;
|
|
}
|
|
|
|
/* get text metrics and/or "average" char width of the specified logfont
|
|
* for the specified dc */
|
|
static void get_text_metr_size( HDC hdc, LOGFONTW *lf, TEXTMETRICW *metric, UINT *psz )
|
|
{
|
|
HFONT hfont, hfontsav;
|
|
TEXTMETRICW tm;
|
|
UINT ret;
|
|
if (!metric) metric = &tm;
|
|
hfont = NtGdiHfontCreate( lf, sizeof(*lf), 0, 0, NULL );
|
|
if (!hfont || !(hfontsav = NtGdiSelectFont( hdc, hfont )))
|
|
{
|
|
metric->tmHeight = -1;
|
|
if (psz) *psz = 10;
|
|
if (hfont) NtGdiDeleteObjectApp( hfont );
|
|
return;
|
|
}
|
|
ret = get_char_dimensions( hdc, metric, NULL );
|
|
if (psz) *psz = ret ? ret : 10;
|
|
NtGdiSelectFont( hdc, hfontsav );
|
|
NtGdiDeleteObjectApp( hfont );
|
|
}
|
|
|
|
DWORD get_dialog_base_units(void)
|
|
{
|
|
static int cx, cy;
|
|
|
|
if (!cx)
|
|
{
|
|
HDC hdc;
|
|
|
|
if ((hdc = NtUserGetDC( 0 )))
|
|
{
|
|
cx = get_char_dimensions( hdc, NULL, &cy );
|
|
NtUserReleaseDC( 0, hdc );
|
|
}
|
|
TRACE( "base units = %d,%d\n", cx, cy );
|
|
}
|
|
|
|
return MAKELONG( muldiv( cx, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ),
|
|
muldiv( cy, get_system_dpi(), USER_DEFAULT_SCREEN_DPI ));
|
|
}
|
|
|
|
/* adjust some of the raw values found in the registry */
|
|
static void normalize_nonclientmetrics( NONCLIENTMETRICSW *pncm)
|
|
{
|
|
TEXTMETRICW tm;
|
|
HDC hdc = get_display_dc();
|
|
|
|
if( pncm->iBorderWidth < 1) pncm->iBorderWidth = 1;
|
|
if( pncm->iCaptionWidth < 8) pncm->iCaptionWidth = 8;
|
|
if( pncm->iScrollWidth < 8) pncm->iScrollWidth = 8;
|
|
if( pncm->iScrollHeight < 8) pncm->iScrollHeight = 8;
|
|
|
|
/* adjust some heights to the corresponding font */
|
|
get_text_metr_size( hdc, &pncm->lfMenuFont, &tm, NULL);
|
|
pncm->iMenuHeight = max( pncm->iMenuHeight, 2 + tm.tmHeight + tm.tmExternalLeading );
|
|
get_text_metr_size( hdc, &pncm->lfCaptionFont, &tm, NULL);
|
|
pncm->iCaptionHeight = max( pncm->iCaptionHeight, 2 + tm.tmHeight);
|
|
get_text_metr_size( hdc, &pncm->lfSmCaptionFont, &tm, NULL);
|
|
pncm->iSmCaptionHeight = max( pncm->iSmCaptionHeight, 2 + tm.tmHeight);
|
|
release_display_dc( hdc );
|
|
}
|
|
|
|
/* load a font (binary) parameter from the registry */
|
|
static BOOL get_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
LOGFONTW font;
|
|
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!entry->hdr.loaded)
|
|
{
|
|
switch (load_entry( &entry->hdr, &font, sizeof(font) ))
|
|
{
|
|
case sizeof(font):
|
|
font.lfCharSet = DEFAULT_CHARSET;
|
|
if (font.lfHeight > 0) /* positive height value means points ( inch/72 ) */
|
|
font.lfHeight = -muldiv( font.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
|
|
entry->font.val = font;
|
|
break;
|
|
case sizeof(LOGFONT16): /* win9x-winME format */
|
|
logfont16to32( (LOGFONT16 *)&font, &entry->font.val );
|
|
entry->font.val.lfCharSet = DEFAULT_CHARSET;
|
|
if (entry->font.val.lfHeight > 0)
|
|
entry->font.val.lfHeight = -muldiv( entry->font.val.lfHeight, USER_DEFAULT_SCREEN_DPI, 72 );
|
|
break;
|
|
default:
|
|
WARN( "Unknown format in key %s value %s\n",
|
|
debugstr_a( parameter_key_names[entry->hdr.base_key] ),
|
|
debugstr_a( entry->hdr.regval ));
|
|
/* fall through */
|
|
case 0: /* use the default GUI font */
|
|
NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(font), &font );
|
|
font.lfCharSet = DEFAULT_CHARSET;
|
|
font.lfHeight = map_from_system_dpi( font.lfHeight );
|
|
font.lfWeight = entry->font.weight;
|
|
entry->font.val = font;
|
|
break;
|
|
}
|
|
get_real_fontname( &entry->font.val, entry->font.fullname );
|
|
entry->hdr.loaded = TRUE;
|
|
}
|
|
font = entry->font.val;
|
|
font.lfHeight = map_to_dpi( font.lfHeight, dpi );
|
|
lstrcpyW( font.lfFaceName, entry->font.fullname );
|
|
*(LOGFONTW *)ptr_param = font;
|
|
return TRUE;
|
|
}
|
|
|
|
/* set a font (binary) parameter in the registry */
|
|
static BOOL set_font_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
LOGFONTW font;
|
|
WCHAR *ptr;
|
|
|
|
memcpy( &font, ptr_param, sizeof(font) );
|
|
/* zero pad the end of lfFaceName so we don't save uninitialised data */
|
|
for (ptr = font.lfFaceName; ptr < font.lfFaceName + LF_FACESIZE && *ptr; ptr++);
|
|
if (ptr < font.lfFaceName + LF_FACESIZE)
|
|
memset( ptr, 0, (font.lfFaceName + LF_FACESIZE - ptr) * sizeof(WCHAR) );
|
|
if (font.lfHeight < 0) font.lfHeight = map_from_system_dpi( font.lfHeight );
|
|
|
|
if (!save_entry( &entry->hdr, &font, sizeof(font), REG_BINARY, flags )) return FALSE;
|
|
entry->font.val = font;
|
|
get_real_fontname( &entry->font.val, entry->font.fullname );
|
|
entry->hdr.loaded = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* initialize a font (binary) parameter */
|
|
static BOOL init_font_entry( union sysparam_all_entry *entry )
|
|
{
|
|
NtGdiExtGetObjectW( GetStockObject( DEFAULT_GUI_FONT ), sizeof(entry->font.val), &entry->font.val );
|
|
entry->font.val.lfCharSet = DEFAULT_CHARSET;
|
|
entry->font.val.lfHeight = map_from_system_dpi( entry->font.val.lfHeight );
|
|
entry->font.val.lfWeight = entry->font.weight;
|
|
get_real_fontname( &entry->font.val, entry->font.fullname );
|
|
return init_entry( &entry->hdr, &entry->font.val, sizeof(entry->font.val), REG_BINARY );
|
|
}
|
|
|
|
/* get a user pref parameter in the registry */
|
|
static BOOL get_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
union sysparam_all_entry *parent_entry = entry->pref.parent;
|
|
BYTE prefs[8];
|
|
|
|
if (!ptr_param) return FALSE;
|
|
|
|
if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, dpi )) return FALSE;
|
|
*(BOOL *)ptr_param = (prefs[entry->pref.offset] & entry->pref.mask) != 0;
|
|
return TRUE;
|
|
}
|
|
|
|
/* set a user pref parameter in the registry */
|
|
static BOOL set_userpref_entry( union sysparam_all_entry *entry, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
union sysparam_all_entry *parent_entry = entry->pref.parent;
|
|
BYTE prefs[8];
|
|
|
|
parent_entry->hdr.loaded = FALSE; /* force loading it again */
|
|
if (!parent_entry->hdr.get( parent_entry, sizeof(prefs), prefs, get_system_dpi() )) return FALSE;
|
|
|
|
if (PtrToUlong( ptr_param )) prefs[entry->pref.offset] |= entry->pref.mask;
|
|
else prefs[entry->pref.offset] &= ~entry->pref.mask;
|
|
|
|
return parent_entry->hdr.set( parent_entry, sizeof(prefs), prefs, flags );
|
|
}
|
|
|
|
static BOOL get_entry_dpi( void *ptr, UINT int_param, void *ptr_param, UINT dpi )
|
|
{
|
|
union sysparam_all_entry *entry = ptr;
|
|
return entry->hdr.get( entry, int_param, ptr_param, dpi );
|
|
}
|
|
|
|
static BOOL get_entry( void *ptr, UINT int_param, void *ptr_param )
|
|
{
|
|
return get_entry_dpi( ptr, int_param, ptr_param, get_system_dpi() );
|
|
}
|
|
|
|
static BOOL set_entry( void *ptr, UINT int_param, void *ptr_param, UINT flags )
|
|
{
|
|
union sysparam_all_entry *entry = ptr;
|
|
return entry->hdr.set( entry, int_param, ptr_param, flags );
|
|
}
|
|
|
|
#define UINT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
|
|
{ .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg }, (val) } }
|
|
|
|
#define UINT_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
|
|
{ .uint = { { get_uint_entry, set_uint_entry, init_uint_entry, base, reg, mirror_base, reg }, (val) } }
|
|
|
|
#define INT_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
|
|
{ .uint = { { get_uint_entry, set_int_entry, init_int_entry, base, reg }, (val) } }
|
|
|
|
#define BOOL_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
|
|
{ .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg }, (val) } }
|
|
|
|
#define BOOL_ENTRY_MIRROR(name,val,base,reg,mirror_base) union sysparam_all_entry entry_##name = \
|
|
{ .bool = { { get_bool_entry, set_bool_entry, init_bool_entry, base, reg, mirror_base, reg }, (val) } }
|
|
|
|
#define TWIPS_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
|
|
{ .uint = { { get_twips_entry, set_twips_entry, init_int_entry, base, reg }, (val) } }
|
|
|
|
#define YESNO_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
|
|
{ .bool = { { get_yesno_entry, set_yesno_entry, init_yesno_entry, base, reg }, (val) } }
|
|
|
|
#define DWORD_ENTRY(name,val,base,reg) union sysparam_all_entry entry_##name = \
|
|
{ .dword = { { get_dword_entry, set_dword_entry, init_dword_entry, base, reg }, (val) } }
|
|
|
|
#define BINARY_ENTRY(name,data,base,reg) union sysparam_all_entry entry_##name = \
|
|
{ .bin = { { get_binary_entry, set_binary_entry, init_binary_entry, base, reg }, data, sizeof(data) } }
|
|
|
|
#define PATH_ENTRY(name,base,reg,buffer) union sysparam_all_entry entry_##name = \
|
|
{ .path = { { get_path_entry, set_path_entry, init_path_entry, base, reg }, buffer } }
|
|
|
|
#define FONT_ENTRY(name,weight,base,reg) union sysparam_all_entry entry_##name = \
|
|
{ .font = { { get_font_entry, set_font_entry, init_font_entry, base, reg }, (weight) } }
|
|
|
|
#define USERPREF_ENTRY(name,offset,mask) union sysparam_all_entry entry_##name = \
|
|
{ .pref = { { get_userpref_entry, set_userpref_entry }, &entry_USERPREFERENCESMASK, (offset), (mask) } }
|
|
|
|
static UINT_ENTRY( DRAGWIDTH, 4, DESKTOP_KEY, "DragWidth" );
|
|
static UINT_ENTRY( DRAGHEIGHT, 4, DESKTOP_KEY, "DragHeight" );
|
|
static UINT_ENTRY( DOUBLECLICKTIME, 500, MOUSE_KEY, "DoubleClickSpeed" );
|
|
static UINT_ENTRY( FONTSMOOTHING, 2, DESKTOP_KEY, "FontSmoothing" );
|
|
static UINT_ENTRY( GRIDGRANULARITY, 0, DESKTOP_KEY, "GridGranularity" );
|
|
static UINT_ENTRY( KEYBOARDDELAY, 1, KEYBOARD_KEY, "KeyboardDelay" );
|
|
static UINT_ENTRY( KEYBOARDSPEED, 31, KEYBOARD_KEY, "KeyboardSpeed" );
|
|
static UINT_ENTRY( MENUSHOWDELAY, 400, DESKTOP_KEY, "MenuShowDelay" );
|
|
static UINT_ENTRY( MINARRANGE, ARW_HIDE, METRICS_KEY, "MinArrange" );
|
|
static UINT_ENTRY( MINHORZGAP, 0, METRICS_KEY, "MinHorzGap" );
|
|
static UINT_ENTRY( MINVERTGAP, 0, METRICS_KEY, "MinVertGap" );
|
|
static UINT_ENTRY( MINWIDTH, 154, METRICS_KEY, "MinWidth" );
|
|
static UINT_ENTRY( MOUSEHOVERHEIGHT, 4, MOUSE_KEY, "MouseHoverHeight" );
|
|
static UINT_ENTRY( MOUSEHOVERTIME, 400, MOUSE_KEY, "MouseHoverTime" );
|
|
static UINT_ENTRY( MOUSEHOVERWIDTH, 4, MOUSE_KEY, "MouseHoverWidth" );
|
|
static UINT_ENTRY( MOUSESPEED, 10, MOUSE_KEY, "MouseSensitivity" );
|
|
static UINT_ENTRY( MOUSETRAILS, 0, MOUSE_KEY, "MouseTrails" );
|
|
static UINT_ENTRY( SCREENSAVETIMEOUT, 300, DESKTOP_KEY, "ScreenSaveTimeOut" );
|
|
static UINT_ENTRY( WHEELSCROLLCHARS, 3, DESKTOP_KEY, "WheelScrollChars" );
|
|
static UINT_ENTRY( WHEELSCROLLLINES, 3, DESKTOP_KEY, "WheelScrollLines" );
|
|
static UINT_ENTRY_MIRROR( DOUBLECLKHEIGHT, 4, MOUSE_KEY, "DoubleClickHeight", DESKTOP_KEY );
|
|
static UINT_ENTRY_MIRROR( DOUBLECLKWIDTH, 4, MOUSE_KEY, "DoubleClickWidth", DESKTOP_KEY );
|
|
static UINT_ENTRY_MIRROR( MENUDROPALIGNMENT, 0, DESKTOP_KEY, "MenuDropAlignment", VERSION_KEY );
|
|
|
|
static INT_ENTRY( MOUSETHRESHOLD1, 6, MOUSE_KEY, "MouseThreshold1" );
|
|
static INT_ENTRY( MOUSETHRESHOLD2, 10, MOUSE_KEY, "MouseThreshold2" );
|
|
static INT_ENTRY( MOUSEACCELERATION, 1, MOUSE_KEY, "MouseSpeed" );
|
|
|
|
static BOOL_ENTRY( BLOCKSENDINPUTRESETS, FALSE, DESKTOP_KEY, "BlockSendInputResets" );
|
|
static BOOL_ENTRY( DRAGFULLWINDOWS, FALSE, DESKTOP_KEY, "DragFullWindows" );
|
|
static BOOL_ENTRY( KEYBOARDPREF, TRUE, KEYBOARDPREF_KEY, "On" );
|
|
static BOOL_ENTRY( LOWPOWERACTIVE, FALSE, DESKTOP_KEY, "LowPowerActive" );
|
|
static BOOL_ENTRY( MOUSEBUTTONSWAP, FALSE, MOUSE_KEY, "SwapMouseButtons" );
|
|
static BOOL_ENTRY( POWEROFFACTIVE, FALSE, DESKTOP_KEY, "PowerOffActive" );
|
|
static BOOL_ENTRY( SCREENREADER, FALSE, SCREENREADER_KEY, "On" );
|
|
static BOOL_ENTRY( SCREENSAVEACTIVE, TRUE, DESKTOP_KEY, "ScreenSaveActive" );
|
|
static BOOL_ENTRY( SCREENSAVERRUNNING, FALSE, DESKTOP_KEY, "WINE_ScreenSaverRunning" ); /* FIXME - real value */
|
|
static BOOL_ENTRY( SHOWSOUNDS, FALSE, SHOWSOUNDS_KEY, "On" );
|
|
static BOOL_ENTRY( SNAPTODEFBUTTON, FALSE, MOUSE_KEY, "SnapToDefaultButton" );
|
|
static BOOL_ENTRY_MIRROR( ICONTITLEWRAP, TRUE, DESKTOP_KEY, "IconTitleWrap", METRICS_KEY );
|
|
static BOOL_ENTRY( AUDIODESC_ON, FALSE, AUDIODESC_KEY, "On" );
|
|
|
|
static TWIPS_ENTRY( BORDER, -15, METRICS_KEY, "BorderWidth" );
|
|
static TWIPS_ENTRY( CAPTIONHEIGHT, -270, METRICS_KEY, "CaptionHeight" );
|
|
static TWIPS_ENTRY( CAPTIONWIDTH, -270, METRICS_KEY, "CaptionWidth" );
|
|
static TWIPS_ENTRY( ICONHORIZONTALSPACING, -1125, METRICS_KEY, "IconSpacing" );
|
|
static TWIPS_ENTRY( ICONVERTICALSPACING, -1125, METRICS_KEY, "IconVerticalSpacing" );
|
|
static TWIPS_ENTRY( MENUHEIGHT, -270, METRICS_KEY, "MenuHeight" );
|
|
static TWIPS_ENTRY( MENUWIDTH, -270, METRICS_KEY, "MenuWidth" );
|
|
static TWIPS_ENTRY( PADDEDBORDERWIDTH, 0, METRICS_KEY, "PaddedBorderWidth" );
|
|
static TWIPS_ENTRY( SCROLLHEIGHT, -240, METRICS_KEY, "ScrollHeight" );
|
|
static TWIPS_ENTRY( SCROLLWIDTH, -240, METRICS_KEY, "ScrollWidth" );
|
|
static TWIPS_ENTRY( SMCAPTIONHEIGHT, -225, METRICS_KEY, "SmCaptionHeight" );
|
|
static TWIPS_ENTRY( SMCAPTIONWIDTH, -225, METRICS_KEY, "SmCaptionWidth" );
|
|
|
|
static YESNO_ENTRY( BEEP, TRUE, SOUND_KEY, "Beep" );
|
|
|
|
static DWORD_ENTRY( ACTIVEWINDOWTRACKING, 0, MOUSE_KEY, "ActiveWindowTracking" );
|
|
static DWORD_ENTRY( ACTIVEWNDTRKTIMEOUT, 0, DESKTOP_KEY, "ActiveWndTrackTimeout" );
|
|
static DWORD_ENTRY( CARETWIDTH, 1, DESKTOP_KEY, "CaretWidth" );
|
|
static DWORD_ENTRY( DPISCALINGVER, 0, DESKTOP_KEY, "DpiScalingVer" );
|
|
static DWORD_ENTRY( FOCUSBORDERHEIGHT, 1, DESKTOP_KEY, "FocusBorderHeight" );
|
|
static DWORD_ENTRY( FOCUSBORDERWIDTH, 1, DESKTOP_KEY, "FocusBorderWidth" );
|
|
static DWORD_ENTRY( FONTSMOOTHINGCONTRAST, 0, DESKTOP_KEY, "FontSmoothingGamma" );
|
|
static DWORD_ENTRY( FONTSMOOTHINGORIENTATION, FE_FONTSMOOTHINGORIENTATIONRGB, DESKTOP_KEY, "FontSmoothingOrientation" );
|
|
static DWORD_ENTRY( FONTSMOOTHINGTYPE, FE_FONTSMOOTHINGSTANDARD, DESKTOP_KEY, "FontSmoothingType" );
|
|
static DWORD_ENTRY( FOREGROUNDFLASHCOUNT, 3, DESKTOP_KEY, "ForegroundFlashCount" );
|
|
static DWORD_ENTRY( FOREGROUNDLOCKTIMEOUT, 0, DESKTOP_KEY, "ForegroundLockTimeout" );
|
|
static DWORD_ENTRY( LOGPIXELS, 0, DESKTOP_KEY, "LogPixels" );
|
|
static DWORD_ENTRY( MOUSECLICKLOCKTIME, 1200, DESKTOP_KEY, "ClickLockTime" );
|
|
static DWORD_ENTRY( AUDIODESC_LOCALE, 0, AUDIODESC_KEY, "Locale" );
|
|
|
|
static WCHAR desk_pattern_path[MAX_PATH];
|
|
static WCHAR desk_wallpaper_path[MAX_PATH];
|
|
static PATH_ENTRY( DESKPATTERN, DESKTOP_KEY, "Pattern", desk_pattern_path );
|
|
static PATH_ENTRY( DESKWALLPAPER, DESKTOP_KEY, "Wallpaper", desk_wallpaper_path );
|
|
|
|
static BYTE user_prefs[8] = { 0x30, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, 0x00 };
|
|
static BINARY_ENTRY( USERPREFERENCESMASK, user_prefs, DESKTOP_KEY, "UserPreferencesMask" );
|
|
|
|
static FONT_ENTRY( CAPTIONLOGFONT, FW_BOLD, METRICS_KEY, "CaptionFont" );
|
|
static FONT_ENTRY( ICONTITLELOGFONT, FW_NORMAL, METRICS_KEY, "IconFont" );
|
|
static FONT_ENTRY( MENULOGFONT, FW_NORMAL, METRICS_KEY, "MenuFont" );
|
|
static FONT_ENTRY( MESSAGELOGFONT, FW_NORMAL, METRICS_KEY, "MessageFont" );
|
|
static FONT_ENTRY( SMCAPTIONLOGFONT, FW_NORMAL, METRICS_KEY, "SmCaptionFont" );
|
|
static FONT_ENTRY( STATUSLOGFONT, FW_NORMAL, METRICS_KEY, "StatusFont" );
|
|
|
|
static USERPREF_ENTRY( MENUANIMATION, 0, 0x02 );
|
|
static USERPREF_ENTRY( COMBOBOXANIMATION, 0, 0x04 );
|
|
static USERPREF_ENTRY( LISTBOXSMOOTHSCROLLING, 0, 0x08 );
|
|
static USERPREF_ENTRY( GRADIENTCAPTIONS, 0, 0x10 );
|
|
static USERPREF_ENTRY( KEYBOARDCUES, 0, 0x20 );
|
|
static USERPREF_ENTRY( ACTIVEWNDTRKZORDER, 0, 0x40 );
|
|
static USERPREF_ENTRY( HOTTRACKING, 0, 0x80 );
|
|
static USERPREF_ENTRY( MENUFADE, 1, 0x02 );
|
|
static USERPREF_ENTRY( SELECTIONFADE, 1, 0x04 );
|
|
static USERPREF_ENTRY( TOOLTIPANIMATION, 1, 0x08 );
|
|
static USERPREF_ENTRY( TOOLTIPFADE, 1, 0x10 );
|
|
static USERPREF_ENTRY( CURSORSHADOW, 1, 0x20 );
|
|
static USERPREF_ENTRY( MOUSESONAR, 1, 0x40 );
|
|
static USERPREF_ENTRY( MOUSECLICKLOCK, 1, 0x80 );
|
|
static USERPREF_ENTRY( MOUSEVANISH, 2, 0x01 );
|
|
static USERPREF_ENTRY( FLATMENU, 2, 0x02 );
|
|
static USERPREF_ENTRY( DROPSHADOW, 2, 0x04 );
|
|
static USERPREF_ENTRY( UIEFFECTS, 3, 0x80 );
|
|
static USERPREF_ENTRY( DISABLEOVERLAPPEDCONTENT, 4, 0x01 );
|
|
static USERPREF_ENTRY( CLIENTAREAANIMATION, 4, 0x02 );
|
|
static USERPREF_ENTRY( CLEARTYPE, 4, 0x10 );
|
|
static USERPREF_ENTRY( SPEECHRECOGNITION, 4, 0x20 );
|
|
|
|
/* System parameter indexes */
|
|
enum spi_index
|
|
{
|
|
SPI_SETWORKAREA_IDX,
|
|
SPI_INDEX_COUNT
|
|
};
|
|
|
|
/* indicators whether system parameter value is loaded */
|
|
static char spi_loaded[SPI_INDEX_COUNT];
|
|
|
|
static struct sysparam_rgb_entry system_colors[] =
|
|
{
|
|
#define RGB_ENTRY(name,val,reg) { { get_rgb_entry, set_rgb_entry, init_rgb_entry, COLORS_KEY, reg }, (val) }
|
|
RGB_ENTRY( COLOR_SCROLLBAR, RGB(212, 208, 200), "Scrollbar" ),
|
|
RGB_ENTRY( COLOR_BACKGROUND, RGB(58, 110, 165), "Background" ),
|
|
RGB_ENTRY( COLOR_ACTIVECAPTION, RGB(10, 36, 106), "ActiveTitle" ),
|
|
RGB_ENTRY( COLOR_INACTIVECAPTION, RGB(128, 128, 128), "InactiveTitle" ),
|
|
RGB_ENTRY( COLOR_MENU, RGB(212, 208, 200), "Menu" ),
|
|
RGB_ENTRY( COLOR_WINDOW, RGB(255, 255, 255), "Window" ),
|
|
RGB_ENTRY( COLOR_WINDOWFRAME, RGB(0, 0, 0), "WindowFrame" ),
|
|
RGB_ENTRY( COLOR_MENUTEXT, RGB(0, 0, 0), "MenuText" ),
|
|
RGB_ENTRY( COLOR_WINDOWTEXT, RGB(0, 0, 0), "WindowText" ),
|
|
RGB_ENTRY( COLOR_CAPTIONTEXT, RGB(255, 255, 255), "TitleText" ),
|
|
RGB_ENTRY( COLOR_ACTIVEBORDER, RGB(212, 208, 200), "ActiveBorder" ),
|
|
RGB_ENTRY( COLOR_INACTIVEBORDER, RGB(212, 208, 200), "InactiveBorder" ),
|
|
RGB_ENTRY( COLOR_APPWORKSPACE, RGB(128, 128, 128), "AppWorkSpace" ),
|
|
RGB_ENTRY( COLOR_HIGHLIGHT, RGB(10, 36, 106), "Hilight" ),
|
|
RGB_ENTRY( COLOR_HIGHLIGHTTEXT, RGB(255, 255, 255), "HilightText" ),
|
|
RGB_ENTRY( COLOR_BTNFACE, RGB(212, 208, 200), "ButtonFace" ),
|
|
RGB_ENTRY( COLOR_BTNSHADOW, RGB(128, 128, 128), "ButtonShadow" ),
|
|
RGB_ENTRY( COLOR_GRAYTEXT, RGB(128, 128, 128), "GrayText" ),
|
|
RGB_ENTRY( COLOR_BTNTEXT, RGB(0, 0, 0), "ButtonText" ),
|
|
RGB_ENTRY( COLOR_INACTIVECAPTIONTEXT, RGB(212, 208, 200), "InactiveTitleText" ),
|
|
RGB_ENTRY( COLOR_BTNHIGHLIGHT, RGB(255, 255, 255), "ButtonHilight" ),
|
|
RGB_ENTRY( COLOR_3DDKSHADOW, RGB(64, 64, 64), "ButtonDkShadow" ),
|
|
RGB_ENTRY( COLOR_3DLIGHT, RGB(212, 208, 200), "ButtonLight" ),
|
|
RGB_ENTRY( COLOR_INFOTEXT, RGB(0, 0, 0), "InfoText" ),
|
|
RGB_ENTRY( COLOR_INFOBK, RGB(255, 255, 225), "InfoWindow" ),
|
|
RGB_ENTRY( COLOR_ALTERNATEBTNFACE, RGB(181, 181, 181), "ButtonAlternateFace" ),
|
|
RGB_ENTRY( COLOR_HOTLIGHT, RGB(0, 0, 200), "HotTrackingColor" ),
|
|
RGB_ENTRY( COLOR_GRADIENTACTIVECAPTION, RGB(166, 202, 240), "GradientActiveTitle" ),
|
|
RGB_ENTRY( COLOR_GRADIENTINACTIVECAPTION, RGB(192, 192, 192), "GradientInactiveTitle" ),
|
|
RGB_ENTRY( COLOR_MENUHILIGHT, RGB(10, 36, 106), "MenuHilight" ),
|
|
RGB_ENTRY( COLOR_MENUBAR, RGB(212, 208, 200), "MenuBar" )
|
|
#undef RGB_ENTRY
|
|
};
|
|
|
|
/* entries that are initialized by default in the registry */
|
|
static union sysparam_all_entry * const default_entries[] =
|
|
{
|
|
(union sysparam_all_entry *)&entry_ACTIVEWINDOWTRACKING,
|
|
(union sysparam_all_entry *)&entry_ACTIVEWNDTRKTIMEOUT,
|
|
(union sysparam_all_entry *)&entry_BEEP,
|
|
(union sysparam_all_entry *)&entry_BLOCKSENDINPUTRESETS,
|
|
(union sysparam_all_entry *)&entry_BORDER,
|
|
(union sysparam_all_entry *)&entry_CAPTIONHEIGHT,
|
|
(union sysparam_all_entry *)&entry_CAPTIONWIDTH,
|
|
(union sysparam_all_entry *)&entry_CARETWIDTH,
|
|
(union sysparam_all_entry *)&entry_DESKWALLPAPER,
|
|
(union sysparam_all_entry *)&entry_DOUBLECLICKTIME,
|
|
(union sysparam_all_entry *)&entry_DOUBLECLKHEIGHT,
|
|
(union sysparam_all_entry *)&entry_DOUBLECLKWIDTH,
|
|
(union sysparam_all_entry *)&entry_DRAGFULLWINDOWS,
|
|
(union sysparam_all_entry *)&entry_DRAGHEIGHT,
|
|
(union sysparam_all_entry *)&entry_DRAGWIDTH,
|
|
(union sysparam_all_entry *)&entry_FOCUSBORDERHEIGHT,
|
|
(union sysparam_all_entry *)&entry_FOCUSBORDERWIDTH,
|
|
(union sysparam_all_entry *)&entry_FONTSMOOTHING,
|
|
(union sysparam_all_entry *)&entry_FONTSMOOTHINGCONTRAST,
|
|
(union sysparam_all_entry *)&entry_FONTSMOOTHINGORIENTATION,
|
|
(union sysparam_all_entry *)&entry_FONTSMOOTHINGTYPE,
|
|
(union sysparam_all_entry *)&entry_FOREGROUNDFLASHCOUNT,
|
|
(union sysparam_all_entry *)&entry_FOREGROUNDLOCKTIMEOUT,
|
|
(union sysparam_all_entry *)&entry_ICONHORIZONTALSPACING,
|
|
(union sysparam_all_entry *)&entry_ICONTITLEWRAP,
|
|
(union sysparam_all_entry *)&entry_ICONVERTICALSPACING,
|
|
(union sysparam_all_entry *)&entry_KEYBOARDDELAY,
|
|
(union sysparam_all_entry *)&entry_KEYBOARDPREF,
|
|
(union sysparam_all_entry *)&entry_KEYBOARDSPEED,
|
|
(union sysparam_all_entry *)&entry_LOWPOWERACTIVE,
|
|
(union sysparam_all_entry *)&entry_MENUHEIGHT,
|
|
(union sysparam_all_entry *)&entry_MENUSHOWDELAY,
|
|
(union sysparam_all_entry *)&entry_MENUWIDTH,
|
|
(union sysparam_all_entry *)&entry_MOUSEACCELERATION,
|
|
(union sysparam_all_entry *)&entry_MOUSEBUTTONSWAP,
|
|
(union sysparam_all_entry *)&entry_MOUSECLICKLOCKTIME,
|
|
(union sysparam_all_entry *)&entry_MOUSEHOVERHEIGHT,
|
|
(union sysparam_all_entry *)&entry_MOUSEHOVERTIME,
|
|
(union sysparam_all_entry *)&entry_MOUSEHOVERWIDTH,
|
|
(union sysparam_all_entry *)&entry_MOUSESPEED,
|
|
(union sysparam_all_entry *)&entry_MOUSETHRESHOLD1,
|
|
(union sysparam_all_entry *)&entry_MOUSETHRESHOLD2,
|
|
(union sysparam_all_entry *)&entry_PADDEDBORDERWIDTH,
|
|
(union sysparam_all_entry *)&entry_SCREENREADER,
|
|
(union sysparam_all_entry *)&entry_SCROLLHEIGHT,
|
|
(union sysparam_all_entry *)&entry_SCROLLWIDTH,
|
|
(union sysparam_all_entry *)&entry_SHOWSOUNDS,
|
|
(union sysparam_all_entry *)&entry_SMCAPTIONHEIGHT,
|
|
(union sysparam_all_entry *)&entry_SMCAPTIONWIDTH,
|
|
(union sysparam_all_entry *)&entry_SNAPTODEFBUTTON,
|
|
(union sysparam_all_entry *)&entry_USERPREFERENCESMASK,
|
|
(union sysparam_all_entry *)&entry_WHEELSCROLLCHARS,
|
|
(union sysparam_all_entry *)&entry_WHEELSCROLLLINES,
|
|
(union sysparam_all_entry *)&entry_AUDIODESC_LOCALE,
|
|
(union sysparam_all_entry *)&entry_AUDIODESC_ON,
|
|
};
|
|
|
|
/***********************************************************************
|
|
* get_config_key
|
|
*
|
|
* Get a config key from either the app-specific or the default config
|
|
*/
|
|
static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
|
|
WCHAR *buffer, DWORD size )
|
|
{
|
|
char buf[2048];
|
|
KEY_VALUE_PARTIAL_INFORMATION *info = (void *)buf;
|
|
|
|
if (appkey && query_reg_ascii_value( appkey, name, info, sizeof(buf) ))
|
|
{
|
|
size = min( info->DataLength, size - sizeof(WCHAR) );
|
|
memcpy( buffer, info->Data, size );
|
|
buffer[size / sizeof(WCHAR)] = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (defkey && query_reg_ascii_value( defkey, name, info, sizeof(buf) ))
|
|
{
|
|
size = min( info->DataLength, size - sizeof(WCHAR) );
|
|
memcpy( buffer, info->Data, size );
|
|
buffer[size / sizeof(WCHAR)] = 0;
|
|
return 0;
|
|
}
|
|
|
|
return ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
void sysparams_init(void)
|
|
{
|
|
WCHAR buffer[MAX_PATH+16], *p, *appname;
|
|
DWORD i, dispos, dpi_scaling;
|
|
WCHAR layout[KL_NAMELENGTH];
|
|
pthread_mutexattr_t attr;
|
|
HKEY hkey, appkey = 0;
|
|
DWORD len;
|
|
|
|
static const WCHAR oneW[] = {'1',0};
|
|
static const WCHAR x11driverW[] = {'\\','X','1','1',' ','D','r','i','v','e','r',0};
|
|
|
|
pthread_mutexattr_init( &attr );
|
|
pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
|
|
pthread_mutex_init( &user_mutex, &attr );
|
|
pthread_mutexattr_destroy( &attr );
|
|
|
|
if ((hkey = reg_create_ascii_key( hkcu_key, "Keyboard Layout\\Preload", 0, NULL )))
|
|
{
|
|
if (NtUserGetKeyboardLayoutName( layout ))
|
|
set_reg_value( hkey, oneW, REG_SZ, (const BYTE *)layout,
|
|
(lstrlenW(layout) + 1) * sizeof(WCHAR) );
|
|
NtClose( hkey );
|
|
}
|
|
|
|
/* this one must be non-volatile */
|
|
if (!(hkey = reg_create_ascii_key( hkcu_key, "Software\\Wine", 0, NULL )))
|
|
{
|
|
ERR("Can't create wine registry branch\n");
|
|
return;
|
|
}
|
|
|
|
/* @@ Wine registry key: HKCU\Software\Wine\Temporary System Parameters */
|
|
if (!(volatile_base_key = reg_create_ascii_key( hkey, "Temporary System Parameters", REG_OPTION_VOLATILE, &dispos )))
|
|
ERR("Can't create non-permanent wine registry branch\n");
|
|
|
|
NtClose( hkey );
|
|
|
|
config_key = reg_create_ascii_key( NULL, config_keyA, 0, NULL );
|
|
|
|
get_dword_entry( (union sysparam_all_entry *)&entry_LOGPIXELS, 0, &system_dpi, 0 );
|
|
if (!system_dpi) /* check fallback key */
|
|
{
|
|
if ((hkey = reg_open_ascii_key( config_key, "Software\\Fonts" )))
|
|
{
|
|
char buffer[offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(DWORD)])];
|
|
KEY_VALUE_PARTIAL_INFORMATION *value = (void *)buffer;
|
|
|
|
if (query_reg_ascii_value( hkey, "LogPixels", value, sizeof(buffer) ) && value->Type == REG_DWORD)
|
|
system_dpi = *(const DWORD *)value->Data;
|
|
NtClose( hkey );
|
|
}
|
|
}
|
|
if (!system_dpi) system_dpi = USER_DEFAULT_SCREEN_DPI;
|
|
|
|
/* FIXME: what do the DpiScalingVer flags mean? */
|
|
get_dword_entry( (union sysparam_all_entry *)&entry_DPISCALINGVER, 0, &dpi_scaling, 0 );
|
|
if (!dpi_scaling) NtUserSetProcessDpiAwarenessContext( NTUSER_DPI_PER_MONITOR_AWARE, 0 );
|
|
|
|
if (volatile_base_key && dispos == REG_CREATED_NEW_KEY) /* first process, initialize entries */
|
|
{
|
|
for (i = 0; i < ARRAY_SIZE( default_entries ); i++)
|
|
default_entries[i]->hdr.init( default_entries[i] );
|
|
}
|
|
|
|
/* @@ Wine registry key: HKCU\Software\Wine\X11 Driver */
|
|
hkey = reg_open_hkcu_key( "Software\\Wine\\X11 Driver" );
|
|
|
|
/* open the app-specific key */
|
|
|
|
appname = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer;
|
|
if ((p = wcsrchr( appname, '/' ))) appname = p + 1;
|
|
if ((p = wcsrchr( appname, '\\' ))) appname = p + 1;
|
|
len = lstrlenW( appname );
|
|
|
|
if (len && len < MAX_PATH)
|
|
{
|
|
HKEY tmpkey;
|
|
int i;
|
|
|
|
for (i = 0; appname[i]; i++) buffer[i] = RtlDowncaseUnicodeChar( appname[i] );
|
|
buffer[i] = 0;
|
|
appname = buffer;
|
|
memcpy( appname + i, x11driverW, sizeof(x11driverW) );
|
|
|
|
/* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\X11 Driver */
|
|
if ((tmpkey = reg_open_hkcu_key( "Software\\Wine\\AppDefaults" )))
|
|
{
|
|
appkey = reg_open_key( tmpkey, appname, lstrlenW( appname ) * sizeof(WCHAR) );
|
|
NtClose( tmpkey );
|
|
}
|
|
}
|
|
|
|
#define IS_OPTION_TRUE(ch) \
|
|
((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
|
|
|
|
if (!get_config_key( hkey, appkey, "GrabPointer", buffer, sizeof(buffer) ))
|
|
grab_pointer = IS_OPTION_TRUE( buffer[0] );
|
|
if (!get_config_key( hkey, appkey, "GrabFullscreen", buffer, sizeof(buffer) ))
|
|
grab_fullscreen = IS_OPTION_TRUE( buffer[0] );
|
|
|
|
#undef IS_OPTION_TRUE
|
|
}
|
|
|
|
static BOOL update_desktop_wallpaper(void)
|
|
{
|
|
/* FIXME: move implementation from user32 */
|
|
entry_DESKWALLPAPER.hdr.loaded = entry_DESKPATTERN.hdr.loaded = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserSystemParametersInfoForDpi (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserSystemParametersInfoForDpi( UINT action, UINT val, PVOID ptr, UINT winini, UINT dpi )
|
|
{
|
|
BOOL ret = FALSE;
|
|
|
|
switch (action)
|
|
{
|
|
case SPI_GETICONTITLELOGFONT:
|
|
ret = get_entry_dpi( &entry_ICONTITLELOGFONT, val, ptr, dpi );
|
|
break;
|
|
case SPI_GETNONCLIENTMETRICS:
|
|
{
|
|
NONCLIENTMETRICSW *ncm = ptr;
|
|
|
|
if (!ncm) break;
|
|
ret = get_entry_dpi( &entry_BORDER, 0, &ncm->iBorderWidth, dpi ) &&
|
|
get_entry_dpi( &entry_SCROLLWIDTH, 0, &ncm->iScrollWidth, dpi ) &&
|
|
get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ncm->iScrollHeight, dpi ) &&
|
|
get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ncm->iCaptionWidth, dpi ) &&
|
|
get_entry_dpi( &entry_CAPTIONHEIGHT, 0, &ncm->iCaptionHeight, dpi ) &&
|
|
get_entry_dpi( &entry_CAPTIONLOGFONT, 0, &ncm->lfCaptionFont, dpi ) &&
|
|
get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ncm->iSmCaptionWidth, dpi ) &&
|
|
get_entry_dpi( &entry_SMCAPTIONHEIGHT, 0, &ncm->iSmCaptionHeight, dpi ) &&
|
|
get_entry_dpi( &entry_SMCAPTIONLOGFONT, 0, &ncm->lfSmCaptionFont, dpi ) &&
|
|
get_entry_dpi( &entry_MENUWIDTH, 0, &ncm->iMenuWidth, dpi ) &&
|
|
get_entry_dpi( &entry_MENUHEIGHT, 0, &ncm->iMenuHeight, dpi ) &&
|
|
get_entry_dpi( &entry_MENULOGFONT, 0, &ncm->lfMenuFont, dpi ) &&
|
|
get_entry_dpi( &entry_STATUSLOGFONT, 0, &ncm->lfStatusFont, dpi ) &&
|
|
get_entry_dpi( &entry_MESSAGELOGFONT, 0, &ncm->lfMessageFont, dpi );
|
|
if (ret && ncm->cbSize == sizeof(NONCLIENTMETRICSW))
|
|
ret = get_entry_dpi( &entry_PADDEDBORDERWIDTH, 0, &ncm->iPaddedBorderWidth, dpi );
|
|
normalize_nonclientmetrics( ncm );
|
|
break;
|
|
}
|
|
case SPI_GETICONMETRICS:
|
|
{
|
|
ICONMETRICSW *im = ptr;
|
|
if (im && im->cbSize == sizeof(*im))
|
|
ret = get_entry_dpi( &entry_ICONHORIZONTALSPACING, 0, &im->iHorzSpacing, dpi ) &&
|
|
get_entry_dpi( &entry_ICONVERTICALSPACING, 0, &im->iVertSpacing, dpi ) &&
|
|
get_entry_dpi( &entry_ICONTITLEWRAP, 0, &im->iTitleWrap, dpi ) &&
|
|
get_entry_dpi( &entry_ICONTITLELOGFONT, 0, &im->lfFont, dpi );
|
|
break;
|
|
}
|
|
default:
|
|
RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserSystemParametersInfo (win32u.@)
|
|
*
|
|
* Each system parameter has flag which shows whether the parameter
|
|
* is loaded or not. Parameters, stored directly in SysParametersInfo are
|
|
* loaded from registry only when they are requested and the flag is
|
|
* "false", after the loading the flag is set to "true". On interprocess
|
|
* notification of the parameter change the corresponding parameter flag is
|
|
* set to "false". The parameter value will be reloaded when it is requested
|
|
* the next time.
|
|
* Parameters, backed by or depend on GetSystemMetrics are processed
|
|
* differently. These parameters are always loaded. They are reloaded right
|
|
* away on interprocess change notification. We can't do lazy loading because
|
|
* we don't want to complicate GetSystemMetrics.
|
|
* Parameters backed by driver settings are read from corresponding setting.
|
|
* On the parameter change request the setting is changed. Interprocess change
|
|
* notifications are ignored.
|
|
* When parameter value is updated the changed value is stored in permanent
|
|
* registry branch if saving is requested. Otherwise it is stored
|
|
* in temporary branch
|
|
*
|
|
* Some SPI values can also be stored as Twips values in the registry,
|
|
* don't forget the conversion!
|
|
*/
|
|
BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT winini )
|
|
{
|
|
#define WINE_SPI_FIXME(x) \
|
|
case x: \
|
|
{ \
|
|
static BOOL warn = TRUE; \
|
|
if (warn) \
|
|
{ \
|
|
warn = FALSE; \
|
|
FIXME( "Unimplemented action: %u (%s)\n", x, #x ); \
|
|
} \
|
|
} \
|
|
RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE ); \
|
|
ret = FALSE; \
|
|
break
|
|
#define WINE_SPI_WARN(x) \
|
|
case x: \
|
|
WARN( "Ignored action: %u (%s)\n", x, #x ); \
|
|
ret = TRUE; \
|
|
break
|
|
|
|
BOOL ret = user_driver->pSystemParametersInfo( action, val, ptr, winini );
|
|
unsigned spi_idx = 0;
|
|
|
|
if (!ret) switch (action)
|
|
{
|
|
case SPI_GETBEEP:
|
|
ret = get_entry( &entry_BEEP, val, ptr );
|
|
break;
|
|
case SPI_SETBEEP:
|
|
ret = set_entry( &entry_BEEP, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMOUSE:
|
|
ret = get_entry( &entry_MOUSETHRESHOLD1, val, (INT *)ptr ) &&
|
|
get_entry( &entry_MOUSETHRESHOLD2, val, (INT *)ptr + 1 ) &&
|
|
get_entry( &entry_MOUSEACCELERATION, val, (INT *)ptr + 2 );
|
|
break;
|
|
case SPI_SETMOUSE:
|
|
ret = set_entry( &entry_MOUSETHRESHOLD1, ((INT *)ptr)[0], ptr, winini ) &&
|
|
set_entry( &entry_MOUSETHRESHOLD2, ((INT *)ptr)[1], ptr, winini ) &&
|
|
set_entry( &entry_MOUSEACCELERATION, ((INT *)ptr)[2], ptr, winini );
|
|
break;
|
|
case SPI_GETBORDER:
|
|
ret = get_entry( &entry_BORDER, val, ptr );
|
|
if (*(INT*)ptr < 1) *(INT*)ptr = 1;
|
|
break;
|
|
case SPI_SETBORDER:
|
|
ret = set_entry( &entry_BORDER, val, ptr, winini );
|
|
break;
|
|
case SPI_GETKEYBOARDSPEED:
|
|
ret = get_entry( &entry_KEYBOARDSPEED, val, ptr );
|
|
break;
|
|
case SPI_SETKEYBOARDSPEED:
|
|
if (val > 31) val = 31;
|
|
ret = set_entry( &entry_KEYBOARDSPEED, val, ptr, winini );
|
|
break;
|
|
|
|
WINE_SPI_WARN(SPI_LANGDRIVER); /* not implemented in Windows */
|
|
|
|
case SPI_ICONHORIZONTALSPACING:
|
|
if (ptr != NULL)
|
|
ret = get_entry( &entry_ICONHORIZONTALSPACING, val, ptr );
|
|
else
|
|
{
|
|
int min_val = map_to_dpi( 32, get_system_dpi() );
|
|
ret = set_entry( &entry_ICONHORIZONTALSPACING, max( min_val, val ), ptr, winini );
|
|
}
|
|
break;
|
|
case SPI_GETSCREENSAVETIMEOUT:
|
|
ret = get_entry( &entry_SCREENSAVETIMEOUT, val, ptr );
|
|
break;
|
|
case SPI_SETSCREENSAVETIMEOUT:
|
|
ret = set_entry( &entry_SCREENSAVETIMEOUT, val, ptr, winini );
|
|
break;
|
|
case SPI_GETSCREENSAVEACTIVE:
|
|
ret = get_entry( &entry_SCREENSAVEACTIVE, val, ptr );
|
|
break;
|
|
case SPI_SETSCREENSAVEACTIVE:
|
|
ret = set_entry( &entry_SCREENSAVEACTIVE, val, ptr, winini );
|
|
break;
|
|
case SPI_GETGRIDGRANULARITY:
|
|
ret = get_entry( &entry_GRIDGRANULARITY, val, ptr );
|
|
break;
|
|
case SPI_SETGRIDGRANULARITY:
|
|
ret = set_entry( &entry_GRIDGRANULARITY, val, ptr, winini );
|
|
break;
|
|
case SPI_SETDESKWALLPAPER:
|
|
if (!ptr || set_entry( &entry_DESKWALLPAPER, val, ptr, winini ))
|
|
ret = update_desktop_wallpaper();
|
|
break;
|
|
case SPI_SETDESKPATTERN:
|
|
if (!ptr || set_entry( &entry_DESKPATTERN, val, ptr, winini ))
|
|
ret = update_desktop_wallpaper();
|
|
break;
|
|
case SPI_GETKEYBOARDDELAY:
|
|
ret = get_entry( &entry_KEYBOARDDELAY, val, ptr );
|
|
break;
|
|
case SPI_SETKEYBOARDDELAY:
|
|
ret = set_entry( &entry_KEYBOARDDELAY, val, ptr, winini );
|
|
break;
|
|
case SPI_ICONVERTICALSPACING:
|
|
if (ptr != NULL)
|
|
ret = get_entry( &entry_ICONVERTICALSPACING, val, ptr );
|
|
else
|
|
{
|
|
int min_val = map_to_dpi( 32, get_system_dpi() );
|
|
ret = set_entry( &entry_ICONVERTICALSPACING, max( min_val, val ), ptr, winini );
|
|
}
|
|
break;
|
|
case SPI_GETICONTITLEWRAP:
|
|
ret = get_entry( &entry_ICONTITLEWRAP, val, ptr );
|
|
break;
|
|
case SPI_SETICONTITLEWRAP:
|
|
ret = set_entry( &entry_ICONTITLEWRAP, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMENUDROPALIGNMENT:
|
|
ret = get_entry( &entry_MENUDROPALIGNMENT, val, ptr );
|
|
break;
|
|
case SPI_SETMENUDROPALIGNMENT:
|
|
ret = set_entry( &entry_MENUDROPALIGNMENT, val, ptr, winini );
|
|
break;
|
|
case SPI_SETDOUBLECLKWIDTH:
|
|
ret = set_entry( &entry_DOUBLECLKWIDTH, val, ptr, winini );
|
|
break;
|
|
case SPI_SETDOUBLECLKHEIGHT:
|
|
ret = set_entry( &entry_DOUBLECLKHEIGHT, val, ptr, winini );
|
|
break;
|
|
case SPI_GETICONTITLELOGFONT:
|
|
ret = get_entry( &entry_ICONTITLELOGFONT, val, ptr );
|
|
break;
|
|
case SPI_SETDOUBLECLICKTIME:
|
|
ret = set_entry( &entry_DOUBLECLICKTIME, val, ptr, winini );
|
|
break;
|
|
case SPI_SETMOUSEBUTTONSWAP:
|
|
ret = set_entry( &entry_MOUSEBUTTONSWAP, val, ptr, winini );
|
|
break;
|
|
case SPI_SETICONTITLELOGFONT:
|
|
ret = set_entry( &entry_ICONTITLELOGFONT, val, ptr, winini );
|
|
break;
|
|
case SPI_GETFASTTASKSWITCH:
|
|
if (!ptr) return FALSE;
|
|
*(BOOL *)ptr = TRUE;
|
|
ret = TRUE;
|
|
break;
|
|
case SPI_SETFASTTASKSWITCH:
|
|
/* the action is disabled */
|
|
ret = FALSE;
|
|
break;
|
|
case SPI_SETDRAGFULLWINDOWS:
|
|
ret = set_entry( &entry_DRAGFULLWINDOWS, val, ptr, winini );
|
|
break;
|
|
case SPI_GETDRAGFULLWINDOWS:
|
|
ret = get_entry( &entry_DRAGFULLWINDOWS, val, ptr );
|
|
break;
|
|
case SPI_GETNONCLIENTMETRICS:
|
|
{
|
|
NONCLIENTMETRICSW *nm = ptr;
|
|
int padded_border;
|
|
|
|
if (!ptr) return FALSE;
|
|
|
|
ret = get_entry( &entry_BORDER, 0, &nm->iBorderWidth ) &&
|
|
get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border ) &&
|
|
get_entry( &entry_SCROLLWIDTH, 0, &nm->iScrollWidth ) &&
|
|
get_entry( &entry_SCROLLHEIGHT, 0, &nm->iScrollHeight ) &&
|
|
get_entry( &entry_CAPTIONWIDTH, 0, &nm->iCaptionWidth ) &&
|
|
get_entry( &entry_CAPTIONHEIGHT, 0, &nm->iCaptionHeight ) &&
|
|
get_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont ) &&
|
|
get_entry( &entry_SMCAPTIONWIDTH, 0, &nm->iSmCaptionWidth ) &&
|
|
get_entry( &entry_SMCAPTIONHEIGHT, 0, &nm->iSmCaptionHeight ) &&
|
|
get_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont ) &&
|
|
get_entry( &entry_MENUWIDTH, 0, &nm->iMenuWidth ) &&
|
|
get_entry( &entry_MENUHEIGHT, 0, &nm->iMenuHeight ) &&
|
|
get_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont ) &&
|
|
get_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont ) &&
|
|
get_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont );
|
|
if (ret)
|
|
{
|
|
nm->iBorderWidth += padded_border;
|
|
if (nm->cbSize == sizeof(NONCLIENTMETRICSW)) nm->iPaddedBorderWidth = 0;
|
|
}
|
|
normalize_nonclientmetrics( nm );
|
|
break;
|
|
}
|
|
case SPI_SETNONCLIENTMETRICS:
|
|
{
|
|
LPNONCLIENTMETRICSW nm = ptr;
|
|
int padded_border;
|
|
|
|
if (nm && (nm->cbSize == sizeof(NONCLIENTMETRICSW) ||
|
|
nm->cbSize == FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth)))
|
|
{
|
|
get_entry( &entry_PADDEDBORDERWIDTH, 0, &padded_border );
|
|
|
|
ret = set_entry( &entry_BORDER, nm->iBorderWidth - padded_border, NULL, winini ) &&
|
|
set_entry( &entry_SCROLLWIDTH, nm->iScrollWidth, NULL, winini ) &&
|
|
set_entry( &entry_SCROLLHEIGHT, nm->iScrollHeight, NULL, winini ) &&
|
|
set_entry( &entry_CAPTIONWIDTH, nm->iCaptionWidth, NULL, winini ) &&
|
|
set_entry( &entry_CAPTIONHEIGHT, nm->iCaptionHeight, NULL, winini ) &&
|
|
set_entry( &entry_SMCAPTIONWIDTH, nm->iSmCaptionWidth, NULL, winini ) &&
|
|
set_entry( &entry_SMCAPTIONHEIGHT, nm->iSmCaptionHeight, NULL, winini ) &&
|
|
set_entry( &entry_MENUWIDTH, nm->iMenuWidth, NULL, winini ) &&
|
|
set_entry( &entry_MENUHEIGHT, nm->iMenuHeight, NULL, winini ) &&
|
|
set_entry( &entry_MENULOGFONT, 0, &nm->lfMenuFont, winini ) &&
|
|
set_entry( &entry_CAPTIONLOGFONT, 0, &nm->lfCaptionFont, winini ) &&
|
|
set_entry( &entry_SMCAPTIONLOGFONT, 0, &nm->lfSmCaptionFont, winini ) &&
|
|
set_entry( &entry_STATUSLOGFONT, 0, &nm->lfStatusFont, winini ) &&
|
|
set_entry( &entry_MESSAGELOGFONT, 0, &nm->lfMessageFont, winini );
|
|
}
|
|
break;
|
|
}
|
|
case SPI_GETMINIMIZEDMETRICS:
|
|
{
|
|
MINIMIZEDMETRICS *mm = ptr;
|
|
if (mm && mm->cbSize == sizeof(*mm)) {
|
|
ret = get_entry( &entry_MINWIDTH, 0, &mm->iWidth ) &&
|
|
get_entry( &entry_MINHORZGAP, 0, &mm->iHorzGap ) &&
|
|
get_entry( &entry_MINVERTGAP, 0, &mm->iVertGap ) &&
|
|
get_entry( &entry_MINARRANGE, 0, &mm->iArrange );
|
|
mm->iWidth = max( 0, mm->iWidth );
|
|
mm->iHorzGap = max( 0, mm->iHorzGap );
|
|
mm->iVertGap = max( 0, mm->iVertGap );
|
|
mm->iArrange &= 0x0f;
|
|
}
|
|
break;
|
|
}
|
|
case SPI_SETMINIMIZEDMETRICS:
|
|
{
|
|
MINIMIZEDMETRICS *mm = ptr;
|
|
if (mm && mm->cbSize == sizeof(*mm))
|
|
ret = set_entry( &entry_MINWIDTH, max( 0, mm->iWidth ), NULL, winini ) &&
|
|
set_entry( &entry_MINHORZGAP, max( 0, mm->iHorzGap ), NULL, winini ) &&
|
|
set_entry( &entry_MINVERTGAP, max( 0, mm->iVertGap ), NULL, winini ) &&
|
|
set_entry( &entry_MINARRANGE, mm->iArrange & 0x0f, NULL, winini );
|
|
break;
|
|
}
|
|
case SPI_GETICONMETRICS:
|
|
{
|
|
ICONMETRICSW *icon = ptr;
|
|
if(icon && icon->cbSize == sizeof(*icon))
|
|
{
|
|
ret = get_entry( &entry_ICONHORIZONTALSPACING, 0, &icon->iHorzSpacing ) &&
|
|
get_entry( &entry_ICONVERTICALSPACING, 0, &icon->iVertSpacing ) &&
|
|
get_entry( &entry_ICONTITLEWRAP, 0, &icon->iTitleWrap ) &&
|
|
get_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont );
|
|
}
|
|
break;
|
|
}
|
|
case SPI_SETICONMETRICS:
|
|
{
|
|
ICONMETRICSW *icon = ptr;
|
|
if (icon && icon->cbSize == sizeof(*icon))
|
|
ret = set_entry( &entry_ICONVERTICALSPACING, max(32,icon->iVertSpacing), NULL, winini ) &&
|
|
set_entry( &entry_ICONHORIZONTALSPACING, max(32,icon->iHorzSpacing), NULL, winini ) &&
|
|
set_entry( &entry_ICONTITLEWRAP, icon->iTitleWrap, NULL, winini ) &&
|
|
set_entry( &entry_ICONTITLELOGFONT, 0, &icon->lfFont, winini );
|
|
break;
|
|
}
|
|
case SPI_SETWORKAREA:
|
|
{
|
|
if (!ptr) return FALSE;
|
|
spi_idx = SPI_SETWORKAREA_IDX;
|
|
work_area = *(RECT*)ptr;
|
|
spi_loaded[spi_idx] = TRUE;
|
|
ret = TRUE;
|
|
break;
|
|
}
|
|
case SPI_GETWORKAREA:
|
|
{
|
|
if (!ptr) return FALSE;
|
|
|
|
spi_idx = SPI_SETWORKAREA_IDX;
|
|
if (!spi_loaded[spi_idx])
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
if (!lock_display_devices()) return FALSE;
|
|
|
|
LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
|
|
{
|
|
if (!is_monitor_primary( monitor )) continue;
|
|
work_area = monitor->rc_work;
|
|
break;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
spi_loaded[spi_idx] = TRUE;
|
|
}
|
|
*(RECT *)ptr = map_dpi_rect( work_area, system_dpi, get_thread_dpi() );
|
|
ret = TRUE;
|
|
TRACE("work area %s\n", wine_dbgstr_rect( &work_area ));
|
|
break;
|
|
}
|
|
|
|
WINE_SPI_FIXME(SPI_SETPENWINDOWS);
|
|
|
|
case SPI_GETFILTERKEYS:
|
|
{
|
|
LPFILTERKEYS filter_keys = ptr;
|
|
WARN("SPI_GETFILTERKEYS not fully implemented\n");
|
|
if (filter_keys && filter_keys->cbSize == sizeof(FILTERKEYS))
|
|
{
|
|
/* Indicate that no FilterKeys feature available */
|
|
filter_keys->dwFlags = 0;
|
|
filter_keys->iWaitMSec = 0;
|
|
filter_keys->iDelayMSec = 0;
|
|
filter_keys->iRepeatMSec = 0;
|
|
filter_keys->iBounceMSec = 0;
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
WINE_SPI_FIXME(SPI_SETFILTERKEYS);
|
|
|
|
case SPI_GETTOGGLEKEYS:
|
|
{
|
|
LPTOGGLEKEYS toggle_keys = ptr;
|
|
WARN("SPI_GETTOGGLEKEYS not fully implemented\n");
|
|
if (toggle_keys && toggle_keys->cbSize == sizeof(TOGGLEKEYS))
|
|
{
|
|
/* Indicate that no ToggleKeys feature available */
|
|
toggle_keys->dwFlags = 0;
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
WINE_SPI_FIXME(SPI_SETTOGGLEKEYS);
|
|
|
|
case SPI_GETMOUSEKEYS:
|
|
{
|
|
MOUSEKEYS *mouse_keys = ptr;
|
|
WARN("SPI_GETMOUSEKEYS not fully implemented\n");
|
|
if (mouse_keys && mouse_keys->cbSize == sizeof(MOUSEKEYS))
|
|
{
|
|
/* Indicate that no MouseKeys feature available */
|
|
mouse_keys->dwFlags = 0;
|
|
mouse_keys->iMaxSpeed = 360;
|
|
mouse_keys->iTimeToMaxSpeed = 1000;
|
|
mouse_keys->iCtrlSpeed = 0;
|
|
mouse_keys->dwReserved1 = 0;
|
|
mouse_keys->dwReserved2 = 0;
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
WINE_SPI_FIXME(SPI_SETMOUSEKEYS);
|
|
|
|
case SPI_GETSHOWSOUNDS:
|
|
ret = get_entry( &entry_SHOWSOUNDS, val, ptr );
|
|
break;
|
|
case SPI_SETSHOWSOUNDS:
|
|
ret = set_entry( &entry_SHOWSOUNDS, val, ptr, winini );
|
|
break;
|
|
case SPI_GETSTICKYKEYS:
|
|
{
|
|
STICKYKEYS *sticky_keys = ptr;
|
|
WARN("SPI_GETSTICKYKEYS not fully implemented\n");
|
|
if (sticky_keys && sticky_keys->cbSize == sizeof(STICKYKEYS))
|
|
{
|
|
/* Indicate that no StickyKeys feature available */
|
|
sticky_keys->dwFlags = 0;
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
WINE_SPI_FIXME(SPI_SETSTICKYKEYS);
|
|
|
|
case SPI_GETACCESSTIMEOUT:
|
|
{
|
|
ACCESSTIMEOUT *access_timeout = ptr;
|
|
WARN("SPI_GETACCESSTIMEOUT not fully implemented\n");
|
|
if (access_timeout && access_timeout->cbSize == sizeof(ACCESSTIMEOUT))
|
|
{
|
|
/* Indicate that no accessibility features timeout is available */
|
|
access_timeout->dwFlags = 0;
|
|
access_timeout->iTimeOutMSec = 0;
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
WINE_SPI_FIXME(SPI_SETACCESSTIMEOUT);
|
|
|
|
case SPI_GETSERIALKEYS:
|
|
{
|
|
LPSERIALKEYSW serial_keys = ptr;
|
|
WARN("SPI_GETSERIALKEYS not fully implemented\n");
|
|
if (serial_keys && serial_keys->cbSize == sizeof(SERIALKEYSW))
|
|
{
|
|
/* Indicate that no SerialKeys feature available */
|
|
serial_keys->dwFlags = 0;
|
|
serial_keys->lpszActivePort = NULL;
|
|
serial_keys->lpszPort = NULL;
|
|
serial_keys->iBaudRate = 0;
|
|
serial_keys->iPortState = 0;
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
WINE_SPI_FIXME(SPI_SETSERIALKEYS);
|
|
|
|
case SPI_GETSOUNDSENTRY:
|
|
{
|
|
SOUNDSENTRYW *sound_sentry = ptr;
|
|
WARN("SPI_GETSOUNDSENTRY not fully implemented\n");
|
|
if (sound_sentry && sound_sentry->cbSize == sizeof(SOUNDSENTRYW))
|
|
{
|
|
/* Indicate that no SoundSentry feature available */
|
|
sound_sentry->dwFlags = 0;
|
|
sound_sentry->iFSTextEffect = 0;
|
|
sound_sentry->iFSTextEffectMSec = 0;
|
|
sound_sentry->iFSTextEffectColorBits = 0;
|
|
sound_sentry->iFSGrafEffect = 0;
|
|
sound_sentry->iFSGrafEffectMSec = 0;
|
|
sound_sentry->iFSGrafEffectColor = 0;
|
|
sound_sentry->iWindowsEffect = 0;
|
|
sound_sentry->iWindowsEffectMSec = 0;
|
|
sound_sentry->lpszWindowsEffectDLL = 0;
|
|
sound_sentry->iWindowsEffectOrdinal = 0;
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
WINE_SPI_FIXME(SPI_SETSOUNDSENTRY);
|
|
|
|
case SPI_GETHIGHCONTRAST:
|
|
{
|
|
HIGHCONTRASTW *high_contrast = ptr;
|
|
WARN("SPI_GETHIGHCONTRAST not fully implemented\n");
|
|
if (high_contrast && high_contrast->cbSize == sizeof(HIGHCONTRASTW))
|
|
{
|
|
/* Indicate that no high contrast feature available */
|
|
high_contrast->dwFlags = 0;
|
|
high_contrast->lpszDefaultScheme = NULL;
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
WINE_SPI_FIXME(SPI_SETHIGHCONTRAST);
|
|
|
|
case SPI_GETKEYBOARDPREF:
|
|
ret = get_entry( &entry_KEYBOARDPREF, val, ptr );
|
|
break;
|
|
case SPI_SETKEYBOARDPREF:
|
|
ret = set_entry( &entry_KEYBOARDPREF, val, ptr, winini );
|
|
break;
|
|
case SPI_GETSCREENREADER:
|
|
ret = get_entry( &entry_SCREENREADER, val, ptr );
|
|
break;
|
|
case SPI_SETSCREENREADER:
|
|
ret = set_entry( &entry_SCREENREADER, val, ptr, winini );
|
|
break;
|
|
|
|
case SPI_GETANIMATION:
|
|
{
|
|
ANIMATIONINFO *anim_info = ptr;
|
|
|
|
/* Tell it "disabled" */
|
|
if (anim_info && anim_info->cbSize == sizeof(ANIMATIONINFO))
|
|
{
|
|
/* Minimize and restore animation is disabled (nonzero == enabled) */
|
|
anim_info->iMinAnimate = 0;
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
WINE_SPI_WARN(SPI_SETANIMATION);
|
|
|
|
case SPI_GETFONTSMOOTHING:
|
|
ret = get_entry( &entry_FONTSMOOTHING, val, ptr );
|
|
if (ret) *(UINT *)ptr = (*(UINT *)ptr != 0);
|
|
break;
|
|
case SPI_SETFONTSMOOTHING:
|
|
val = val ? 2 : 0; /* Win NT4/2k/XP behavior */
|
|
ret = set_entry( &entry_FONTSMOOTHING, val, ptr, winini );
|
|
break;
|
|
case SPI_SETDRAGWIDTH:
|
|
ret = set_entry( &entry_DRAGWIDTH, val, ptr, winini );
|
|
break;
|
|
case SPI_SETDRAGHEIGHT:
|
|
ret = set_entry( &entry_DRAGHEIGHT, val, ptr, winini );
|
|
break;
|
|
|
|
WINE_SPI_FIXME(SPI_SETHANDHELD);
|
|
WINE_SPI_FIXME(SPI_GETLOWPOWERTIMEOUT);
|
|
WINE_SPI_FIXME(SPI_GETPOWEROFFTIMEOUT);
|
|
WINE_SPI_FIXME(SPI_SETLOWPOWERTIMEOUT);
|
|
WINE_SPI_FIXME(SPI_SETPOWEROFFTIMEOUT);
|
|
|
|
case SPI_GETLOWPOWERACTIVE:
|
|
ret = get_entry( &entry_LOWPOWERACTIVE, val, ptr );
|
|
break;
|
|
case SPI_SETLOWPOWERACTIVE:
|
|
ret = set_entry( &entry_LOWPOWERACTIVE, val, ptr, winini );
|
|
break;
|
|
case SPI_GETPOWEROFFACTIVE:
|
|
ret = get_entry( &entry_POWEROFFACTIVE, val, ptr );
|
|
break;
|
|
case SPI_SETPOWEROFFACTIVE:
|
|
ret = set_entry( &entry_POWEROFFACTIVE, val, ptr, winini );
|
|
break;
|
|
|
|
WINE_SPI_FIXME(SPI_SETCURSORS);
|
|
WINE_SPI_FIXME(SPI_SETICONS);
|
|
|
|
case SPI_GETDEFAULTINPUTLANG:
|
|
ret = NtUserGetKeyboardLayout(0) != 0;
|
|
break;
|
|
|
|
WINE_SPI_FIXME(SPI_SETDEFAULTINPUTLANG);
|
|
WINE_SPI_FIXME(SPI_SETLANGTOGGLE);
|
|
|
|
case SPI_GETWINDOWSEXTENSION:
|
|
WARN( "pretend no support for Win9x Plus! for now.\n" );
|
|
ret = FALSE; /* yes, this is the result value */
|
|
break;
|
|
case SPI_SETMOUSETRAILS:
|
|
ret = set_entry( &entry_MOUSETRAILS, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMOUSETRAILS:
|
|
ret = get_entry( &entry_MOUSETRAILS, val, ptr );
|
|
break;
|
|
case SPI_GETSNAPTODEFBUTTON:
|
|
ret = get_entry( &entry_SNAPTODEFBUTTON, val, ptr );
|
|
break;
|
|
case SPI_SETSNAPTODEFBUTTON:
|
|
ret = set_entry( &entry_SNAPTODEFBUTTON, val, ptr, winini );
|
|
break;
|
|
case SPI_SETSCREENSAVERRUNNING:
|
|
ret = set_entry( &entry_SCREENSAVERRUNNING, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMOUSEHOVERWIDTH:
|
|
ret = get_entry( &entry_MOUSEHOVERWIDTH, val, ptr );
|
|
break;
|
|
case SPI_SETMOUSEHOVERWIDTH:
|
|
ret = set_entry( &entry_MOUSEHOVERWIDTH, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMOUSEHOVERHEIGHT:
|
|
ret = get_entry( &entry_MOUSEHOVERHEIGHT, val, ptr );
|
|
break;
|
|
case SPI_SETMOUSEHOVERHEIGHT:
|
|
ret = set_entry( &entry_MOUSEHOVERHEIGHT, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMOUSEHOVERTIME:
|
|
ret = get_entry( &entry_MOUSEHOVERTIME, val, ptr );
|
|
break;
|
|
case SPI_SETMOUSEHOVERTIME:
|
|
ret = set_entry( &entry_MOUSEHOVERTIME, val, ptr, winini );
|
|
break;
|
|
case SPI_GETWHEELSCROLLLINES:
|
|
ret = get_entry( &entry_WHEELSCROLLLINES, val, ptr );
|
|
break;
|
|
case SPI_SETWHEELSCROLLLINES:
|
|
ret = set_entry( &entry_WHEELSCROLLLINES, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMENUSHOWDELAY:
|
|
ret = get_entry( &entry_MENUSHOWDELAY, val, ptr );
|
|
break;
|
|
case SPI_SETMENUSHOWDELAY:
|
|
ret = set_entry( &entry_MENUSHOWDELAY, val, ptr, winini );
|
|
break;
|
|
case SPI_GETWHEELSCROLLCHARS:
|
|
ret = get_entry( &entry_WHEELSCROLLCHARS, val, ptr );
|
|
break;
|
|
case SPI_SETWHEELSCROLLCHARS:
|
|
ret = set_entry( &entry_WHEELSCROLLCHARS, val, ptr, winini );
|
|
break;
|
|
|
|
WINE_SPI_FIXME(SPI_GETSHOWIMEUI);
|
|
WINE_SPI_FIXME(SPI_SETSHOWIMEUI);
|
|
|
|
case SPI_GETMOUSESPEED:
|
|
ret = get_entry( &entry_MOUSESPEED, val, ptr );
|
|
break;
|
|
case SPI_SETMOUSESPEED:
|
|
ret = set_entry( &entry_MOUSESPEED, val, ptr, winini );
|
|
break;
|
|
case SPI_GETSCREENSAVERRUNNING:
|
|
ret = get_entry( &entry_SCREENSAVERRUNNING, val, ptr );
|
|
break;
|
|
case SPI_GETDESKWALLPAPER:
|
|
ret = get_entry( &entry_DESKWALLPAPER, val, ptr );
|
|
break;
|
|
case SPI_GETACTIVEWINDOWTRACKING:
|
|
ret = get_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr );
|
|
break;
|
|
case SPI_SETACTIVEWINDOWTRACKING:
|
|
ret = set_entry( &entry_ACTIVEWINDOWTRACKING, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMENUANIMATION:
|
|
ret = get_entry( &entry_MENUANIMATION, val, ptr );
|
|
break;
|
|
case SPI_SETMENUANIMATION:
|
|
ret = set_entry( &entry_MENUANIMATION, val, ptr, winini );
|
|
break;
|
|
case SPI_GETCOMBOBOXANIMATION:
|
|
ret = get_entry( &entry_COMBOBOXANIMATION, val, ptr );
|
|
break;
|
|
case SPI_SETCOMBOBOXANIMATION:
|
|
ret = set_entry( &entry_COMBOBOXANIMATION, val, ptr, winini );
|
|
break;
|
|
case SPI_GETLISTBOXSMOOTHSCROLLING:
|
|
ret = get_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr );
|
|
break;
|
|
case SPI_SETLISTBOXSMOOTHSCROLLING:
|
|
ret = set_entry( &entry_LISTBOXSMOOTHSCROLLING, val, ptr, winini );
|
|
break;
|
|
case SPI_GETGRADIENTCAPTIONS:
|
|
ret = get_entry( &entry_GRADIENTCAPTIONS, val, ptr );
|
|
break;
|
|
case SPI_SETGRADIENTCAPTIONS:
|
|
ret = set_entry( &entry_GRADIENTCAPTIONS, val, ptr, winini );
|
|
break;
|
|
case SPI_GETKEYBOARDCUES:
|
|
ret = get_entry( &entry_KEYBOARDCUES, val, ptr );
|
|
break;
|
|
case SPI_SETKEYBOARDCUES:
|
|
ret = set_entry( &entry_KEYBOARDCUES, val, ptr, winini );
|
|
break;
|
|
case SPI_GETACTIVEWNDTRKZORDER:
|
|
ret = get_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr );
|
|
break;
|
|
case SPI_SETACTIVEWNDTRKZORDER:
|
|
ret = set_entry( &entry_ACTIVEWNDTRKZORDER, val, ptr, winini );
|
|
break;
|
|
case SPI_GETHOTTRACKING:
|
|
ret = get_entry( &entry_HOTTRACKING, val, ptr );
|
|
break;
|
|
case SPI_SETHOTTRACKING:
|
|
ret = set_entry( &entry_HOTTRACKING, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMENUFADE:
|
|
ret = get_entry( &entry_MENUFADE, val, ptr );
|
|
break;
|
|
case SPI_SETMENUFADE:
|
|
ret = set_entry( &entry_MENUFADE, val, ptr, winini );
|
|
break;
|
|
case SPI_GETSELECTIONFADE:
|
|
ret = get_entry( &entry_SELECTIONFADE, val, ptr );
|
|
break;
|
|
case SPI_SETSELECTIONFADE:
|
|
ret = set_entry( &entry_SELECTIONFADE, val, ptr, winini );
|
|
break;
|
|
case SPI_GETTOOLTIPANIMATION:
|
|
ret = get_entry( &entry_TOOLTIPANIMATION, val, ptr );
|
|
break;
|
|
case SPI_SETTOOLTIPANIMATION:
|
|
ret = set_entry( &entry_TOOLTIPANIMATION, val, ptr, winini );
|
|
break;
|
|
case SPI_GETTOOLTIPFADE:
|
|
ret = get_entry( &entry_TOOLTIPFADE, val, ptr );
|
|
break;
|
|
case SPI_SETTOOLTIPFADE:
|
|
ret = set_entry( &entry_TOOLTIPFADE, val, ptr, winini );
|
|
break;
|
|
case SPI_GETCURSORSHADOW:
|
|
ret = get_entry( &entry_CURSORSHADOW, val, ptr );
|
|
break;
|
|
case SPI_SETCURSORSHADOW:
|
|
ret = set_entry( &entry_CURSORSHADOW, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMOUSESONAR:
|
|
ret = get_entry( &entry_MOUSESONAR, val, ptr );
|
|
break;
|
|
case SPI_SETMOUSESONAR:
|
|
ret = set_entry( &entry_MOUSESONAR, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMOUSECLICKLOCK:
|
|
ret = get_entry( &entry_MOUSECLICKLOCK, val, ptr );
|
|
break;
|
|
case SPI_SETMOUSECLICKLOCK:
|
|
ret = set_entry( &entry_MOUSECLICKLOCK, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMOUSEVANISH:
|
|
ret = get_entry( &entry_MOUSEVANISH, val, ptr );
|
|
break;
|
|
case SPI_SETMOUSEVANISH:
|
|
ret = set_entry( &entry_MOUSEVANISH, val, ptr, winini );
|
|
break;
|
|
case SPI_GETFLATMENU:
|
|
ret = get_entry( &entry_FLATMENU, val, ptr );
|
|
break;
|
|
case SPI_SETFLATMENU:
|
|
ret = set_entry( &entry_FLATMENU, val, ptr, winini );
|
|
break;
|
|
case SPI_GETDROPSHADOW:
|
|
ret = get_entry( &entry_DROPSHADOW, val, ptr );
|
|
break;
|
|
case SPI_SETDROPSHADOW:
|
|
ret = set_entry( &entry_DROPSHADOW, val, ptr, winini );
|
|
break;
|
|
case SPI_GETBLOCKSENDINPUTRESETS:
|
|
ret = get_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr );
|
|
break;
|
|
case SPI_SETBLOCKSENDINPUTRESETS:
|
|
ret = set_entry( &entry_BLOCKSENDINPUTRESETS, val, ptr, winini );
|
|
break;
|
|
case SPI_GETUIEFFECTS:
|
|
ret = get_entry( &entry_UIEFFECTS, val, ptr );
|
|
break;
|
|
case SPI_SETUIEFFECTS:
|
|
/* FIXME: this probably should mask other UI effect values when unset */
|
|
ret = set_entry( &entry_UIEFFECTS, val, ptr, winini );
|
|
break;
|
|
case SPI_GETDISABLEOVERLAPPEDCONTENT:
|
|
ret = get_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr );
|
|
break;
|
|
case SPI_SETDISABLEOVERLAPPEDCONTENT:
|
|
ret = set_entry( &entry_DISABLEOVERLAPPEDCONTENT, val, ptr, winini );
|
|
break;
|
|
case SPI_GETCLIENTAREAANIMATION:
|
|
ret = get_entry( &entry_CLIENTAREAANIMATION, val, ptr );
|
|
break;
|
|
case SPI_SETCLIENTAREAANIMATION:
|
|
ret = set_entry( &entry_CLIENTAREAANIMATION, val, ptr, winini );
|
|
break;
|
|
case SPI_GETCLEARTYPE:
|
|
ret = get_entry( &entry_CLEARTYPE, val, ptr );
|
|
break;
|
|
case SPI_SETCLEARTYPE:
|
|
ret = set_entry( &entry_CLEARTYPE, val, ptr, winini );
|
|
break;
|
|
case SPI_GETSPEECHRECOGNITION:
|
|
ret = get_entry( &entry_SPEECHRECOGNITION, val, ptr );
|
|
break;
|
|
case SPI_SETSPEECHRECOGNITION:
|
|
ret = set_entry( &entry_SPEECHRECOGNITION, val, ptr, winini );
|
|
break;
|
|
case SPI_GETFOREGROUNDLOCKTIMEOUT:
|
|
ret = get_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr );
|
|
break;
|
|
case SPI_SETFOREGROUNDLOCKTIMEOUT:
|
|
/* FIXME: this should check that the calling thread
|
|
* is able to change the foreground window */
|
|
ret = set_entry( &entry_FOREGROUNDLOCKTIMEOUT, val, ptr, winini );
|
|
break;
|
|
case SPI_GETACTIVEWNDTRKTIMEOUT:
|
|
ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
|
|
break;
|
|
case SPI_SETACTIVEWNDTRKTIMEOUT:
|
|
ret = get_entry( &entry_ACTIVEWNDTRKTIMEOUT, val, ptr );
|
|
break;
|
|
case SPI_GETFOREGROUNDFLASHCOUNT:
|
|
ret = get_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr );
|
|
break;
|
|
case SPI_SETFOREGROUNDFLASHCOUNT:
|
|
ret = set_entry( &entry_FOREGROUNDFLASHCOUNT, val, ptr, winini );
|
|
break;
|
|
case SPI_GETCARETWIDTH:
|
|
ret = get_entry( &entry_CARETWIDTH, val, ptr );
|
|
break;
|
|
case SPI_SETCARETWIDTH:
|
|
ret = set_entry( &entry_CARETWIDTH, val, ptr, winini );
|
|
break;
|
|
case SPI_GETMOUSECLICKLOCKTIME:
|
|
ret = get_entry( &entry_MOUSECLICKLOCKTIME, val, ptr );
|
|
break;
|
|
case SPI_SETMOUSECLICKLOCKTIME:
|
|
ret = set_entry( &entry_MOUSECLICKLOCKTIME, val, ptr, winini );
|
|
break;
|
|
case SPI_GETFONTSMOOTHINGTYPE:
|
|
ret = get_entry( &entry_FONTSMOOTHINGTYPE, val, ptr );
|
|
break;
|
|
case SPI_SETFONTSMOOTHINGTYPE:
|
|
ret = set_entry( &entry_FONTSMOOTHINGTYPE, val, ptr, winini );
|
|
break;
|
|
case SPI_GETFONTSMOOTHINGCONTRAST:
|
|
ret = get_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr );
|
|
break;
|
|
case SPI_SETFONTSMOOTHINGCONTRAST:
|
|
ret = set_entry( &entry_FONTSMOOTHINGCONTRAST, val, ptr, winini );
|
|
break;
|
|
case SPI_GETFOCUSBORDERWIDTH:
|
|
ret = get_entry( &entry_FOCUSBORDERWIDTH, val, ptr );
|
|
break;
|
|
case SPI_GETFOCUSBORDERHEIGHT:
|
|
ret = get_entry( &entry_FOCUSBORDERHEIGHT, val, ptr );
|
|
break;
|
|
case SPI_SETFOCUSBORDERWIDTH:
|
|
ret = set_entry( &entry_FOCUSBORDERWIDTH, val, ptr, winini );
|
|
break;
|
|
case SPI_SETFOCUSBORDERHEIGHT:
|
|
ret = set_entry( &entry_FOCUSBORDERHEIGHT, val, ptr, winini );
|
|
break;
|
|
case SPI_GETFONTSMOOTHINGORIENTATION:
|
|
ret = get_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr );
|
|
break;
|
|
case SPI_SETFONTSMOOTHINGORIENTATION:
|
|
ret = set_entry( &entry_FONTSMOOTHINGORIENTATION, val, ptr, winini );
|
|
break;
|
|
case SPI_GETAUDIODESCRIPTION:
|
|
{
|
|
AUDIODESCRIPTION *audio = ptr;
|
|
if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
|
|
{
|
|
ret = get_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled ) &&
|
|
get_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale );
|
|
}
|
|
break;
|
|
}
|
|
case SPI_SETAUDIODESCRIPTION:
|
|
{
|
|
AUDIODESCRIPTION *audio = ptr;
|
|
if (audio && audio->cbSize == sizeof(AUDIODESCRIPTION) && val == sizeof(AUDIODESCRIPTION) )
|
|
{
|
|
ret = set_entry( &entry_AUDIODESC_ON, 0, &audio->Enabled, winini) &&
|
|
set_entry( &entry_AUDIODESC_LOCALE, 0, &audio->Locale, winini );
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
FIXME( "Unknown action: %u\n", action );
|
|
RtlSetLastWin32Error( ERROR_INVALID_SPI_VALUE );
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (ret && (winini & SPIF_UPDATEINIFILE))
|
|
{
|
|
static const WCHAR emptyW[1];
|
|
if (winini & (SPIF_SENDWININICHANGE | SPIF_SENDCHANGE))
|
|
send_message_timeout( HWND_BROADCAST, WM_SETTINGCHANGE, action, (LPARAM) emptyW,
|
|
SMTO_ABORTIFHUNG, 2000, FALSE );
|
|
}
|
|
TRACE( "(%u, %u, %p, %u) ret %d\n", action, val, ptr, winini, ret );
|
|
return ret;
|
|
|
|
#undef WINE_SPI_FIXME
|
|
#undef WINE_SPI_WARN
|
|
}
|
|
|
|
int get_system_metrics( int index )
|
|
{
|
|
NONCLIENTMETRICSW ncm;
|
|
MINIMIZEDMETRICS mm;
|
|
ICONMETRICSW im;
|
|
RECT rect;
|
|
UINT ret;
|
|
HDC hdc;
|
|
|
|
/* some metrics are dynamic */
|
|
switch (index)
|
|
{
|
|
case SM_CXVSCROLL:
|
|
case SM_CYHSCROLL:
|
|
get_entry( &entry_SCROLLWIDTH, 0, &ret );
|
|
return max( ret, 8 );
|
|
case SM_CYCAPTION:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
|
|
return ncm.iCaptionHeight + 1;
|
|
case SM_CXBORDER:
|
|
case SM_CYBORDER:
|
|
/* SM_C{X,Y}BORDER always returns 1 regardless of 'BorderWidth' value in registry */
|
|
return 1;
|
|
case SM_CXDLGFRAME:
|
|
case SM_CYDLGFRAME:
|
|
return 3;
|
|
case SM_CYVTHUMB:
|
|
case SM_CXHTHUMB:
|
|
case SM_CYVSCROLL:
|
|
case SM_CXHSCROLL:
|
|
get_entry( &entry_SCROLLHEIGHT, 0, &ret );
|
|
return max( ret, 8 );
|
|
case SM_CXICON:
|
|
case SM_CYICON:
|
|
return map_to_dpi( 32, get_system_dpi() );
|
|
case SM_CXCURSOR:
|
|
case SM_CYCURSOR:
|
|
ret = map_to_dpi( 32, get_system_dpi() );
|
|
if (ret >= 64) return 64;
|
|
if (ret >= 48) return 48;
|
|
return 32;
|
|
case SM_CYMENU:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
|
|
return ncm.iMenuHeight + 1;
|
|
case SM_CXFULLSCREEN:
|
|
/* see the remark for SM_CXMAXIMIZED, at least this formulation is correct */
|
|
return get_system_metrics( SM_CXMAXIMIZED ) - 2 * get_system_metrics( SM_CXFRAME );
|
|
case SM_CYFULLSCREEN:
|
|
/* see the remark for SM_CYMAXIMIZED, at least this formulation is
|
|
* correct */
|
|
return get_system_metrics( SM_CYMAXIMIZED ) - get_system_metrics( SM_CYMIN );
|
|
case SM_CYKANJIWINDOW:
|
|
return 0;
|
|
case SM_MOUSEPRESENT:
|
|
return 1;
|
|
case SM_DEBUG:
|
|
return 0;
|
|
case SM_SWAPBUTTON:
|
|
get_entry( &entry_MOUSEBUTTONSWAP, 0, &ret );
|
|
return ret;
|
|
case SM_RESERVED1:
|
|
case SM_RESERVED2:
|
|
case SM_RESERVED3:
|
|
case SM_RESERVED4:
|
|
return 0;
|
|
case SM_CXMIN:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
|
|
hdc = get_display_dc();
|
|
get_text_metr_size( hdc, &ncm.lfCaptionFont, NULL, &ret );
|
|
release_display_dc( hdc );
|
|
return 3 * ncm.iCaptionWidth + ncm.iCaptionHeight + 4 * ret +
|
|
2 * get_system_metrics( SM_CXFRAME ) + 4;
|
|
case SM_CYMIN:
|
|
return get_system_metrics( SM_CYCAPTION ) + 2 * get_system_metrics( SM_CYFRAME );
|
|
case SM_CXSIZE:
|
|
get_entry( &entry_CAPTIONWIDTH, 0, &ret );
|
|
return max( ret, 8 );
|
|
case SM_CYSIZE:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
|
|
return ncm.iCaptionHeight;
|
|
case SM_CXFRAME:
|
|
get_entry( &entry_BORDER, 0, &ret );
|
|
ret = max( ret, 1 );
|
|
return get_system_metrics( SM_CXDLGFRAME ) + ret;
|
|
case SM_CYFRAME:
|
|
get_entry( &entry_BORDER, 0, &ret );
|
|
ret = max( ret, 1 );
|
|
return get_system_metrics( SM_CYDLGFRAME ) + ret;
|
|
case SM_CXMINTRACK:
|
|
return get_system_metrics( SM_CXMIN );
|
|
case SM_CYMINTRACK:
|
|
return get_system_metrics( SM_CYMIN );
|
|
case SM_CXDOUBLECLK:
|
|
get_entry( &entry_DOUBLECLKWIDTH, 0, &ret );
|
|
return ret;
|
|
case SM_CYDOUBLECLK:
|
|
get_entry( &entry_DOUBLECLKHEIGHT, 0, &ret );
|
|
return ret;
|
|
case SM_CXICONSPACING:
|
|
im.cbSize = sizeof(im);
|
|
NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
|
|
return im.iHorzSpacing;
|
|
case SM_CYICONSPACING:
|
|
im.cbSize = sizeof(im);
|
|
NtUserSystemParametersInfo( SPI_GETICONMETRICS, sizeof(im), &im, 0 );
|
|
return im.iVertSpacing;
|
|
case SM_MENUDROPALIGNMENT:
|
|
NtUserSystemParametersInfo( SPI_GETMENUDROPALIGNMENT, 0, &ret, 0 );
|
|
return ret;
|
|
case SM_PENWINDOWS:
|
|
return 0;
|
|
case SM_DBCSENABLED:
|
|
return ansi_cp.MaximumCharacterSize > 1;
|
|
case SM_CMOUSEBUTTONS:
|
|
return 3;
|
|
case SM_SECURE:
|
|
return 0;
|
|
case SM_CXEDGE:
|
|
return get_system_metrics( SM_CXBORDER ) + 1;
|
|
case SM_CYEDGE:
|
|
return get_system_metrics( SM_CYBORDER ) + 1;
|
|
case SM_CXMINSPACING:
|
|
mm.cbSize = sizeof(mm);
|
|
NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
|
|
return get_system_metrics( SM_CXMINIMIZED ) + mm.iHorzGap;
|
|
case SM_CYMINSPACING:
|
|
mm.cbSize = sizeof(mm);
|
|
NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
|
|
return get_system_metrics( SM_CYMINIMIZED ) + mm.iVertGap;
|
|
case SM_CXSMICON:
|
|
case SM_CYSMICON:
|
|
return map_to_dpi( 16, get_system_dpi() ) & ~1;
|
|
case SM_CYSMCAPTION:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
|
|
return ncm.iSmCaptionHeight + 1;
|
|
case SM_CXSMSIZE:
|
|
get_entry( &entry_SMCAPTIONWIDTH, 0, &ret );
|
|
return ret;
|
|
case SM_CYSMSIZE:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
|
|
return ncm.iSmCaptionHeight;
|
|
case SM_CXMENUSIZE:
|
|
get_entry( &entry_MENUWIDTH, 0, &ret );
|
|
return ret;
|
|
case SM_CYMENUSIZE:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
|
|
return ncm.iMenuHeight;
|
|
case SM_ARRANGE:
|
|
mm.cbSize = sizeof(mm);
|
|
NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
|
|
return mm.iArrange;
|
|
case SM_CXMINIMIZED:
|
|
mm.cbSize = sizeof(mm);
|
|
NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, 0 );
|
|
return mm.iWidth + 6;
|
|
case SM_CYMINIMIZED:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
|
|
return ncm.iCaptionHeight + 6;
|
|
case SM_CXMAXTRACK:
|
|
return get_system_metrics( SM_CXVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CXFRAME );
|
|
case SM_CYMAXTRACK:
|
|
return get_system_metrics( SM_CYVIRTUALSCREEN ) + 4 + 2 * get_system_metrics( SM_CYFRAME );
|
|
case SM_CXMAXIMIZED:
|
|
/* FIXME: subtract the width of any vertical application toolbars*/
|
|
return get_system_metrics( SM_CXSCREEN ) + 2 * get_system_metrics( SM_CXFRAME );
|
|
case SM_CYMAXIMIZED:
|
|
/* FIXME: subtract the width of any horizontal application toolbars*/
|
|
return get_system_metrics( SM_CYSCREEN ) + 2 * get_system_metrics( SM_CYCAPTION );
|
|
case SM_NETWORK:
|
|
return 3; /* FIXME */
|
|
case SM_CLEANBOOT:
|
|
return 0; /* 0 = ok, 1 = failsafe, 2 = failsafe + network */
|
|
case SM_CXDRAG:
|
|
get_entry( &entry_DRAGWIDTH, 0, &ret );
|
|
return ret;
|
|
case SM_CYDRAG:
|
|
get_entry( &entry_DRAGHEIGHT, 0, &ret );
|
|
return ret;
|
|
case SM_SHOWSOUNDS:
|
|
get_entry( &entry_SHOWSOUNDS, 0, &ret );
|
|
return ret;
|
|
case SM_CXMENUCHECK:
|
|
case SM_CYMENUCHECK:
|
|
{
|
|
TEXTMETRICW tm;
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
|
|
hdc = get_display_dc();
|
|
get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL );
|
|
release_display_dc( hdc );
|
|
return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading + 1) / 2) * 2 - 1;
|
|
}
|
|
case SM_SLOWMACHINE:
|
|
return 0; /* Never true */
|
|
case SM_MIDEASTENABLED:
|
|
return 0; /* FIXME */
|
|
case SM_MOUSEWHEELPRESENT:
|
|
return 1;
|
|
case SM_CXSCREEN:
|
|
rect = get_primary_monitor_rect( get_thread_dpi() );
|
|
return rect.right - rect.left;
|
|
case SM_CYSCREEN:
|
|
rect = get_primary_monitor_rect( get_thread_dpi() );
|
|
return rect.bottom - rect.top;
|
|
case SM_XVIRTUALSCREEN:
|
|
rect = get_virtual_screen_rect( get_thread_dpi() );
|
|
return rect.left;
|
|
case SM_YVIRTUALSCREEN:
|
|
rect = get_virtual_screen_rect( get_thread_dpi() );
|
|
return rect.top;
|
|
case SM_CXVIRTUALSCREEN:
|
|
rect = get_virtual_screen_rect( get_thread_dpi() );
|
|
return rect.right - rect.left;
|
|
case SM_CYVIRTUALSCREEN:
|
|
rect = get_virtual_screen_rect( get_thread_dpi() );
|
|
return rect.bottom - rect.top;
|
|
case SM_CMONITORS:
|
|
if (!lock_display_devices()) return FALSE;
|
|
ret = active_unique_monitor_count();
|
|
unlock_display_devices();
|
|
return ret;
|
|
case SM_SAMEDISPLAYFORMAT:
|
|
return 1;
|
|
case SM_IMMENABLED:
|
|
return 0; /* FIXME */
|
|
case SM_CXFOCUSBORDER:
|
|
case SM_CYFOCUSBORDER:
|
|
return 1;
|
|
case SM_TABLETPC:
|
|
case SM_MEDIACENTER:
|
|
return 0;
|
|
case SM_CMETRICS:
|
|
return SM_CMETRICS;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int get_system_metrics_for_dpi( int index, unsigned int dpi )
|
|
{
|
|
NONCLIENTMETRICSW ncm;
|
|
ICONMETRICSW im;
|
|
UINT ret;
|
|
HDC hdc;
|
|
|
|
/* some metrics are dynamic */
|
|
switch (index)
|
|
{
|
|
case SM_CXVSCROLL:
|
|
case SM_CYHSCROLL:
|
|
get_entry_dpi( &entry_SCROLLWIDTH, 0, &ret, dpi );
|
|
return max( ret, 8 );
|
|
case SM_CYCAPTION:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
|
|
return ncm.iCaptionHeight + 1;
|
|
case SM_CYVTHUMB:
|
|
case SM_CXHTHUMB:
|
|
case SM_CYVSCROLL:
|
|
case SM_CXHSCROLL:
|
|
get_entry_dpi( &entry_SCROLLHEIGHT, 0, &ret, dpi );
|
|
return max( ret, 8 );
|
|
case SM_CXICON:
|
|
case SM_CYICON:
|
|
return map_to_dpi( 32, dpi );
|
|
case SM_CXCURSOR:
|
|
case SM_CYCURSOR:
|
|
ret = map_to_dpi( 32, dpi );
|
|
if (ret >= 64) return 64;
|
|
if (ret >= 48) return 48;
|
|
return 32;
|
|
case SM_CYMENU:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
|
|
return ncm.iMenuHeight + 1;
|
|
case SM_CXSIZE:
|
|
get_entry_dpi( &entry_CAPTIONWIDTH, 0, &ret, dpi );
|
|
return max( ret, 8 );
|
|
case SM_CYSIZE:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
|
|
return ncm.iCaptionHeight;
|
|
case SM_CXFRAME:
|
|
get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
|
|
ret = max( ret, 1 );
|
|
return get_system_metrics_for_dpi( SM_CXDLGFRAME, dpi ) + ret;
|
|
case SM_CYFRAME:
|
|
get_entry_dpi( &entry_BORDER, 0, &ret, dpi );
|
|
ret = max( ret, 1 );
|
|
return get_system_metrics_for_dpi( SM_CYDLGFRAME, dpi ) + ret;
|
|
case SM_CXICONSPACING:
|
|
im.cbSize = sizeof(im);
|
|
NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
|
|
return im.iHorzSpacing;
|
|
case SM_CYICONSPACING:
|
|
im.cbSize = sizeof(im);
|
|
NtUserSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im), &im, 0, dpi );
|
|
return im.iVertSpacing;
|
|
case SM_CXSMICON:
|
|
case SM_CYSMICON:
|
|
return map_to_dpi( 16, dpi ) & ~1;
|
|
case SM_CYSMCAPTION:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
|
|
return ncm.iSmCaptionHeight + 1;
|
|
case SM_CXSMSIZE:
|
|
get_entry_dpi( &entry_SMCAPTIONWIDTH, 0, &ret, dpi );
|
|
return ret;
|
|
case SM_CYSMSIZE:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
|
|
return ncm.iSmCaptionHeight;
|
|
case SM_CXMENUSIZE:
|
|
get_entry_dpi( &entry_MENUWIDTH, 0, &ret, dpi );
|
|
return ret;
|
|
case SM_CYMENUSIZE:
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
|
|
return ncm.iMenuHeight;
|
|
case SM_CXMENUCHECK:
|
|
case SM_CYMENUCHECK:
|
|
{
|
|
TEXTMETRICW tm;
|
|
ncm.cbSize = sizeof(ncm);
|
|
NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi );
|
|
hdc = get_display_dc();
|
|
get_text_metr_size( hdc, &ncm.lfMenuFont, &tm, NULL);
|
|
release_display_dc( hdc );
|
|
return tm.tmHeight <= 0 ? 13 : ((tm.tmHeight + tm.tmExternalLeading - 1) | 1);
|
|
}
|
|
default:
|
|
return get_system_metrics( index );
|
|
}
|
|
}
|
|
|
|
COLORREF get_sys_color( int index )
|
|
{
|
|
COLORREF ret = 0;
|
|
|
|
if (index >= 0 && index < ARRAY_SIZE( system_colors ))
|
|
get_entry( &system_colors[index], 0, &ret );
|
|
return ret;
|
|
}
|
|
|
|
HBRUSH get_55aa_brush(void)
|
|
{
|
|
static const WORD pattern[] = { 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa };
|
|
static HBRUSH brush_55aa;
|
|
|
|
if (!brush_55aa)
|
|
{
|
|
HBITMAP bitmap = NtGdiCreateBitmap( 8, 8, 1, 1, pattern );
|
|
HBRUSH brush = NtGdiCreatePatternBrushInternal( bitmap, FALSE, FALSE );
|
|
NtGdiDeleteObjectApp( bitmap );
|
|
make_gdi_object_system( brush, TRUE );
|
|
if (InterlockedCompareExchangePointer( (void **)&brush_55aa, brush, 0 ))
|
|
{
|
|
make_gdi_object_system( brush, FALSE );
|
|
NtGdiDeleteObjectApp( brush );
|
|
}
|
|
}
|
|
return brush_55aa;
|
|
}
|
|
|
|
HBRUSH get_sys_color_brush( unsigned int index )
|
|
{
|
|
if (index == COLOR_55AA_BRUSH) return get_55aa_brush();
|
|
if (index >= ARRAY_SIZE( system_colors )) return 0;
|
|
|
|
if (!system_colors[index].brush)
|
|
{
|
|
HBRUSH brush = NtGdiCreateSolidBrush( get_sys_color( index ), NULL );
|
|
make_gdi_object_system( brush, TRUE );
|
|
if (InterlockedCompareExchangePointer( (void **)&system_colors[index].brush, brush, 0 ))
|
|
{
|
|
make_gdi_object_system( brush, FALSE );
|
|
NtGdiDeleteObjectApp( brush );
|
|
}
|
|
}
|
|
return system_colors[index].brush;
|
|
}
|
|
|
|
HPEN get_sys_color_pen( unsigned int index )
|
|
{
|
|
if (index >= ARRAY_SIZE( system_colors )) return 0;
|
|
|
|
if (!system_colors[index].pen)
|
|
{
|
|
HPEN pen = NtGdiCreatePen( PS_SOLID, 1, get_sys_color( index ), NULL );
|
|
make_gdi_object_system( pen, TRUE );
|
|
if (InterlockedCompareExchangePointer( (void **)&system_colors[index].pen, pen, 0 ))
|
|
{
|
|
make_gdi_object_system( pen, FALSE );
|
|
NtGdiDeleteObjectApp( pen );
|
|
}
|
|
}
|
|
return system_colors[index].pen;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* NtUserGetDoubleClickTime (win32u.@)
|
|
*/
|
|
UINT WINAPI NtUserGetDoubleClickTime(void)
|
|
{
|
|
UINT time = 0;
|
|
|
|
get_entry( &entry_DOUBLECLICKTIME, 0, &time );
|
|
if (!time) time = 500;
|
|
return time;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* NtUserSetSysColors (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserSetSysColors( INT count, const INT *colors, const COLORREF *values )
|
|
{
|
|
int i;
|
|
|
|
if (IS_INTRESOURCE(colors)) return FALSE; /* stupid app passes a color instead of an array */
|
|
|
|
for (i = 0; i < count; i++)
|
|
if (colors[i] >= 0 && colors[i] < ARRAY_SIZE( system_colors ))
|
|
set_entry( &system_colors[colors[i]], values[i], 0, 0 );
|
|
|
|
/* Send WM_SYSCOLORCHANGE message to all windows */
|
|
send_message_timeout( HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0,
|
|
SMTO_ABORTIFHUNG, 2000, FALSE );
|
|
/* Repaint affected portions of all visible windows */
|
|
NtUserRedrawWindow( 0, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static LONG dpi_awareness;
|
|
|
|
/***********************************************************************
|
|
* NtUserSetProcessDpiAwarenessContext (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtUserSetProcessDpiAwarenessContext( ULONG awareness, ULONG unknown )
|
|
{
|
|
switch (awareness)
|
|
{
|
|
case NTUSER_DPI_UNAWARE:
|
|
case NTUSER_DPI_SYSTEM_AWARE:
|
|
case NTUSER_DPI_PER_MONITOR_AWARE:
|
|
case NTUSER_DPI_PER_MONITOR_AWARE_V2:
|
|
case NTUSER_DPI_PER_UNAWARE_GDISCALED:
|
|
break;
|
|
default:
|
|
RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
return !InterlockedCompareExchange( &dpi_awareness, awareness, 0 );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserGetProcessDpiAwarenessContext (win32u.@)
|
|
*/
|
|
ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process )
|
|
{
|
|
DPI_AWARENESS val;
|
|
|
|
if (process && process != GetCurrentProcess())
|
|
{
|
|
WARN( "not supported on other process %p\n", process );
|
|
return NTUSER_DPI_UNAWARE;
|
|
}
|
|
|
|
val = ReadNoFence( &dpi_awareness );
|
|
if (!val) return NTUSER_DPI_UNAWARE;
|
|
return val;
|
|
}
|
|
|
|
BOOL message_beep( UINT i )
|
|
{
|
|
BOOL active = TRUE;
|
|
NtUserSystemParametersInfo( SPI_GETBEEP, 0, &active, FALSE );
|
|
if (active) user_driver->pBeep();
|
|
return TRUE;
|
|
}
|
|
|
|
static DWORD exiting_thread_id;
|
|
|
|
/**********************************************************************
|
|
* is_exiting_thread
|
|
*/
|
|
BOOL is_exiting_thread( DWORD tid )
|
|
{
|
|
return tid == exiting_thread_id;
|
|
}
|
|
|
|
static void thread_detach(void)
|
|
{
|
|
struct user_thread_info *thread_info = get_user_thread_info();
|
|
|
|
user_driver->pThreadDetach();
|
|
|
|
free( thread_info->key_state );
|
|
thread_info->key_state = 0;
|
|
free( thread_info->rawinput );
|
|
|
|
destroy_thread_windows();
|
|
cleanup_imm_thread();
|
|
NtClose( thread_info->server_queue );
|
|
|
|
exiting_thread_id = 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserCallNoParam (win32u.@)
|
|
*/
|
|
ULONG_PTR WINAPI NtUserCallNoParam( ULONG code )
|
|
{
|
|
switch(code)
|
|
{
|
|
case NtUserCallNoParam_DestroyCaret:
|
|
return destroy_caret();
|
|
|
|
case NtUserCallNoParam_GetDesktopWindow:
|
|
return HandleToUlong( get_desktop_window() );
|
|
|
|
case NtUserCallNoParam_GetDialogBaseUnits:
|
|
return get_dialog_base_units();
|
|
|
|
case NtUserCallNoParam_GetInputState:
|
|
return get_input_state();
|
|
|
|
case NtUserCallNoParam_GetProcessDefaultLayout:
|
|
return process_layout;
|
|
|
|
case NtUserCallNoParam_GetProgmanWindow:
|
|
return HandleToUlong( get_progman_window() );
|
|
|
|
case NtUserCallNoParam_GetShellWindow:
|
|
return HandleToUlong( get_shell_window() );
|
|
|
|
case NtUserCallNoParam_GetTaskmanWindow:
|
|
return HandleToUlong( get_taskman_window() );
|
|
|
|
case NtUserCallNoParam_ReleaseCapture:
|
|
return release_capture();
|
|
|
|
/* temporary exports */
|
|
case NtUserExitingThread:
|
|
exiting_thread_id = GetCurrentThreadId();
|
|
return 0;
|
|
|
|
case NtUserThreadDetach:
|
|
thread_detach();
|
|
return 0;
|
|
|
|
default:
|
|
FIXME( "invalid code %u\n", (int)code );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserCallOneParam (win32u.@)
|
|
*/
|
|
ULONG_PTR WINAPI NtUserCallOneParam( ULONG_PTR arg, ULONG code )
|
|
{
|
|
switch(code)
|
|
{
|
|
case NtUserCallOneParam_BeginDeferWindowPos:
|
|
return HandleToUlong( begin_defer_window_pos( arg ));
|
|
|
|
case NtUserCallOneParam_CreateCursorIcon:
|
|
return HandleToUlong( alloc_cursoricon_handle( arg ));
|
|
|
|
case NtUserCallOneParam_CreateMenu:
|
|
return HandleToUlong( create_menu( arg ) );
|
|
|
|
case NtUserCallOneParam_EnableDC:
|
|
return set_dce_flags( UlongToHandle(arg), DCHF_ENABLEDC );
|
|
|
|
case NtUserCallOneParam_EnableThunkLock:
|
|
enable_thunk_lock = arg;
|
|
return 0;
|
|
|
|
case NtUserCallOneParam_EnumClipboardFormats:
|
|
return enum_clipboard_formats( arg );
|
|
|
|
case NtUserCallOneParam_GetClipCursor:
|
|
return get_clip_cursor( (RECT *)arg );
|
|
|
|
case NtUserCallOneParam_GetCursorPos:
|
|
return get_cursor_pos( (POINT *)arg );
|
|
|
|
case NtUserCallOneParam_GetIconParam:
|
|
return get_icon_param( UlongToHandle(arg) );
|
|
|
|
case NtUserCallOneParam_GetMenuItemCount:
|
|
return get_menu_item_count( UlongToHandle(arg) );
|
|
|
|
case NtUserCallOneParam_GetSysColor:
|
|
return get_sys_color( arg );
|
|
|
|
case NtUserCallOneParam_IsWindowRectFullScreen:
|
|
return is_window_rect_full_screen( (const RECT *)arg );
|
|
|
|
case NtUserCallOneParam_RealizePalette:
|
|
return realize_palette( UlongToHandle(arg) );
|
|
|
|
case NtUserCallOneParam_GetPrimaryMonitorRect:
|
|
*(RECT *)arg = get_primary_monitor_rect( 0 );
|
|
return 1;
|
|
|
|
case NtUserCallOneParam_GetSysColorBrush:
|
|
return HandleToUlong( get_sys_color_brush(arg) );
|
|
|
|
case NtUserCallOneParam_GetSysColorPen:
|
|
return HandleToUlong( get_sys_color_pen(arg) );
|
|
|
|
case NtUserCallOneParam_GetSystemMetrics:
|
|
return get_system_metrics( arg );
|
|
|
|
case NtUserCallOneParam_GetVirtualScreenRect:
|
|
*(RECT *)arg = get_virtual_screen_rect( 0 );
|
|
return 1;
|
|
|
|
case NtUserCallOneParam_MessageBeep:
|
|
return message_beep( arg );
|
|
|
|
case NtUserCallOneParam_ReplyMessage:
|
|
return reply_message_result( arg );
|
|
|
|
case NtUserCallOneParam_SetCaretBlinkTime:
|
|
return set_caret_blink_time( arg );
|
|
|
|
case NtUserCallOneParam_SetProcessDefaultLayout:
|
|
process_layout = arg;
|
|
return TRUE;
|
|
|
|
case NtUserCallOneParam_SetKeyboardAutoRepeat:
|
|
return set_keyboard_auto_repeat( arg );
|
|
|
|
/* temporary exports */
|
|
case NtUserGetDeskPattern:
|
|
return get_entry( &entry_DESKPATTERN, 256, (WCHAR *)arg );
|
|
|
|
default:
|
|
FIXME( "invalid code %u\n", (int)code );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserCallTwoParam (win32u.@)
|
|
*/
|
|
ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code )
|
|
{
|
|
switch(code)
|
|
{
|
|
case NtUserCallTwoParam_GetDialogProc:
|
|
return (ULONG_PTR)get_dialog_proc( (DLGPROC)arg1, arg2 );
|
|
|
|
case NtUserCallTwoParam_GetMenuInfo:
|
|
return get_menu_info( UlongToHandle(arg1), (MENUINFO *)arg2 );
|
|
|
|
case NtUserCallTwoParam_GetMonitorInfo:
|
|
return get_monitor_info( UlongToHandle(arg1), (MONITORINFO *)arg2 );
|
|
|
|
case NtUserCallTwoParam_GetSystemMetricsForDpi:
|
|
return get_system_metrics_for_dpi( arg1, arg2 );
|
|
|
|
case NtUserCallTwoParam_MonitorFromRect:
|
|
return HandleToUlong( monitor_from_rect( (const RECT *)arg1, arg2, get_thread_dpi() ));
|
|
|
|
case NtUserCallTwoParam_SetCaretPos:
|
|
return set_caret_pos( arg1, arg2 );
|
|
|
|
case NtUserCallTwoParam_SetIconParam:
|
|
return set_icon_param( UlongToHandle(arg1), arg2 );
|
|
|
|
case NtUserCallTwoParam_UnhookWindowsHook:
|
|
return unhook_windows_hook( arg1, (HOOKPROC)arg2 );
|
|
|
|
/* temporary exports */
|
|
case NtUserAllocWinProc:
|
|
return (UINT_PTR)alloc_winproc( (WNDPROC)arg1, arg2 );
|
|
|
|
default:
|
|
FIXME( "invalid code %u\n", (int)code );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtUserDisplayConfigGetDeviceInfo (win32u.@)
|
|
*/
|
|
NTSTATUS WINAPI NtUserDisplayConfigGetDeviceInfo( DISPLAYCONFIG_DEVICE_INFO_HEADER *packet )
|
|
{
|
|
NTSTATUS ret = STATUS_UNSUCCESSFUL;
|
|
char buffer[CCHDEVICENAME];
|
|
|
|
TRACE( "packet %p.\n", packet );
|
|
|
|
if (!packet || packet->size < sizeof(*packet))
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME:
|
|
{
|
|
DISPLAYCONFIG_SOURCE_DEVICE_NAME *source_name = (DISPLAYCONFIG_SOURCE_DEVICE_NAME *)packet;
|
|
struct adapter *adapter;
|
|
|
|
TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME.\n" );
|
|
|
|
if (packet->size < sizeof(*source_name))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
|
|
|
|
LIST_FOR_EACH_ENTRY(adapter, &adapters, struct adapter, entry)
|
|
{
|
|
if (source_name->header.id != adapter->id) continue;
|
|
if (memcmp( &source_name->header.adapterId, &adapter->gpu->luid, sizeof(adapter->gpu->luid) )) continue;
|
|
|
|
sprintf( buffer, "\\\\.\\DISPLAY%d", adapter->id + 1 );
|
|
asciiz_to_unicode( source_name->viewGdiDeviceName, buffer );
|
|
ret = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
return ret;
|
|
}
|
|
case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME:
|
|
{
|
|
DISPLAYCONFIG_TARGET_DEVICE_NAME *target_name = (DISPLAYCONFIG_TARGET_DEVICE_NAME *)packet;
|
|
char buffer[ARRAY_SIZE(target_name->monitorFriendlyDeviceName)];
|
|
struct monitor *monitor;
|
|
|
|
TRACE( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME.\n" );
|
|
|
|
if (packet->size < sizeof(*target_name))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
|
|
|
|
memset( &target_name->flags, 0, sizeof(*target_name) - offsetof(DISPLAYCONFIG_TARGET_DEVICE_NAME, flags) );
|
|
|
|
LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
|
|
{
|
|
if (target_name->header.id != monitor->output_id) continue;
|
|
if (memcmp( &target_name->header.adapterId, &monitor->adapter->gpu->luid,
|
|
sizeof(monitor->adapter->gpu->luid) ))
|
|
continue;
|
|
|
|
target_name->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL;
|
|
snprintf( buffer, ARRAY_SIZE(buffer), "Display%u", monitor->output_id + 1 );
|
|
asciiz_to_unicode( target_name->monitorFriendlyDeviceName, buffer );
|
|
monitor_get_interface_name( monitor, target_name->monitorDevicePath );
|
|
if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_ID)
|
|
{
|
|
target_name->edidManufactureId = monitor->edid_info.manufacturer;
|
|
target_name->edidProductCodeId = monitor->edid_info.product_code;
|
|
target_name->flags.edidIdsValid = 1;
|
|
}
|
|
if (monitor->edid_info.flags & MONITOR_INFO_HAS_MONITOR_NAME)
|
|
{
|
|
wcscpy( target_name->monitorFriendlyDeviceName, monitor->edid_info.monitor_name );
|
|
target_name->flags.friendlyNameFromEdid = 1;
|
|
}
|
|
ret = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
return ret;
|
|
}
|
|
case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE:
|
|
{
|
|
DISPLAYCONFIG_TARGET_PREFERRED_MODE *preferred_mode = (DISPLAYCONFIG_TARGET_PREFERRED_MODE *)packet;
|
|
DISPLAYCONFIG_VIDEO_SIGNAL_INFO *signal_info = &preferred_mode->targetMode.targetVideoSignalInfo;
|
|
unsigned int i, display_freq;
|
|
DEVMODEW *found_mode = NULL;
|
|
BOOL have_edid_mode = FALSE;
|
|
struct monitor *monitor;
|
|
|
|
FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE semi-stub.\n" );
|
|
|
|
if (packet->size < sizeof(*preferred_mode))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
|
|
|
|
memset( &preferred_mode->width, 0, sizeof(*preferred_mode) - offsetof(DISPLAYCONFIG_TARGET_PREFERRED_MODE, width) );
|
|
|
|
LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
|
|
{
|
|
if (preferred_mode->header.id != monitor->output_id) continue;
|
|
if (memcmp( &preferred_mode->header.adapterId, &monitor->adapter->gpu->luid,
|
|
sizeof(monitor->adapter->gpu->luid) ))
|
|
continue;
|
|
|
|
for (i = 0; i < monitor->adapter->mode_count; ++i)
|
|
{
|
|
DEVMODEW *mode = &monitor->adapter->modes[i];
|
|
|
|
if (!have_edid_mode && monitor->edid_info.flags & MONITOR_INFO_HAS_PREFERRED_MODE
|
|
&& mode->dmPelsWidth == monitor->edid_info.preferred_width
|
|
&& mode->dmPelsHeight == monitor->edid_info.preferred_height)
|
|
{
|
|
found_mode = mode;
|
|
have_edid_mode = TRUE;
|
|
}
|
|
|
|
if (!have_edid_mode && (!found_mode
|
|
|| (mode->dmPelsWidth > found_mode->dmPelsWidth && mode->dmPelsHeight >= found_mode->dmPelsHeight)
|
|
|| (mode->dmPelsHeight > found_mode->dmPelsHeight && mode->dmPelsWidth >= found_mode->dmPelsWidth)))
|
|
found_mode = mode;
|
|
|
|
if (mode->dmPelsWidth == found_mode->dmPelsWidth
|
|
&& mode->dmPelsHeight == found_mode->dmPelsHeight
|
|
&& mode->dmDisplayFrequency > found_mode->dmDisplayFrequency)
|
|
found_mode = mode;
|
|
}
|
|
|
|
if (!found_mode)
|
|
{
|
|
ERR( "No mode found.\n" );
|
|
break;
|
|
}
|
|
preferred_mode->width = found_mode->dmPelsWidth;
|
|
preferred_mode->height = found_mode->dmPelsHeight;
|
|
display_freq = found_mode->dmDisplayFrequency;
|
|
|
|
signal_info->pixelRate = display_freq * preferred_mode->width * preferred_mode->height;
|
|
signal_info->hSyncFreq.Numerator = display_freq * preferred_mode->width;
|
|
signal_info->hSyncFreq.Denominator = 1;
|
|
signal_info->vSyncFreq.Numerator = display_freq;
|
|
signal_info->vSyncFreq.Denominator = 1;
|
|
signal_info->activeSize.cx = preferred_mode->width;
|
|
signal_info->activeSize.cy = preferred_mode->height;
|
|
signal_info->totalSize.cx = preferred_mode->width;
|
|
signal_info->totalSize.cy = preferred_mode->height;
|
|
signal_info->videoStandard = D3DKMDT_VSS_OTHER;
|
|
if (!(found_mode->dmFields & DM_DISPLAYFLAGS))
|
|
signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
|
|
else if (found_mode->dmDisplayFlags & DM_INTERLACED)
|
|
signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED;
|
|
else
|
|
signal_info->scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
|
|
ret = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
unlock_display_devices();
|
|
return ret;
|
|
}
|
|
case DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME:
|
|
{
|
|
DISPLAYCONFIG_ADAPTER_NAME *adapter_name = (DISPLAYCONFIG_ADAPTER_NAME *)packet;
|
|
|
|
FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME stub.\n" );
|
|
|
|
if (packet->size < sizeof(*adapter_name))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
case DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE:
|
|
case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE:
|
|
case DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION:
|
|
case DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION:
|
|
case DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO:
|
|
case DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE:
|
|
case DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL:
|
|
default:
|
|
FIXME( "Unimplemented packet type %u.\n", packet->type );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtGdiDdDDIEnumAdapters2 (win32u.@)
|
|
*/
|
|
NTSTATUS WINAPI NtGdiDdDDIEnumAdapters2( D3DKMT_ENUMADAPTERS2 *desc )
|
|
{
|
|
D3DKMT_OPENADAPTERFROMLUID open_adapter_from_luid;
|
|
struct gpu *gpu, *current_gpus[34];
|
|
D3DKMT_CLOSEADAPTER close_adapter;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
UINT idx = 0, count = 0;
|
|
|
|
TRACE( "(%p)\n", desc );
|
|
|
|
if (!desc) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!desc->pAdapters)
|
|
{
|
|
desc->NumAdapters = ARRAY_SIZE(current_gpus);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!lock_display_devices()) return STATUS_UNSUCCESSFUL;
|
|
LIST_FOR_EACH_ENTRY( gpu, &gpus, struct gpu, entry )
|
|
{
|
|
if (count >= ARRAY_SIZE(current_gpus))
|
|
{
|
|
WARN( "Too many adapters (%u), only up to %zu can be enumerated.\n",
|
|
count, ARRAY_SIZE(current_gpus) );
|
|
break;
|
|
}
|
|
current_gpus[count++] = gpu_acquire( gpu );
|
|
}
|
|
unlock_display_devices();
|
|
|
|
if (count > desc->NumAdapters)
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
goto done;
|
|
}
|
|
|
|
for (idx = 0; idx < count; idx++)
|
|
{
|
|
gpu = current_gpus[idx];
|
|
if (!gpu->luid.LowPart && !gpu->luid.HighPart)
|
|
{
|
|
ERR( "Adapter %u does not have a LUID.\n", idx );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
open_adapter_from_luid.AdapterLuid = gpu->luid;
|
|
|
|
/* give the GDI driver a chance to be notified about new adapter handle */
|
|
if ((status = NtGdiDdDDIOpenAdapterFromLuid( &open_adapter_from_luid )))
|
|
{
|
|
ERR( "Failed to open adapter %u from LUID, status %#x.\n", idx, (UINT)status );
|
|
break;
|
|
}
|
|
|
|
desc->pAdapters[idx].hAdapter = open_adapter_from_luid.hAdapter;
|
|
desc->pAdapters[idx].AdapterLuid = gpu->luid;
|
|
desc->pAdapters[idx].NumOfSources = gpu->adapter_count;
|
|
desc->pAdapters[idx].bPrecisePresentRegionsPreferred = FALSE;
|
|
}
|
|
|
|
if (idx != count)
|
|
{
|
|
while (idx)
|
|
{
|
|
close_adapter.hAdapter = desc->pAdapters[--idx].hAdapter;
|
|
NtGdiDdDDICloseAdapter( &close_adapter );
|
|
}
|
|
|
|
memset( desc->pAdapters, 0, desc->NumAdapters * sizeof(D3DKMT_ADAPTERINFO) );
|
|
}
|
|
desc->NumAdapters = idx;
|
|
|
|
done:
|
|
while (count) gpu_release( current_gpus[--count] );
|
|
return status;
|
|
}
|
|
|
|
/* Find the Vulkan device UUID corresponding to a LUID */
|
|
BOOL get_vulkan_uuid_from_luid( const LUID *luid, GUID *uuid )
|
|
{
|
|
BOOL found = FALSE;
|
|
struct gpu *gpu;
|
|
|
|
if (!lock_display_devices()) return FALSE;
|
|
|
|
LIST_FOR_EACH_ENTRY( gpu, &gpus, struct gpu, entry )
|
|
{
|
|
if ((found = !memcmp( &gpu->luid, luid, sizeof(*luid) )))
|
|
{
|
|
*uuid = gpu->vulkan_uuid;
|
|
break;
|
|
}
|
|
}
|
|
|
|
unlock_display_devices();
|
|
return found;
|
|
}
|