From e3035b20b1caa15a5380575093dabf0faa9d673d Mon Sep 17 00:00:00 2001
From: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Date: Sat, 2 May 2020 03:52:55 +0200
Subject: [PATCH] xwayland: Add XWAYLAND protocol extension to configure a
 global hidpi scaling factor for all xwayland windows.

This puts the extension scaffolding in place, in order to implement
the actual scaling in a next commit.
---
 hw/xwayland/meson.build       |   2 +
 hw/xwayland/xwayland-ext.c    | 134 ++++++++++++++++++++++++++++++++++
 hw/xwayland/xwayland-ext.h    |  76 +++++++++++++++++++
 hw/xwayland/xwayland-screen.c |   8 ++
 hw/xwayland/xwayland-screen.h |   3 +
 hw/xwayland/xwayland.c        |   4 +
 6 files changed, 227 insertions(+)
 create mode 100644 hw/xwayland/xwayland-ext.c
 create mode 100644 hw/xwayland/xwayland-ext.h

diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build
index 1dea64bff2..7b3d3a4f78 100644
--- a/hw/xwayland/meson.build
+++ b/hw/xwayland/meson.build
@@ -4,6 +4,8 @@ srcs = [
     'xwayland-input.h',
     'xwayland-cursor.c',
     'xwayland-cursor.h',
+    'xwayland-ext.c',
+    'xwayland-ext.h',
     'xwayland-glamor.h',
     'xwayland-glx.h',
     'xwayland-pixmap.c',
diff --git a/hw/xwayland/xwayland-ext.c b/hw/xwayland/xwayland-ext.c
new file mode 100644
index 0000000000..1668dda570
--- /dev/null
+++ b/hw/xwayland/xwayland-ext.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2020 Dario Nieuwenhuis
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include "dixstruct.h"
+
+#include <X11/X.h>
+#include "misc.h"
+#include "os.h"
+#include "extinit.h"
+
+#include "xwayland-ext.h"
+#include "xwayland-screen.h"
+
+#ifdef DEBUG
+#define DEBUG_P(x) DebugF(x"\n")
+#else
+#define DEBUG_P(x) /**/
+#endif
+
+static int XwaylandErrorBase;
+
+static int
+ProcXwaylandQueryVersion(ClientPtr client)
+{
+    xXwaylandQueryVersionReply rep = {
+        .type = X_Reply,
+        .sequenceNumber = client->sequence,
+        .length = 0,
+        .majorVersion = XWAYLAND_MAJOR_VERSION,
+        .minorVersion = XWAYLAND_MINOR_VERSION
+    };
+
+    DEBUG_P("XwaylandQueryVersion");
+
+    REQUEST_SIZE_MATCH(xXwaylandQueryVersionReq);
+
+    if (client->swapped) {
+        swaps(&rep.sequenceNumber);
+        swapl(&rep.length);
+        swaps(&rep.majorVersion);
+        swaps(&rep.minorVersion);
+    }
+    WriteToClient(client, sizeof(xXwaylandQueryVersionReply), &rep);
+    return Success;
+}
+
+static int
+ProcXwaylandSetScale(ClientPtr client)
+{
+    DEBUG_P("XwaylandSetScale");
+
+    REQUEST(xXwaylandSetScaleReq);
+    REQUEST_SIZE_MATCH(xXwaylandSetScaleReq);
+
+    if (stuff->screen >= screenInfo.numScreens)
+        return BadValue;
+    ScreenPtr pScreen = screenInfo.screens[stuff->screen];
+
+    struct xwl_screen* xwl_screen = xwl_screen_get(pScreen);
+    if (xwl_screen == NULL)
+        return BadImplementation;
+
+    if(stuff->scale < 1)
+        return BadValue;
+
+    xwl_screen_set_global_scale(xwl_screen, stuff->scale);
+
+    return Success;
+}
+
+static int
+ProcXwaylandDispatch(ClientPtr client)
+{
+    REQUEST(xReq);
+    switch (stuff->data) {
+        case X_XwaylandQueryVersion:
+            return ProcXwaylandQueryVersion(client);
+        case X_XwaylandSetScale:
+            return ProcXwaylandSetScale(client);
+        default:
+            return BadRequest;
+    }
+}
+
+static int _X_COLD
+SProcXwaylandDispatch(ClientPtr client)
+{
+    REQUEST(xReq);
+    switch (stuff->data) {
+        default:
+            return BadRequest;
+    }
+}
+
+void
+xwlExtensionInit(void)
+{
+    ExtensionEntry* extEntry;
+    if ((extEntry = AddExtension(XWAYLANDNAME,
+                                 XwaylandNumberEvents,
+                                 XwaylandNumberErrors,
+                                 ProcXwaylandDispatch,
+                                 SProcXwaylandDispatch,
+                                 NULL, StandardMinorOpcode))) {
+        XwaylandErrorBase = extEntry->errorBase;
+    }
+}
+
diff --git a/hw/xwayland/xwayland-ext.h b/hw/xwayland/xwayland-ext.h
new file mode 100644
index 0000000000..be8fb39d92
--- /dev/null
+++ b/hw/xwayland/xwayland-ext.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2020 Dario Nieuwenhuis
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#ifndef XWAYLAND_EXT_H
+#define XWAYLAND_EXT_H
+
+#include <xwayland-config.h>
+
+#define XWAYLANDNAME "XWAYLAND"
+#define XwaylandNumberEvents 0
+#define XwaylandNumberErrors 0
+
+#define XWAYLAND_MAJOR_VERSION	1	/* current version numbers */
+#define XWAYLAND_MINOR_VERSION	0
+
+#define X_XwaylandQueryVersion	0
+#define X_XwaylandSetScale  	1
+
+
+typedef struct _XwaylandQueryVersion {
+    CARD8	reqType;		    /* always XwaylandReqCode */
+    CARD8	xwaylandReqType;	/* always X_XwaylandQueryVersion */
+    CARD16	length;
+} xXwaylandQueryVersionReq;
+#define sz_xXwaylandQueryVersionReq	4
+
+typedef struct {
+    BYTE	type;			    /* X_Reply */
+    BOOL	pad1;
+    CARD16	sequenceNumber;
+    CARD32	length;
+    CARD16	majorVersion;		/* major version of Xwayland */
+    CARD16	minorVersion;		/* minor version of Xwayland */
+    CARD32	pad2;
+    CARD32	pad3;
+    CARD32	pad4;
+    CARD32	pad5;
+    CARD32	pad6;
+} xXwaylandQueryVersionReply;
+#define sz_xXwaylandQueryVersionReply	32
+
+
+typedef struct {
+    CARD8	reqType;		    /* always XwaylandReqCode */
+    CARD8	xwaylandReqType;	/* always X_XwaylandSetScale */
+    CARD16	length;
+    CARD16	screen;
+    CARD16	scale;
+} xXwaylandSetScaleReq;
+#define sz_xXwaylandSetScaleReq	8
+
+void xwlExtensionInit(void);
+
+#endif /* XWAYLAND_EXT_H */
diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index b39a3d2fd6..3ce375d19e 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -538,6 +538,13 @@ xwl_screen_roundtrip(struct xwl_screen *xwl_screen)
         xwl_give_up("could not connect to wayland server\n");
 }
 
