2 * Wine X11drv display settings functions
4 * Copyright 2003 Alexander James Pasadyn
5 * Copyright 2020 Zhiyi Zhang for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
33 #include "wine/debug.h"
34 #include "wine/heap.h"
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(x11settings
);
39 struct x11drv_display_setting
45 DEVMODEW desired_mode
;
48 struct x11drv_display_depth
55 /* Display device emulated depth list, protected by modes_section */
56 static struct list x11drv_display_depth_list
= LIST_INIT(x11drv_display_depth_list
);
58 /* All Windows drivers seen so far either support 32 bit depths, or 24 bit depths, but never both. So if we have
59 * a 32 bit framebuffer, report 32 bit bpps, otherwise 24 bit ones.
61 static const unsigned int depths_24
[] = {8, 16, 24};
62 static const unsigned int depths_32
[] = {8, 16, 32};
63 const unsigned int *depths
;
65 static struct x11drv_settings_handler handler
;
67 /* Cached display modes for a device, protected by modes_section */
68 static WCHAR cached_device_name
[CCHDEVICENAME
];
69 static DWORD cached_flags
;
70 static DEVMODEW
*cached_modes
;
71 static UINT cached_mode_count
;
73 static CRITICAL_SECTION modes_section
;
74 static CRITICAL_SECTION_DEBUG modes_critsect_debug
=
77 {&modes_critsect_debug
.ProcessLocksList
, &modes_critsect_debug
.ProcessLocksList
},
78 0, 0, {(DWORD_PTR
)(__FILE__
": modes_section")}
80 static CRITICAL_SECTION modes_section
= {&modes_critsect_debug
, -1, 0, 0, 0, 0};
82 void X11DRV_Settings_SetHandler(const struct x11drv_settings_handler
*new_handler
)
84 if (new_handler
->priority
> handler
.priority
)
86 handler
= *new_handler
;
87 TRACE("Display settings are now handled by: %s.\n", handler
.name
);
91 /***********************************************************************
92 * Default handlers if resolution switching is not enabled
95 static BOOL
nores_get_id(const WCHAR
*device_name
, ULONG_PTR
*id
)
97 WCHAR primary_adapter
[CCHDEVICENAME
];
99 if (!get_primary_adapter( primary_adapter
))
102 *id
= !lstrcmpiW( device_name
, primary_adapter
) ? 1 : 0;
106 static BOOL
nores_get_modes(ULONG_PTR id
, DWORD flags
, DEVMODEW
**new_modes
, UINT
*mode_count
)
108 RECT primary
= get_host_primary_monitor_rect();
111 modes
= heap_calloc(1, sizeof(*modes
));
114 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
118 modes
[0].dmSize
= sizeof(*modes
);
119 modes
[0].dmDriverExtra
= 0;
120 modes
[0].dmFields
= DM_DISPLAYORIENTATION
| DM_BITSPERPEL
| DM_PELSWIDTH
| DM_PELSHEIGHT
|
121 DM_DISPLAYFLAGS
| DM_DISPLAYFREQUENCY
;
122 modes
[0].u1
.s2
.dmDisplayOrientation
= DMDO_DEFAULT
;
123 modes
[0].dmBitsPerPel
= screen_bpp
;
124 modes
[0].dmPelsWidth
= primary
.right
;
125 modes
[0].dmPelsHeight
= primary
.bottom
;
126 modes
[0].u2
.dmDisplayFlags
= 0;
127 modes
[0].dmDisplayFrequency
= 60;
134 static void nores_free_modes(DEVMODEW
*modes
)
139 static BOOL
nores_get_current_mode(ULONG_PTR id
, DEVMODEW
*mode
)
141 RECT primary
= get_host_primary_monitor_rect();
143 mode
->dmFields
= DM_DISPLAYORIENTATION
| DM_BITSPERPEL
| DM_PELSWIDTH
| DM_PELSHEIGHT
|
144 DM_DISPLAYFLAGS
| DM_DISPLAYFREQUENCY
| DM_POSITION
;
145 mode
->u1
.s2
.dmDisplayOrientation
= DMDO_DEFAULT
;
146 mode
->u2
.dmDisplayFlags
= 0;
147 mode
->u1
.s2
.dmPosition
.x
= 0;
148 mode
->u1
.s2
.dmPosition
.y
= 0;
152 FIXME("Non-primary adapters are unsupported.\n");
153 mode
->dmBitsPerPel
= 0;
154 mode
->dmPelsWidth
= 0;
155 mode
->dmPelsHeight
= 0;
156 mode
->dmDisplayFrequency
= 0;
160 mode
->dmBitsPerPel
= screen_bpp
;
161 mode
->dmPelsWidth
= primary
.right
;
162 mode
->dmPelsHeight
= primary
.bottom
;
163 mode
->dmDisplayFrequency
= 60;
167 static LONG
nores_set_current_mode(ULONG_PTR id
, DEVMODEW
*mode
)
169 WARN("NoRes settings handler, ignoring mode change request.\n");
170 return DISP_CHANGE_SUCCESSFUL
;
173 /* default handler only gets the current X desktop resolution */
174 void X11DRV_Settings_Init(void)
176 struct x11drv_settings_handler nores_handler
;
178 depths
= screen_bpp
== 32 ? depths_32
: depths_24
;
180 nores_handler
.name
= "NoRes";
181 nores_handler
.priority
= 1;
182 nores_handler
.get_id
= nores_get_id
;
183 nores_handler
.get_modes
= nores_get_modes
;
184 nores_handler
.free_modes
= nores_free_modes
;
185 nores_handler
.get_current_mode
= nores_get_current_mode
;
186 nores_handler
.set_current_mode
= nores_set_current_mode
;
187 X11DRV_Settings_SetHandler(&nores_handler
);
190 /* Initialize registry display settings when new display devices are added */
191 void init_registry_display_settings(void)
193 DEVMODEW dm
= {.dmSize
= sizeof(dm
)};
194 DISPLAY_DEVICEW dd
= {sizeof(dd
)};
198 while (EnumDisplayDevicesW(NULL
, i
++, &dd
, 0))
200 /* Skip if the device already has registry display settings */
201 if (EnumDisplaySettingsExW(dd
.DeviceName
, ENUM_REGISTRY_SETTINGS
, &dm
, 0))
204 if (!EnumDisplaySettingsExW(dd
.DeviceName
, ENUM_CURRENT_SETTINGS
, &dm
, 0))
206 ERR("Failed to query current display settings for %s.\n", wine_dbgstr_w(dd
.DeviceName
));
210 TRACE("Device %s current display mode %ux%u %ubits %uHz at %d,%d.\n",
211 wine_dbgstr_w(dd
.DeviceName
), dm
.dmPelsWidth
, dm
.dmPelsHeight
, dm
.dmBitsPerPel
,
212 dm
.dmDisplayFrequency
, dm
.u1
.s2
.dmPosition
.x
, dm
.u1
.s2
.dmPosition
.y
);
214 ret
= ChangeDisplaySettingsExW(dd
.DeviceName
, &dm
, NULL
,
215 CDS_GLOBAL
| CDS_NORESET
| CDS_UPDATEREGISTRY
, NULL
);
216 if (ret
!= DISP_CHANGE_SUCCESSFUL
)
217 ERR("Failed to save registry display settings for %s, returned %d.\n",
218 wine_dbgstr_w(dd
.DeviceName
), ret
);
222 static BOOL
get_display_device_reg_key(const WCHAR
*device_name
, WCHAR
*key
, unsigned len
)
224 static const WCHAR display
[] = {'\\','\\','.','\\','D','I','S','P','L','A','Y'};
225 static const WCHAR video_value_fmt
[] = {'\\','D','e','v','i','c','e','\\',
226 'V','i','d','e','o','%','d',0};
227 static const WCHAR video_key
[] = {'H','A','R','D','W','A','R','E','\\',
228 'D','E','V','I','C','E','M','A','P','\\',
229 'V','I','D','E','O','\\',0};
230 WCHAR value_name
[MAX_PATH
], buffer
[MAX_PATH
], *end_ptr
;
231 DWORD adapter_index
, size
;
233 /* Device name has to be \\.\DISPLAY%d */
234 if (strncmpiW(device_name
, display
, ARRAY_SIZE(display
)))
237 /* Parse \\.\DISPLAY* */
238 adapter_index
= strtolW(device_name
+ ARRAY_SIZE(display
), &end_ptr
, 10) - 1;
242 /* Open \Device\Video* in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
243 sprintfW(value_name
, video_value_fmt
, adapter_index
);
244 size
= sizeof(buffer
);
245 if (RegGetValueW(HKEY_LOCAL_MACHINE
, video_key
, value_name
, RRF_RT_REG_SZ
, NULL
, buffer
, &size
))
248 if (len
< lstrlenW(buffer
+ 18) + 1)
251 /* Skip \Registry\Machine\ prefix */
252 lstrcpyW(key
, buffer
+ 18);
253 TRACE("display device %s registry settings key %s.\n", wine_dbgstr_w(device_name
), wine_dbgstr_w(key
));
257 static BOOL
read_registry_settings(const WCHAR
*device_name
, DEVMODEW
*dm
)
259 WCHAR wine_x11_reg_key
[MAX_PATH
];
267 mutex
= get_display_device_init_mutex();
268 if (!get_display_device_reg_key(device_name
, wine_x11_reg_key
, ARRAY_SIZE(wine_x11_reg_key
)))
270 release_display_device_init_mutex(mutex
);
274 if (RegOpenKeyExW(HKEY_CURRENT_CONFIG
, wine_x11_reg_key
, 0, KEY_READ
, &hkey
))
276 release_display_device_init_mutex(mutex
);
280 #define query_value(name, data) \
281 size = sizeof(DWORD); \
282 if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
283 type != REG_DWORD || size != sizeof(DWORD)) \
286 query_value("DefaultSettings.BitsPerPel", &dm
->dmBitsPerPel
);
287 dm
->dmFields
|= DM_BITSPERPEL
;
288 query_value("DefaultSettings.XResolution", &dm
->dmPelsWidth
);
289 dm
->dmFields
|= DM_PELSWIDTH
;
290 query_value("DefaultSettings.YResolution", &dm
->dmPelsHeight
);
291 dm
->dmFields
|= DM_PELSHEIGHT
;
292 query_value("DefaultSettings.VRefresh", &dm
->dmDisplayFrequency
);
293 dm
->dmFields
|= DM_DISPLAYFREQUENCY
;
294 query_value("DefaultSettings.Flags", &dm
->u2
.dmDisplayFlags
);
295 dm
->dmFields
|= DM_DISPLAYFLAGS
;
296 query_value("DefaultSettings.XPanning", &dm
->u1
.s2
.dmPosition
.x
);
297 query_value("DefaultSettings.YPanning", &dm
->u1
.s2
.dmPosition
.y
);
298 dm
->dmFields
|= DM_POSITION
;
299 query_value("DefaultSettings.Orientation", &dm
->u1
.s2
.dmDisplayOrientation
);
300 dm
->dmFields
|= DM_DISPLAYORIENTATION
;
301 query_value("DefaultSettings.FixedOutput", &dm
->u1
.s2
.dmDisplayFixedOutput
);
306 release_display_device_init_mutex(mutex
);
310 static BOOL
write_registry_settings(const WCHAR
*device_name
, const DEVMODEW
*dm
)
312 WCHAR wine_x11_reg_key
[MAX_PATH
];
317 mutex
= get_display_device_init_mutex();
318 if (!get_display_device_reg_key(device_name
, wine_x11_reg_key
, ARRAY_SIZE(wine_x11_reg_key
)))
320 release_display_device_init_mutex(mutex
);
324 if (RegCreateKeyExW(HKEY_CURRENT_CONFIG
, wine_x11_reg_key
, 0, NULL
,
325 REG_OPTION_VOLATILE
, KEY_WRITE
, NULL
, &hkey
, NULL
))
327 release_display_device_init_mutex(mutex
);
331 #define set_value(name, data) \
332 if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
335 set_value("DefaultSettings.BitsPerPel", &dm
->dmBitsPerPel
);
336 set_value("DefaultSettings.XResolution", &dm
->dmPelsWidth
);
337 set_value("DefaultSettings.YResolution", &dm
->dmPelsHeight
);
338 set_value("DefaultSettings.VRefresh", &dm
->dmDisplayFrequency
);
339 set_value("DefaultSettings.Flags", &dm
->u2
.dmDisplayFlags
);
340 set_value("DefaultSettings.XPanning", &dm
->u1
.s2
.dmPosition
.x
);
341 set_value("DefaultSettings.YPanning", &dm
->u1
.s2
.dmPosition
.y
);
342 set_value("DefaultSettings.Orientation", &dm
->u1
.s2
.dmDisplayOrientation
);
343 set_value("DefaultSettings.FixedOutput", &dm
->u1
.s2
.dmDisplayFixedOutput
);
348 release_display_device_init_mutex(mutex
);
352 BOOL
get_primary_adapter(WCHAR
*name
)
358 for (i
= 0; EnumDisplayDevicesW(NULL
, i
, &dd
, 0); ++i
)
360 if (dd
.StateFlags
& DISPLAY_DEVICE_PRIMARY_DEVICE
)
362 lstrcpyW(name
, dd
.DeviceName
);
370 static int mode_compare(const void *p1
, const void *p2
)
372 DWORD a_width
, a_height
, b_width
, b_height
;
373 const DEVMODEW
*a
= p1
, *b
= p2
;
375 /* Use the width and height in landscape mode for comparison */
376 if (a
->u1
.s2
.dmDisplayOrientation
== DMDO_DEFAULT
|| a
->u1
.s2
.dmDisplayOrientation
== DMDO_180
)
378 a_width
= a
->dmPelsWidth
;
379 a_height
= a
->dmPelsHeight
;
383 a_width
= a
->dmPelsHeight
;
384 a_height
= a
->dmPelsWidth
;
387 if (b
->u1
.s2
.dmDisplayOrientation
== DMDO_DEFAULT
|| b
->u1
.s2
.dmDisplayOrientation
== DMDO_180
)
389 b_width
= b
->dmPelsWidth
;
390 b_height
= b
->dmPelsHeight
;
394 b_width
= b
->dmPelsHeight
;
395 b_height
= b
->dmPelsWidth
;
398 /* Depth in descending order */
399 if (a
->dmBitsPerPel
!= b
->dmBitsPerPel
)
400 return b
->dmBitsPerPel
- a
->dmBitsPerPel
;
402 /* Width in ascending order */
403 if (a_width
!= b_width
)
404 return a_width
- b_width
;
406 /* Height in ascending order */
407 if (a_height
!= b_height
)
408 return a_height
- b_height
;
410 /* Frequency in descending order */
411 if (a
->dmDisplayFrequency
!= b
->dmDisplayFrequency
)
412 return b
->dmDisplayFrequency
- a
->dmDisplayFrequency
;
414 /* Orientation in ascending order */
415 return a
->u1
.s2
.dmDisplayOrientation
- b
->u1
.s2
.dmDisplayOrientation
;
418 static void set_display_depth(ULONG_PTR display_id
, DWORD depth
)
420 struct x11drv_display_depth
*display_depth
;
422 EnterCriticalSection(&modes_section
);
423 LIST_FOR_EACH_ENTRY(display_depth
, &x11drv_display_depth_list
, struct x11drv_display_depth
, entry
)
425 if (display_depth
->display_id
== display_id
)
427 display_depth
->depth
= depth
;
428 LeaveCriticalSection(&modes_section
);
433 display_depth
= heap_alloc(sizeof(*display_depth
));
436 ERR("Failed to allocate memory.\n");
437 LeaveCriticalSection(&modes_section
);
441 display_depth
->display_id
= display_id
;
442 display_depth
->depth
= depth
;
443 list_add_head(&x11drv_display_depth_list
, &display_depth
->entry
);
444 LeaveCriticalSection(&modes_section
);
447 static DWORD
get_display_depth(ULONG_PTR display_id
)
449 struct x11drv_display_depth
*display_depth
;
452 EnterCriticalSection(&modes_section
);
453 LIST_FOR_EACH_ENTRY(display_depth
, &x11drv_display_depth_list
, struct x11drv_display_depth
, entry
)
455 if (display_depth
->display_id
== display_id
)
457 depth
= display_depth
->depth
;
458 LeaveCriticalSection(&modes_section
);
462 LeaveCriticalSection(&modes_section
);
466 /***********************************************************************
467 * EnumDisplaySettingsEx (X11DRV.@)
470 BOOL CDECL
X11DRV_EnumDisplaySettingsEx( LPCWSTR name
, DWORD n
, LPDEVMODEW devmode
, DWORD flags
)
472 static const WCHAR dev_name
[CCHDEVICENAME
] =
473 { 'W','i','n','e',' ','X','1','1',' ','d','r','i','v','e','r',0 };
478 if (n
== ENUM_REGISTRY_SETTINGS
)
480 if (!read_registry_settings(name
, devmode
))
482 ERR("Failed to get %s registry display settings.\n", wine_dbgstr_w(name
));
488 if (n
== ENUM_CURRENT_SETTINGS
)
490 if (!handler
.get_id(name
, &id
) || !handler
.get_current_mode(id
, devmode
))
492 ERR("Failed to get %s current display settings.\n", wine_dbgstr_w(name
));
496 if (!is_detached_mode(devmode
))
497 devmode
->dmBitsPerPel
= get_display_depth(id
);
502 EnterCriticalSection(&modes_section
);
503 if (n
== 0 || lstrcmpiW(cached_device_name
, name
) || cached_flags
!= flags
)
505 if (!handler
.get_id(name
, &id
) || !handler
.get_modes(id
, flags
, &modes
, &mode_count
))
507 ERR("Failed to get %s supported display modes.\n", wine_dbgstr_w(name
));
508 LeaveCriticalSection(&modes_section
);
512 qsort(modes
, mode_count
, sizeof(*modes
) + modes
[0].dmDriverExtra
, mode_compare
);
515 handler
.free_modes(cached_modes
);
516 lstrcpyW(cached_device_name
, name
);
517 cached_flags
= flags
;
518 cached_modes
= modes
;
519 cached_mode_count
= mode_count
;
522 if (n
>= cached_mode_count
)
524 LeaveCriticalSection(&modes_section
);
525 WARN("handler:%s device:%s mode index:%#x not found.\n", handler
.name
, wine_dbgstr_w(name
), n
);
526 SetLastError(ERROR_NO_MORE_FILES
);
530 memcpy(devmode
, (BYTE
*)cached_modes
+ (sizeof(*cached_modes
) + cached_modes
[0].dmDriverExtra
) * n
, sizeof(*devmode
));
531 LeaveCriticalSection(&modes_section
);
534 /* Set generic fields */
535 devmode
->dmSize
= FIELD_OFFSET(DEVMODEW
, dmICMMethod
);
536 devmode
->dmDriverExtra
= 0;
537 devmode
->dmSpecVersion
= DM_SPECVERSION
;
538 devmode
->dmDriverVersion
= DM_SPECVERSION
;
539 lstrcpyW(devmode
->dmDeviceName
, dev_name
);
543 BOOL
is_detached_mode(const DEVMODEW
*mode
)
545 return mode
->dmFields
& DM_POSITION
&&
546 mode
->dmFields
& DM_PELSWIDTH
&&
547 mode
->dmFields
& DM_PELSHEIGHT
&&
548 mode
->dmPelsWidth
== 0 &&
549 mode
->dmPelsHeight
== 0;
552 /* Get the full display mode with all the necessary fields set.
553 * Return NULL on failure. Caller should call free_full_mode() to free the returned mode. */
554 static DEVMODEW
*get_full_mode(ULONG_PTR id
, DEVMODEW
*dev_mode
)
556 DEVMODEW
*modes
, *full_mode
, *found_mode
= NULL
;
557 UINT mode_count
, mode_idx
;
559 if (is_detached_mode(dev_mode
))
562 if (!handler
.get_modes(id
, EDS_ROTATEDMODE
, &modes
, &mode_count
))
565 qsort(modes
, mode_count
, sizeof(*modes
) + modes
[0].dmDriverExtra
, mode_compare
);
566 for (mode_idx
= 0; mode_idx
< mode_count
; ++mode_idx
)
568 found_mode
= (DEVMODEW
*)((BYTE
*)modes
+ (sizeof(*modes
) + modes
[0].dmDriverExtra
) * mode_idx
);
570 if (dev_mode
->dmFields
& DM_BITSPERPEL
&&
571 dev_mode
->dmBitsPerPel
&&
572 found_mode
->dmBitsPerPel
!= dev_mode
->dmBitsPerPel
)
574 if (dev_mode
->dmFields
& DM_PELSWIDTH
&& found_mode
->dmPelsWidth
!= dev_mode
->dmPelsWidth
)
576 if (dev_mode
->dmFields
& DM_PELSHEIGHT
&& found_mode
->dmPelsHeight
!= dev_mode
->dmPelsHeight
)
578 if (dev_mode
->dmFields
& DM_DISPLAYFREQUENCY
&&
579 dev_mode
->dmDisplayFrequency
&&
580 found_mode
->dmDisplayFrequency
&&
581 dev_mode
->dmDisplayFrequency
!= 1 &&
582 dev_mode
->dmDisplayFrequency
!= found_mode
->dmDisplayFrequency
)
584 if (dev_mode
->dmFields
& DM_DISPLAYORIENTATION
&&
585 found_mode
->u1
.s2
.dmDisplayOrientation
!= dev_mode
->u1
.s2
.dmDisplayOrientation
)
591 if (!found_mode
|| mode_idx
== mode_count
)
593 handler
.free_modes(modes
);
597 if (!(full_mode
= heap_alloc(sizeof(*found_mode
) + found_mode
->dmDriverExtra
)))
599 handler
.free_modes(modes
);
603 memcpy(full_mode
, found_mode
, sizeof(*found_mode
) + found_mode
->dmDriverExtra
);
604 handler
.free_modes(modes
);
606 full_mode
->dmFields
|= DM_POSITION
;
607 full_mode
->u1
.s2
.dmPosition
= dev_mode
->u1
.s2
.dmPosition
;
611 static void free_full_mode(DEVMODEW
*mode
)
613 if (!is_detached_mode(mode
))
617 static LONG
get_display_settings(struct x11drv_display_setting
**new_displays
,
618 INT
*new_display_count
, const WCHAR
*dev_name
, DEVMODEW
*dev_mode
)
620 struct x11drv_display_setting
*displays
;
621 DEVMODEW registry_mode
, current_mode
;
622 INT display_idx
, display_count
= 0;
623 DISPLAY_DEVICEW display_device
;
624 LONG ret
= DISP_CHANGE_FAILED
;
626 display_device
.cb
= sizeof(display_device
);
627 for (display_idx
= 0; EnumDisplayDevicesW(NULL
, display_idx
, &display_device
, 0); ++display_idx
)
630 displays
= heap_calloc(display_count
, sizeof(*displays
));
634 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
636 if (!EnumDisplayDevicesW(NULL
, display_idx
, &display_device
, 0))
639 if (!handler
.get_id(display_device
.DeviceName
, &displays
[display_idx
].id
))
641 ret
= DISP_CHANGE_BADPARAM
;
647 memset(®istry_mode
, 0, sizeof(registry_mode
));
648 registry_mode
.dmSize
= sizeof(registry_mode
);
649 if (!EnumDisplaySettingsExW(display_device
.DeviceName
, ENUM_REGISTRY_SETTINGS
, ®istry_mode
, 0))
652 displays
[display_idx
].desired_mode
= registry_mode
;
654 else if (!lstrcmpiW(dev_name
, display_device
.DeviceName
))
656 displays
[display_idx
].desired_mode
= *dev_mode
;
657 if (!(dev_mode
->dmFields
& DM_POSITION
))
659 memset(¤t_mode
, 0, sizeof(current_mode
));
660 current_mode
.dmSize
= sizeof(current_mode
);
661 if (!EnumDisplaySettingsExW(display_device
.DeviceName
, ENUM_CURRENT_SETTINGS
, ¤t_mode
, 0))
664 displays
[display_idx
].desired_mode
.dmFields
|= DM_POSITION
;
665 displays
[display_idx
].desired_mode
.u1
.s2
.dmPosition
= current_mode
.u1
.s2
.dmPosition
;
670 memset(¤t_mode
, 0, sizeof(current_mode
));
671 current_mode
.dmSize
= sizeof(current_mode
);
672 if (!EnumDisplaySettingsExW(display_device
.DeviceName
, ENUM_CURRENT_SETTINGS
, ¤t_mode
, 0))
675 displays
[display_idx
].desired_mode
= current_mode
;
678 SetRect(&displays
[display_idx
].desired_rect
,
679 displays
[display_idx
].desired_mode
.u1
.s2
.dmPosition
.x
,
680 displays
[display_idx
].desired_mode
.u1
.s2
.dmPosition
.y
,
681 displays
[display_idx
].desired_mode
.u1
.s2
.dmPosition
.x
+ displays
[display_idx
].desired_mode
.dmPelsWidth
,
682 displays
[display_idx
].desired_mode
.u1
.s2
.dmPosition
.y
+ displays
[display_idx
].desired_mode
.dmPelsHeight
);
683 lstrcpyW(displays
[display_idx
].desired_mode
.dmDeviceName
, display_device
.DeviceName
);
686 *new_displays
= displays
;
687 *new_display_count
= display_count
;
688 return DISP_CHANGE_SUCCESSFUL
;
695 static INT
offset_length(POINT offset
)
697 return offset
.x
* offset
.x
+ offset
.y
* offset
.y
;
700 /* Check if a rect overlaps with placed display rects */
701 static BOOL
overlap_placed_displays(const RECT
*rect
, const struct x11drv_display_setting
*displays
, INT display_count
)
706 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
708 if (displays
[display_idx
].placed
&&
709 IntersectRect(&intersect
, &displays
[display_idx
].new_rect
, rect
))
715 /* Get the offset with minimum length to place a display next to the placed displays with no spacing and overlaps */
716 static POINT
get_placement_offset(const struct x11drv_display_setting
*displays
, INT display_count
, INT placing_idx
)
718 POINT points
[8], left_top
, offset
, min_offset
= {0, 0};
719 INT display_idx
, point_idx
, point_count
, vertex_idx
;
720 BOOL has_placed
= FALSE
, first
= TRUE
;
724 /* If the display to be placed is detached, no offset is needed to place it */
725 if (IsRectEmpty(&displays
[placing_idx
].desired_rect
))
728 /* If there is no placed and attached display, place this display as it is */
729 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
731 if (displays
[display_idx
].placed
&& !IsRectEmpty(&displays
[display_idx
].new_rect
))
741 /* Try to place this display with each of its four vertices at every vertex of the placed
742 * displays and see which combination has the minimum offset length */
743 width
= displays
[placing_idx
].desired_rect
.right
- displays
[placing_idx
].desired_rect
.left
;
744 height
= displays
[placing_idx
].desired_rect
.bottom
- displays
[placing_idx
].desired_rect
.top
;
746 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
748 if (!displays
[display_idx
].placed
|| IsRectEmpty(&displays
[display_idx
].new_rect
))
751 /* Get four vertices of the placed display rectangle */
752 points
[0].x
= displays
[display_idx
].new_rect
.left
;
753 points
[0].y
= displays
[display_idx
].new_rect
.top
;
754 points
[1].x
= displays
[display_idx
].new_rect
.left
;
755 points
[1].y
= displays
[display_idx
].new_rect
.bottom
;
756 points
[2].x
= displays
[display_idx
].new_rect
.right
;
757 points
[2].y
= displays
[display_idx
].new_rect
.top
;
758 points
[3].x
= displays
[display_idx
].new_rect
.right
;
759 points
[3].y
= displays
[display_idx
].new_rect
.bottom
;
762 /* Intersected points when moving the display to be placed horizontally */
763 if (displays
[placing_idx
].desired_rect
.bottom
>= displays
[display_idx
].new_rect
.top
&&
764 displays
[placing_idx
].desired_rect
.top
<= displays
[display_idx
].new_rect
.bottom
)
766 points
[point_count
].x
= displays
[display_idx
].new_rect
.left
;
767 points
[point_count
++].y
= displays
[placing_idx
].desired_rect
.top
;
768 points
[point_count
].x
= displays
[display_idx
].new_rect
.right
;
769 points
[point_count
++].y
= displays
[placing_idx
].desired_rect
.top
;
771 /* Intersected points when moving the display to be placed vertically */
772 if (displays
[placing_idx
].desired_rect
.left
<= displays
[display_idx
].new_rect
.right
&&
773 displays
[placing_idx
].desired_rect
.right
>= displays
[display_idx
].new_rect
.left
)
775 points
[point_count
].x
= displays
[placing_idx
].desired_rect
.left
;
776 points
[point_count
++].y
= displays
[display_idx
].new_rect
.top
;
777 points
[point_count
].x
= displays
[placing_idx
].desired_rect
.left
;
778 points
[point_count
++].y
= displays
[display_idx
].new_rect
.bottom
;
781 /* Try moving each vertex of the display rectangle to each points */
782 for (point_idx
= 0; point_idx
< point_count
; ++point_idx
)
784 for (vertex_idx
= 0; vertex_idx
< 4; ++vertex_idx
)
788 /* Move the bottom right vertex to the point */
790 left_top
.x
= points
[point_idx
].x
- width
;
791 left_top
.y
= points
[point_idx
].y
- height
;
793 /* Move the bottom left vertex to the point */
795 left_top
.x
= points
[point_idx
].x
;
796 left_top
.y
= points
[point_idx
].y
- height
;
798 /* Move the top right vertex to the point */
800 left_top
.x
= points
[point_idx
].x
- width
;
801 left_top
.y
= points
[point_idx
].y
;
803 /* Move the top left vertex to the point */
805 left_top
.x
= points
[point_idx
].x
;
806 left_top
.y
= points
[point_idx
].y
;
810 offset
.x
= left_top
.x
- displays
[placing_idx
].desired_rect
.left
;
811 offset
.y
= left_top
.y
- displays
[placing_idx
].desired_rect
.top
;
812 rect
= displays
[placing_idx
].desired_rect
;
813 OffsetRect(&rect
, offset
.x
, offset
.y
);
814 if (!overlap_placed_displays(&rect
, displays
, display_count
))
823 if (offset_length(offset
) < offset_length(min_offset
))
833 static void place_all_displays(struct x11drv_display_setting
*displays
, INT display_count
)
835 INT left_most
= INT_MAX
, top_most
= INT_MAX
;
836 INT placing_idx
, display_idx
;
837 POINT min_offset
, offset
;
839 /* Place all displays with no extra space between them and no overlapping */
842 /* Place the unplaced display with the minimum offset length first */
844 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
846 if (displays
[display_idx
].placed
)
849 offset
= get_placement_offset(displays
, display_count
, display_idx
);
850 if (placing_idx
== -1 || offset_length(offset
) < offset_length(min_offset
))
853 placing_idx
= display_idx
;
857 /* If all displays are placed */
858 if (placing_idx
== -1)
861 displays
[placing_idx
].new_rect
= displays
[placing_idx
].desired_rect
;
862 OffsetRect(&displays
[placing_idx
].new_rect
, min_offset
.x
, min_offset
.y
);
863 displays
[placing_idx
].placed
= TRUE
;
866 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
868 displays
[display_idx
].desired_mode
.u1
.s2
.dmPosition
.x
= displays
[display_idx
].new_rect
.left
;
869 displays
[display_idx
].desired_mode
.u1
.s2
.dmPosition
.y
= displays
[display_idx
].new_rect
.top
;
870 left_most
= min(left_most
, displays
[display_idx
].new_rect
.left
);
871 top_most
= min(top_most
, displays
[display_idx
].new_rect
.top
);
874 /* Convert virtual screen coordinates to root coordinates */
875 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
877 displays
[display_idx
].desired_mode
.u1
.s2
.dmPosition
.x
-= left_most
;
878 displays
[display_idx
].desired_mode
.u1
.s2
.dmPosition
.y
-= top_most
;
882 static LONG
apply_display_settings(struct x11drv_display_setting
*displays
, INT display_count
, BOOL do_attach
)
889 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
891 attached_mode
= !is_detached_mode(&displays
[display_idx
].desired_mode
);
892 if ((attached_mode
&& !do_attach
) || (!attached_mode
&& do_attach
))
895 full_mode
= get_full_mode(displays
[display_idx
].id
, &displays
[display_idx
].desired_mode
);
897 return DISP_CHANGE_BADMODE
;
899 TRACE("handler:%s changing %s to position:(%d,%d) resolution:%ux%u frequency:%uHz "
900 "depth:%ubits orientation:%#x.\n", handler
.name
,
901 wine_dbgstr_w(displays
[display_idx
].desired_mode
.dmDeviceName
),
902 full_mode
->u1
.s2
.dmPosition
.x
, full_mode
->u1
.s2
.dmPosition
.y
, full_mode
->dmPelsWidth
,
903 full_mode
->dmPelsHeight
, full_mode
->dmDisplayFrequency
, full_mode
->dmBitsPerPel
,
904 full_mode
->u1
.s2
.dmDisplayOrientation
);
906 ret
= handler
.set_current_mode(displays
[display_idx
].id
, full_mode
);
907 if (attached_mode
&& ret
== DISP_CHANGE_SUCCESSFUL
)
908 set_display_depth(displays
[display_idx
].id
, full_mode
->dmBitsPerPel
);
909 free_full_mode(full_mode
);
910 if (ret
!= DISP_CHANGE_SUCCESSFUL
)
914 return DISP_CHANGE_SUCCESSFUL
;
917 static BOOL
all_detached_settings(const struct x11drv_display_setting
*displays
, INT display_count
)
921 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
923 if (!is_detached_mode(&displays
[display_idx
].desired_mode
))
930 /***********************************************************************
931 * ChangeDisplaySettingsEx (X11DRV.@)
934 LONG CDECL
X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname
, LPDEVMODEW devmode
,
935 HWND hwnd
, DWORD flags
, LPVOID lpvoid
)
937 struct x11drv_display_setting
*displays
;
938 INT display_idx
, display_count
;
942 ret
= get_display_settings(&displays
, &display_count
, devname
, devmode
);
943 if (ret
!= DISP_CHANGE_SUCCESSFUL
)
946 if (flags
& CDS_UPDATEREGISTRY
&& devname
&& devmode
)
948 for (display_idx
= 0; display_idx
< display_count
; ++display_idx
)
950 if (!lstrcmpiW(displays
[display_idx
].desired_mode
.dmDeviceName
, devname
))
952 full_mode
= get_full_mode(displays
[display_idx
].id
, &displays
[display_idx
].desired_mode
);
956 return DISP_CHANGE_BADMODE
;
959 if (!write_registry_settings(devname
, full_mode
))
961 ERR("Failed to write %s display settings to registry.\n", wine_dbgstr_w(devname
));
962 free_full_mode(full_mode
);
964 return DISP_CHANGE_NOTUPDATED
;
967 free_full_mode(full_mode
);
973 if (flags
& (CDS_TEST
| CDS_NORESET
))
976 return DISP_CHANGE_SUCCESSFUL
;
979 if (all_detached_settings(displays
, display_count
))
981 WARN("Detaching all displays is not permitted.\n");
983 return DISP_CHANGE_SUCCESSFUL
;
986 place_all_displays(displays
, display_count
);
988 /* Detach displays first to free up CRTCs */
989 ret
= apply_display_settings(displays
, display_count
, FALSE
);
990 if (ret
== DISP_CHANGE_SUCCESSFUL
)
991 ret
= apply_display_settings(displays
, display_count
, TRUE
);
992 if (ret
== DISP_CHANGE_SUCCESSFUL
)
993 X11DRV_DisplayDevices_Update(TRUE
);