[PATCH 3/4] winex11.drv: Support XRandR display device handler.

Zhiyi Zhang zzhang at codeweavers.com
Thu Sep 12 02:29:25 CDT 2019


XRandR supports multiple GPUs and runtime device change
compared to Xinerama.

Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
 configure.ac                |   7 +-
 dlls/winex11.drv/x11drv.h   |   1 +
 dlls/winex11.drv/xinerama.c |   2 +-
 dlls/winex11.drv/xrandr.c   | 421 ++++++++++++++++++++++++++++++++++++
 4 files changed, 429 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index b39b6b2c90..692c8d1c77 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1223,7 +1223,12 @@ then
                     [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <X11/Xlib.h>
 #include <X11/extensions/Xrandr.h>]], [[static typeof(XRRGetScreenResources) *f; if (f) return 0;]])],
                       [AC_DEFINE(HAVE_XRRGETSCREENRESOURCES, 1,
-                        [Define if Xrandr has the XRRGetScreenResources function])])],,[$X_LIBS $X_EXTRA_LIBS])])
+                        [Define if Xrandr has the XRRGetScreenResources function])])
+                     AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>]], [[static typeof(XRRGetProviderResources) *f; if (f) return 0;]])],
+                      [AC_DEFINE(HAVE_XRRGETPROVIDERREDOURCES, 1,
+                        [Define if Xrandr has the XRRGetProviderResources function])],
+                      [WINE_NOTICE([libxrandr ${notice_platform}development files too old, XRandR display device handler won't be supported.])])],,[$X_LIBS $X_EXTRA_LIBS])])
         fi
         WINE_NOTICE_WITH(xrandr,[test "x$ac_cv_lib_soname_Xrandr" = "x"],
                          [libxrandr ${notice_platform}development files not found, XRandr won't be supported.])
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index d4e476facb..1d3d68ad30 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -640,6 +640,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_virtual_screen_rect(void) DECLSPEC_HIDDEN;
 extern RECT get_primary_monitor_rect(void) DECLSPEC_HIDDEN;
+extern void query_work_area( RECT *rc_work ) DECLSPEC_HIDDEN;
 extern void xinerama_init( unsigned int width, unsigned int height ) DECLSPEC_HIDDEN;
 
 struct x11drv_mode_info
diff --git a/dlls/winex11.drv/xinerama.c b/dlls/winex11.drv/xinerama.c
index 8e08221343..61c422e835 100644
--- a/dlls/winex11.drv/xinerama.c
+++ b/dlls/winex11.drv/xinerama.c
@@ -55,7 +55,7 @@ static inline MONITORINFOEXW *get_primary(void)
     return &monitors[idx];
 }
 
-static void query_work_area( RECT *rc_work )
+void query_work_area( RECT *rc_work )
 {
     Atom type;
     int format;
diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c
index 288b83fde5..fd3bfd8b87 100644
--- a/dlls/winex11.drv/xrandr.c
+++ b/dlls/winex11.drv/xrandr.c
@@ -3,6 +3,7 @@
  *
  * Copyright 2003 Alexander James Pasadyn
  * Copyright 2012 Henri Verbeet for CodeWeavers
+ * Copyright 2019 Zhiyi Zhang for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -34,7 +35,9 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
 #include <X11/extensions/Xrandr.h>
 #include "x11drv.h"
 
+#include "wine/heap.h"
 #include "wine/library.h"
+#include "wine/unicode.h"
 
 static void *xrandr_handle;
 
@@ -64,6 +67,14 @@ static RRMode *xrandr12_modes;
 static int primary_crtc;
 #endif
 
+#ifdef HAVE_XRRGETPROVIDERREDOURCES
+MAKE_FUNCPTR(XRRGetOutputPrimary)
+MAKE_FUNCPTR(XRRGetProviderResources)
+MAKE_FUNCPTR(XRRFreeProviderResources)
+MAKE_FUNCPTR(XRRGetProviderInfo)
+MAKE_FUNCPTR(XRRFreeProviderInfo)
+#endif
+
 #undef MAKE_FUNCPTR
 
 static struct x11drv_mode_info *dd_modes;
@@ -106,6 +117,16 @@ static int load_xrandr(void)
         LOAD_FUNCPTR(XRRSetScreenSize)
         r = 2;
 #endif
+
+#ifdef HAVE_XRRGETPROVIDERREDOURCES
+        LOAD_FUNCPTR(XRRGetOutputPrimary)
+        LOAD_FUNCPTR(XRRGetProviderResources)
+        LOAD_FUNCPTR(XRRFreeProviderResources)
+        LOAD_FUNCPTR(XRRGetProviderInfo)
+        LOAD_FUNCPTR(XRRFreeProviderInfo)
+        r = 4;
+#endif
+
 #undef LOAD_FUNCPTR
 
 sym_not_found:
@@ -560,8 +581,393 @@ done:
 
 #endif /* HAVE_XRRGETSCREENRESOURCES */
 
+#ifdef HAVE_XRRGETPROVIDERREDOURCES
+
+static BOOL xrandr14_get_gpus(struct x11drv_gpu **new_gpus, int *count)
+{
+    struct x11drv_gpu *gpus = NULL;
+    XRRScreenResources *screen_resources = NULL;
+    XRRProviderResources *provider_resources = NULL;
+    XRRProviderInfo *provider_info = NULL;
+    RROutput primary_output;
+    INT primary_provider = 0;
+    BOOL ret = FALSE;
+    INT i, j;
+
+    screen_resources = xrandr_get_screen_resources();
+    if (!screen_resources)
+        goto done;
+
+    provider_resources = pXRRGetProviderResources( gdi_display, root_window );
+    if (!provider_resources)
+        goto done;
+
+    gpus = heap_calloc( provider_resources->nproviders, sizeof(*gpus) );
+    if (!gpus)
+        goto done;
+
+    primary_output = pXRRGetOutputPrimary( gdi_display, root_window );
+
+    for (i = 0; i < provider_resources->nproviders; i++)
+    {
+        provider_info = pXRRGetProviderInfo( gdi_display, screen_resources, provider_resources->providers[i] );
+        if (!provider_info)
+            goto done;
+
+        for (j = 0; !primary_provider && j < provider_info->noutputs; j++)
+        {
+            if (primary_output == provider_info->outputs[j])
+            {
+                primary_provider = i;
+                break;
+            }
+        }
+
+        gpus[i].id = provider_resources->providers[i];
+        MultiByteToWideChar( CP_UTF8, 0, provider_info->name, -1, gpus[i].name, ARRAY_SIZE(gpus[i].name) );
+        /* PCI ID is all zero because there is currently no portable way to get it via XRandR. Some AMD drivers report
+         * their PCI address in the name but many others don't */
+        pXRRFreeProviderInfo( provider_info );
+    }
+
+    /* Make primary GPU the first */
+    if (primary_provider)
+    {
+        struct x11drv_gpu tmp;
+        tmp = gpus[0];
+        gpus[0] = gpus[primary_provider];
+        gpus[primary_provider] = tmp;
+    }
+
+    *new_gpus = gpus;
+    *count = provider_resources->nproviders;
+    ret = TRUE;
+done:
+    if (provider_resources)
+        pXRRFreeProviderResources( provider_resources );
+    if (screen_resources)
+        pXRRFreeScreenResources( screen_resources );
+    if (!ret)
+    {
+        heap_free( gpus );
+        ERR("Failed to get gpus\n");
+    }
+    return ret;
+}
+
+static void xrandr14_free_gpus( struct x11drv_gpu *gpus )
+{
+    heap_free( gpus );
+}
+
+static BOOL xrandr14_get_adapters( ULONG_PTR gpu_id, struct x11drv_adapter **new_adapters, int *count )
+{
+    struct x11drv_adapter *adapters = NULL;
+    XRRScreenResources *screen_resources = NULL;
+    XRRProviderInfo *provider_info = NULL;
+    XRRCrtcInfo *enum_crtc_info, *crtc_info = NULL;
+    XRROutputInfo *output_info = NULL;
+    RROutput primary_output;
+    INT primary_adapter = 0;
+    INT adapter_count = 0;
+    BOOL mirrored, detached;
+    BOOL ret = FALSE;
+    INT i, j;
+
+    screen_resources = xrandr_get_screen_resources();
+    if (!screen_resources)
+        goto done;
+
+    provider_info = pXRRGetProviderInfo( gdi_display, screen_resources, gpu_id );
+    if (!provider_info)
+        goto done;
+
+    /* Actual adapter count could be less */
+    adapters = heap_calloc( provider_info->ncrtcs, sizeof(*adapters) );
+    if (!adapters)
+        goto done;
+
+    primary_output = pXRRGetOutputPrimary( gdi_display, root_window );
+    for (i = 0; i < provider_info->noutputs; i++)
+    {
+        output_info = pXRRGetOutputInfo( gdi_display, screen_resources, provider_info->outputs[i] );
+        if (!output_info)
+            goto done;
+
+        /* Only connected output are considered as monitors */
+        if (output_info->connection != RR_Connected)
+        {
+            pXRRFreeOutputInfo( output_info );
+            output_info = NULL;
+            continue;
+        }
+
+        /* Connected output doesn't mean the output is attached to a crtc */
+        detached = FALSE;
+        if (output_info->crtc)
+        {
+            crtc_info = pXRRGetCrtcInfo( gdi_display, screen_resources, output_info->crtc );
+            if (!crtc_info)
+                goto done;
+        }
+
+        if (!output_info->crtc || (!crtc_info->width && !crtc_info->height))
+            detached = TRUE;
+
+        /* Ignore crtc mirroring slaves because mirrored monitors are under the same adapter */
+        mirrored = FALSE;
+        if (!detached)
+        {
+            for (j = 0; j < screen_resources->ncrtc; j++)
+            {
+                enum_crtc_info = pXRRGetCrtcInfo( gdi_display, screen_resources, screen_resources->crtcs[j] );
+                if (!enum_crtc_info)
+                    goto done;
+
+                /* Some crtc on different provider may have the same coordinates, aka mirrored.
+                 * Choose the crtc with the lowest value as primary and the rest will then be slaves
+                 * in the mirroring set */
+                if (crtc_info->x == enum_crtc_info->x &&
+                    crtc_info->y == enum_crtc_info->y &&
+                    crtc_info->width == enum_crtc_info->width &&
+                    crtc_info->height == enum_crtc_info->height &&
+                    output_info->crtc > screen_resources->crtcs[j])
+                {
+                    mirrored = TRUE;
+                    pXRRFreeCrtcInfo( enum_crtc_info );
+                    break;
+                }
+
+                pXRRFreeCrtcInfo( enum_crtc_info );
+            }
+        }
+
+        if (!mirrored || detached)
+        {
+            /* Use RROutput as adapter id. The reason of not using RRCrtc is that we need to detect inactive but
+              * attached monitors */
+            adapters[adapter_count].id = provider_info->outputs[i];
+            if (!detached)
+                adapters[adapter_count].state_flags |= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
+            if (primary_output == provider_info->outputs[i])
+            {
+                adapters[adapter_count].state_flags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
+                primary_adapter = adapter_count;
+            }
+
+            adapter_count++;
+        }
+
+        pXRRFreeOutputInfo( output_info );
+        output_info = NULL;
+        if (crtc_info)
+        {
+            pXRRFreeCrtcInfo( crtc_info );
+            crtc_info = NULL;
+        }
+    }
+
+    /* Make primary adapter the first */
+    if (primary_adapter)
+    {
+        struct x11drv_adapter tmp;
+        tmp = adapters[0];
+        adapters[0] = adapters[primary_adapter];
+        adapters[primary_adapter] = tmp;
+    }
+
+    *new_adapters = adapters;
+    *count = adapter_count;
+    ret = TRUE;
+done:
+    if (screen_resources)
+        pXRRFreeScreenResources( screen_resources );
+    if (provider_info)
+        pXRRFreeProviderInfo( provider_info );
+    if (output_info)
+        pXRRFreeOutputInfo( output_info);
+    if (crtc_info)
+        pXRRFreeCrtcInfo( crtc_info );
+    if (!ret)
+    {
+        heap_free( adapters );
+        ERR("Failed to get adapters\n");
+    }
+    return ret;
+}
+
+static void xrandr14_free_adapters( struct x11drv_adapter *adapters )
+{
+    heap_free( adapters );
+}
+
+static BOOL xrandr14_get_monitors( ULONG_PTR adapter_id, struct x11drv_monitor **new_monitors, int *count )
+{
+    static const WCHAR generic_nonpnp_monitorW[] = {
+        'G','e','n','e','r','i','c',' ',
+        'N','o','n','-','P','n','P',' ','M','o','n','i','t','o','r',0};
+    struct x11drv_monitor *realloc_monitors, *monitors = NULL;
+    XRRScreenResources *screen_resources = NULL;
+    XRROutputInfo *output_info = NULL, *enum_output_info = NULL, *primary_output_info = NULL;
+    XRRCrtcInfo *crtc_info = NULL, *enum_crtc_info, *primary_crtc_info = NULL;
+    INT primary_index = 0, monitor_count = 0, capacity;
+    RROutput primary_output;
+    RECT rc_work;
+    BOOL ret = FALSE;
+    INT i;
+
+    screen_resources = xrandr_get_screen_resources();
+    if (!screen_resources)
+        goto done;
+
+    /* First start with a 2 monitors, should be enough for most cases */
+    capacity = 2;
+    monitors = heap_calloc( capacity, sizeof(*monitors) );
+    if (!monitors)
+        goto done;
+
+    output_info = pXRRGetOutputInfo( gdi_display, screen_resources, adapter_id );
+    if (!output_info)
+        goto done;
+
+    if (output_info->crtc)
+    {
+        crtc_info = pXRRGetCrtcInfo( gdi_display, screen_resources, output_info->crtc );
+        if (!crtc_info)
+            goto done;
+    }
+
+    /* Inactive but attached monitor, no need to check for mirrored/slave monitors */
+    if (!output_info->crtc || (!crtc_info->width && !crtc_info->height))
+    {
+        lstrcpyW( monitors[monitor_count].name, generic_nonpnp_monitorW );
+        monitors[monitor_count].state_flags = DISPLAY_DEVICE_ATTACHED;
+        monitor_count = 1;
+    }
+    /* Active monitors, need to find other monitors with the same coordinates as mirrored */
+    else
+    {
+        query_work_area( &rc_work );
+        primary_output = pXRRGetOutputPrimary( gdi_display, root_window );
+        primary_output_info = pXRRGetOutputInfo( gdi_display, screen_resources, primary_output );
+        if (!primary_output_info)
+            goto done;
+
+        primary_crtc_info = pXRRGetCrtcInfo( gdi_display, screen_resources, primary_output_info->crtc );
+        if (!primary_crtc_info)
+            goto done;
+
+        for (i = 0; i < screen_resources->noutput; i++)
+        {
+            enum_output_info = pXRRGetOutputInfo( gdi_display, screen_resources, screen_resources->outputs[i] );
+            if (!enum_output_info)
+                goto done;
+
+            /* Detached outputs don't count */
+            if (enum_output_info->connection != RR_Connected)
+            {
+                pXRRFreeOutputInfo( enum_output_info );
+                enum_output_info = NULL;
+                continue;
+            }
+
+            /* Allocate more space if needed */
+            if (monitor_count >= capacity)
+            {
+                capacity *= 2;
+                realloc_monitors = heap_realloc( monitors, capacity * sizeof(*monitors) );
+                if (!realloc_monitors)
+                    goto done;
+                monitors = realloc_monitors;
+            }
+
+            if (enum_output_info->crtc)
+            {
+                enum_crtc_info = pXRRGetCrtcInfo( gdi_display, screen_resources, enum_output_info->crtc );
+                if (!enum_crtc_info)
+                    goto done;
+
+                if (enum_crtc_info->x == crtc_info->x &&
+                    enum_crtc_info->y == crtc_info->y &&
+                    enum_crtc_info->width == crtc_info->width &&
+                    enum_crtc_info->height == crtc_info->height)
+                {
+                    /* FIXME: Read output EDID property and parse the data to get the correct name */
+                    lstrcpyW( monitors[monitor_count].name, generic_nonpnp_monitorW );
+
+                    SetRect( &monitors[monitor_count].rc_monitor, crtc_info->x, crtc_info->y,
+                             crtc_info->x + crtc_info->width, crtc_info->y + crtc_info->height );
+                    if (!IntersectRect( &monitors[monitor_count].rc_work, &rc_work, &monitors[monitor_count].rc_monitor ))
+                        monitors[monitor_count].rc_work = monitors[monitor_count].rc_monitor;
+
+                    monitors[monitor_count].state_flags = DISPLAY_DEVICE_ATTACHED;
+                    if (!IsRectEmpty( &monitors[monitor_count].rc_monitor ))
+                        monitors[monitor_count].state_flags |= DISPLAY_DEVICE_ACTIVE;
+
+                    if (primary_output == screen_resources->outputs[i])
+                        primary_index = monitor_count;
+                    monitor_count++;
+                }
+
+                pXRRFreeCrtcInfo( enum_crtc_info );
+            }
+
+            pXRRFreeOutputInfo( enum_output_info );
+            enum_output_info = NULL;
+        }
+
+        /* Make sure the first monitor is the primary */
+        if (primary_index)
+        {
+            struct x11drv_monitor tmp;
+
+            tmp = monitors[0];
+            monitors[0] = monitors[primary_index];
+            monitors[primary_index] = tmp;
+        }
+
+        /* Make sure the primary monitor origin is at (0,0) */
+        for (i = 0; i < monitor_count; i++)
+        {
+            OffsetRect( &monitors[i].rc_monitor, -primary_crtc_info->x, -primary_crtc_info->y );
+            OffsetRect( &monitors[i].rc_work, -primary_crtc_info->x, -primary_crtc_info->y );
+        }
+    }
+
+    *new_monitors = monitors;
+    *count = monitor_count;
+    ret = TRUE;
+done:
+    if (screen_resources)
+        pXRRFreeScreenResources( screen_resources );
+    if (output_info)
+        pXRRFreeOutputInfo( output_info);
+    if (crtc_info)
+        pXRRFreeCrtcInfo( crtc_info );
+    if (primary_crtc_info)
+        pXRRFreeCrtcInfo( primary_crtc_info );
+    if (primary_output_info)
+        pXRRFreeOutputInfo( primary_output_info );
+    if (enum_output_info)
+        pXRRFreeOutputInfo( enum_output_info );
+    if (!ret)
+    {
+        heap_free( monitors );
+        ERR("Failed to get monitors\n");
+    }
+    return ret;
+}
+
+static void xrandr14_free_monitors( struct x11drv_monitor *monitors )
+{
+    heap_free( monitors );
+}
+
+#endif
+
 void X11DRV_XRandR_Init(void)
 {
+    struct x11drv_display_device_handler handler;
     int event_base, error_base, minor, ret;
     static int major;
     Bool ok;
@@ -591,6 +997,21 @@ void X11DRV_XRandR_Init(void)
     if (!pXRRGetScreenResourcesCurrent || xrandr12_init_modes() < 0)
 #endif
         xrandr10_init_modes();
+
+#ifdef HAVE_XRRGETPROVIDERREDOURCES
+    if (ret >= 4 && (major > 1 || (major == 1 && minor >= 4)))
+    {
+        handler.name = "XRandR 1.4";
+        handler.priority = 200;
+        handler.pGetGpus = xrandr14_get_gpus;
+        handler.pFreeGpus = xrandr14_free_gpus;
+        handler.pGetAdapters = xrandr14_get_adapters;
+        handler.pFreeAdapters = xrandr14_free_adapters;
+        handler.pGetMonitors = xrandr14_get_monitors;
+        handler.pFreeMonitors = xrandr14_free_monitors;
+        X11DRV_DisplayDevices_SetHandler( &handler );
+    }
+#endif
 }
 
 #else /* SONAME_LIBXRANDR */
-- 
2.23.0





More information about the wine-devel mailing list