From d370f1e58ac2365cc7396435f7182259dcf35a9c Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <ofourdan@redhat.com>
Date: Thu, 12 May 2022 12:04:29 +0200
Subject: [PATCH] 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@redhat.com>
Reviewed-by: Adam Jackson <ajax@redhat.com>
---
 hw/xwayland/man/Xwayland.man  |  5 ++
 hw/xwayland/xwayland-screen.c | 13 ++++++
 hw/xwayland/xwayland-screen.h |  1 +
 hw/xwayland/xwayland-window.c | 86 +++++++++++++++++++++++++++++++++--
 hw/xwayland/xwayland-window.h |  1 +
 hw/xwayland/xwayland.c        |  4 ++
 hw/xwayland/xwayland.pc.in    |  1 +
 7 files changed, 108 insertions(+), 3 deletions(-)

diff --git a/hw/xwayland/man/Xwayland.man b/hw/xwayland/man/Xwayland.man
index b9202160c8..97e3c6957b 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 771f262b07..5261125143 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 5f2782d330..360e2aa855 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 491ce223ec..25c8f6cf7e 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 1975b749c0..24147e3fc7 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 d28d6f8e71..8fb929eeb6 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 0b21e7acc1..5976bd6b14 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
-- 
GitLab