+void
+xwl_screen_set_global_scale( struct xwl_screen *xwl_screen, int32_t scale)
+{
+    struct xwl_output *it;
+    xwl_screen->global_output_scale = scale;
+}
+
 Bool
 xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
 {
@@ -573,6 +580,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
 #ifdef XWL_HAS_GLAMOR
     xwl_screen->glamor = 1;
 #endif
+    xwl_screen->global_output_scale = 1;
 
     for (i = 1; i < argc; i++) {
         if (strcmp(argv[i], "-rootless") == 0) {
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index b965dddd7f..a4fd042fa3 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -72,6 +72,8 @@ struct xwl_screen {
     struct xorg_list damage_window_list;
     struct xorg_list window_list;
 
+    int32_t global_output_scale;
+
     int wayland_fd;
     struct wl_display *display;
     struct wl_registry *registry;
@@ -134,5 +136,6 @@ void xwl_screen_roundtrip (struct xwl_screen *xwl_screen);
 void xwl_surface_damage(struct xwl_screen *xwl_screen,
                         struct wl_surface *surface,
                         int32_t x, int32_t y, int32_t width, int32_t height);
+void xwl_screen_set_global_scale( struct xwl_screen *xwl_screen, int32_t scale);
 
 #endif /* XWAYLAND_SCREEN_H */
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index 21587dbb64..7a02515044 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -41,12 +41,15 @@
 
 #include "xwayland-screen.h"
 #include "xwayland-vidmode.h"
+#include "xwayland-ext.h"
 
 #ifdef XF86VIDMODE
 #include <X11/extensions/xf86vmproto.h>
 extern _X_EXPORT Bool noXFree86VidModeExtension;
 #endif
 
+_X_EXPORT Bool noXwaylandExtension;
+
 void
 ddxGiveUp(enum ExitCode error)
 {
@@ -246,6 +249,7 @@ xwl_log_handler(const char *format, va_list args)
 static const ExtensionModule xwayland_extensions[] = {
 #ifdef XF86VIDMODE
     { xwlVidModeExtensionInit, XF86VIDMODENAME, &noXFree86VidModeExtension },
+    { xwlExtensionInit, XWAYLANDNAME, &noXwaylandExtension },
 #endif
 };
 
