[PATCH v5 1/7] winex11.drv: Add Xinerama display device handler.

Zhiyi Zhang zzhang at codeweavers.com
Mon May 20 09:14:22 CDT 2019


Display device handlers are used to initialize the display device
registry data. Different handlers can be implemented according to
the defined interface, for example, via Xinerama or XRandR.
With those registry data, EnumDisplayDevices, EnumDisplayMonitors
and GetMonitorInfo can be built on top of it.

Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
v2: Supersede 162308~162314, fix xinerama primary adapter reporting, fix registry data reinit failure.
v3: Rebase
v4: Merge patches. Rebase. Supersede 164594~164599, 164612~164613
v5: Fix explorer patch triggered user32 test failures. Supersede 165122~165128.

 dlls/winex11.drv/Makefile.in   |   1 +
 dlls/winex11.drv/display.c     |  74 ++++++++++++++++
 dlls/winex11.drv/x11drv.h      |  77 +++++++++++++++++
 dlls/winex11.drv/x11drv_main.c |   1 +
 dlls/winex11.drv/xinerama.c    | 154 +++++++++++++++++++++++++++++++++
 5 files changed, 307 insertions(+)
 create mode 100644 dlls/winex11.drv/display.c

diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in
index 747f509b44..9ca2fc6efe 100644
--- a/dlls/winex11.drv/Makefile.in
+++ b/dlls/winex11.drv/Makefile.in
@@ -9,6 +9,7 @@ C_SRCS = \
 	brush.c \
 	clipboard.c \
 	desktop.c \
+	display.c \
 	event.c \
 	graphics.c \
 	ime.c \
diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c
new file mode 100644
index 0000000000..7d81de09ac
--- /dev/null
+++ b/dlls/winex11.drv/display.c
@@ -0,0 +1,74 @@
+/*
+ * X11DRV display device functions
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "x11drv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
+
+static const WCHAR video_keyW[] = {
+    'H','A','R','D','W','A','R','E','\\',
+    'D','E','V','I','C','E','M','A','P','\\',
+    'V','I','D','E','O',0};
+
+static struct x11drv_display_device_handler handler;
+
+void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler *new_handler)
+{
+    if (new_handler->priority > handler.priority)
+    {
+        handler = *new_handler;
+        TRACE("Display device functions are now handled by: %s\n", handler.name);
+    }
+}
+
+void X11DRV_DisplayDevices_Init(void)
+{
+    HKEY video_hkey = NULL;
+    DWORD disposition = 0;
+    BOOL success = FALSE;
+
+    TRACE("via %s\n", wine_dbgstr_a(handler.name));
+
+    if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, video_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &video_hkey,
+                        &disposition))
+        goto fail;
+
+    /* Ensure only one thread is initializing the registry and avoid unnecessary reinit */
+    if (disposition != REG_CREATED_NEW_KEY)
+    {
+        success = TRUE;
+        goto fail;
+    }
+
+    success = TRUE;
+fail:
+    RegCloseKey(video_hkey);
+    if (!success)
+        ERR("Failed to initialize display devices\n");
+}
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index c633102a38..40392b03c0 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -665,6 +665,83 @@ struct x11drv_mode_info *X11DRV_Settings_SetHandlers(const char *name,
 void X11DRV_XF86VM_Init(void) DECLSPEC_HIDDEN;
 void X11DRV_XRandR_Init(void) DECLSPEC_HIDDEN;
 
+/* X11 display device handler. Used to initialize display device registry data */
+
+/* Represent a physical GPU in the PCI slots */
+struct x11drv_gpu
+{
+    /* ID to uniquely identify a GPU in handler */
+    ULONG_PTR id;
+    /* Name */
+    WCHAR name[128];
+    /* PCI ID */
+    UINT vendor_id;
+    UINT device_id;
+    UINT subsys_id;
+    UINT revision_id;
+};
+
+/* Represent an adapter in EnumDisplayDevices context */
+struct x11drv_adapter
+{
+    /* ID to uniquely identify an adapter in handler */
+    ULONG_PTR id;
+    /* as StateFlags in DISPLAY_DEVICE struct */
+    DWORD state_flags;
+};
+
+/* Represent a monitor in EnumDisplayDevices context */
+struct x11drv_monitor
+{
+    /* Name */
+    WCHAR name[128];
+    /* as RcMonitor in MONITORINFO struct */
+    RECT rc_monitor;
+    /* as RcWork in MONITORINFO struct */
+    RECT rc_work;
+    /* StateFlags in DISPLAY_DEVICE struct */
+    DWORD state_flags;
+};
+
+/* Required functions for display device registry initialization */
+struct x11drv_display_device_handler
+{
+    /* A name to tell what host driver is used */
+    CHAR name[MAX_PATH];
+
+    /* Higher priority can override handlers with lower proprity */
+    INT priority;
+
+    /* pGetGpus will be called to get a list of GPUs. First GPU has to be where the primary adapter is.
+     *
+     * Return FALSE on failure with parameters unchanged */
+    BOOL (*pGetGpus)(struct x11drv_gpu **gpus, int *count);
+
+    /* pGetAdapters will be called to get a list of adapters in EnumDisplayDevices context under a GPU.
+     * The first adapter has to be primary if GPU is primary.
+     *
+     * Return FALSE on failure with parameters unchanged */
+    BOOL (*pGetAdapters)(ULONG_PTR gpu_id, struct x11drv_adapter **adapters, int *count);
+
+    /* pGetMonitors will be called to get a list of monitors in EnumDisplayDevices context under an adapter.
+     * The first monitor has to be primary if adapter is primary.
+     *
+     * Return FALSE on failure with parameters unchanged */
+    BOOL (*pGetMonitors)(ULONG_PTR adapter_id, struct x11drv_monitor **monitors, int *count);
+
+    /* pFreeGpus will be called to free a GPU list from pGetGpus */
+    void (*pFreeGpus)(struct x11drv_gpu *gpus);
+
+    /* pFreeAdapters will be called to free an adapter list from pGetAdapters */
+    void (*pFreeAdapters)(struct x11drv_adapter *adapters);
+
+    /* pFreeMonitors will be called to free a monitor list from pGetMonitors */
+    void (*pFreeMonitors)(struct x11drv_monitor *monitors);
+};
+
+extern void X11DRV_DisplayDevices_SetHandler(const struct x11drv_display_device_handler *handler) DECLSPEC_HIDDEN;
+extern void X11DRV_DisplayDevices_Init(void) DECLSPEC_HIDDEN;
+
 /* XIM support */
 extern BOOL X11DRV_InitXIM( const char *input_style ) DECLSPEC_HIDDEN;
 extern XIC X11DRV_CreateIC(XIM xim, struct x11drv_win_data *data) DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c
index e67a3c05a9..69478741f1 100644
--- a/dlls/winex11.drv/x11drv_main.c
+++ b/dlls/winex11.drv/x11drv_main.c
@@ -597,6 +597,7 @@ static BOOL process_attach(void)
     X11DRV_InitKeyboard( gdi_display );
     if (use_xim) use_xim = X11DRV_InitXIM( input_style );
 
+    X11DRV_DisplayDevices_Init();
     return TRUE;
 }
 
