xserver: Branch 'master' - 19 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Fri Jul 1 07:40:24 UTC 2022


 .gitlab-ci.yml                                       |    8 
 .gitlab-ci/debian-install.sh                         |   11 
 hw/xwayland/desktop/org.freedesktop.Xwayland.desktop |    7 
 hw/xwayland/man/Xwayland.man                         |   31 +
 hw/xwayland/meson.build                              |   12 
 hw/xwayland/xwayland-drm-lease.c                     |   12 
 hw/xwayland/xwayland-input.c                         |  134 ++++++
 hw/xwayland/xwayland-output.c                        |  292 ++++++++++++--
 hw/xwayland/xwayland-output.h                        |   12 
 hw/xwayland/xwayland-screen.c                        |  154 +++++++
 hw/xwayland/xwayland-screen.h                        |   18 
 hw/xwayland/xwayland-vidmode.c                       |   39 +
 hw/xwayland/xwayland-window.c                        |  375 ++++++++++++++++---
 hw/xwayland/xwayland-window.h                        |   10 
 hw/xwayland/xwayland.c                               |   19 
 hw/xwayland/xwayland.pc.in                           |    4 
 include/meson.build                                  |    1 
 include/xwayland-config.h.meson.in                   |    3 
 meson.build                                          |   15 
 meson_options.txt                                    |    2 
 20 files changed, 1035 insertions(+), 124 deletions(-)

New commits:
commit 727507eb31d7206884b7c8c7f8bdd2adfd81b005
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Fri May 13 11:21:51 2022 +0200

    ci: add libdecor
    
    Add libdecor 0.1.0 to CI.
    
    v2: Build with and without libdecor
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c7851fbf7..15be58bec 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,7 +11,7 @@ variables:
     FDO_UPSTREAM_REPO: xorg/xserver
     FDO_DISTRIBUTION_VERSION: buster-slim
     FDO_DISTRIBUTION_EXEC: 'env FDO_CI_CONCURRENT=${FDO_CI_CONCURRENT} bash .gitlab-ci/debian-install.sh'
-    FDO_DISTRIBUTION_TAG: "2021-10-22"
+    FDO_DISTRIBUTION_TAG: "2022-05-04.1"
 
 include:
   - project: 'freedesktop/ci-templates'
@@ -73,6 +73,12 @@ meson-noglamor:
         MESON_EXTRA_OPTIONS: >
           -Dglamor=false
 
+meson-nolibdecor:
+    extends: meson
+    variables:
+        MESON_EXTRA_OPTIONS: >
+          -Dlibdecor=false
+
 mingw-cross-build:
     extends: .common-build-and-test
     script:
diff --git a/.gitlab-ci/debian-install.sh b/.gitlab-ci/debian-install.sh
index 89ca49e14..e931c6790 100644
--- a/.gitlab-ci/debian-install.sh
+++ b/.gitlab-ci/debian-install.sh
@@ -32,6 +32,7 @@ apt-get install -y \
 	libaudit-dev \
 	libbsd-dev \
 	libcairo2 \
+	libcairo2-dev \
 	libdbus-1-dev \
 	libdmx-dev \
 	libdrm-dev \
@@ -47,6 +48,8 @@ apt-get install -y \
 	libglx-mesa0 \
 	libinput10 \
 	libnvidia-egl-wayland-dev \
+	libpango1.0-0 \
+	libpango1.0-dev \
 	libpciaccess-dev \
 	libpixman-1-dev \
 	libselinux1-dev \
@@ -151,6 +154,14 @@ ninja -C _build -j${FDO_CI_CONCURRENT:-4} install
 cd ..
 rm -rf weston
 
+# Install libdecor for Xwayland
+git clone https://gitlab.gnome.org/jadahl/libdecor.git --depth 1 --branch=0.1.0
+cd libdecor
+meson _build -D{demo,install_demo}=false
+ninja -C _build -j${FDO_CI_CONCURRENT:-4} install
+cd ..
+rm -rf libdecor
+
 git clone https://gitlab.freedesktop.org/mesa/piglit.git --depth 1
 
 git clone https://gitlab.freedesktop.org/xorg/test/xts --depth 1
commit c74c6add3e7080d5fdfdaa20b3e639938f742424
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Wed May 4 14:42:41 2022 +0200

    xwayland: add optional support for libdecor
    
    When running rootful, the Xwayland window is not decorated (as all
    Wayland surfaces), which makes it quite inconvenient to move on screen.
    
    libdecor is "a client-side decorations library for Wayland clients"
    which can be used precisely for adding decorations to Wayland surfaces.
    
    Add optional support for libdecor in Xwayland to gain decorations when
    running rootful and a new command line option "-decorate".
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>
    Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1332

diff --git a/hw/xwayland/man/Xwayland.man b/hw/xwayland/man/Xwayland.man
index 4e7877730..02b333be9 100644
--- a/hw/xwayland/man/Xwayland.man
+++ b/hw/xwayland/man/Xwayland.man
@@ -46,6 +46,14 @@ Like all of the X servers, \fIXwayland\fP accepts the command line options
 described in the \fIXserver\fP(@miscmansuffix@) manual page.
 The following additional arguments are supported as well.
 .TP 8
+.B \-decorate
+Add decorations to the Xwayland root window when running rootful.
+
+This option has no effect when \fIXwayland\fP is built without libdecor
+support (optional).
+
+This option is not compatible with rootless mode (\fI-rootless\fP).
+.TP 8
 .B \-eglstream
 Use EGLStream backend for NVidia GPUs. If \fIXwayland\fP was compiled with
 EGLStream support, this option will instruct \fIXwayland\fP to try that
diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build
index 40aec1587..6c04c4cf6 100644
--- a/hw/xwayland/meson.build
+++ b/hw/xwayland/meson.build
@@ -128,6 +128,10 @@ if libdrm_dep.found()
     xwayland_dep += libdrm_dep
 endif
 
+if have_libdecor
+    xwayland_dep += libdecor_dep
+endif
+
 xwayland_server = executable(
     'Xwayland',
     srcs,
@@ -158,6 +162,7 @@ xwayland_data.set('PACKAGE_VERSION', meson.project_version())
 xwayland_data.set('xwayland_path', xwayland_path)
 xwayland_data.set('have_glamor', build_glamor ? 'true' : 'false')
 xwayland_data.set('have_eglstream', build_eglstream ? 'true' : 'false')
+xwayland_data.set('have_libdecor', have_libdecor ? 'true' : 'false')
 configure_file(
     input: 'xwayland.pc.in',
     output: 'xwayland.pc',
diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index a69fc4dc7..9b1ad0ec5 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -513,6 +513,33 @@ xwl_display_pollout (struct xwl_screen *xwl_screen, int timeout)
     return xserver_poll(&poll_fd, 1, timeout);
 }
 
+#ifdef XWL_HAS_LIBDECOR
+static void
+xwl_dispatch_events_with_libdecor(struct xwl_screen *xwl_screen)
+{
+    int ret = 0;
+
+    assert(!xwl_screen->rootless);
+
+    ret = libdecor_dispatch(xwl_screen->libdecor_context, 0);
+    if (ret == -1)
+        xwl_give_up("failed to dispatch Wayland events with libdecor: %s\n",
+                    strerror(errno));
+}
+
+static void
+handle_libdecor_error(struct libdecor *context,
+                      enum libdecor_error error,
+                      const char *message)
+{
+    xwl_give_up("libdecor error (%d): %s\n", error, message);
+}
+
+static struct libdecor_interface libdecor_iface = {
+    .error = handle_libdecor_error,
+};
+#endif
+
 static void
 xwl_dispatch_events (struct xwl_screen *xwl_screen)
 {
@@ -551,6 +578,12 @@ socket_handler(int fd, int ready, void *data)
 {
     struct xwl_screen *xwl_screen = data;
 
+#ifdef XWL_HAS_LIBDECOR
+    if (xwl_screen->libdecor_context) {
+        xwl_dispatch_events_with_libdecor(xwl_screen);
+        return;
+    }
+#endif
     xwl_read_events (xwl_screen);
 }
 
@@ -565,12 +598,24 @@ block_handler(void *data, void *timeout)
     struct xwl_screen *xwl_screen = data;
 
     xwl_screen_post_damage(xwl_screen);
+#ifdef XWL_HAS_LIBDECOR
+    if (xwl_screen->libdecor_context) {
+        xwl_dispatch_events_with_libdecor(xwl_screen);
+        return;
+    }
+#endif
     xwl_dispatch_events (xwl_screen);
 }
 
 void
 xwl_sync_events (struct xwl_screen *xwl_screen)
 {
+#ifdef XWL_HAS_LIBDECOR
+    if (xwl_screen->libdecor_context) {
+        xwl_dispatch_events_with_libdecor(xwl_screen);
+        return;
+    }
+#endif
     xwl_dispatch_events (xwl_screen);
     xwl_read_events (xwl_screen);
 }
@@ -689,6 +734,13 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
             xwl_screen->host_grab = 1;
             xwl_screen->has_grab = 1;
         }
+        else if (strcmp(argv[i], "-decorate") == 0) {
+#ifdef XWL_HAS_LIBDECOR
+            xwl_screen->decorate = 1;
+#else
+            ErrorF("This build does not have libdecor support\n");
+#endif
+        }
     }
 
     if (use_fixed_size) {
@@ -745,11 +797,17 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
                              &registry_listener, xwl_screen);
     xwl_screen_roundtrip(xwl_screen);
 
+
     if (xwl_screen->fullscreen && xwl_screen->rootless) {
         ErrorF("error, cannot set fullscreen when running rootless\n");
         return FALSE;
     }
 
+    if (xwl_screen->fullscreen && xwl_screen->decorate) {
+        ErrorF("error, cannot use the decorate option when running fullscreen\n");
+        return FALSE;
+    }
+
     if (xwl_screen->fullscreen && !xwl_screen_has_viewport_support(xwl_screen)) {
         ErrorF("missing viewport support in the compositor, ignoring fullscreen\n");
         xwl_screen->fullscreen = FALSE;
@@ -796,7 +854,16 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
         return FALSE;
 #endif
 
-    xwl_screen->wayland_fd = wl_display_get_fd(xwl_screen->display);
+#ifdef XWL_HAS_LIBDECOR
+    if (xwl_screen->decorate && !xwl_screen->rootless) {
+        xwl_screen->libdecor_context = libdecor_new(xwl_screen->display, &libdecor_iface);
+        xwl_screen->wayland_fd = libdecor_get_fd(xwl_screen->libdecor_context);
+    }
+    else
+#endif
+    {
+        xwl_screen->wayland_fd = wl_display_get_fd(xwl_screen->display);
+    }
     SetNotifyFd(xwl_screen->wayland_fd, socket_handler, X_NOTIFY_READ, xwl_screen);
     RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, xwl_screen);
 
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index 6ddb6f751..fd201cdf5 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -38,6 +38,10 @@
 #include "xwayland-glamor.h"
 #include "xwayland-drm-lease.h"
 
