diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index a99163860f4..097aab64eaf 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -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); diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index d53774f0e02..671c0c5dea2 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -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 ) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index e1fa665608e..6bd028abb61 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -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 ); } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 5e1faf8a16e..a424eb112e8 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -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; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 497e270ee8a..77e72752017 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 847b8fb6efb..6835aa4a331 100644 --- a/dlls/winex11.drv/xinerama.c +++ b/dlls/winex11.drv/xinerama.c @@ -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; diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c index 2b75e3b28eb..b4d613cf56a 100644 --- a/dlls/winex11.drv/xrandr.c +++ b/dlls/winex11.drv/xrandr.c @@ -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; }