diff --git a/dlls/winex11.drv/xinerama.c b/dlls/winex11.drv/xinerama.c
index 739b139736..49427890cb 100644
--- a/dlls/winex11.drv/xinerama.c
+++ b/dlls/winex11.drv/xinerama.c
@@ -30,6 +30,8 @@
 #include "wine/library.h"
 #include "x11drv.h"
 #include "wine/debug.h"
+#include "wine/heap.h"
+#include "wine/unicode.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
 
@@ -199,8 +201,150 @@ RECT get_primary_monitor_rect(void)
     return get_primary()->rcMonitor;
 }
 
+static BOOL xinerama_get_gpus( struct x11drv_gpu **new_gpus, int *count )
+{
+    static const WCHAR wine_gpuW[] = {'W','i','n','e',' ','G','P','U',0};
+    struct x11drv_gpu *gpus;
+
+    /* Xinerama has no support for GPU, faking one */
+    gpus = heap_calloc( 1, sizeof(*gpus) );
+    if (!gpus)
+        return FALSE;
+
+    strcpyW( gpus[0].name, wine_gpuW );
+
+    *new_gpus = gpus;
+    *count = 1;
+
+    return TRUE;
+}
+
+static void xinerama_free_gpus( struct x11drv_gpu *gpus )
+{
+    heap_free( gpus );
+}
+
+static BOOL xinerama_get_adapters( ULONG_PTR gpu_id, struct x11drv_adapter **new_adapters, int *count )
+{
+    struct x11drv_adapter *adapters = NULL;
+    INT index = 0;
+    INT i, j;
+    INT primary_index;
+    BOOL mirrored;
+
+    if (gpu_id)
+        return FALSE;
+
+    /* Being lazy, actual adapter count maybe less */
+    adapters = heap_calloc( nb_monitors, sizeof(*adapters) );
+    if (!adapters)
+        return FALSE;
+
+    primary_index = primary_monitor;
+    if (primary_index >= nb_monitors)
+        primary_index = 0;
+
+    for (i = 0; i < nb_monitors; i++)
+    {
+        mirrored = FALSE;
+        for (j = 0; j < i; j++)
+        {
+            if (EqualRect( &monitors[i].rcMonitor, &monitors[j].rcMonitor) && !IsRectEmpty( &monitors[j].rcMonitor ))
+            {
+                mirrored = TRUE;
+                break;
+            }
+        }
+
+        /* Mirrored monitors share the same adapter */
+        if (mirrored)
+            continue;
+
+        /* Use monitor index as id */
+        adapters[index].id = (ULONG_PTR)i;
+
+        if (i == primary_index)
+            adapters[index].state_flags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
+
+        if (!IsRectEmpty( &monitors[i].rcMonitor ))
+            adapters[index].state_flags |= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
+
+        index++;
+    }
+
+    /* Primary adapter has to be first */
+    if (primary_index)
+    {
+        struct x11drv_adapter tmp;
+        tmp = adapters[primary_index];
+        adapters[primary_index] = adapters[0];
+        adapters[0] = tmp;
+    }
+
+    *new_adapters = adapters;
+    *count = index;
+    return TRUE;
+}
+
+static void xinerama_free_adapters( struct x11drv_adapter *adapters )
+{
+    heap_free( adapters );
+}
+
+static BOOL xinerama_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 *monitor;
+    INT first = (INT)adapter_id;
+    INT monitor_count = 0;
+    INT index = 0;
+    INT i;
+
+    for (i = first; i < nb_monitors; i++)
+    {
+        if (i == first
+            || (EqualRect( &monitors[i].rcMonitor, &monitors[first].rcMonitor )
+                && !IsRectEmpty( &monitors[first].rcMonitor )))
+            monitor_count++;
+    }
+
+    monitor = heap_calloc( monitor_count, sizeof(*monitor) );
+    if (!monitor)
+        return FALSE;
+
+    for (i = first; i < nb_monitors; i++)
+    {
+        if (i == first
+            || (EqualRect( &monitors[i].rcMonitor, &monitors[first].rcMonitor )
+                && !IsRectEmpty( &monitors[first].rcMonitor )))
+        {
+            strcpyW( monitor[index].name, generic_nonpnp_monitorW );
+            monitor[index].rc_monitor = monitors[i].rcMonitor;
+            monitor[index].rc_work = monitors[i].rcWork;
+            /* Xinerama only reports monitors already attached */
+            monitor[index].state_flags = DISPLAY_DEVICE_ATTACHED;
+            if (!IsRectEmpty( &monitors[i].rcMonitor ))
+                monitor[index].state_flags |= DISPLAY_DEVICE_ACTIVE;
+
+            index++;
+        }
+    }
+
+    *new_monitors = monitor;
+    *count = monitor_count;
+    return TRUE;
+}
+
+static void xinerama_free_monitors( struct x11drv_monitor *monitors )
+{
+    heap_free( monitors );
+}
+
 void xinerama_init( unsigned int width, unsigned int height )
 {
+    struct x11drv_display_device_handler handler;
     MONITORINFOEXW *primary;
     int i;
     RECT rect;
@@ -234,6 +378,16 @@ void xinerama_init( unsigned int width, unsigned int height )
                (monitors[i].dwFlags & MONITORINFOF_PRIMARY) ? " (primary)" : "" );
     }
 
+    strcpy( handler.name, "Xinerama" );
+    handler.priority = 100;
+    handler.pGetGpus = xinerama_get_gpus;
+    handler.pGetAdapters = xinerama_get_adapters;
+    handler.pGetMonitors = xinerama_get_monitors;
+    handler.pFreeGpus = xinerama_free_gpus;
+    handler.pFreeAdapters = xinerama_free_adapters;
+    handler.pFreeMonitors = xinerama_free_monitors;
+    X11DRV_DisplayDevices_SetHandler( &handler );
+
     TRACE( "virtual size: %s primary: %s\n",
            wine_dbgstr_rect(&virtual_screen_rect), wine_dbgstr_rect(&primary->rcMonitor) );
 }
-- 
2.20.1





More information about the wine-devel mailing list