+#ifdef XWL_HAS_LIBDECOR
+#include <libdecor.h>
+#endif
+
 struct xwl_format {
     uint32_t format;
     int num_modifiers;
@@ -61,6 +65,7 @@ struct xwl_screen {
     int fullscreen;
     int host_grab;
     int has_grab;
+    int decorate;
 
     CreateScreenResourcesProcPtr CreateScreenResources;
     CloseScreenProcPtr CloseScreen;
@@ -123,6 +128,10 @@ struct xwl_screen {
 
     /* The preferred GLVND vendor. If NULL, "mesa" is assumed. */
     const char *glvnd_vendor;
+#ifdef XWL_HAS_LIBDECOR
+    int libdecor_fd;
+    struct libdecor *libdecor_context;
+#endif
 };
 
 /* Apps which use randr/vidmode to change the mode when going fullscreen,
diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 8b7e447af..0f6cacd3f 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -498,6 +498,11 @@ xwl_window_rootful_update_title(struct xwl_window *xwl_window)
 
     snprintf(title, sizeof(title), "Xwayland on :%s%s", display, grab_message);
 
+#ifdef XWL_HAS_LIBDECOR
+    if (xwl_window->libdecor_frame)
+        libdecor_frame_set_title(xwl_window->libdecor_frame, title);
+    else
+#endif
     if (xwl_window->xdg_toplevel)
         xdg_toplevel_set_title(xwl_window->xdg_toplevel, title);
 }
@@ -507,10 +512,78 @@ xwl_window_rootful_set_app_id(struct xwl_window *xwl_window)
 {
     const char *app_id = "org.freedesktop.Xwayland";
 
+#ifdef XWL_HAS_LIBDECOR
+    if (xwl_window->libdecor_frame)
+        libdecor_frame_set_app_id(xwl_window->libdecor_frame, app_id);
+    else
+#endif
     if (xwl_window->xdg_toplevel)
         xdg_toplevel_set_app_id(xwl_window->xdg_toplevel, app_id);
 }
 
+#ifdef XWL_HAS_LIBDECOR
+static void
+xwl_window_update_libdecor_size(struct xwl_window *xwl_window, int width, int height)
+{
+    struct libdecor_state *state;
+
+    if (xwl_window->libdecor_frame) {
+	state = libdecor_state_new(width, height);
+	libdecor_frame_commit(xwl_window->libdecor_frame, state, NULL);
+	libdecor_state_free(state);
+    }
+}
+
+static void
+handle_libdecor_configure(struct libdecor_frame *frame,
+                          struct libdecor_configuration *configuration,
+                          void *data)
+{
+    struct xwl_window *xwl_window = data;
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+    struct libdecor_state *state;
+
+    state = libdecor_state_new(xwl_screen->width, xwl_screen->height);
+    libdecor_frame_commit(frame, state, configuration);
+    libdecor_state_free(state);
+
+    if (libdecor_frame_has_capability(frame, LIBDECOR_ACTION_RESIZE))
+        libdecor_frame_unset_capabilities(frame, LIBDECOR_ACTION_RESIZE);
+    if (libdecor_frame_has_capability(frame, LIBDECOR_ACTION_FULLSCREEN))
+        libdecor_frame_unset_capabilities(frame, LIBDECOR_ACTION_FULLSCREEN);
+}
+
+static void
+handle_libdecor_close(struct libdecor_frame *frame,
+                      void *data)
+{
+    DebugF("Terminating on compositor request");
+    GiveUp(0);
+}
+
+static void
+handle_libdecor_commit(struct libdecor_frame *frame,
+                       void *data)
+{
+    struct xwl_window *xwl_window = data;
+    wl_surface_commit(xwl_window->surface);
+}
+
+static void
+handle_libdecor_dismiss_popup(struct libdecor_frame *frame,
+                              const char *seat_name,
+                              void *data)
+{
+}
+
+static struct libdecor_frame_interface libdecor_frame_iface = {
+    handle_libdecor_configure,
+    handle_libdecor_close,
+    handle_libdecor_commit,
+    handle_libdecor_dismiss_popup,
+};
+#endif
+
 static void
 xdg_surface_handle_configure(void *data,
                              struct xdg_surface *xdg_surface,
@@ -591,29 +664,43 @@ xwl_create_root_surface(struct xwl_window *xwl_window)
     WindowPtr window = xwl_window->window;
     struct wl_region *region;
 
-    xwl_window->xdg_surface =
-        xdg_wm_base_get_xdg_surface(xwl_screen->xdg_wm_base, xwl_window->surface);
-    if (xwl_window->xdg_surface == NULL) {
-        ErrorF("Failed creating xdg_wm_base xdg_surface\n");
-        goto err_surf;
-    }
 
-    xwl_window->xdg_toplevel =
-        xdg_surface_get_toplevel(xwl_window->xdg_surface);
-    if (xwl_window->xdg_surface == NULL) {
-        ErrorF("Failed creating xdg_toplevel\n");
-        goto err_surf;
+#ifdef XWL_HAS_LIBDECOR
+    if (xwl_screen->decorate) {
+        xwl_window->libdecor_frame =
+            libdecor_decorate(xwl_screen->libdecor_context,
+                              xwl_window->surface,
+                              &libdecor_frame_iface,
+                              xwl_window);
+        libdecor_frame_map(xwl_window->libdecor_frame);
     }
+    else
+#endif
+    {
+        xwl_window->xdg_surface =
+            xdg_wm_base_get_xdg_surface(xwl_screen->xdg_wm_base, xwl_window->surface);
+        if (xwl_window->xdg_surface == NULL) {
+            ErrorF("Failed creating xdg_wm_base xdg_surface\n");
+            goto err_surf;
+        }
+
+        xwl_window->xdg_toplevel =
+            xdg_surface_get_toplevel(xwl_window->xdg_surface);
+        if (xwl_window->xdg_surface == NULL) {
+            ErrorF("Failed creating xdg_toplevel\n");
+            goto err_surf;
+        }
 
-    wl_surface_add_listener(xwl_window->surface,
-                            &surface_listener, xwl_window);
+        wl_surface_add_listener(xwl_window->surface,
+                                &surface_listener, xwl_window);
 
-    xdg_surface_add_listener(xwl_window->xdg_surface,
-                             &xdg_surface_listener, xwl_window);
+        xdg_surface_add_listener(xwl_window->xdg_surface,
+                                 &xdg_surface_listener, xwl_window);
 
-    xdg_toplevel_add_listener(xwl_window->xdg_toplevel,
-                              &xdg_toplevel_listener,
-                              NULL);
+        xdg_toplevel_add_listener(xwl_window->xdg_toplevel,
+                                  &xdg_toplevel_listener,
+                                  NULL);
+    }
 
     xwl_window_rootful_update_title(xwl_window);
     xwl_window_rootful_set_app_id(xwl_window);
@@ -908,8 +995,14 @@ xwl_resize_window(WindowPtr window,
     xwl_screen->ResizeWindow = screen->ResizeWindow;
     screen->ResizeWindow = xwl_resize_window;
 
-    if (xwl_window && (xwl_window_get(window) || xwl_window_is_toplevel(window)))
-        xwl_window_check_resolution_change_emulation(xwl_window);
+    if (xwl_window) {
+        if (xwl_window_get(window) || xwl_window_is_toplevel(window))
+            xwl_window_check_resolution_change_emulation(xwl_window);
+#ifdef XWL_HAS_LIBDECOR
+        if (window == screen->root)
+            xwl_window_update_libdecor_size(xwl_window, width, height);
+#endif
+    }
 }
 
 void
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index 7fb542593..3dbfd2dc2 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -58,6 +58,9 @@ struct xwl_window {
     struct xorg_list frame_callback_list;
     Bool present_flipped;
 #endif
+#ifdef XWL_HAS_LIBDECOR
+    struct libdecor_frame *libdecor_frame;
+#endif
 };
 
 struct xwl_window *xwl_window_get(WindowPtr window);
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index e4983ed1e..51d3147f1 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -106,6 +106,9 @@ ddxUseMsg(void)
     ErrorF("-version               show the server version and exit\n");
     ErrorF("-noTouchPointerEmulation  disable touch pointer emulation\n");
     ErrorF("-force-xrandr-emulation   force non-native modes to be exposed when viewporter is not exposed by the compositor\n");
+#ifdef XWL_HAS_LIBDECOR
+    ErrorF("-decorate              add decorations to Xwayland when rootful (experimental)\n");
+#endif
 }
 
 static int init_fd = -1;
@@ -237,6 +240,9 @@ ddxProcessArgument(int argc, char *argv[], int i)
     else if (strcmp(argv[i], "-host-grab") == 0) {
         return 1;
     }
+    else if (strcmp(argv[i], "-decorate") == 0) {
+        return 1;
+    }
 
     return 0;
 }
diff --git a/hw/xwayland/xwayland.pc.in b/hw/xwayland/xwayland.pc.in
index 70db37db7..c62a95a02 100644
--- a/hw/xwayland/xwayland.pc.in
+++ b/hw/xwayland/xwayland.pc.in
@@ -16,3 +16,4 @@ have_force_xrandr_emulation=true
 have_geometry=true
 have_fullscreen=true
 have_host_grab=true
+have_decorate=@have_libdecor@
diff --git a/include/meson.build b/include/meson.build
index 7f1e4fd8f..57466dc79 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -415,6 +415,7 @@ configure_file(output : 'xwin-config.h',
 xwayland_data = configuration_data()
 xwayland_data.set('XWL_HAS_GLAMOR', build_glamor and (gbm_dep.found() or build_eglstream) ? '1' : false)
 xwayland_data.set('XWL_HAS_EGLSTREAM', build_eglstream ? '1' : false)
+xwayland_data.set('XWL_HAS_LIBDECOR', have_libdecor ? '1' : false)
 
 configure_file(output : 'xwayland-config.h',
                input : 'xwayland-config.h.meson.in',
diff --git a/include/xwayland-config.h.meson.in b/include/xwayland-config.h.meson.in
index 0943ff57d..bb121e355 100644
--- a/include/xwayland-config.h.meson.in
+++ b/include/xwayland-config.h.meson.in
@@ -9,3 +9,6 @@
 
 /* Build eglstream support for Xwayland */
 #mesondefine XWL_HAS_EGLSTREAM
+
+/* Build Xwayland with libdecor support*/
+#mesondefine XWL_HAS_LIBDECOR
diff --git a/meson.build b/meson.build
index 0793f0e54..7bcec7d4e 100644
--- a/meson.build
+++ b/meson.build
@@ -336,6 +336,21 @@ if build_glamor
     epoxy_dep = dependency('epoxy', required: false)
 endif
 
+if build_xwayland
+    libdecor_dep = dependency('libdecor-0', required: false)
+    libdecor_option = get_option('libdecor')
+    if libdecor_option == 'auto'
+        have_libdecor = libdecor_dep.found()
+    else
+        have_libdecor = libdecor_option == 'true'
+        if have_libdecor and not libdecor_dep.found()
+            error('libdecor support requested but not found')
+        endif
+    endif
+else
+    have_libdecor = false
+endif
+
 eglstream_option = get_option('xwayland_eglstream')
 if build_xwayland and build_glamor
     eglstream_dep = dependency('wayland-eglstream-protocols', required:false)
diff --git a/meson_options.txt b/meson_options.txt
index 3a494478e..16377dcc3 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -135,6 +135,8 @@ option('libunwind', type: 'boolean', value: false,
         description: 'Use libunwind for backtrace reporting')
 
 option('xwayland-path', type: 'string', description: 'Directory containing Xwayland executable')
+option('libdecor', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto',
+        description: 'Whether Xwayland should use libdecor when running rootful.')
 
 option('docs', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto',
         description: 'Build documentation')
commit 8a5f3ddb2e4232cbf47bdce25d4b4a929e80e2c9
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Wed May 4 14:25:29 2022 +0200

    xwayland: set tag on our surfaces
    
    That allows to differentiate Xwayland's own surfaces from others.
    
    This is preparation work for optional libdecor support.
    
    v2: Check for surface not being NULL (Jonas Ådahl <jadahl at gmail.com>)
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index a5181356d..325bcac0b 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -525,6 +525,9 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer,
     if (surface == NULL)
         return;
 
+    if (!is_surface_from_xwl_window(surface))
+        return;
+
     xwl_seat->xwl_screen->serial = serial;
     xwl_seat->pointer_enter_serial = serial;
 
@@ -867,6 +870,9 @@ pointer_gesture_swipe_handle_begin(void *data,
 {
     struct xwl_seat *xwl_seat = data;
 
+    if (surface != NULL && !is_surface_from_xwl_window(surface))
+        return;
+
     xwl_seat->pointer_gesture_swipe_fingers = fingers;
     QueueGestureSwipeEvents(xwl_seat->pointer_gestures,
                             XI_GestureSwipeBegin, fingers, 0, 0.0, 0.0, 0.0, 0.0);
@@ -926,6 +932,9 @@ pointer_gesture_pinch_handle_begin(void *data,
 {
     struct xwl_seat *xwl_seat = data;
 
+    if (surface != NULL && !is_surface_from_xwl_window(surface))
+        return;
+
     xwl_seat->pointer_gesture_pinch_fingers = fingers;
     xwl_seat->pointer_gesture_pinch_last_scale = 1.0;
     QueueGesturePinchEvents(xwl_seat->pointer_gestures,
@@ -1095,6 +1104,9 @@ keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
     struct xwl_seat *xwl_seat = data;
     uint32_t *k;
 
+    if (surface != NULL && !is_surface_from_xwl_window(surface))
+        return;
+
     xwl_seat->xwl_screen->serial = serial;
     xwl_seat->keyboard_focus = surface;
 
@@ -1112,6 +1124,9 @@ keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
     struct xwl_seat *xwl_seat = data;
     uint32_t *k;
 
+    if (surface != NULL && !is_surface_from_xwl_window(surface))
+        return;
+
     xwl_seat->xwl_screen->serial = serial;
 
     wl_array_for_each(k, &xwl_seat->keys)
@@ -1314,6 +1329,9 @@ touch_handle_down(void *data, struct wl_touch *wl_touch,
     if (surface == NULL)
         return;
 
+    if (!is_surface_from_xwl_window(surface))
+        return;
+
     xwl_touch = calloc(1, sizeof *xwl_touch);
     if (xwl_touch == NULL) {
         ErrorF("%s: ENOMEM\n", __func__);
@@ -1997,6 +2015,9 @@ tablet_tool_proximity_in(void *data, struct zwp_tablet_tool_v2 *tool,
     if (wl_surface == NULL)
         return;
 
+    if (!is_surface_from_xwl_window(wl_surface))
+        return;
+
     xwl_tablet_tool->proximity_in_serial = serial;
     xwl_seat->tablet_focus_window = wl_surface_get_user_data(wl_surface);
 
diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index f3ca987fd..8b7e447af 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -48,6 +48,7 @@
 
 static DevPrivateKeyRec xwl_window_private_key;
 static DevPrivateKeyRec xwl_damage_private_key;
+static const char *xwl_surface_tag = "xwl-surface";
 
 static void
 xwl_window_set_allow_commits(struct xwl_window *xwl_window, Bool allow,
@@ -112,6 +113,18 @@ xwl_window_from_window(WindowPtr window)
     return NULL;
 }
 
+static void
+xwl_window_set_xwayland_tag(struct xwl_window *xwl_window)
+{
+    wl_proxy_set_tag((struct wl_proxy *)xwl_window->surface, &xwl_surface_tag);
+}
+
+Bool
+is_surface_from_xwl_window(struct wl_surface *surface)
+{
+    return wl_proxy_get_tag((struct wl_proxy *) surface) == &xwl_surface_tag;
+}
+
 void
 xwl_window_update_property(struct xwl_window *xwl_window,
                            PropertyStateRec *propstate)
@@ -672,6 +685,7 @@ ensure_surface_for_window(WindowPtr window)
     send_surface_id_event(xwl_window);
 
     wl_surface_set_user_data(xwl_window->surface, xwl_window);
+    xwl_window_set_xwayland_tag(xwl_window);
 
     compRedirectWindow(serverClient, window, CompositeRedirectManual);
 
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index 52edb1d58..7fb542593 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -63,6 +63,8 @@ struct xwl_window {
 struct xwl_window *xwl_window_get(WindowPtr window);
 struct xwl_window *xwl_window_from_window(WindowPtr window);
 
+Bool is_surface_from_xwl_window(struct wl_surface *surface);
+
 void xwl_window_update_property(struct xwl_window *xwl_window,
                                 PropertyStateRec *propstate);
 Bool xwl_window_has_viewport_enabled(struct xwl_window *xwl_window);
commit 41f3419feeef292abd822ac578e0a8e1c57890e5
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Fri Apr 29 10:22:15 2022 +0200

    xwayland: set the app_id and install a desktop launcher
    
    The app_id is used to identify applications (and group windows), some
    desktops (such as GNOME Shell) use it in their top bar.
    
    Set the XDG toplevel "app_id" to "org.freedesktop.Xwayland" and install
    a desktop file for Xwayland rootful.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/desktop/org.freedesktop.Xwayland.desktop b/hw/xwayland/desktop/org.freedesktop.Xwayland.desktop
new file mode 100644
index 000000000..d0122adf5
--- /dev/null
+++ b/hw/xwayland/desktop/org.freedesktop.Xwayland.desktop
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Name=Xwayland
+Comment=A rootful instance of the Xwayland X11 server
+Terminal=false
+Type=Application
+Categories=System;
+NoDisplay=true
diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build
index cd5bf7a53..40aec1587 100644
--- a/hw/xwayland/meson.build
+++ b/hw/xwayland/meson.build
@@ -173,3 +173,7 @@ xwayland_manpage = configure_file(
     configuration: manpage_config,
 )
 install_man(xwayland_manpage)
+
+datadir = join_paths(get_option('prefix'), get_option('datadir'))
+desktopdir = join_paths(datadir, 'applications')
+install_data('desktop/org.freedesktop.Xwayland.desktop', install_dir : desktopdir)
diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index a8dce8d45..f3ca987fd 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -489,6 +489,15 @@ xwl_window_rootful_update_title(struct xwl_window *xwl_window)
         xdg_toplevel_set_title(xwl_window->xdg_toplevel, title);
 }
 
+static void
+xwl_window_rootful_set_app_id(struct xwl_window *xwl_window)
+{
+    const char *app_id = "org.freedesktop.Xwayland";
+
+    if (xwl_window->xdg_toplevel)
+        xdg_toplevel_set_app_id(xwl_window->xdg_toplevel, app_id);
+}
+
 static void
 xdg_surface_handle_configure(void *data,
                              struct xdg_surface *xdg_surface,
@@ -594,6 +603,7 @@ xwl_create_root_surface(struct xwl_window *xwl_window)
                               NULL);
 
     xwl_window_rootful_update_title(xwl_window);
+    xwl_window_rootful_set_app_id(xwl_window);
 
     wl_surface_commit(xwl_window->surface);
 
commit f31f059934ceb88c231f1f467349ed462dacc9e3
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Fri Apr 29 10:45:01 2022 +0200

    xwayland: add xdg-toplevel listener
    
    So that when running rootful, the compositor can close the Xwayland
    window using the xdg-toplevel protocol.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 6a56ac424..a8dce8d45 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -540,6 +540,28 @@ static const struct wl_surface_listener surface_listener = {
     xwl_window_surface_leave
 };
 
+static void
+xdg_toplevel_handle_configure(void *data,
+                              struct xdg_toplevel *xdg_toplevel,
+                              int32_t width,
+                              int32_t height,
+                              struct wl_array *states)
+{
+}
+
+static void
+xdg_toplevel_handle_close(void *data,
+                          struct xdg_toplevel *xdg_toplevel)
+{
+    DebugF("Terminating on compositor request");
+    GiveUp(0);
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+    xdg_toplevel_handle_configure,
+    xdg_toplevel_handle_close,
+};
+
 static Bool
 xwl_create_root_surface(struct xwl_window *xwl_window)
 {
@@ -567,6 +589,10 @@ xwl_create_root_surface(struct xwl_window *xwl_window)
     xdg_surface_add_listener(xwl_window->xdg_surface,
                              &xdg_surface_listener, xwl_window);
 
+    xdg_toplevel_add_listener(xwl_window->xdg_toplevel,
+                              &xdg_toplevel_listener,
+                              NULL);
+
     xwl_window_rootful_update_title(xwl_window);
 
     wl_surface_commit(xwl_window->surface);
commit 7a517f3967f64fa9b3214b4099f4aa3454baf1a6
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu Apr 28 15:51:55 2022 +0200

    xwayland: set the surface title when running rootful
    
    Set a meaningful title for the xdg_surface, it's nicer when running
    rootful.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index 0cf6fb65f..a5181356d 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -987,6 +987,7 @@ static void
 maybe_toggle_fake_grab(struct xwl_seat *xwl_seat, uint32_t key)
 {
     struct xwl_screen *xwl_screen = xwl_seat->xwl_screen;
+    struct xwl_window *xwl_window;
     XkbStateRec state_rec;
     uint32_t xkb_state;
 
@@ -1008,6 +1009,10 @@ maybe_toggle_fake_grab(struct xwl_seat *xwl_seat, uint32_t key)
             maybe_fake_grab_devices(xwl_seat);
         else
             maybe_fake_ungrab_devices(xwl_seat);
+
+        xwl_window = xwl_window_get(xwl_screen->screen->root);
+        if (xwl_window)
+            xwl_window_rootful_update_title(xwl_window);
     }
 }
 
diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index dc520a0c6..6a56ac424 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -469,6 +469,26 @@ xwl_window_set_fullscreen(struct xwl_window *xwl_window)
     return TRUE;
 }
 
+void
+xwl_window_rootful_update_title(struct xwl_window *xwl_window)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+    char title[128];
+    const char *grab_message = "";
+
+    if (xwl_screen->host_grab) {
+        if (xwl_screen->has_grab)
+            grab_message = " - ([ctrl]+[shift] releases mouse and keyboard)";
+        else
+            grab_message = " - ([ctrl]+[shift] grabs mouse and keyboard)";
+    }
+
+    snprintf(title, sizeof(title), "Xwayland on :%s%s", display, grab_message);
+
+    if (xwl_window->xdg_toplevel)
+        xdg_toplevel_set_title(xwl_window->xdg_toplevel, title);
+}
+
 static void
 xdg_surface_handle_configure(void *data,
                              struct xdg_surface *xdg_surface,
@@ -547,6 +567,8 @@ xwl_create_root_surface(struct xwl_window *xwl_window)
     xdg_surface_add_listener(xwl_window->xdg_surface,
                              &xdg_surface_listener, xwl_window);
 
+    xwl_window_rootful_update_title(xwl_window);
+
     wl_surface_commit(xwl_window->surface);
 
     region = wl_compositor_create_region(xwl_screen->compositor);
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index 24147e3fc..52edb1d58 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -68,8 +68,10 @@ void xwl_window_update_property(struct xwl_window *xwl_window,
 Bool xwl_window_has_viewport_enabled(struct xwl_window *xwl_window);
 Bool xwl_window_is_toplevel(WindowPtr window);
 void xwl_window_check_resolution_change_emulation(struct xwl_window *xwl_window);
+void xwl_window_rootful_update_title(struct xwl_window *xwl_window);
 
 void xwl_window_set_window_pixmap(WindowPtr window, PixmapPtr pixmap);
+
 Bool xwl_realize_window(WindowPtr window);
 Bool xwl_unrealize_window(WindowPtr window);
 Bool xwl_change_window_attributes(WindowPtr window, unsigned long mask);
commit d0466e842a1dbde028abdab647fa64bbe8f7cd78
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu Apr 28 15:49:51 2022 +0200

    xwayland: move the root window surface to its own function
    
    Currently, when running rootful, the toplevel root surface is created in
    the same function as the rest of the Wayland surfaces.
    
    Move it to its own function to improve readability - No function change.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 25c8f6cf7..dc520a0c6 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -520,13 +520,64 @@ static const struct wl_surface_listener surface_listener = {
     xwl_window_surface_leave
 };
 
+static Bool
+xwl_create_root_surface(struct xwl_window *xwl_window)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+    WindowPtr window = xwl_window->window;
+    struct wl_region *region;
+
+    xwl_window->xdg_surface =
+        xdg_wm_base_get_xdg_surface(xwl_screen->xdg_wm_base, xwl_window->surface);
+    if (xwl_window->xdg_surface == NULL) {
+        ErrorF("Failed creating xdg_wm_base xdg_surface\n");
+        goto err_surf;
+    }
+
+    xwl_window->xdg_toplevel =
+        xdg_surface_get_toplevel(xwl_window->xdg_surface);
+    if (xwl_window->xdg_surface == NULL) {
+        ErrorF("Failed creating xdg_toplevel\n");
+        goto err_surf;
+    }
+
+    wl_surface_add_listener(xwl_window->surface,
+                            &surface_listener, xwl_window);
+
+    xdg_surface_add_listener(xwl_window->xdg_surface,
+                             &xdg_surface_listener, xwl_window);
+
+    wl_surface_commit(xwl_window->surface);
+
+    region = wl_compositor_create_region(xwl_screen->compositor);
+    if (region == NULL) {
+        ErrorF("Failed creating region\n");
+        goto err_surf;
+    }
+
+    wl_region_add(region, 0, 0,
+                  window->drawable.width, window->drawable.height);
+    wl_surface_set_opaque_region(xwl_window->surface, region);
+    wl_region_destroy(region);
+
+    return TRUE;
+
+err_surf:
+    if (xwl_window->xdg_toplevel)
+        xdg_toplevel_destroy(xwl_window->xdg_toplevel);
+    if (xwl_window->xdg_surface)
+        xdg_surface_destroy(xwl_window->xdg_surface);
+    wl_surface_destroy(xwl_window->surface);
+
+    return FALSE;
+}
+
 static Bool
 ensure_surface_for_window(WindowPtr window)
 {
     ScreenPtr screen = window->drawable.pScreen;
     struct xwl_screen *xwl_screen;
     struct xwl_window *xwl_window;
-    struct wl_region *region;
     WindowPtr toplevel;
 
     if (xwl_window_from_window(window))
@@ -555,40 +606,8 @@ ensure_surface_for_window(WindowPtr window)
         goto err;
     }
 
-    if (!xwl_screen->rootless) {
-        xwl_window->xdg_surface =
-            xdg_wm_base_get_xdg_surface(xwl_screen->xdg_wm_base, xwl_window->surface);
-        if (xwl_window->xdg_surface == NULL) {
-            ErrorF("Failed creating xdg_wm_base xdg_surface\n");
-            goto err_surf;
-        }
-
-        xwl_window->xdg_toplevel =
-            xdg_surface_get_toplevel(xwl_window->xdg_surface);
-        if (xwl_window->xdg_surface == NULL) {
-            ErrorF("Failed creating xdg_toplevel\n");
-            goto err_surf;
-        }
-
-        wl_surface_add_listener(xwl_window->surface,
-                                &surface_listener, xwl_window);
-
-        xdg_surface_add_listener(xwl_window->xdg_surface,
-                                 &xdg_surface_listener, xwl_window);
-
-        wl_surface_commit(xwl_window->surface);
-
-        region = wl_compositor_create_region(xwl_screen->compositor);
-        if (region == NULL) {
-            ErrorF("Failed creating region\n");
-            goto err_surf;
-        }
-
-        wl_region_add(region, 0, 0,
-                      window->drawable.width, window->drawable.height);
-        wl_surface_set_opaque_region(xwl_window->surface, region);
-        wl_region_destroy(region);
-    }
+    if (!xwl_screen->rootless && !xwl_create_root_surface(xwl_window))
+        goto err;
 
     wl_display_flush(xwl_screen->display);
 
@@ -624,12 +643,6 @@ ensure_surface_for_window(WindowPtr window)
 
     return TRUE;
 
-err_surf:
-    if (xwl_window->xdg_toplevel)
-        xdg_toplevel_destroy(xwl_window->xdg_toplevel);
-    if (xwl_window->xdg_surface)
-        xdg_surface_destroy(xwl_window->xdg_surface);
-    wl_surface_destroy(xwl_window->surface);
 err:
     free(xwl_window);
     return FALSE;
commit c03e582f0cf3a497ed8475a8cab675b2b6031013
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu Apr 28 12:26:49 2022 +0200

    xwayland: add (fake) device grab support
    
    Add a new command line option "-host-grab" to disable the keyboard
    shortcuts and confine the pointer on the host so that Xwayland can
    receive all keyboard events.
    
    This is useful when running a complete desktop environment within
    Xwayland rootful.
    
    Use [CTRL]+[SHIFT] to release the keyboard and pointer.
    
    This option is not compatible with rootless mode.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/man/Xwayland.man b/hw/xwayland/man/Xwayland.man
index 97e3c6957..4e7877730 100644
--- a/hw/xwayland/man/Xwayland.man
+++ b/hw/xwayland/man/Xwayland.man
@@ -61,6 +61,19 @@ This option is not compatible with rootless mode (\fI-rootless\fP).
 .B \-geometry \fIWxH\fP
 Sets the geometry of the \fIXwayland\fP window to \fIWxH\fP when running rootful.
 
+This option is not compatible with rootless mode (\fI-rootless\fP).
+.TP 8
+.B \-host-grab
+Disable host keyboard shorcuts and confine the pointer when running rootful.
+
+This feature relies on the protocol for inhibiting the compositor keyboard
+shortcuts and on the protocol for pointer locking and confinement and may
+have no effect if the Wayland compositor in use does not support these
+protocols.
+
+Use the keys [CTRL]+[SHIFT] simultaneously to release the keyboard and
+pointer devices.
+
 This option is not compatible with rootless mode (\fI-rootless\fP).
 .TP 8
 .B \-initfd \fIfd\fP
diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build
index 37c39497c..cd5bf7a53 100644
--- a/hw/xwayland/meson.build
+++ b/hw/xwayland/meson.build
@@ -46,6 +46,7 @@ dmabuf_xml = join_paths(protodir, 'unstable', 'linux-dmabuf', 'linux-dmabuf-unst
 viewporter_xml = join_paths(protodir, 'stable', 'viewporter', 'viewporter.xml')
 xdg_shell_xml = join_paths(protodir, 'stable', 'xdg-shell', 'xdg-shell.xml')
 drm_lease_xml = join_paths(protodir, 'staging', 'drm-lease', 'drm-lease-v1.xml')
+shortcuts_inhibit_xml = join_paths(protodir, 'unstable', 'keyboard-shortcuts-inhibit', 'keyboard-shortcuts-inhibit-unstable-v1.xml')
 
 client_header = generator(scanner,
     output : '@BASENAME at -client-protocol.h',
@@ -72,6 +73,7 @@ srcs += client_header.process(dmabuf_xml)
 srcs += client_header.process(viewporter_xml)
 srcs += client_header.process(xdg_shell_xml)
 srcs += client_header.process(drm_lease_xml)
+srcs += client_header.process(shortcuts_inhibit_xml)
 srcs += code.process(relative_xml)
 srcs += code.process(pointer_xml)
 srcs += code.process(gestures_xml)
@@ -82,6 +84,7 @@ srcs += code.process(dmabuf_xml)
 srcs += code.process(viewporter_xml)
 srcs += code.process(xdg_shell_xml)
 srcs += code.process(drm_lease_xml)
+srcs += code.process(shortcuts_inhibit_xml)
 
 xwayland_glamor = []
 eglstream_srcs = []
diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index 177c57303..0cf6fb65f 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -49,6 +49,7 @@
 #include "tablet-unstable-v2-client-protocol.h"
 #include "pointer-gestures-unstable-v1-client-protocol.h"
 #include "xwayland-keyboard-grab-unstable-v1-client-protocol.h"
+#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
 
 struct axis_discrete_pending {
     struct xorg_list l;
@@ -128,6 +129,57 @@ init_pointer_buttons(DeviceIntPtr device)
     return TRUE;
 }
 
+static void
+maybe_fake_grab_devices(struct xwl_seat *xwl_seat)
+{
+    struct xwl_screen *xwl_screen = xwl_seat->xwl_screen;
+    struct xwl_window *xwl_window;
+
+    if (xwl_screen->rootless)
+        return;
+
+    if (!xwl_screen->host_grab)
+        return;
+
+    if (!xwl_screen->has_grab)
+        return;
+
+    if (!xwl_screen->screen->root)
+        return;
+
+    xwl_window = xwl_window_get(xwl_screen->screen->root);
+    if (!xwl_window)
+        return;
+
+    xwl_seat_confine_pointer(xwl_seat, xwl_window);
+
+    if (!xwl_screen->shortcuts_inhibit_manager)
+        return;
+
+    if (xwl_screen->shortcuts_inhibit)
+        return;
+
+    xwl_screen->shortcuts_inhibit =
+        zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts (
+            xwl_screen->shortcuts_inhibit_manager,
+            xwl_window->surface,
+            xwl_seat->seat);
+}
+
+static void
+maybe_fake_ungrab_devices(struct xwl_seat *xwl_seat)
+{
+    struct xwl_screen *xwl_screen = xwl_seat->xwl_screen;
+
+    xwl_seat_unconfine_pointer(xwl_seat);
+
+    if (!xwl_screen->shortcuts_inhibit)
+        return;
+
+    zwp_keyboard_shortcuts_inhibitor_v1_destroy (xwl_screen->shortcuts_inhibit);
+    xwl_screen->shortcuts_inhibit = NULL;
+}
+
 static int
 xwl_pointer_proc(DeviceIntPtr device, int what)
 {
@@ -520,6 +572,8 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer,
     else {
         xwl_seat_maybe_lock_on_hidden_cursor(xwl_seat);
     }
+
+    maybe_fake_grab_devices(xwl_seat);
 }
 
 static void
@@ -539,6 +593,8 @@ pointer_handle_leave(void *data, struct wl_pointer *pointer,
         xwl_seat->focus_window = NULL;
         CheckMotion(NULL, GetMaster(dev, POINTER_OR_FLOAT));
     }
+
+    maybe_fake_ungrab_devices(xwl_seat);
 }
 
 static void
@@ -927,6 +983,34 @@ static const struct zwp_pointer_gesture_pinch_v1_listener pointer_gesture_pinch_
     pointer_gesture_pinch_handle_end
 };
 
+static void
+maybe_toggle_fake_grab(struct xwl_seat *xwl_seat, uint32_t key)
+{
+    struct xwl_screen *xwl_screen = xwl_seat->xwl_screen;
+    XkbStateRec state_rec;
+    uint32_t xkb_state;
+
+    if (xwl_screen->rootless)
+        return;
+
+    if (!xwl_screen->host_grab)
+        return;
+
+    state_rec = xwl_seat->keyboard->key->xkbInfo->state;
+    xkb_state = (XkbStateFieldFromRec(&state_rec) & 0xff);
+
+    if (((key == KEY_LEFTSHIFT || key == KEY_RIGHTSHIFT) && (xkb_state & ControlMask)) ||
+        ((key == KEY_LEFTCTRL || key == KEY_RIGHTCTRL) && (xkb_state & ShiftMask))) {
+
+        xwl_screen->has_grab = !xwl_screen->has_grab;
+
+        if (xwl_screen->has_grab)
+            maybe_fake_grab_devices(xwl_seat);
+        else
+            maybe_fake_ungrab_devices(xwl_seat);
+    }
+}
+
 static void
 keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial,
                     uint32_t time, uint32_t key, uint32_t state)
@@ -949,6 +1033,9 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial,
 
     QueueKeyboardEvents(xwl_seat->keyboard,
                         state ? KeyPress : KeyRelease, key + 8);
+
+    if (!state)
+        maybe_toggle_fake_grab(xwl_seat, key);
 }
 
 static void
@@ -1009,6 +1096,8 @@ keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
     wl_array_copy(&xwl_seat->keys, keys);
     wl_array_for_each(k, &xwl_seat->keys)
         QueueKeyboardEvents(xwl_seat->keyboard, EnterNotify, *k + 8);
+
+    maybe_fake_grab_devices(xwl_seat);
 }
 
 static void
@@ -1024,6 +1113,8 @@ keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
         QueueKeyboardEvents(xwl_seat->keyboard, LeaveNotify, *k + 8);
 
     xwl_seat->keyboard_focus = NULL;
+
+    maybe_fake_ungrab_devices(xwl_seat);
 }
 
 static void
@@ -2824,6 +2915,16 @@ init_keyboard_grab(struct xwl_screen *xwl_screen,
     }
 }
 
+static void
+init_keyboard_shortcuts_inhibit(struct xwl_screen *xwl_screen,
+                                uint32_t id, uint32_t version)
+{
+    xwl_screen->shortcuts_inhibit_manager =
+         wl_registry_bind(xwl_screen->registry, id,
+                          &zwp_keyboard_shortcuts_inhibit_manager_v1_interface,
+                          1);
+}
+
 /* The compositor may send us wl_seat and its capabilities before sending e.g.
    relative_pointer_manager or pointer_gesture interfaces. This would result in
    devices being created in capabilities handler, but listeners not, because
@@ -2873,6 +2974,8 @@ input_handler(void *data, struct wl_registry *registry, uint32_t id,
         init_tablet_manager(xwl_screen, id, version);
     } else if (strcmp(interface, "zwp_xwayland_keyboard_grab_manager_v1") == 0) {
         init_keyboard_grab(xwl_screen, id, version);
+    } else if (strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) {
+        init_keyboard_shortcuts_inhibit(xwl_screen, id, version);
     }
 }
 
diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index 526112514..a69fc4dc7 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -306,6 +306,12 @@ xwl_cursor_confined_to(DeviceIntPtr device,
     struct xwl_seat *xwl_seat = device->public.devicePrivate;
     struct xwl_window *xwl_window;
 
+    /* If running rootful with host grab requested, do not tamper with
+     * pointer confinement.
+     */
+    if (!xwl_screen->rootless && xwl_screen->host_grab && xwl_screen->has_grab)
+        return;
+
     if (!xwl_seat)
         xwl_seat = xwl_screen_get_default_seat(xwl_screen);
 
@@ -679,6 +685,10 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
         else if (strcmp(argv[i], "-fullscreen") == 0) {
             xwl_screen->fullscreen = 1;
         }
+        else if (strcmp(argv[i], "-host-grab") == 0) {
+            xwl_screen->host_grab = 1;
+            xwl_screen->has_grab = 1;
+        }
     }
 
     if (use_fixed_size) {
@@ -745,6 +755,11 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
         xwl_screen->fullscreen = FALSE;
     }
 
+    if (xwl_screen->host_grab && xwl_screen->rootless) {
+        ErrorF("error, cannot use host grab when running rootless\n");
+        return FALSE;
+    }
+
     if (!xwl_screen->rootless && !xwl_screen->xdg_wm_base) {
         ErrorF("missing XDG-WM-Base protocol\n");
         return FALSE;
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index 360e2aa85..6ddb6f751 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -59,6 +59,8 @@ struct xwl_screen {
     int present;
     int force_xrandr_emulation;
     int fullscreen;
+    int host_grab;
+    int has_grab;
 
     CreateScreenResourcesProcPtr CreateScreenResources;
     CloseScreenProcPtr CloseScreen;
@@ -88,6 +90,8 @@ struct xwl_screen {
     struct zwp_pointer_constraints_v1 *pointer_constraints;
     struct zwp_pointer_gestures_v1 *pointer_gestures;
     struct zwp_xwayland_keyboard_grab_manager_v1 *wp_grab;
+    struct zwp_keyboard_shortcuts_inhibit_manager_v1 *shortcuts_inhibit_manager;
+    struct zwp_keyboard_shortcuts_inhibitor_v1 *shortcuts_inhibit;
     struct zwp_linux_dmabuf_v1 *dmabuf;
     struct zxdg_output_manager_v1 *xdg_output_manager;
     struct wp_viewporter *viewporter;
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index 8fb929eeb..e4983ed1e 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -93,6 +93,7 @@ ddxUseMsg(void)
     ErrorF("-rootless              run rootless, requires wm support\n");
     ErrorF("-fullscreen            run fullscreen when rootful\n");
     ErrorF("-geometry WxH          set Xwayland window size when rootful\n");
+    ErrorF("-host-grab             disable host keyboard shortcuts when rootful\n");
     ErrorF("-wm fd                 create X client for wm on given fd\n");
     ErrorF("-initfd fd             add given fd as a listen socket for initialization clients\n");
     ErrorF("-listenfd fd           add given fd as a listen socket\n");
@@ -233,6 +234,9 @@ ddxProcessArgument(int argc, char *argv[], int i)
     else if (strcmp(argv[i], "-fullscreen") == 0) {
         return 1;
     }
+    else if (strcmp(argv[i], "-host-grab") == 0) {
+        return 1;
+    }
 
     return 0;
 }
diff --git a/hw/xwayland/xwayland.pc.in b/hw/xwayland/xwayland.pc.in
index 5976bd6b1..70db37db7 100644
--- a/hw/xwayland/xwayland.pc.in
+++ b/hw/xwayland/xwayland.pc.in
@@ -15,3 +15,4 @@ have_no_touch_pointer_emulation=true
 have_force_xrandr_emulation=true
 have_geometry=true
 have_fullscreen=true
+have_host_grab=true
commit 503e7663f8d1c20b842fdfeb46798f0bd80f6757
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu May 5 16:38:53 2022 +0200

    xwayland: do not auto-lock pointer when rootful
    
    Xwayland tries to be smart and guess the intention of the X11 clients
    sometimes, like issuing a pointer lock when a client hides the pointer
    when confined.
    
    While this is a good thing when running rootless, this is problematic
    when running rootful as the pointer will be automatically locked unless
    the "retro" mode is used (which doesn't hide the cursor, unlike the
    default).
    
    Make sure we don't trigger the automatic pointer lock when the cursor is
    hidden when running rootful.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index eda69a193..177c57303 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -3202,6 +3202,8 @@ xwl_seat_emulate_pointer_warp(struct xwl_seat *xwl_seat,
 static Bool
 xwl_seat_maybe_lock_on_hidden_cursor(struct xwl_seat *xwl_seat)
 {
+    struct xwl_screen *xwl_screen = xwl_seat->xwl_screen;
+
     /* Some clients use hidden cursor+confineTo+relative motion
      * to implement infinite panning (eg. 3D views), lock the
      * pointer for so the relative pointer is used.
@@ -3212,6 +3214,9 @@ xwl_seat_maybe_lock_on_hidden_cursor(struct xwl_seat *xwl_seat)
     if (!xwl_seat->focus_window)
         return FALSE;
 
+    if (!xwl_screen->rootless)
+        return FALSE;
+
     if (xwl_seat->cursor_confinement_window != xwl_seat->focus_window)
         return FALSE;
 
commit d370f1e58ac2365cc7396435f7182259dcf35a9c
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu May 12 12:04:29 2022 +0200

    xwayland: add fullscreen mode for rootful
    
    Add a new command line option "-fullscreen" to make the rootful Xwayland
    window appear fullscreen.
    
    This requires viewport support in the compositor and when used with
    "-geometry" can emulate the full range of XRandR resolutions.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/man/Xwayland.man b/hw/xwayland/man/Xwayland.man
index b9202160c..97e3c6957 100644
--- a/hw/xwayland/man/Xwayland.man
+++ b/hw/xwayland/man/Xwayland.man
@@ -53,6 +53,11 @@ backend first, then fallback to the GBM backend if EGLStream is not supported
 by the Wayland server. Without this option, \fIXwayland\fP tries the GBM
 backend first, and fallback to EGLStream if GBM is not usable.
 .TP 8
+.B \-fullscreen
+Set the Xwayland window fullscreen when running rootful.
+
+This option is not compatible with rootless mode (\fI-rootless\fP).
+.TP 8
 .B \-geometry \fIWxH\fP
 Sets the geometry of the \fIXwayland\fP window to \fIWxH\fP when running rootful.
 
diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index 771f262b0..526112514 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -676,6 +676,9 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
             }
             use_fixed_size = 1;
         }
+        else if (strcmp(argv[i], "-fullscreen") == 0) {
+            xwl_screen->fullscreen = 1;
+        }
     }
 
     if (use_fixed_size) {
@@ -732,6 +735,16 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
                              &registry_listener, xwl_screen);
     xwl_screen_roundtrip(xwl_screen);
 
+    if (xwl_screen->fullscreen && xwl_screen->rootless) {
+        ErrorF("error, cannot set fullscreen when running rootless\n");
+        return FALSE;
+    }
+
+    if (xwl_screen->fullscreen && !xwl_screen_has_viewport_support(xwl_screen)) {
+        ErrorF("missing viewport support in the compositor, ignoring fullscreen\n");
+        xwl_screen->fullscreen = FALSE;
+    }
+
     if (!xwl_screen->rootless && !xwl_screen->xdg_wm_base) {
         ErrorF("missing XDG-WM-Base protocol\n");
         return FALSE;
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index 5f2782d33..360e2aa85 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -58,6 +58,7 @@ struct xwl_screen {
     int glamor;
     int present;
     int force_xrandr_emulation;
+    int fullscreen;
 
     CreateScreenResourcesProcPtr CreateScreenResources;
     CloseScreenProcPtr CloseScreen;
diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 491ce223e..25c8f6cf7 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -267,6 +267,41 @@ window_get_client_toplevel(WindowPtr window)
     return window;
 }
 
+static struct xwl_output *
+xwl_window_get_output(struct xwl_window *xwl_window)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+    struct xwl_output *xwl_output;
+
+    xwl_output = xwl_output_from_wl_output(xwl_screen, xwl_window->wl_output);
+
+    if (xwl_output)
+        return xwl_output;
+
+    return xwl_screen_get_first_output(xwl_screen);
+}
+
+static Bool
+xwl_window_should_enable_viewport_fullscreen(struct xwl_window *xwl_window,
+                                             struct xwl_output **xwl_output_ret,
+                                             struct xwl_emulated_mode *emulated_mode_ret)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+    struct xwl_output *xwl_output;
+
+    xwl_output = xwl_window_get_output(xwl_window);
+    if (!xwl_output)
+        return FALSE;
+
+    *xwl_output_ret = xwl_output;
+    emulated_mode_ret->server_output_id = 0;
+    emulated_mode_ret->width = xwl_screen->width;
+    emulated_mode_ret->height = xwl_screen->height;
+    emulated_mode_ret->from_vidmode = FALSE;
+
+    return TRUE;
+}
+
 static Bool
 xwl_window_should_enable_viewport(struct xwl_window *xwl_window,
                                   struct xwl_output **xwl_output_ret,
@@ -279,7 +314,15 @@ xwl_window_should_enable_viewport(struct xwl_window *xwl_window,
     WindowPtr window;
     DrawablePtr drawable;
 
-    if (!xwl_screen_has_resolution_change_emulation(xwl_screen))
+    if (!xwl_screen_has_viewport_support(xwl_screen))
+        return FALSE;
+
+    if (xwl_screen->fullscreen)
+        return xwl_window_should_enable_viewport_fullscreen(xwl_window,
+                                                            xwl_output_ret,
+                                                            emulated_mode_ret);
+
+    if (!xwl_screen->rootless)
         return FALSE;
 
     window = window_get_client_toplevel(xwl_window->window);
@@ -401,12 +444,44 @@ send_surface_id_event(struct xwl_window *xwl_window)
                           &e, 1, SubstructureRedirectMask, NullGrab);
 }
 
+static Bool
+xwl_window_set_fullscreen(struct xwl_window *xwl_window)
+{
+    struct xwl_output *xwl_output;
+    struct wl_output *wl_output = NULL;
+
+    if (!xwl_window->xdg_toplevel)
+        return FALSE;
+
+    xwl_output = xwl_window_get_output(xwl_window);
+    if (xwl_output)
+        wl_output = xwl_output->output;
+
+    if (wl_output && xwl_window->wl_output_fullscreen == wl_output)
+        return FALSE;
+
+    xdg_toplevel_set_fullscreen(xwl_window->xdg_toplevel, wl_output);
+    xwl_window_check_resolution_change_emulation(xwl_window);
+    wl_surface_commit(xwl_window->surface);
+
+    xwl_window->wl_output_fullscreen = wl_output;
+
+    return TRUE;
+}
+
 static void
 xdg_surface_handle_configure(void *data,
                              struct xdg_surface *xdg_surface,
                              uint32_t serial)
 {
+    struct xwl_window *xwl_window = data;
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+
+    if (xwl_screen->fullscreen)
+        xwl_window_set_fullscreen(xwl_window);
+
     xdg_surface_ack_configure(xdg_surface, serial);
+    wl_surface_commit(xwl_window->surface);
 }
 
 static const struct xdg_surface_listener xdg_surface_listener = {
@@ -419,9 +494,14 @@ xwl_window_surface_enter(void *data,
                          struct wl_output *wl_output)
 {
     struct xwl_window *xwl_window = data;
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
 
-    if (xwl_window->wl_output != wl_output)
+    if (xwl_window->wl_output != wl_output) {
         xwl_window->wl_output = wl_output;
+
+        if (xwl_screen->fullscreen)
+            xwl_window_set_fullscreen(xwl_window);
+    }
 }
 
 static void
@@ -533,7 +613,7 @@ ensure_surface_for_window(WindowPtr window)
     /* When a new window-manager window is realized, then the randr emulation
      * props may have not been set on the managed client window yet.
      */
-    if (window_is_wm_window(window)) {
+    if (!xwl_screen->fullscreen && window_is_wm_window(window)) {
         toplevel = window_get_client_toplevel(window);
         if (toplevel)
             xwl_output_set_window_randr_emu_props(xwl_screen, toplevel);
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index 1975b749c..24147e3fc 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -53,6 +53,7 @@ struct xwl_window {
     struct xorg_list window_buffers_unavailable;
     OsTimerPtr window_buffers_timer;
     struct wl_output *wl_output;
+    struct wl_output *wl_output_fullscreen;
 #ifdef GLAMOR_HAS_GBM
     struct xorg_list frame_callback_list;
     Bool present_flipped;
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index d28d6f8e7..8fb929eeb 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -91,6 +91,7 @@ void
 ddxUseMsg(void)
 {
     ErrorF("-rootless              run rootless, requires wm support\n");
+    ErrorF("-fullscreen            run fullscreen when rootful\n");
     ErrorF("-geometry WxH          set Xwayland window size when rootful\n");
     ErrorF("-wm fd                 create X client for wm on given fd\n");
     ErrorF("-initfd fd             add given fd as a listen socket for initialization clients\n");
@@ -229,6 +230,9 @@ ddxProcessArgument(int argc, char *argv[], int i)
         CHECK_FOR_REQUIRED_ARGUMENTS(1);
         return 2;
     }
+    else if (strcmp(argv[i], "-fullscreen") == 0) {
+        return 1;
+    }
 
     return 0;
 }
diff --git a/hw/xwayland/xwayland.pc.in b/hw/xwayland/xwayland.pc.in
index 0b21e7acc..5976bd6b1 100644
--- a/hw/xwayland/xwayland.pc.in
+++ b/hw/xwayland/xwayland.pc.in
@@ -14,3 +14,4 @@ have_terminate_delay=true
 have_no_touch_pointer_emulation=true
 have_force_xrandr_emulation=true
 have_geometry=true
+have_fullscreen=true
commit 5ef4ad0af2104f4e6f7ba26bb587df5972b15032
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Tue May 31 14:49:26 2022 +0200

    xwayland: update the Xwayland screen size first
    
    When updating the overall screen size, Xwayland would first walk the
    window tree then update both the xwl_screen and screen size.
    
    As a result, if any ResizeWindow() handler tries to use the xwl_screen
    size, it would get the old (wrong) size instead of the new one.
    
    Make sure to update the xwl_screen size first, prior to traverse the
    window tree.
    
    This is preparation work for Xwayland fullscreen mode.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c
index 39434b5ec..818df6d08 100644
--- a/hw/xwayland/xwayland-output.c
+++ b/hw/xwayland/xwayland-output.c
@@ -184,14 +184,15 @@ update_backing_pixmaps(struct xwl_screen *xwl_screen, int width, int height)
 static void
 update_screen_size(struct xwl_screen *xwl_screen, int width, int height)
 {
+    xwl_screen->width = width;
+    xwl_screen->height = height;
+
     if (xwl_screen->root_clip_mode == ROOT_CLIP_FULL)
         SetRootClip(xwl_screen->screen, ROOT_CLIP_NONE);
 
     if (!xwl_screen->rootless && xwl_screen->screen->root)
         update_backing_pixmaps (xwl_screen, width, height);
 
-    xwl_screen->width = width;
-    xwl_screen->height = height;
     xwl_screen->screen->width = width;
     xwl_screen->screen->height = height;
     xwl_screen->screen->mmWidth = (width * 25.4) / monitorResolution;
commit 28e5faab283d9f672bc9b38ff9a233c896b55a41
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu May 12 16:02:09 2022 +0200

    xwayland: pass the emulated mode by reference
    
    When using xrandr emulation, the emulated mode is passed as a pointer to
    the XRandR mode from the xwl_output associated with the X11 client.
    
    In preparation for fullscreen mode, we want to be able to reuse that
    code but use a separate emulated mode.
    
    Simply change the internal API to pass a reference to the emulated mode.
    
    This introduces no functional change.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 2c6b74fce..491ce223e 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -270,7 +270,7 @@ window_get_client_toplevel(WindowPtr window)
 static Bool
 xwl_window_should_enable_viewport(struct xwl_window *xwl_window,
                                   struct xwl_output **xwl_output_ret,
-                                  struct xwl_emulated_mode **emulated_mode_ret)
+                                  struct xwl_emulated_mode *emulated_mode_ret)
 {
     struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
     struct xwl_emulated_mode *emulated_mode;
@@ -302,7 +302,7 @@ xwl_window_should_enable_viewport(struct xwl_window *xwl_window,
             drawable->width  == emulated_mode->width &&
             drawable->height == emulated_mode->height) {
 
-            *emulated_mode_ret = emulated_mode;
+            memcpy(emulated_mode_ret, emulated_mode, sizeof(struct xwl_emulated_mode));
             *xwl_output_ret = xwl_output;
             return TRUE;
         }
@@ -320,7 +320,7 @@ xwl_window_should_enable_viewport(struct xwl_window *xwl_window,
         drawable->width  == xwl_screen->width &&
         drawable->height == xwl_screen->height) {
 
-        *emulated_mode_ret = emulated_mode;
+        memcpy(emulated_mode_ret, emulated_mode, sizeof(struct xwl_emulated_mode));
         *xwl_output_ret = xwl_output;
         return TRUE;
     }
@@ -331,11 +331,11 @@ xwl_window_should_enable_viewport(struct xwl_window *xwl_window,
 void
 xwl_window_check_resolution_change_emulation(struct xwl_window *xwl_window)
 {
-    struct xwl_emulated_mode *emulated_mode;
+    struct xwl_emulated_mode emulated_mode;
     struct xwl_output *xwl_output;
 
     if (xwl_window_should_enable_viewport(xwl_window, &xwl_output, &emulated_mode))
-        xwl_window_enable_viewport(xwl_window, xwl_output, emulated_mode);
+        xwl_window_enable_viewport(xwl_window, xwl_output, &emulated_mode);
     else if (xwl_window_has_viewport_enabled(xwl_window))
         xwl_window_disable_viewport(xwl_window);
 }
commit f3e32cae5164841a3dd2858c956dd78b6017f272
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu May 12 15:24:49 2022 +0200

    xwayland: keep the xdg_toplevel around
    
    The xdg_toplevel object was used solely when creating the window
    surface, and the value of the object discarded.
    
    To be able to make the surface fullscreen using the xdg_toplevel
    protocol, we need to have access that object, so keep it around along
    with the xwl_window.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 919c0a0ca..2c6b74fce 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -483,14 +483,19 @@ ensure_surface_for_window(WindowPtr window)
             goto err_surf;
         }
 
+        xwl_window->xdg_toplevel =
+            xdg_surface_get_toplevel(xwl_window->xdg_surface);
+        if (xwl_window->xdg_surface == NULL) {
+            ErrorF("Failed creating xdg_toplevel\n");
+            goto err_surf;
+        }
+
         wl_surface_add_listener(xwl_window->surface,
                                 &surface_listener, xwl_window);
 
         xdg_surface_add_listener(xwl_window->xdg_surface,
                                  &xdg_surface_listener, xwl_window);
 
-        xdg_surface_get_toplevel(xwl_window->xdg_surface);
-
         wl_surface_commit(xwl_window->surface);
 
         region = wl_compositor_create_region(xwl_screen->compositor);
@@ -540,6 +545,8 @@ ensure_surface_for_window(WindowPtr window)
     return TRUE;
 
 err_surf:
+    if (xwl_window->xdg_toplevel)
+        xdg_toplevel_destroy(xwl_window->xdg_toplevel);
     if (xwl_window->xdg_surface)
         xdg_surface_destroy(xwl_window->xdg_surface);
     wl_surface_destroy(xwl_window->surface);
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index 9739af17c..1975b749c 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -43,6 +43,7 @@ struct xwl_window {
     struct wp_viewport *viewport;
     float scale_x, scale_y;
     struct xdg_surface *xdg_surface;
+    struct xdg_toplevel *xdg_toplevel;
     WindowPtr window;
     struct xorg_list link_damage;
     struct xorg_list link_window;
commit c7a50db7ffaf0eb0c8b06c508ea86e65c86e7ef1
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu May 12 15:30:38 2022 +0200

    xwayland: keep track of the wl_output enter/leave
    
    Keep track of the output the surface enters/leaves.
    
    This is fairly basic tracking though, we do not keep a full list of
    outputs a surface may be covering partially, we just keep the output
    the surface entered last.
    
    This is sufficient as a preparation work for fullscreen though.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 41061e344..919c0a0ca 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -413,6 +413,33 @@ static const struct xdg_surface_listener xdg_surface_listener = {
     xdg_surface_handle_configure,
 };
 
+static void
+xwl_window_surface_enter(void *data,
+                         struct wl_surface *wl_surface,
+                         struct wl_output *wl_output)
+{
+    struct xwl_window *xwl_window = data;
+
+    if (xwl_window->wl_output != wl_output)
+        xwl_window->wl_output = wl_output;
+}
+
+static void
+xwl_window_surface_leave(void *data,
+                         struct wl_surface *wl_surface,
+                         struct wl_output *wl_output)
+{
+    struct xwl_window *xwl_window = data;
+
+    if (xwl_window->wl_output == wl_output)
+        xwl_window->wl_output = NULL;
+}
+
+static const struct wl_surface_listener surface_listener = {
+    xwl_window_surface_enter,
+    xwl_window_surface_leave
+};
+
 static Bool
 ensure_surface_for_window(WindowPtr window)
 {
@@ -456,6 +483,9 @@ ensure_surface_for_window(WindowPtr window)
             goto err_surf;
         }
 
+        wl_surface_add_listener(xwl_window->surface,
+                                &surface_listener, xwl_window);
+
         xdg_surface_add_listener(xwl_window->xdg_surface,
                                  &xdg_surface_listener, xwl_window);
 
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index d94f07204..9739af17c 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -51,6 +51,7 @@ struct xwl_window {
     struct xorg_list window_buffers_available;
     struct xorg_list window_buffers_unavailable;
     OsTimerPtr window_buffers_timer;
+    struct wl_output *wl_output;
 #ifdef GLAMOR_HAS_GBM
     struct xorg_list frame_callback_list;
     Bool present_flipped;
commit f80bf834651f12fb98432376477350327963b476
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu May 12 11:53:18 2022 +0200

    xwayland: add xwl_output_from_wl_output()
    
    Add a convenient function to get the xwl_output from a given wl_output.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c
index 3d9ef3677..39434b5ec 100644
--- a/hw/xwayland/xwayland-output.c
+++ b/hw/xwayland/xwayland-output.c
@@ -752,6 +752,20 @@ xwl_output_set_emulated(struct xwl_output *xwl_output)
                            &val, FALSE, FALSE);
 }
 
+struct xwl_output*
+xwl_output_from_wl_output(struct xwl_screen *xwl_screen,
+                          struct wl_output* wl_output)
+{
+    struct xwl_output *xwl_output;
+
+    xorg_list_for_each_entry(xwl_output, &xwl_screen->output_list, link) {
+        if (xwl_output->output == wl_output)
+            return xwl_output;
+    }
+
+    return NULL;
+}
+
 struct xwl_output *
 xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id, Bool with_xrandr)
 {
diff --git a/hw/xwayland/xwayland-output.h b/hw/xwayland/xwayland-output.h
index 63aee8244..603b54527 100644
--- a/hw/xwayland/xwayland-output.h
+++ b/hw/xwayland/xwayland-output.h
@@ -79,6 +79,9 @@ Bool xwl_screen_init_randr_fixed(struct xwl_screen *xwl_screen);
 void xwl_output_set_mode_fixed(struct xwl_output *xwl_output,
                                RRModePtr mode);
 
+struct xwl_output *xwl_output_from_wl_output(struct xwl_screen *xwl_screen,
+                                             struct wl_output* wl_output);
+
 struct xwl_output *xwl_output_create(struct xwl_screen *xwl_screen,
                                      uint32_t id, Bool with_xrandr);
 
commit b0cee5e7033787e0720af1f128a33e967231b323
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Tue Apr 26 15:28:50 2022 +0200

    xwayland: add a fixed geometry size for rootful
    
    When running rootless as well as rootful, Xwayland gets its outputs
    configuration from the Wayland compositor.
    
    When running rootful, it means that we end up with a large black
    surface the size of all monitors combined, that's not very convenient
    and there is no way for set the desired size of the Xwayland window.
    
    Add a new command line option "-geometry" to force a specific mode when
    running rootful for the user to specify the root window size to use for
    Xwayland.
    
    That option has no effect when Xwayland is running rootless.
    
    v2: Not using libxcvt as the mode may not be a valid CVT mode.
    v3: Add a set of XRandR modes and the RR hooks to make that work.
        Update the man page for Xwayland.
    v4: Add RandR 1.0 support for older clients
    v5: Fix XVidMode failing with a BadMatch
    v6: Add a separate xwl_output specifically for fixed mode, instead of
        using the existing output list - that will allow for further
        improvements like a fullscreen mode eventually.
    v7: Sort the RR modes
    v8: Fix RandR 1.0
    v9: Add physical size
    v10: Cleanup
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>
    Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1338

diff --git a/hw/xwayland/man/Xwayland.man b/hw/xwayland/man/Xwayland.man
index 2ceec9432..b9202160c 100644
--- a/hw/xwayland/man/Xwayland.man
+++ b/hw/xwayland/man/Xwayland.man
@@ -53,6 +53,11 @@ backend first, then fallback to the GBM backend if EGLStream is not supported
 by the Wayland server. Without this option, \fIXwayland\fP tries the GBM
 backend first, and fallback to EGLStream if GBM is not usable.
 .TP 8
+.B \-geometry \fIWxH\fP
+Sets the geometry of the \fIXwayland\fP window to \fIWxH\fP when running rootful.
+
+This option is not compatible with rootless mode (\fI-rootless\fP).
+.TP 8
 .B \-initfd \fIfd\fP
 Add the given \fIfd\fP as a listen socket for initialization of X clients.
 This options is aimed at \fIWayland\fP servers which run \fIXwayland\fP
diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c
index bdce26191..3d9ef3677 100644
--- a/hw/xwayland/xwayland-output.c
+++ b/hw/xwayland/xwayland-output.c
@@ -88,10 +88,12 @@ output_handle_geometry(void *data, struct wl_output *wl_output, int x, int y,
 {
     struct xwl_output *xwl_output = data;
 
-    RROutputSetPhysicalSize(xwl_output->randr_output,
-                            physical_width, physical_height);
-    RROutputSetSubpixelOrder(xwl_output->randr_output,
-                             wl_subpixel_to_xrandr(subpixel));
+    if (xwl_output->randr_output) {
+        RROutputSetPhysicalSize(xwl_output->randr_output,
+                                physical_width, physical_height);
+        RROutputSetSubpixelOrder(xwl_output->randr_output,
+                                 wl_subpixel_to_xrandr(subpixel));
+    }
 
     /* Apply the change from wl_output only if xdg-output is not supported */
     if (!xwl_output->xdg_output) {
@@ -221,6 +223,10 @@ xwl_output_get_emulated_mode_for_client(struct xwl_output *xwl_output,
     if (!xwl_output)
         return NULL;
 
+    /* We don't do XRandr emulation when rootful */
+    if (!xwl_output->xwl_screen->rootless)
+        return NULL;
+
     for (i = 0; i < XWL_CLIENT_MAX_EMULATED_MODES; i++) {
         if (xwl_client->emulated_modes[i].server_output_id ==
             xwl_output->server_output_id)
@@ -611,17 +617,18 @@ apply_output_change(struct xwl_output *xwl_output)
         mode_width = xwl_output->height;
         mode_height = xwl_output->width;
     }
-
-    /* Build a fresh modes array using the current refresh rate */
-    randr_modes = output_get_rr_modes(xwl_output, mode_width, mode_height, &count);
-    RROutputSetModes(xwl_output->randr_output, randr_modes, count, 1);
-    RRCrtcNotify(xwl_output->randr_crtc, randr_modes[0],
-                 xwl_output->x, xwl_output->y,
-                 xwl_output->rotation, NULL, 1, &xwl_output->randr_output);
-    /* RROutputSetModes takes ownership of the passed in modes, so we only
-     * have to free the pointer array.
-     */
-    free(randr_modes);
+    if (xwl_output->randr_output) {
+        /* Build a fresh modes array using the current refresh rate */
+        randr_modes = output_get_rr_modes(xwl_output, mode_width, mode_height, &count);
+        RROutputSetModes(xwl_output->randr_output, randr_modes, count, 1);
+        RRCrtcNotify(xwl_output->randr_crtc, randr_modes[0],
+                     xwl_output->x, xwl_output->y,
+                     xwl_output->rotation, NULL, 1, &xwl_output->randr_output);
+        /* RROutputSetModes takes ownership of the passed in modes, so we only
+         * have to free the pointer array.
+         */
+        free(randr_modes);
+    }
 
     xorg_list_for_each_entry(it, &xwl_screen->output_list, link) {
         /* output done event is sent even when some property
@@ -643,7 +650,8 @@ apply_output_change(struct xwl_output *xwl_output)
 	--xwl_screen->expecting_event;
     }
 
-    update_screen_size(xwl_screen, width, height);
+    if (xwl_screen->fixed_output == NULL)
+        update_screen_size(xwl_screen, width, height);
 }
 
 static void
@@ -745,7 +753,7 @@ xwl_output_set_emulated(struct xwl_output *xwl_output)
 }
 
 struct xwl_output *
-xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id)
+xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id, Bool with_xrandr)
 {
     struct xwl_output *xwl_output;
     char name[256];
@@ -766,30 +774,32 @@ xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id)
     xwl_output->server_output_id = id;
     wl_output_add_listener(xwl_output->output, &output_listener, xwl_output);
 
-    snprintf(name, sizeof name, "XWAYLAND%d",
-             xwl_screen_get_next_output_serial(xwl_screen));
-
     xwl_output->xwl_screen = xwl_screen;
-    xwl_output->randr_crtc = RRCrtcCreate(xwl_screen->screen, xwl_output);
-    if (!xwl_output->randr_crtc) {
-        ErrorF("Failed creating RandR CRTC\n");
-        goto err;
-    }
-    RRCrtcSetRotations (xwl_output->randr_crtc, ALL_ROTATIONS);
 
-    xwl_output->randr_output = RROutputCreate(xwl_screen->screen, name,
-                                              strlen(name), xwl_output);
-    if (!xwl_output->randr_output) {
-        ErrorF("Failed creating RandR Output\n");
-        goto err;
-    }
-    xwl_output_set_emulated(xwl_output);
+    if (with_xrandr) {
+        snprintf(name, sizeof name, "XWAYLAND%d",
+                 xwl_screen_get_next_output_serial(xwl_screen));
 
-    RRCrtcGammaSetSize(xwl_output->randr_crtc, 256);
-    RROutputSetCrtcs(xwl_output->randr_output, &xwl_output->randr_crtc, 1);
-    RROutputSetConnection(xwl_output->randr_output, RR_Connected);
-    RRTellChanged(xwl_screen->screen);
+        xwl_output->randr_crtc = RRCrtcCreate(xwl_screen->screen, xwl_output);
+        if (!xwl_output->randr_crtc) {
+            ErrorF("Failed creating RandR CRTC\n");
+            goto err;
+        }
+        RRCrtcSetRotations (xwl_output->randr_crtc, ALL_ROTATIONS);
+
+        xwl_output->randr_output = RROutputCreate(xwl_screen->screen, name,
+                                                  strlen(name), xwl_output);
+        if (!xwl_output->randr_output) {
+            ErrorF("Failed creating RandR Output\n");
+            goto err;
+        }
+        xwl_output_set_emulated(xwl_output);
 
+        RRCrtcGammaSetSize(xwl_output->randr_crtc, 256);
+        RROutputSetCrtcs(xwl_output->randr_output, &xwl_output->randr_crtc, 1);
+        RROutputSetConnection(xwl_output->randr_output, RR_Connected);
+        RRTellChanged(xwl_screen->screen);
+    }
     /* We want the output to be in the list as soon as created so we can
      * use it when binding to the xdg-output protocol...
      */
@@ -831,16 +841,21 @@ xwl_output_remove(struct xwl_output *xwl_output)
 
     xorg_list_del(&xwl_output->link);
 
-    RROutputSetConnection(xwl_output->randr_output, RR_Disconnected);
-
-    xorg_list_for_each_entry(it, &xwl_screen->output_list, link)
-        output_get_new_size(it, &width, &height);
-    update_screen_size(xwl_screen, width, height);
+    if (xwl_output->randr_output)
+        RROutputSetConnection(xwl_output->randr_output, RR_Disconnected);
 
-    RRCrtcDestroy(xwl_output->randr_crtc);
-    RROutputDestroy(xwl_output->randr_output);
-    RRTellChanged(xwl_screen->screen);
+    if (xwl_screen->fixed_output == NULL) {
+        xorg_list_for_each_entry(it, &xwl_screen->output_list, link)
+            output_get_new_size(it, &width, &height);
+        update_screen_size(xwl_screen, width, height);
+    }
 
+    if (xwl_output->randr_crtc)
+        RRCrtcDestroy(xwl_output->randr_crtc);
+    if (xwl_output->randr_output) {
+        RROutputDestroy(xwl_output->randr_output);
+        RRTellChanged(xwl_screen->screen);
+    }
     xwl_output_destroy(xwl_output);
 }
 
@@ -980,6 +995,166 @@ xwl_screen_init_output(struct xwl_screen *xwl_screen)
     return TRUE;
 }
 
+static int
+mode_sort(const void *left, const void *right)
+{
+    const RRModePtr *mode_a = left;
+    const RRModePtr *mode_b = right;
+
+    if ((*mode_b)->mode.width == (*mode_a)->mode.width)
+        return (*mode_b)->mode.height - (*mode_a)->mode.height;
+
+    return (*mode_b)->mode.width - (*mode_a)->mode.width;
+}
+
+static Bool
+xwl_randr_add_modes_fixed(struct xwl_output *xwl_output,
+                          int current_width, int current_height)
+{
+    RRModePtr *modes = NULL;
+    RRModePtr mode;
+    xRRModeInfo mode_info = { 0, };
+    char mode_name[128];
+    int i, nmodes, current;
+
+
+    modes = xallocarray(ARRAY_SIZE(xwl_output_fake_modes) + 1, sizeof(RRModePtr));
+    if (!modes) {
+        ErrorF("Failed to allocated RandR modes\n");
+        return FALSE;
+    }
+
+    nmodes = 0;
+    current = 0;
+
+    /* Add fake modes */
+    for (i = 0; i < ARRAY_SIZE(xwl_output_fake_modes); i++) {
+        if (xwl_output_fake_modes[i][0] == current_width &&
+            xwl_output_fake_modes[i][1] == current_height)
+            current = 1;
+
+        mode = xwayland_cvt(xwl_output_fake_modes[i][0],
+                            xwl_output_fake_modes[i][1],
+                            60, 0, 0);
+
+        if (mode)
+            modes[nmodes++] = mode;
+    }
+
+    if (!current) {
+        /* Add the current mode as it's not part of the fake modes.
+         * Not using libcvt as the size is set from the command line and
+         * may not be a valid CVT mode.
+         */
+        mode_info.width = current_width;
+        mode_info.height = current_height;
+        mode_info.hTotal = current_width;
+        mode_info.vTotal = current_height;
+        mode_info.dotClock = 60 * 1000 * 1000;
+
+        snprintf(mode_name, sizeof(mode_name), "%dx%d",
+                 current_width, current_height);
+        mode_info.nameLength = strlen(mode_name);
+
+        modes[nmodes++] = RRModeGet(&mode_info, mode_name);
+    }
+
+    qsort(modes, nmodes, sizeof(RRModePtr), mode_sort);
+    RROutputSetModes(xwl_output->randr_output, modes, nmodes, 1);
+    free(modes);
+
+    return TRUE;
+}
+
+void
+xwl_output_set_mode_fixed(struct xwl_output *xwl_output, RRModePtr mode)
+{
+    struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
+
+    update_screen_size(xwl_screen, mode->mode.width, mode->mode.height);
+
+    RRCrtcNotify(xwl_output->randr_crtc, mode, 0, 0, RR_Rotate_0,
+                 NULL, 1, &xwl_output->randr_output);
+}
+
+static Bool
+xwl_randr_set_config_fixed(ScreenPtr pScreen,
+                           Rotation randr, int rate, RRScreenSizePtr pSize)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
+
+    update_screen_size(xwl_screen, pSize->width, pSize->height);
+
+    return TRUE;
+}
+
+/* Create a single RR output/mode used with a fixed geometry */
+Bool
+xwl_screen_init_randr_fixed(struct xwl_screen *xwl_screen)
+{
+    struct xwl_output *xwl_output;
+    rrScrPrivPtr rp;
+    RRModePtr mode;
+
+    xwl_output = calloc(1, sizeof *xwl_output);
+    if (xwl_output == NULL) {
+        ErrorF("%s ENOMEM\n", __func__);
+        return FALSE;
+    }
+
+    if (!RRScreenInit(xwl_screen->screen))
+        goto err;
+
+    RRScreenSetSizeRange(xwl_screen->screen, 16, 16, 32767, 32767);
+
+    rp = rrGetScrPriv(xwl_screen->screen);
+    rp->rrGetInfo = xwl_randr_get_info;
+    rp->rrSetConfig = xwl_randr_set_config_fixed;
+
+    xwl_output->randr_output = RROutputCreate(xwl_screen->screen, "XWAYLAND0", 9, NULL);
+    if (!xwl_output->randr_output) {
+        ErrorF("Failed to create RandR output\n");
+        goto err;
+    }
+
+    xwl_output->randr_crtc = RRCrtcCreate(xwl_screen->screen, xwl_output);
+    if (!xwl_output->randr_crtc) {
+        ErrorF("Failed to create RandR CRTC\n");
+        goto err;
+    }
+    RRCrtcSetRotations (xwl_output->randr_crtc, RR_Rotate_0);
+    RRCrtcGammaSetSize(xwl_output->randr_crtc, 256);
+    RROutputSetCrtcs(xwl_output->randr_output, &xwl_output->randr_crtc, 1);
+
+    xwl_randr_add_modes_fixed(xwl_output,
+                              xwl_screen->width, xwl_screen->height);
+    /* Current mode */
+    mode = xwl_output_find_mode(xwl_output,
+                                xwl_screen->width, xwl_screen->height);
+    RRCrtcNotify(xwl_output->randr_crtc, mode, 0, 0, RR_Rotate_0,
+                 NULL, 1, &xwl_output->randr_output);
+
+    RROutputSetPhysicalSize(xwl_output->randr_output,
+                            (xwl_screen->width * 25.4) / monitorResolution,
+                            (xwl_screen->height * 25.4) / monitorResolution);
+
+    RROutputSetConnection(xwl_output->randr_output, RR_Connected);
+
+    xwl_output->xwl_screen = xwl_screen;
+    xwl_screen->fixed_output = xwl_output;
+
+    return TRUE;
+
+err:
+    if (xwl_output->randr_crtc)
+        RRCrtcDestroy(xwl_output->randr_crtc);
+    if (xwl_output->randr_output)
+        RROutputDestroy(xwl_output->randr_output);
+    free(xwl_output);
+
+    return FALSE;
+}
+
 static void
 xwl_output_get_xdg_output(struct xwl_output *xwl_output)
 {
diff --git a/hw/xwayland/xwayland-output.h b/hw/xwayland/xwayland-output.h
index 10a353ddc..63aee8244 100644
--- a/hw/xwayland/xwayland-output.h
+++ b/hw/xwayland/xwayland-output.h
@@ -74,8 +74,13 @@ struct xwl_emulated_mode {
 
 Bool xwl_screen_init_output(struct xwl_screen *xwl_screen);
 
+Bool xwl_screen_init_randr_fixed(struct xwl_screen *xwl_screen);
+
+void xwl_output_set_mode_fixed(struct xwl_output *xwl_output,
+                               RRModePtr mode);
+
 struct xwl_output *xwl_output_create(struct xwl_screen *xwl_screen,
-                                     uint32_t id);
+                                     uint32_t id, Bool with_xrandr);
 
 void xwl_output_destroy(struct xwl_output *xwl_output);
 
diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index c9b93a2da..771f262b0 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -127,6 +127,15 @@ xwl_screen_get_first_output(struct xwl_screen *xwl_screen)
     return xorg_list_first_entry(&xwl_screen->output_list, struct xwl_output, link);
 }
 
+struct xwl_output *
+xwl_screen_get_fixed_or_first_output(struct xwl_screen *xwl_screen)
+{
+    if (xwl_screen->fixed_output)
+        return xwl_screen->fixed_output;
+
+    return xwl_screen_get_first_output(xwl_screen);
+}
+
 static void
 xwl_property_callback(CallbackListPtr *pcbl, void *closure,
                       void *calldata)
@@ -180,6 +189,9 @@ xwl_close_screen(ScreenPtr screen)
                                   &xwl_screen->output_list, link)
         xwl_output_destroy(xwl_output);
 
+    if (xwl_screen->fixed_output)
+        xwl_output_destroy(xwl_screen->fixed_output);
+
     xorg_list_for_each_entry_safe(xwl_seat, next_xwl_seat,
                                   &xwl_screen->seat_list, link)
         xwl_seat_destroy(xwl_seat);
@@ -406,7 +418,7 @@ registry_global(void *data, struct wl_registry *registry, uint32_t id,
                                  NULL);
     }
     else if (strcmp(interface, "wl_output") == 0 && version >= 2) {
-        if (xwl_output_create(xwl_screen, id))
+        if (xwl_output_create(xwl_screen, id, (xwl_screen->fixed_output == NULL)))
             xwl_screen->expecting_event++;
     }
     else if (strcmp(interface, "zxdg_output_manager_v1") == 0) {
@@ -594,9 +606,12 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
     struct xwl_screen *xwl_screen;
     Pixel red_mask, blue_mask, green_mask;
     int ret, bpc, green_bpc, i;
+    unsigned int xwl_width = 0;
+    unsigned int xwl_height = 0;
 #ifdef XWL_HAS_GLAMOR
     Bool use_eglstreams = FALSE;
 #endif
+    Bool use_fixed_size = FALSE;
 
     if (!dixRegisterPrivateKey(&xwl_screen_private_key, PRIVATE_SCREEN, 0))
         return FALSE;
@@ -653,6 +668,24 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
         else if (strcmp(argv[i], "-force-xrandr-emulation") == 0) {
             xwl_screen->force_xrandr_emulation = 1;
         }
+        else if (strcmp(argv[i], "-geometry") == 0) {
+            sscanf(argv[i + 1], "%ix%i", &xwl_width, &xwl_height);
+            if (xwl_width == 0 || xwl_height == 0) {
+                ErrorF("invalid argument for -geometry %s\n", argv[i + 1]);
+                return FALSE;
+            }
+            use_fixed_size = 1;
+        }
+    }
+
+    if (use_fixed_size) {
+        if (xwl_screen->rootless) {
+            ErrorF("error, cannot set a geometry when running rootless\n");
+            return FALSE;
+        } else {
+            xwl_screen->width = xwl_width;
+            xwl_screen->height = xwl_height;
+        }
     }
 
 #ifdef XWL_HAS_GLAMOR
@@ -685,8 +718,13 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
         return FALSE;
     }
 
-    if (!xwl_screen_init_output(xwl_screen))
-        return FALSE;
+    if (use_fixed_size) {
+        if (!xwl_screen_init_randr_fixed(xwl_screen))
+            return FALSE;
+    } else {
+        if (!xwl_screen_init_output(xwl_screen))
+            return FALSE;
+    }
 
     xwl_screen->expecting_event = 0;
     xwl_screen->registry = wl_display_get_registry(xwl_screen->display);
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index a4738882a..5f2782d33 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -93,6 +93,7 @@ struct xwl_screen {
     struct xorg_list drm_lease_devices;
     struct xorg_list queued_drm_lease_devices;
     struct xorg_list drm_leases;
+    struct xwl_output *fixed_output;
     uint32_t serial;
 
 #define XWL_FORMAT_ARGB8888 (1 << 0)
@@ -134,6 +135,7 @@ Bool xwl_screen_has_viewport_support(struct xwl_screen *xwl_screen);
 Bool xwl_screen_has_resolution_change_emulation(struct xwl_screen *xwl_screen);
 void xwl_screen_check_resolution_change_emulation(struct xwl_screen *xwl_screen);
 struct xwl_output *xwl_screen_get_first_output(struct xwl_screen *xwl_screen);
+struct xwl_output *xwl_screen_get_fixed_or_first_output(struct xwl_screen *xwl_screen);
 Bool xwl_close_screen(ScreenPtr screen);
 Bool xwl_screen_init(ScreenPtr pScreen, int argc, char **argv);
 void xwl_sync_events (struct xwl_screen *xwl_screen);
diff --git a/hw/xwayland/xwayland-vidmode.c b/hw/xwayland/xwayland-vidmode.c
index 87e9c3d73..eed60256e 100644
--- a/hw/xwayland/xwayland-vidmode.c
+++ b/hw/xwayland/xwayland-vidmode.c
@@ -110,8 +110,9 @@ static RRModePtr
 xwlVidModeGetRRMode(ScreenPtr pScreen, int32_t width, int32_t height)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
-    struct xwl_output *xwl_output = xwl_screen_get_first_output(xwl_screen);
+    struct xwl_output *xwl_output;
 
+    xwl_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
     if (!xwl_output)
         return NULL;
 
@@ -122,9 +123,10 @@ static RRModePtr
 xwlVidModeGetCurrentRRMode(ScreenPtr pScreen)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
-    struct xwl_output *xwl_output = xwl_screen_get_first_output(xwl_screen);
     struct xwl_emulated_mode *emulated_mode;
+    struct xwl_output *xwl_output;
 
+    xwl_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
     if (!xwl_output)
         return NULL;
 
@@ -238,14 +240,18 @@ static Bool
 xwlVidModeGetNextModeline(ScreenPtr pScreen, DisplayModePtr *mode, int *dotClock)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
-    struct xwl_output *xwl_output = xwl_screen_get_first_output(xwl_screen);
+    struct xwl_output *xwl_output;
     VidModePtr pVidMode;
     DisplayModePtr pMod;
     intptr_t index;
 
+    xwl_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
+    if (xwl_output == NULL)
+        return FALSE;
+
     pMod = dixLookupPrivate(&pScreen->devPrivates, xwlVidModePrivateKey);
     pVidMode = VidModeGetPtr(pScreen);
-    if (xwl_output == NULL || pMod == NULL || pVidMode == NULL)
+    if (pMod == NULL || pVidMode == NULL)
         return FALSE;
 
     index = (intptr_t)pVidMode->Next;
@@ -294,9 +300,10 @@ static Bool
 xwlVidModeSetViewPort(ScreenPtr pScreen, int x, int y)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
-    struct xwl_output *xwl_output = xwl_screen_get_first_output(xwl_screen);
+    struct xwl_output *xwl_output;
 
-    if (!xwl_output)
+    xwl_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
+    if (xwl_output == NULL)
         return FALSE;
 
     /* Support only default viewport */
@@ -307,9 +314,10 @@ static Bool
 xwlVidModeGetViewPort(ScreenPtr pScreen, int *x, int *y)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
-    struct xwl_output *xwl_output = xwl_screen_get_first_output(xwl_screen);
+    struct xwl_output *xwl_output;
 
-    if (!xwl_output)
+    xwl_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
+    if (xwl_output == NULL)
         return FALSE;
 
     *x = xwl_output->x;
@@ -322,17 +330,22 @@ static Bool
 xwlVidModeSwitchMode(ScreenPtr pScreen, DisplayModePtr mode)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
-    struct xwl_output *xwl_output = xwl_screen_get_first_output(xwl_screen);
+    struct xwl_output *xwl_output;
     RRModePtr rrmode;
 
-    if (!xwl_output)
+    xwl_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
+    if (xwl_output == NULL)
         return FALSE;
 
     rrmode = xwl_output_find_mode(xwl_output, mode->HDisplay, mode->VDisplay);
     if (rrmode == NULL)
         return FALSE;
 
-    xwl_output_set_emulated_mode(xwl_output, GetCurrentClient(), rrmode, TRUE);
+    if (xwl_screen->rootless)
+        xwl_output_set_emulated_mode(xwl_output, GetCurrentClient(), rrmode, TRUE);
+    else if (xwl_screen->fixed_output)
+        xwl_output_set_mode_fixed(xwl_screen->fixed_output, rrmode);
+
     return TRUE;
 }
 
@@ -388,7 +401,9 @@ static int
 xwlVidModeGetNumOfModes(ScreenPtr pScreen)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
-    struct xwl_output *xwl_output = xwl_screen_get_first_output(xwl_screen);
+    struct xwl_output *xwl_output;
+
+    xwl_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
 
     return xwl_output ? xwl_output->randr_output->numModes : 0;
 }
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index 6cc5f8e0f..d28d6f8e7 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -91,6 +91,7 @@ void
 ddxUseMsg(void)
 {
     ErrorF("-rootless              run rootless, requires wm support\n");
+    ErrorF("-geometry WxH          set Xwayland window size when rootful\n");
     ErrorF("-wm fd                 create X client for wm on given fd\n");
     ErrorF("-initfd fd             add given fd as a listen socket for initialization clients\n");
     ErrorF("-listenfd fd           add given fd as a listen socket\n");
@@ -224,6 +225,10 @@ ddxProcessArgument(int argc, char *argv[], int i)
         touchEmulatePointer = FALSE;
         return 1;
     }
+    else if (strcmp(argv[i], "-geometry") == 0) {
+        CHECK_FOR_REQUIRED_ARGUMENTS(1);
+        return 2;
+    }
 
     return 0;
 }
diff --git a/hw/xwayland/xwayland.pc.in b/hw/xwayland/xwayland.pc.in
index e2c06b801..0b21e7acc 100644
--- a/hw/xwayland/xwayland.pc.in
+++ b/hw/xwayland/xwayland.pc.in
@@ -13,3 +13,4 @@ have_verbose=true
 have_terminate_delay=true
 have_no_touch_pointer_emulation=true
 have_force_xrandr_emulation=true
+have_geometry=true
commit 5cc0319ca5d997324566015194c8de4d36c95264
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Wed Apr 27 14:34:37 2022 +0200

    xwayland: update_screen_size() takes a screen argument
    
    update_screen_size() takes an xwl_output argument, mostly for historical
    reasons, whereas it actually applies to a screen (as its name implies).
    
    Reshuffle the code to take an xwl_screen instead, in preparation for
    the geometry mode in Xwayland - No functional change.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c
index 4a6a98f52..bdce26191 100644
--- a/hw/xwayland/xwayland-output.c
+++ b/hw/xwayland/xwayland-output.c
@@ -180,10 +180,8 @@ update_backing_pixmaps(struct xwl_screen *xwl_screen, int width, int height)
 }
 
 static void
-update_screen_size(struct xwl_output *xwl_output, int width, int height)
+update_screen_size(struct xwl_screen *xwl_screen, int width, int height)
 {
-    struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
-
     if (xwl_screen->root_clip_mode == ROOT_CLIP_FULL)
         SetRootClip(xwl_screen->screen, ROOT_CLIP_NONE);
 
@@ -645,7 +643,7 @@ apply_output_change(struct xwl_output *xwl_output)
 	--xwl_screen->expecting_event;
     }
 
-    update_screen_size(xwl_output, width, height);
+    update_screen_size(xwl_screen, width, height);
 }
 
 static void
@@ -837,7 +835,7 @@ xwl_output_remove(struct xwl_output *xwl_output)
 
     xorg_list_for_each_entry(it, &xwl_screen->output_list, link)
         output_get_new_size(it, &width, &height);
-    update_screen_size(xwl_output, width, height);
+    update_screen_size(xwl_screen, width, height);
 
     RRCrtcDestroy(xwl_output->randr_crtc);
     RROutputDestroy(xwl_output->randr_output);
commit eae3c06c23af59f192ab0c409a56d80f6bc34ce4
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Tue May 17 18:40:11 2022 +0200

    xwayland: make the output serials belong to the screen
    
    Xwayland uses an output serial number it increments each time a new
    Wayland output is added.
    
    On server regeneration, that static value is not cleared, and therfore
    the output numbers keep increasing each time the Xserver restarts.
    
    To avoid that issue, make the output serial part of the xwl_screen,
    which gets recreated on server regeneration, so that index is reset to 0
    automatically on server regeneration.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-drm-lease.c b/hw/xwayland/xwayland-drm-lease.c
index 8bbb27e70..d623b4b79 100644
--- a/hw/xwayland/xwayland-drm-lease.c
+++ b/hw/xwayland/xwayland-drm-lease.c
@@ -336,6 +336,7 @@ drm_lease_device_handle_connector(void *data,
                                   struct wp_drm_lease_connector_v1 *connector)
 {
     struct xwl_drm_lease_device *lease_device = data;
+    struct xwl_screen *xwl_screen = lease_device->xwl_screen;
     struct xwl_output *xwl_output;
     char name[256];
 
@@ -345,18 +346,19 @@ drm_lease_device_handle_connector(void *data,
         return;
     }
 
-    snprintf(name, sizeof name, "XWAYLAND%d", xwl_get_next_output_serial());
+    snprintf(name, sizeof name, "XWAYLAND%d",
+             xwl_screen_get_next_output_serial(xwl_screen));
 
     xwl_output->lease_device = lease_device;
-    xwl_output->xwl_screen = lease_device->xwl_screen;
+    xwl_output->xwl_screen = xwl_screen;
     xwl_output->lease_connector = connector;
-    xwl_output->randr_crtc = RRCrtcCreate(lease_device->xwl_screen->screen, xwl_output);
+    xwl_output->randr_crtc = RRCrtcCreate(xwl_screen->screen, xwl_output);
     if (!xwl_output->randr_crtc) {
         ErrorF("Failed creating RandR CRTC\n");
         goto err;
     }
     RRCrtcSetRotations(xwl_output->randr_crtc, ALL_ROTATIONS);
-    xwl_output->randr_output = RROutputCreate(lease_device->xwl_screen->screen,
+    xwl_output->randr_output = RROutputCreate(xwl_screen->screen,
                                               name, strlen(name), xwl_output);
     if (!xwl_output->randr_output) {
         ErrorF("Failed creating RandR Output\n");
@@ -373,7 +375,7 @@ drm_lease_device_handle_connector(void *data,
                                             &lease_connector_listener,
                                             xwl_output);
 
-    xorg_list_append(&xwl_output->link, &lease_device->xwl_screen->output_list);
+    xorg_list_append(&xwl_output->link, &xwl_screen->output_list);
     return;
 
 err:
diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c
index 1dd5a5f89..4a6a98f52 100644
--- a/hw/xwayland/xwayland-output.c
+++ b/hw/xwayland/xwayland-output.c
@@ -768,7 +768,8 @@ xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id)
     xwl_output->server_output_id = id;
     wl_output_add_listener(xwl_output->output, &output_listener, xwl_output);
 
-    snprintf(name, sizeof name, "XWAYLAND%d", xwl_get_next_output_serial());
+    snprintf(name, sizeof name, "XWAYLAND%d",
+             xwl_screen_get_next_output_serial(xwl_screen));
 
     xwl_output->xwl_screen = xwl_screen;
     xwl_output->randr_crtc = RRCrtcCreate(xwl_screen->screen, xwl_output);
@@ -1011,10 +1012,3 @@ xwl_screen_init_xdg_output(struct xwl_screen *xwl_screen)
     xorg_list_for_each_entry(it, &xwl_screen->output_list, link)
         xwl_output_get_xdg_output(it);
 }
-
-int
-xwl_get_next_output_serial(void)
-{
-    static int output_name_serial = 0;
-    return output_name_serial++;
-}
diff --git a/hw/xwayland/xwayland-output.h b/hw/xwayland/xwayland-output.h
index 74a46994f..10a353ddc 100644
--- a/hw/xwayland/xwayland-output.h
+++ b/hw/xwayland/xwayland-output.h
@@ -94,6 +94,4 @@ void xwl_output_set_window_randr_emu_props(struct xwl_screen *xwl_screen,
 
 void xwl_screen_init_xdg_output(struct xwl_screen *xwl_screen);
 
-int xwl_get_next_output_serial(void);
-
 #endif /* XWAYLAND_OUTPUT_H */
diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index dd520819b..c9b93a2da 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -580,6 +580,13 @@ xwl_screen_roundtrip(struct xwl_screen *xwl_screen)
         xwl_give_up("could not connect to wayland server\n");
 }
 
+
+int
+xwl_screen_get_next_output_serial(struct xwl_screen *xwl_screen)
+{
+    return xwl_screen->output_name_serial++;
+}
+
 Bool
 xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
 {
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index f8aee2bdc..a4738882a 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -48,6 +48,7 @@ struct xwl_screen {
     int width;
     int height;
     int depth;
+    int output_name_serial;
     ScreenPtr screen;
     int wm_client_id;
     int expecting_event;
@@ -140,5 +141,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);
+int xwl_screen_get_next_output_serial(struct xwl_screen * xwl_screen);
 
 #endif /* XWAYLAND_SCREEN_H */
commit a4aba5ab301a79ebd3239610d205188581bbcd0d
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Wed Apr 27 14:07:49 2022 +0200

    xwayland: catch SetWindowPixmap() even when rootful
    
    Xwayland's own SetWindowPixmap() handler would be ignored when running
    rootful.
    
    This is fine as long as we do not plan to resize the root window,
    however this is becoming problematic if we plan to resize the root
    window dynamically when running rootful.
    
    Just add the xwl_window_set_window_pixmap() handler regardless of
    rootful/rootless mode.
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index d6e19282c..dd520819b 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -777,10 +777,8 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
     xwl_screen->MoveWindow = pScreen->MoveWindow;
     pScreen->MoveWindow = xwl_move_window;
 
-    if (xwl_screen->rootless) {
-        xwl_screen->SetWindowPixmap = pScreen->SetWindowPixmap;
-        pScreen->SetWindowPixmap = xwl_window_set_window_pixmap;
-    }
+    xwl_screen->SetWindowPixmap = pScreen->SetWindowPixmap;
+    pScreen->SetWindowPixmap = xwl_window_set_window_pixmap;
 
     pScreen->CursorWarpedTo = xwl_cursor_warped_to;
     pScreen->CursorConfinedTo = xwl_cursor_confined_to;


More information about the xorg-commit mailing list