[PATCH 2/2] winex11.drv: Support _NET_WM_FULLSCREEN_MONITORS.

Zhiyi Zhang zzhang at codeweavers.com
Wed Feb 23 02:35:13 CST 2022


Please ignore this series. It causes new test failures on TestBot.

On 2/23/22 15:53, Zhiyi Zhang wrote:
> 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 at codeweavers.com>
> ---
>  dlls/user32/tests/win.c        |  1 -
>  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, 127 insertions(+), 1 deletion(-)
>
> diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c
> index 63c18cd9948..da2ab5beb0e 100644
> --- a/dlls/user32/tests/win.c
> +++ b/dlls/user32/tests/win.c
> @@ -9295,7 +9295,6 @@ static void test_fullscreen(void)
>          flush_events(TRUE);
>  
>          GetWindowRect(hwnd, &rc);
> -        todo_wine
>          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;
>  }
>  




More information about the wine-devel mailing list