xorg-x11-server/0008-xwayland-Add-support-for-randr-resolution-change-emu.patch
Troy Dawson 6c2dd5fc20 RHEL 9.0.0 Alpha bootstrap
The content of this branch was automatically imported from Fedora ELN
with the following as its source:
https://src.fedoraproject.org/rpms/xorg-x11-server#eaf442296937a83e622ce4995c5f33122bcd6f02
2020-10-15 14:12:16 -07:00

463 lines
17 KiB
Diff

From 1317f1f02fd0037e2bf7a678f2c3a9a4170161e9 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Tue, 2 Jul 2019 11:55:26 +0200
Subject: [PATCH xserver 08/24] xwayland: Add support for randr-resolution
change emulation using viewport
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add support for per client randr-resolution change emulation using viewport,
for apps which want to change the resolution when going fullscreen.
Partly based on earlier work on this by Robert Mader <robert.mader@posteo.de>
Note SDL2 and SFML do not restore randr resolution when going from
fullscreen -> windowed, I believe this is caused by us still reporting the
desktop resolution when they query the resolution. This is not a problem
because when windowed the toplevel window size includes the window-decorations
so it never matches the emulated resolution.
One exception would be the window being resizable in Windowed mode and the
user resizing the window so that including decorations it matches the
emulated resolution *and* the window being at pos 0x0. But this is an
extreme corner case. Still I will submit patches upstream to SDL2
and SFML to always restore the desktop resolution under Xwayland,
disabling resolution emulation all together when going windowed.
Reviewed-by: Olivier Fourdan <ofourdan@redhat.com>
Acked-by: Michel Dänzer <mdaenzer@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit d99b9ff0f237d15e7eb507484493c73b393d5dba)
---
hw/xwayland/xwayland-input.c | 5 +
hw/xwayland/xwayland-output.c | 63 ++++++++++-
hw/xwayland/xwayland.c | 199 ++++++++++++++++++++++++++++++++++
hw/xwayland/xwayland.h | 15 +++
4 files changed, 276 insertions(+), 6 deletions(-)
diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index a05d178ff..7d75a8f54 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -488,6 +488,11 @@ dispatch_pointer_motion_event(struct xwl_seat *xwl_seat)
int dx = xwl_seat->focus_window->window->drawable.x;
int dy = xwl_seat->focus_window->window->drawable.y;
+ if (xwl_window_has_viewport_enabled(xwl_seat->focus_window)) {
+ sx *= xwl_seat->focus_window->scale_x;
+ sy *= xwl_seat->focus_window->scale_y;
+ }
+
x = dx + sx;
y = dy + sy;
} else {
diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c
index 64794dee7..e09d00108 100644
--- a/hw/xwayland/xwayland-output.c
+++ b/hw/xwayland/xwayland-output.c
@@ -371,6 +371,42 @@ err:
FatalError("Failed to allocate memory for list of RR modes");
}
+RRModePtr
+xwl_output_find_mode(struct xwl_output *xwl_output,
+ int32_t width, int32_t height)
+{
+ RROutputPtr output = xwl_output->randr_output;
+ int i;
+
+ /* width & height -1 means we want the actual output mode, which is idx 0 */
+ if (width == -1 && height == -1 && output->modes)
+ return output->modes[0];
+
+ for (i = 0; i < output->numModes; i++) {
+ if (output->modes[i]->mode.width == width && output->modes[i]->mode.height == height)
+ return output->modes[i];
+ }
+
+ ErrorF("XWAYLAND: mode %dx%d is not available\n", width, height);
+ return NULL;
+}
+
+void
+xwl_output_set_emulated_mode(struct xwl_output *xwl_output, ClientPtr client,
+ RRModePtr mode, Bool from_vidmode)
+{
+ DebugF("XWAYLAND: xwl_output_set_emulated_mode from %s: %dx%d\n",
+ from_vidmode ? "vidmode" : "randr",
+ mode->mode.width, mode->mode.height);
+
+ if (mode->mode.width == xwl_output->width && mode->mode.height == xwl_output->height)
+ xwl_output_remove_emulated_mode_for_client(xwl_output, client);
+ else
+ xwl_output_add_emulated_mode_for_client(xwl_output, client, mode, from_vidmode);
+
+ xwl_screen_check_resolution_change_emulation(xwl_output->xwl_screen);
+}
+
static void
apply_output_change(struct xwl_output *xwl_output)
{
@@ -613,21 +649,36 @@ xwl_randr_screen_set_size(ScreenPtr pScreen,
static Bool
xwl_randr_crtc_set(ScreenPtr pScreen,
RRCrtcPtr crtc,
- RRModePtr mode,
+ RRModePtr new_mode,
int x,
int y,
Rotation rotation,
int numOutputs, RROutputPtr * outputs)
{
struct xwl_output *xwl_output = crtc->devPrivate;
+ RRModePtr mode;
- if (!mode || (mode->mode.width == xwl_output->width &&
- mode->mode.height == xwl_output->height)) {
- RRCrtcChanged(crtc, TRUE);
- return TRUE;
+ if (new_mode) {
+ mode = xwl_output_find_mode(xwl_output,
+ new_mode->mode.width,
+ new_mode->mode.height);
+ } else {
+ mode = xwl_output_find_mode(xwl_output, -1, -1);
}
+ if (!mode)
+ return FALSE;
- return FALSE;
+ xwl_output_set_emulated_mode(xwl_output, GetCurrentClient(), mode, FALSE);
+
+ /* A real randr implementation would call:
+ * RRCrtcNotify(xwl_output->randr_crtc, mode, xwl_output->x, xwl_output->y,
+ * xwl_output->rotation, NULL, 1, &xwl_output->randr_output);
+ * here to update the mode reported to clients querying the randr settings
+ * but that influences *all* clients and we do randr mode change emulation
+ * on a per client basis. So we just return success here.
+ */
+
+ return TRUE;
}
static Bool
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index e00dba334..9c6cf7cf5 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -178,6 +178,23 @@ xwl_screen_has_resolution_change_emulation(struct xwl_screen *xwl_screen)
return xwl_screen->rootless && xwl_screen_has_viewport_support(xwl_screen);
}
+/* Return the output @ 0x0, falling back to the first output in the list */
+struct xwl_output *
+xwl_screen_get_first_output(struct xwl_screen *xwl_screen)
+{
+ struct xwl_output *xwl_output;
+
+ xorg_list_for_each_entry(xwl_output, &xwl_screen->output_list, link) {
+ if (xwl_output->x == 0 && xwl_output->y == 0)
+ return xwl_output;
+ }
+
+ if (xorg_list_is_empty(&xwl_screen->output_list))
+ return NULL;
+
+ return xorg_list_first_entry(&xwl_screen->output_list, struct xwl_output, link);
+}
+
static void
xwl_window_set_allow_commits(struct xwl_window *xwl_window, Bool allow,
const char *debug_msg)
@@ -514,6 +531,150 @@ xwl_pixmap_get(PixmapPtr pixmap)
return dixLookupPrivate(&pixmap->devPrivates, &xwl_pixmap_private_key);
}
+Bool
+xwl_window_has_viewport_enabled(struct xwl_window *xwl_window)
+{
+ return (xwl_window->viewport != NULL);
+}
+
+static void
+xwl_window_disable_viewport(struct xwl_window *xwl_window)
+{
+ assert (xwl_window->viewport);
+
+ DebugF("XWAYLAND: disabling viewport\n");
+ wp_viewport_destroy(xwl_window->viewport);
+ xwl_window->viewport = NULL;
+}
+
+static void
+xwl_window_enable_viewport(struct xwl_window *xwl_window,
+ struct xwl_output *xwl_output,
+ struct xwl_emulated_mode *emulated_mode)
+{
+ /* If necessary disable old viewport to apply new settings */
+ if (xwl_window_has_viewport_enabled(xwl_window))
+ xwl_window_disable_viewport(xwl_window);
+
+ DebugF("XWAYLAND: enabling viewport %dx%d -> %dx%d\n",
+ emulated_mode->width, emulated_mode->height,
+ xwl_output->width, xwl_output->height);
+
+ xwl_window->viewport =
+ wp_viewporter_get_viewport(xwl_window->xwl_screen->viewporter,
+ xwl_window->surface);
+
+ wp_viewport_set_source(xwl_window->viewport,
+ wl_fixed_from_int(0),
+ wl_fixed_from_int(0),
+ wl_fixed_from_int(emulated_mode->width),
+ wl_fixed_from_int(emulated_mode->height));
+ wp_viewport_set_destination(xwl_window->viewport,
+ xwl_output->width,
+ xwl_output->height);
+
+ xwl_window->scale_x = (float)emulated_mode->width / xwl_output->width;
+ xwl_window->scale_y = (float)emulated_mode->height / xwl_output->height;
+}
+
+static Bool
+xwl_screen_client_is_window_manager(struct xwl_screen *xwl_screen,
+ ClientPtr client)
+{
+ WindowPtr root = xwl_screen->screen->root;
+ OtherClients *others;
+
+ for (others = wOtherClients(root); others; others = others->next) {
+ if (SameClient(others, client)) {
+ if (others->mask & (SubstructureRedirectMask | ResizeRedirectMask))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static ClientPtr
+xwl_window_get_owner(struct xwl_window *xwl_window)
+{
+ WindowPtr window = xwl_window->window;
+ ClientPtr client = wClient(window);
+
+ /* If the toplevel window is owned by the window-manager, then the
+ * actual client toplevel window has been reparented to a window-manager
+ * decoration window. In that case return the client of the
+ * first *and only* child of the toplevel (decoration) window.
+ */
+ if (xwl_screen_client_is_window_manager(xwl_window->xwl_screen, client)) {
+ if (window->firstChild && window->firstChild == window->lastChild)
+ return wClient(window->firstChild);
+ else
+ return NULL; /* Should never happen, skip resolution emulation */
+ }
+
+ return client;
+}
+
+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_screen *xwl_screen = xwl_window->xwl_screen;
+ struct xwl_emulated_mode *emulated_mode;
+ struct xwl_output *xwl_output;
+ ClientPtr owner;
+
+ if (!xwl_screen_has_resolution_change_emulation(xwl_screen))
+ return FALSE;
+
+ owner = xwl_window_get_owner(xwl_window);
+ if (!owner)
+ return FALSE;
+
+ /* 1. Test if the window matches the emulated mode on one of the outputs
+ * This path gets hit by most games / libs (e.g. SDL, SFML, OGRE)
+ */
+ xorg_list_for_each_entry(xwl_output, &xwl_screen->output_list, link) {
+ emulated_mode = xwl_output_get_emulated_mode_for_client(xwl_output, owner);
+ if (!emulated_mode)
+ continue;
+
+ if (xwl_window->x == xwl_output->x &&
+ xwl_window->y == xwl_output->y &&
+ xwl_window->width == emulated_mode->width &&
+ xwl_window->height == emulated_mode->height) {
+
+ *emulated_mode_ret = emulated_mode;
+ *xwl_output_ret = xwl_output;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+xwl_window_check_resolution_change_emulation(struct xwl_window *xwl_window)
+{
+ 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);
+ else if (xwl_window_has_viewport_enabled(xwl_window))
+ xwl_window_disable_viewport(xwl_window);
+}
+
+void
+xwl_screen_check_resolution_change_emulation(struct xwl_screen *xwl_screen)
+{
+ struct xwl_window *xwl_window;
+
+ xorg_list_for_each_entry(xwl_window, &xwl_screen->window_list, link_window)
+ xwl_window_check_resolution_change_emulation(xwl_window);
+}
+
static void
xwl_window_init_allow_commits(struct xwl_window *xwl_window)
{
@@ -584,6 +745,8 @@ ensure_surface_for_window(WindowPtr window)
xwl_window->xwl_screen = xwl_screen;
xwl_window->window = window;
+ xwl_window->width = window->drawable.width;
+ xwl_window->height = window->drawable.height;
xwl_window->surface = wl_compositor_create_surface(xwl_screen->compositor);
if (xwl_window->surface == NULL) {
ErrorF("wl_display_create_surface failed\n");
@@ -625,6 +788,7 @@ ensure_surface_for_window(WindowPtr window)
dixSetPrivate(&window->devPrivates, &xwl_window_private_key, xwl_window);
xorg_list_init(&xwl_window->link_damage);
+ xorg_list_add(&xwl_window->link_window, &xwl_screen->window_list);
#ifdef GLAMOR_HAS_GBM
xorg_list_init(&xwl_window->frame_callback_list);
@@ -718,8 +882,12 @@ xwl_unrealize_window(WindowPtr window)
if (!xwl_window)
return ret;
+ if (xwl_window_has_viewport_enabled(xwl_window))
+ xwl_window_disable_viewport(xwl_window);
+
wl_surface_destroy(xwl_window->surface);
xorg_list_del(&xwl_window->link_damage);
+ xorg_list_del(&xwl_window->link_window);
unregister_damage(window);
if (xwl_window->frame_callback)
@@ -769,6 +937,33 @@ xwl_set_window_pixmap(WindowPtr window,
ensure_surface_for_window(window);
}
+static void
+xwl_resize_window(WindowPtr window,
+ int x, int y,
+ unsigned int width, unsigned int height,
+ WindowPtr sib)
+{
+ ScreenPtr screen = window->drawable.pScreen;
+ struct xwl_screen *xwl_screen;
+ struct xwl_window *xwl_window;
+
+ xwl_screen = xwl_screen_get(screen);
+ xwl_window = xwl_window_get(window);
+
+ screen->ResizeWindow = xwl_screen->ResizeWindow;
+ (*screen->ResizeWindow) (window, x, y, width, height, sib);
+ xwl_screen->ResizeWindow = screen->ResizeWindow;
+ screen->ResizeWindow = xwl_resize_window;
+
+ if (xwl_window) {
+ xwl_window->x = x;
+ xwl_window->y = y;
+ xwl_window->width = width;
+ xwl_window->height = height;
+ xwl_window_check_resolution_change_emulation(xwl_window);
+ }
+}
+
static void
frame_callback(void *data,
struct wl_callback *callback,
@@ -1246,6 +1441,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
xorg_list_init(&xwl_screen->output_list);
xorg_list_init(&xwl_screen->seat_list);
xorg_list_init(&xwl_screen->damage_window_list);
+ xorg_list_init(&xwl_screen->window_list);
xwl_screen->depth = 24;
if (!monitorResolution)
@@ -1340,6 +1536,9 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
xwl_screen->CloseScreen = pScreen->CloseScreen;
pScreen->CloseScreen = xwl_close_screen;
+ xwl_screen->ResizeWindow = pScreen->ResizeWindow;
+ pScreen->ResizeWindow = xwl_resize_window;
+
if (xwl_screen->rootless) {
xwl_screen->SetWindowPixmap = pScreen->SetWindowPixmap;
pScreen->SetWindowPixmap = xwl_set_window_pixmap;
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index c886d77e9..36c4c4c8b 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -135,10 +135,12 @@ struct xwl_screen {
DestroyWindowProcPtr DestroyWindow;
XYToWindowProcPtr XYToWindow;
SetWindowPixmapProcPtr SetWindowPixmap;
+ ResizeWindowProcPtr ResizeWindow;
struct xorg_list output_list;
struct xorg_list seat_list;
struct xorg_list damage_window_list;
+ struct xorg_list window_list;
int wayland_fd;
struct wl_display *display;
@@ -179,9 +181,13 @@ struct xwl_screen {
struct xwl_window {
struct xwl_screen *xwl_screen;
struct wl_surface *surface;
+ struct wp_viewport *viewport;
+ int32_t x, y, width, height;
+ float scale_x, scale_y;
struct wl_shell_surface *shell_surface;
WindowPtr window;
struct xorg_list link_damage;
+ struct xorg_list link_window;
struct wl_callback *frame_callback;
Bool allow_commits;
#ifdef GLAMOR_HAS_GBM
@@ -411,6 +417,9 @@ Bool xwl_screen_init_cursor(struct xwl_screen *xwl_screen);
struct xwl_screen *xwl_screen_get(ScreenPtr screen);
Bool xwl_screen_has_resolution_change_emulation(struct xwl_screen *xwl_screen);
+struct xwl_output *xwl_screen_get_first_output(struct xwl_screen *xwl_screen);
+void xwl_screen_check_resolution_change_emulation(struct xwl_screen *xwl_screen);
+Bool xwl_window_has_viewport_enabled(struct xwl_window *xwl_window);
void xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *tool);
void xwl_seat_set_cursor(struct xwl_seat *xwl_seat);
@@ -444,6 +453,12 @@ void xwl_output_remove(struct xwl_output *xwl_output);
struct xwl_emulated_mode *xwl_output_get_emulated_mode_for_client(
struct xwl_output *xwl_output, ClientPtr client);
+RRModePtr xwl_output_find_mode(struct xwl_output *xwl_output,
+ int32_t width, int32_t height);
+void xwl_output_set_emulated_mode(struct xwl_output *xwl_output,
+ ClientPtr client, RRModePtr mode,
+ Bool from_vidmode);
+
RRModePtr xwayland_cvt(int HDisplay, int VDisplay,
float VRefresh, Bool Reduced, Bool Interlaced);
--
2.26.2