Commit bd8ec8f2 authored by Zhiyi Zhang's avatar Zhiyi Zhang Committed by Alexandre Julliard

winex11.drv: Support _NET_WM_FULLSCREEN_MONITORS.

Support _NET_WM_FULLSCREEN_MONITORS for fullscreen windows spanning multiple monitors. This property is used to hint window managers about which monitor a fullscreen window should cover. Window managers make a fullscreen window spanning multiple monitors cover only one monitor when this property is missing. Fix Project Cars 2/3 incorrect game window size when the triple-screen mode is on.
parent 3dd68caa
......@@ -9282,7 +9282,8 @@ static void test_fullscreen(void)
flush_events(TRUE);
GetWindowRect(hwnd, &rc);
todo_wine
/* FVWM used by TestBots 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);
......
......@@ -533,6 +533,12 @@ void X11DRV_DisplayDevices_RegisterEventHandlers(void)
handler->register_event_handlers();
}
/* Report whether a display device handler supports detecting dynamic device changes */
BOOL X11DRV_DisplayDevices_SupportEventHandlers(void)
{
return !!host_handler.register_event_handlers;
}
static BOOL force_display_devices_refresh;
BOOL X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manager, BOOL force, void *param )
......
......@@ -970,6 +970,46 @@ 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];
XEvent xev;
if (!(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) || is_virtual_desktop())
return;
/* If the current display device handler can not detect dynamic device changes, do not use
* _NET_WM_FULLSCREEN_MONITORS because xinerama_get_fullscreen_monitors() may report wrong
* indices because of stale xinerama monitor information */
if (!X11DRV_DisplayDevices_SupportEventHandlers())
return;
if (!xinerama_get_fullscreen_monitors( &data->whole_rect, monitors ))
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
*/
......@@ -1051,6 +1091,7 @@ void update_net_wm_states( struct x11drv_win_data *data )
}
}
data->net_wm_state = new_state;
update_net_wm_fullscreen_monitors( data );
}
/***********************************************************************
......@@ -1143,6 +1184,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 );
}
......
......@@ -479,6 +479,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,
......@@ -691,6 +692,7 @@ extern POINT virtual_screen_to_root( INT x, INT y ) DECLSPEC_HIDDEN;
extern POINT root_to_virtual_screen( INT x, INT y ) DECLSPEC_HIDDEN;
extern RECT get_host_primary_monitor_rect(void) DECLSPEC_HIDDEN;
extern RECT get_work_area( const RECT *monitor_rect ) DECLSPEC_HIDDEN;
extern BOOL xinerama_get_fullscreen_monitors( const RECT *rect, long *indices ) DECLSPEC_HIDDEN;
extern void xinerama_init( unsigned int width, unsigned int height ) DECLSPEC_HIDDEN;
extern void init_recursive_mutex( pthread_mutex_t *mutex ) DECLSPEC_HIDDEN;
......@@ -803,6 +805,7 @@ extern BOOL get_host_primary_gpu(struct gdi_gpu *gpu) DECLSPEC_HIDDEN;
extern void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler *handler) DECLSPEC_HIDDEN;
extern void X11DRV_DisplayDevices_Init(BOOL force) DECLSPEC_HIDDEN;
extern void X11DRV_DisplayDevices_RegisterEventHandlers(void) DECLSPEC_HIDDEN;
extern BOOL X11DRV_DisplayDevices_SupportEventHandlers(void) DECLSPEC_HIDDEN;
/* Display device handler used in virtual desktop mode */
extern struct x11drv_display_device_handler desktop_handler DECLSPEC_HIDDEN;
......
......@@ -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",
......
......@@ -45,6 +45,7 @@ static MONITORINFOEXW default_monitor =
{ '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 } /* szDevice */
};
static pthread_mutex_t xinerama_mutex = PTHREAD_MUTEX_INITIALIZER;
static MONITORINFOEXW *monitors;
static int nb_monitors;
......@@ -122,6 +123,73 @@ static inline int query_screens(void)
#endif /* SONAME_LIBXINERAMA */
/* Get xinerama monitor indices required for _NET_WM_FULLSCREEN_MONITORS */
BOOL xinerama_get_fullscreen_monitors( const RECT *rect, long *indices )
{
RECT window_rect, intersected_rect, monitor_rect;
BOOL ret = FALSE;
POINT offset;
INT i;
pthread_mutex_lock( &xinerama_mutex );
if (nb_monitors == 1)
{
memset( indices, 0, sizeof(*indices) * 4 );
ret = TRUE;
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 );
intersect_rect( &intersected_rect, &window_rect, &monitor_rect );
if (EqualRect( &intersected_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;
}
}
if (indices[0] == -1 || indices[1] == -1 || indices[2] == -1 || indices[3] == -1)
ERR("Failed to get xinerama fullscreen monitor indices.\n");
else
ret = TRUE;
done:
pthread_mutex_unlock( &xinerama_mutex );
if (ret)
TRACE( "fullsceen monitors: %ld,%ld,%ld,%ld.\n", indices[0], indices[1], indices[2], indices[3] );
return ret;
}
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};
......@@ -157,9 +225,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 */
pthread_mutex_lock( &xinerama_mutex );
adapters = calloc( nb_monitors, sizeof(*adapters) );
if (!adapters)
{
pthread_mutex_unlock( &xinerama_mutex );
return FALSE;
}
primary_index = primary_monitor;
if (primary_index >= nb_monitors)
......@@ -204,6 +276,7 @@ static BOOL xinerama_get_adapters( ULONG_PTR gpu_id, struct gdi_adapter **new_ad
*new_adapters = adapters;
*count = index;
pthread_mutex_unlock( &xinerama_mutex );
return TRUE;
}
......@@ -223,6 +296,8 @@ static BOOL xinerama_get_monitors( ULONG_PTR adapter_id, struct gdi_monitor **ne
INT index = 0;
INT i;
pthread_mutex_lock( &xinerama_mutex );
for (i = first; i < nb_monitors; i++)
{
if (i == first
......@@ -233,7 +308,10 @@ static BOOL xinerama_get_monitors( ULONG_PTR adapter_id, struct gdi_monitor **ne
monitor = calloc( monitor_count, sizeof(*monitor) );
if (!monitor)
{
pthread_mutex_unlock( &xinerama_mutex );
return FALSE;
}
for (i = first; i < nb_monitors; i++)
{
......@@ -257,6 +335,7 @@ static BOOL xinerama_get_monitors( ULONG_PTR adapter_id, struct gdi_monitor **ne
*new_monitors = monitor;
*count = monitor_count;
pthread_mutex_unlock( &xinerama_mutex );
return TRUE;
}
......@@ -275,6 +354,8 @@ void xinerama_init( unsigned int width, unsigned int height )
if (is_virtual_desktop())
return;
pthread_mutex_lock( &xinerama_mutex );
SetRect( &rect, 0, 0, width, height );
if (!query_screens())
{
......@@ -298,6 +379,8 @@ void xinerama_init( unsigned int width, unsigned int height )
(monitors[i].dwFlags & MONITORINFOF_PRIMARY) ? " (primary)" : "" );
}
pthread_mutex_unlock( &xinerama_mutex );
handler.name = "Xinerama";
handler.priority = 100;
handler.get_gpus = xinerama_get_gpus;
......
......@@ -1190,6 +1190,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 == NtUserGetDesktopWindow() && NtUserGetWindowThread( hwnd, NULL ) == GetCurrentThreadId())
{
......@@ -1197,6 +1199,9 @@ static BOOL xrandr14_device_change_handler( HWND hwnd, XEvent *event )
init_registry_display_settings();
X11DRV_resize_desktop();
}
/* 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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment