Support _NET_WM_FULLSCREEN_MONITORS for fullscreen windows spanning multiple monitors. This property is used to hint the window managers which monitors a fullscreen window should cover. Window managers make a fullscreen window spanning multiple monitors cover only the primary monitor when this property is absent.
Fix Project Cars 2/3 incorrect game window size when the triple-screen mode is on.
Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/user32/tests/win.c | 3 +- dlls/winex11.drv/window.c | 38 ++++++++++++++++ dlls/winex11.drv/x11drv.h | 2 + dlls/winex11.drv/x11drv_main.c | 1 + dlls/winex11.drv/xinerama.c | 81 ++++++++++++++++++++++++++++++++++ dlls/winex11.drv/xrandr.c | 5 +++ 6 files changed, 129 insertions(+), 1 deletion(-)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 63c18cd9948..7642a84ef1e 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -9295,7 +9295,8 @@ static void test_fullscreen(void) flush_events(TRUE);
GetWindowRect(hwnd, &rc); - todo_wine + /* FVWM used by TestBot doesn't support _NET_WM_FULLSCREEN_MONITORS */ + todo_wine_if(!EqualRect(&rc, &virtual_rect)) ok(EqualRect(&rc, &virtual_rect), "Expected %s, got %s.\n", wine_dbgstr_rect(&virtual_rect), wine_dbgstr_rect(&rc)); DestroyWindow(hwnd); diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 36fb41ac710..b6b3507e88f 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -965,6 +965,42 @@ void update_user_time( Time time ) XUnlockDisplay( gdi_display ); }
+/* Update _NET_WM_FULLSCREEN_MONITORS when _NET_WM_STATE_FULLSCREEN is set to support fullscreen + * windows spanning multiple monitors */ +static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) +{ + long monitors[4] = {0}; + XEvent xev; + + if (!(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) || is_virtual_desktop()) + return; + + /* No need to set _NET_WM_FULLSCREEN_MONITORS when it covers only the primary monitor */ + xinerama_get_fullscreen_monitors( &data->whole_rect, monitors ); + if (monitors[0] == 0 && monitors[1] == 0 && monitors[2] == 0 && monitors[3] == 0) + return; + + if (!data->mapped) + { + XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_FULLSCREEN_MONITORS), + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)monitors, 4 ); + } + else + { + xev.xclient.type = ClientMessage; + xev.xclient.window = data->whole_window; + xev.xclient.message_type = x11drv_atom(_NET_WM_FULLSCREEN_MONITORS); + xev.xclient.serial = 0; + xev.xclient.display = data->display; + xev.xclient.send_event = True; + xev.xclient.format = 32; + xev.xclient.data.l[4] = 1; + memcpy( xev.xclient.data.l, monitors, sizeof(monitors) ); + XSendEvent( data->display, root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev ); + } +} + /*********************************************************************** * update_net_wm_states */ @@ -1045,6 +1081,7 @@ void update_net_wm_states( struct x11drv_win_data *data ) } } data->net_wm_state = new_state; + update_net_wm_fullscreen_monitors( data ); }
/*********************************************************************** @@ -1137,6 +1174,7 @@ static void map_window( HWND hwnd, DWORD new_style )
data->mapped = TRUE; data->iconic = (new_style & WS_MINIMIZE) != 0; + update_net_wm_fullscreen_monitors( data ); } release_win_data( data ); } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index f389f3e0836..363435a2cf6 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -490,6 +490,7 @@ enum x11drv_atoms XATOM__NET_SYSTEM_TRAY_OPCODE, XATOM__NET_SYSTEM_TRAY_S0, XATOM__NET_SYSTEM_TRAY_VISUAL, + XATOM__NET_WM_FULLSCREEN_MONITORS, XATOM__NET_WM_ICON, XATOM__NET_WM_MOVERESIZE, XATOM__NET_WM_NAME, @@ -704,6 +705,7 @@ extern RECT get_virtual_screen_rect(void) DECLSPEC_HIDDEN; extern RECT get_primary_monitor_rect(void) DECLSPEC_HIDDEN; extern RECT get_host_primary_monitor_rect(void) DECLSPEC_HIDDEN; extern RECT get_work_area( const RECT *monitor_rect ) DECLSPEC_HIDDEN; +extern void xinerama_get_fullscreen_monitors( const RECT *rect, long *indices ) DECLSPEC_HIDDEN; extern void xinerama_init( unsigned int width, unsigned int height ) DECLSPEC_HIDDEN;
#define DEPTH_COUNT 3 diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 32beb84a009..7896afcb780 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -162,6 +162,7 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "_NET_SYSTEM_TRAY_OPCODE", "_NET_SYSTEM_TRAY_S0", "_NET_SYSTEM_TRAY_VISUAL", + "_NET_WM_FULLSCREEN_MONITORS", "_NET_WM_ICON", "_NET_WM_MOVERESIZE", "_NET_WM_NAME", diff --git a/dlls/winex11.drv/xinerama.c b/dlls/winex11.drv/xinerama.c index b707574a0ab..568044e4230 100644 --- a/dlls/winex11.drv/xinerama.c +++ b/dlls/winex11.drv/xinerama.c @@ -42,6 +42,14 @@ static MONITORINFOEXW default_monitor = { '\','\','.','\','D','I','S','P','L','A','Y','1',0 } /* szDevice */ };
+static CRITICAL_SECTION xinerama_section; +static CRITICAL_SECTION_DEBUG xinerama_critsect_debug = +{ + 0, 0, &xinerama_section, + {&xinerama_critsect_debug.ProcessLocksList, &xinerama_critsect_debug.ProcessLocksList}, + 0, 0, {(DWORD_PTR)(__FILE__ ": xinerama_section")} +}; +static CRITICAL_SECTION xinerama_section = {&xinerama_critsect_debug, -1, 0, 0, 0, 0}; static MONITORINFOEXW *monitors; static int nb_monitors;
@@ -119,6 +127,64 @@ static inline int query_screens(void)
#endif /* SONAME_LIBXINERAMA */
+/* Get xinerama monitor indices required for _NET_WM_FULLSCREEN_MONITORS */ +void xinerama_get_fullscreen_monitors( const RECT *rect, long *indices ) +{ + RECT window_rect, intersect_rect, monitor_rect; + POINT offset; + INT i; + + EnterCriticalSection( &xinerama_section ); + if (nb_monitors == 1) + { + memset( indices, 0, sizeof(*indices) * 4 ); + goto done; + } + + /* Convert window rectangle to root coordinates */ + offset = virtual_screen_to_root( rect->left, rect->top ); + window_rect.left = offset.x; + window_rect.top = offset.y; + window_rect.right = window_rect.left + rect->right - rect->left; + window_rect.bottom = window_rect.top + rect->bottom - rect->top; + + /* Compare to xinerama monitor rectangles in root coordinates */ + offset.x = INT_MAX; + offset.y = INT_MAX; + for (i = 0; i < nb_monitors; ++i) + { + offset.x = min( offset.x, monitors[i].rcMonitor.left ); + offset.y = min( offset.y, monitors[i].rcMonitor.top ); + } + + indices[0] = -1; + indices[1] = -1; + indices[2] = -1; + indices[3] = -1; + for (i = 0; i < nb_monitors; ++i) + { + SetRect( &monitor_rect, monitors[i].rcMonitor.left - offset.x, + monitors[i].rcMonitor.top - offset.y, monitors[i].rcMonitor.right - offset.x, + monitors[i].rcMonitor.bottom - offset.y ); + IntersectRect( &intersect_rect, &window_rect, &monitor_rect ); + if (EqualRect( &intersect_rect, &monitor_rect )) + { + if (indices[0] == -1 || monitors[i].rcMonitor.top < monitors[indices[0]].rcMonitor.top) + indices[0] = i; + if (indices[1] == -1 || monitors[i].rcMonitor.bottom > monitors[indices[1]].rcMonitor.bottom) + indices[1] = i; + if (indices[2] == -1 || monitors[i].rcMonitor.left < monitors[indices[2]].rcMonitor.left) + indices[2] = i; + if (indices[3] == -1 || monitors[i].rcMonitor.right > monitors[indices[3]].rcMonitor.right) + indices[3] = i; + } + } + +done: + LeaveCriticalSection( &xinerama_section ); + TRACE( "fullsceen monitors: %ld,%ld,%ld,%ld.\n", indices[0], indices[1], indices[2], indices[3] ); +} + static BOOL xinerama_get_gpus( struct gdi_gpu **new_gpus, int *count ) { static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0}; @@ -154,9 +220,13 @@ static BOOL xinerama_get_adapters( ULONG_PTR gpu_id, struct gdi_adapter **new_ad return FALSE;
/* Being lazy, actual adapter count may be less */ + EnterCriticalSection( &xinerama_section ); adapters = heap_calloc( nb_monitors, sizeof(*adapters) ); if (!adapters) + { + LeaveCriticalSection( &xinerama_section ); return FALSE; + }
primary_index = primary_monitor; if (primary_index >= nb_monitors) @@ -201,6 +271,7 @@ static BOOL xinerama_get_adapters( ULONG_PTR gpu_id, struct gdi_adapter **new_ad
*new_adapters = adapters; *count = index; + LeaveCriticalSection( &xinerama_section ); return TRUE; }
@@ -220,6 +291,8 @@ static BOOL xinerama_get_monitors( ULONG_PTR adapter_id, struct gdi_monitor **ne INT index = 0; INT i;
+ EnterCriticalSection( &xinerama_section ); + for (i = first; i < nb_monitors; i++) { if (i == first @@ -230,7 +303,10 @@ static BOOL xinerama_get_monitors( ULONG_PTR adapter_id, struct gdi_monitor **ne
monitor = heap_calloc( monitor_count, sizeof(*monitor) ); if (!monitor) + { + LeaveCriticalSection( &xinerama_section ); return FALSE; + }
for (i = first; i < nb_monitors; i++) { @@ -254,6 +330,7 @@ static BOOL xinerama_get_monitors( ULONG_PTR adapter_id, struct gdi_monitor **ne
*new_monitors = monitor; *count = monitor_count; + LeaveCriticalSection( &xinerama_section ); return TRUE; }
@@ -272,6 +349,8 @@ void xinerama_init( unsigned int width, unsigned int height ) if (is_virtual_desktop()) return;
+ EnterCriticalSection( &xinerama_section ); + SetRect( &rect, 0, 0, width, height ); if (!query_screens()) { @@ -295,6 +374,8 @@ void xinerama_init( unsigned int width, unsigned int height ) (monitors[i].dwFlags & MONITORINFOF_PRIMARY) ? " (primary)" : "" ); }
+ LeaveCriticalSection( &xinerama_section ); + handler.name = "Xinerama"; handler.priority = 100; handler.get_gpus = xinerama_get_gpus; diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c index fb7a4405a1e..71b76bda0fb 100644 --- a/dlls/winex11.drv/xrandr.c +++ b/dlls/winex11.drv/xrandr.c @@ -1181,6 +1181,8 @@ static void xrandr14_free_monitors( struct gdi_monitor *monitors, int count )
static BOOL xrandr14_device_change_handler( HWND hwnd, XEvent *event ) { + RECT rect; + xrandr14_invalidate_current_mode_cache(); if (hwnd == GetDesktopWindow() && GetWindowThreadProcessId( hwnd, NULL ) == GetCurrentThreadId()) { @@ -1191,6 +1193,9 @@ static BOOL xrandr14_device_change_handler( HWND hwnd, XEvent *event )
init_registry_display_settings(); } + /* Update xinerama monitors for xinerama_get_fullscreen_monitors() */ + rect = get_host_primary_monitor_rect(); + xinerama_init( rect.right - rect.left, rect.bottom - rect.top ); return FALSE; }