Merge branch 'xwayland-randr-emulation-improvements' into 'master'

xwayland: Miscellaneous RandR emulation improvements

See merge request xorg/xserver!2095
This commit is contained in:
Michel Dänzer 2025-12-19 08:54:14 +00:00
commit b024affa09
5 changed files with 215 additions and 123 deletions

View file

@ -102,8 +102,8 @@ output_handle_geometry(void *data, struct wl_output *wl_output, int x, int y,
/* Apply the change from wl_output only if xdg-output is not supported */
if (!xwl_output->xdg_output) {
xwl_output->x = x;
xwl_output->y = y;
xwl_output->logical_x = x;
xwl_output->logical_y = y;
}
xwl_output->rotation = wl_transform_to_xrandr(transform);
}
@ -119,12 +119,43 @@ output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
/* Apply the change from wl_output only if xdg-output is not supported */
if (!xwl_output->xdg_output) {
xwl_output->width = width;
xwl_output->height = height;
xwl_output->logical_w = width;
xwl_output->logical_h = height;
}
xwl_output->mode_width = width;
xwl_output->mode_height = height;
xwl_output->refresh = refresh;
}
static void
output_get_logical_mode(struct xwl_output *xwl_output, int *width, int *height)
{
/* When we have xdg-output support the stored size is already rotated. */
if (xwl_output->xdg_output == NULL ||
(xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180))) {
*width = xwl_output->logical_w;
*height = xwl_output->logical_h;
} else {
*width = xwl_output->logical_h;
*height = xwl_output->logical_w;
}
}
void
output_get_logical_extents(struct xwl_output *xwl_output, int *width, int *height)
{
/* When we have xdg-output support the stored size is already rotated. */
if (xwl_output->xdg_output ||
(xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180))) {
*width = xwl_output->logical_w;
*height = xwl_output->logical_h;
} else {
*width = xwl_output->logical_h;
*height = xwl_output->logical_w;
}
}
/**
* Decides on the maximum expanse of an output in logical space (i.e. in the
* Wayland compositor plane) respective to some fix width and height values. The
@ -133,23 +164,23 @@ output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
static inline void
output_get_new_size(struct xwl_output *xwl_output, int *width, int *height)
{
int output_width, output_height;
int logical_width, logical_height, max_width, max_height;
/* When we have xdg-output support the stored size is already rotated. */
if (xwl_output->xdg_output
|| (xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180))) {
output_width = xwl_output->width;
output_height = xwl_output->height;
output_get_logical_extents(xwl_output, &logical_width, &logical_height);
if (xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180)) {
max_width = max(logical_width, xwl_output->mode_width);
max_height = max(logical_height, xwl_output->mode_height);
} else {
output_width = xwl_output->height;
output_height = xwl_output->width;
max_width = max(logical_width, xwl_output->mode_height);
max_height = max(logical_height, xwl_output->mode_width);
}
if (*width < xwl_output->x + output_width)
*width = xwl_output->x + output_width;
if (*width < xwl_output->logical_x + max_width)
*width = xwl_output->logical_x + max_width;
if (*height < xwl_output->y + output_height)
*height = xwl_output->y + output_height;
if (*height < xwl_output->logical_y + max_height)
*height = xwl_output->logical_y + max_height;
}
static int
@ -299,49 +330,46 @@ xwl_output_remove_emulated_mode_for_client(struct xwl_output *xwl_output,
/* From hw/xfree86/common/xf86DefModeSet.c with some obscure modes dropped */
const int32_t xwl_output_fake_modes[][2] = {
/* 4:3 (1.33) */
{ 2048, 1536 },
{ 1920, 1440 },
{ 1600, 1200 },
{ 1440, 1080 },
{ 1400, 1050 },
{ 5120, 2880 }, /* 16:9 (1.77) */
{ 4096, 2304 }, /* 16:9 (1.77) */
{ 3840, 2160 }, /* 16:9 (1.77) */
{ 3200, 1800 }, /* 16:9 (1.77) */
{ 2880, 1620 }, /* 16:9 (1.77) */
{ 2560, 1600 }, /* 16:10 (1.6) */
{ 2560, 1440 }, /* 16:9 (1.77) */
{ 2048, 1536 }, /* 4:3 (1.33) */
{ 2048, 1152 }, /* 16:9 (1.77) */
{ 1920, 1440 }, /* 4:3 (1.33) */
{ 1920, 1200 }, /* 16:10 (1.6) */
{ 1920, 1080 }, /* 16:9 (1.77) */
{ 1680, 1050 }, /* 16:10 (1.6) */
{ 1600, 1200 }, /* 4:3 (1.33) */
{ 1600, 900 }, /* 16:9 (1.77) */
{ 1440, 1080 }, /* 4:3 (1.33) */
{ 1440, 900 }, /* 16:10 (1.6) */
{ 1400, 1050 }, /* 4:3 (1.33) */
{ 1368, 768 }, /* 16:9 (1.77) */
{ 1280, 1024 }, /* 5:4 (1.25) */
{ 1280, 960 },
{ 1152, 864 },
{ 1024, 768 },
{ 800, 600 },
{ 640, 480 },
{ 320, 240 },
/* 16:10 (1.6) */
{ 2560, 1600 },
{ 1920, 1200 },
{ 1680, 1050 },
{ 1440, 900 },
{ 1280, 800 },
{ 1152, 720 },
{ 960, 600 },
{ 928, 580 },
{ 800, 500 },
{ 768, 480 },
{ 1280, 960 }, /* 4:3 (1.33) */
{ 1280, 800 }, /* 16:10 (1.6) */
{ 1280, 720 }, /* 16:9 (1.77) */
{ 1152, 864 }, /* 4:3 (1.33) */
{ 1152, 720 }, /* 16:10 (1.6) */
{ 1024, 768 }, /* 4:3 (1.33) */
{ 1024, 576 }, /* 16:9 (1.77) */
{ 960, 600 }, /* 16:10 (1.6) */
{ 928, 580 }, /* 16:10 (1.6) */
{ 864, 486 }, /* 16:9 (1.77) */
{ 800, 600 }, /* 4:3 (1.33) */
{ 800, 500 }, /* 16:10 (1.6) */
{ 768, 480 }, /* 16:10 (1.6) */
{ 720, 480 }, /* 3:2 (1.5) */
{ 640, 400 },
{ 320, 200 },
/* 16:9 (1.77) */
{ 5120, 2880 },
{ 4096, 2304 },
{ 3840, 2160 },
{ 3200, 1800 },
{ 2880, 1620 },
{ 2560, 1440 },
{ 2048, 1152 },
{ 1920, 1080 },
{ 1600, 900 },
{ 1368, 768 },
{ 1280, 720 },
{ 1024, 576 },
{ 864, 486 },
{ 720, 400 },
{ 640, 350 },
{ 720, 400 }, /* 16:9 (1.77) */
{ 640, 480 }, /* 4:3 (1.33) */
{ 640, 400 }, /* 16:10 (1.6) */
{ 640, 350 }, /* 16:9 (1.77) */
{ 320, 240 }, /* 4:3 (1.33) */
{ 320, 200 }, /* 16:10 (1.6) */
};
/* Build an array with RRModes the first mode is the actual output mode, the
@ -352,36 +380,74 @@ const int32_t xwl_output_fake_modes[][2] = {
static RRModePtr *
output_get_rr_modes(struct xwl_output *xwl_output,
int32_t width, int32_t height,
int *count)
int *count, int *logical_mode)
{
struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
RRModePtr *rr_modes;
int i;
int i = 0;
rr_modes = xallocarray(ARRAY_SIZE(xwl_output_fake_modes) + 1, sizeof(RRModePtr));
rr_modes = xallocarray(ARRAY_SIZE(xwl_output_fake_modes) + 2, sizeof(RRModePtr));
if (!rr_modes)
goto err;
/* Add actual output mode */
rr_modes[0] = xwayland_cvt(width, height, xwl_output->refresh / 1000.0, 0, 0);
*count = 0;
if (xwl_screen_has_resolution_change_emulation(xwl_screen) &&
(width != xwl_output->mode_width || height != xwl_output->mode_height)) {
/* Add native output mode as preferred */
rr_modes[0] = xwayland_cvt(xwl_output->mode_width, xwl_output->mode_height,
xwl_output->refresh / 1000.0, 0, 0);
if (!rr_modes[0])
goto err;
*count = 1;
/* Add fake modes larger than logical mode */
for (; i < ARRAY_SIZE(xwl_output_fake_modes); i++) {
if (xwl_output_fake_modes[i][0] <= width &&
xwl_output_fake_modes[i][1] <= height)
break;
/* Skip modes which are too big, avoid downscaling */
if (xwl_output_fake_modes[i][0] >= xwl_output->mode_width &&
xwl_output_fake_modes[i][1] >= xwl_output->mode_height)
continue;
rr_modes[*count] = xwayland_cvt(xwl_output_fake_modes[i][0],
xwl_output_fake_modes[i][1],
xwl_output->refresh / 1000.0, 0, 0);
if (!rr_modes[*count])
goto err;
(*count)++;
}
}
/* Add logical output mode */
rr_modes[*count] = xwayland_cvt(width, height, xwl_output->refresh / 1000.0, 0, 0);
if (!rr_modes[*count])
goto err;
*logical_mode = (*count)++;
if (!xwl_screen_has_resolution_change_emulation(xwl_screen) && !xwl_screen->force_xrandr_emulation)
return rr_modes;
/* Add fake modes */
for (i = 0; i < ARRAY_SIZE(xwl_output_fake_modes); i++) {
/* Skip actual output mode, already added */
for (; i < ARRAY_SIZE(xwl_output_fake_modes); i++) {
/* Skip logical output mode, already added */
if (xwl_output_fake_modes[i][0] == width &&
xwl_output_fake_modes[i][1] == height)
continue;
/* Skip native output mode, already added */
if (xwl_output_fake_modes[i][0] == xwl_output->mode_width &&
xwl_output_fake_modes[i][1] == xwl_output->mode_height)
continue;
/* Skip modes which are too big, avoid downscaling */
if (xwl_output_fake_modes[i][0] > width ||
xwl_output_fake_modes[i][1] > height)
if (xwl_output_fake_modes[i][0] > max(width, xwl_output->mode_width) ||
xwl_output_fake_modes[i][1] > max(height, xwl_output->mode_height))
continue;
rr_modes[*count] = xwayland_cvt(xwl_output_fake_modes[i][0],
@ -407,13 +473,17 @@ xwl_output_find_mode(struct xwl_output *xwl_output,
/* width & height -1 means we want the actual output mode */
if (width == -1 && height == -1) {
if (xwl_output->mode_width > 0 && xwl_output->mode_height > 0) {
/* If running rootful, use the current mode size to search for the mode */
if (xwl_output == xwl_output->xwl_screen->fixed_output &&
xwl_output->mode_width > 0 && xwl_output->mode_height > 0) {
/* If running rootful, use the current fixed size to search for the mode */
width = xwl_output->mode_width;
height = xwl_output->mode_height;
}
else if (output->modes) {
/* else return the mode at first idx 0 */
else {
output_get_logical_mode(xwl_output, &width, &height);
if (output->numModes &&
(width <= 0 || height <= 0))
return output->modes[0];
}
}
@ -449,10 +519,17 @@ xwl_output_randr_emu_prop(struct xwl_screen *xwl_screen, ClientPtr client,
if (!emulated_mode)
continue;
prop->rects[index][0] = xwl_output->x;
prop->rects[index][1] = xwl_output->y;
prop->rects[index][0] = xwl_output->logical_x;
prop->rects[index][1] = xwl_output->logical_y;
if (xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180)) {
prop->rects[index][2] = emulated_mode->width;
prop->rects[index][3] = emulated_mode->height;
} else {
prop->rects[index][2] = emulated_mode->height;
prop->rects[index][3] = emulated_mode->width;
}
index++;
}
@ -583,6 +660,7 @@ xwl_output_set_emulated_mode(struct xwl_output *xwl_output, ClientPtr client,
RRModePtr mode, Bool from_vidmode)
{
int old_emulated_width, old_emulated_height;
int logical_width, logical_height;
int new_emulated_width, new_emulated_height;
DebugF("XWAYLAND: xwl_output_set_emulated_mode from %s: %dx%d\n",
@ -592,8 +670,9 @@ xwl_output_set_emulated_mode(struct xwl_output *xwl_output, ClientPtr client,
xwl_output_get_emulated_root_size(xwl_output, client,
&old_emulated_width, &old_emulated_height);
/* modes[0] is the actual (not-emulated) output mode */
if (mode == xwl_output->randr_output->modes[0])
/* Skip the logical (not-emulated) output mode */
output_get_logical_mode(xwl_output, &logical_width, &logical_height);
if (mode->mode.width == logical_width && mode->mode.height == logical_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);
@ -632,31 +711,24 @@ apply_output_change(struct xwl_output *xwl_output)
{
struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
struct xwl_output *it;
int mode_width, mode_height, count;
int width = 0, height = 0, has_this_output = 0;
int logical_width, logical_height, count, has_this_output = 0;
RRModePtr *randr_modes;
int logical_mode;
/* Clear out the "done" received flags */
xwl_output->wl_output_done = FALSE;
xwl_output->xdg_output_done = FALSE;
/* When we have received an xdg-output for the mode size we might need to
* rotate back the stored logical size it provided.
*/
if (xwl_output->xdg_output == NULL
|| xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180)) {
mode_width = xwl_output->width;
mode_height = xwl_output->height;
} else {
mode_width = xwl_output->height;
mode_height = xwl_output->width;
}
output_get_logical_mode(xwl_output, &logical_width, &logical_height);
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);
randr_modes = output_get_rr_modes(xwl_output, logical_width, logical_height,
&count, &logical_mode);
RROutputSetModes(xwl_output->randr_output, randr_modes, count, 1);
RRCrtcNotify(xwl_output->randr_crtc, randr_modes[0],
xwl_output->x, xwl_output->y,
RRCrtcNotify(xwl_output->randr_crtc, randr_modes[logical_mode],
xwl_output->logical_x, xwl_output->logical_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.
@ -664,6 +736,7 @@ apply_output_change(struct xwl_output *xwl_output)
free(randr_modes);
}
logical_width = logical_height = 0;
xorg_list_for_each_entry(it, &xwl_screen->output_list, link) {
/* output done event is sent even when some property
* of output is changed. That means that we may already
@ -672,20 +745,20 @@ apply_output_change(struct xwl_output *xwl_output)
if (it == xwl_output)
has_this_output = 1;
output_get_new_size(it, &width, &height);
output_get_new_size(it, &logical_width, &logical_height);
}
if (!has_this_output) {
xorg_list_append(&xwl_output->link, &xwl_screen->output_list);
/* we did not check this output for new screen size, do it now */
output_get_new_size(xwl_output, &width, &height);
output_get_new_size(xwl_output, &logical_width, &logical_height);
--xwl_screen->expecting_event;
}
if (xwl_screen->fixed_output == NULL)
update_screen_size(xwl_screen, width, height);
update_screen_size(xwl_screen, logical_width, logical_height);
else
RRTellChanged(xwl_screen->screen);
@ -791,8 +864,8 @@ xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output
{
struct xwl_output *xwl_output = data;
xwl_output->x = x;
xwl_output->y = y;
xwl_output->logical_x = x;
xwl_output->logical_y = y;
}
static void
@ -801,8 +874,8 @@ xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output,
{
struct xwl_output *xwl_output = data;
xwl_output->width = width;
xwl_output->height = height;
xwl_output->logical_w = width;
xwl_output->logical_h = height;
}
static void
@ -1145,6 +1218,8 @@ xwl_screen_init_output(struct xwl_screen *xwl_screen)
if (!RRScreenInit(xwl_screen->screen))
return FALSE;
xwl_screen->screen->ConstrainCursorHarder = NULL;
RRScreenSetSizeRange(xwl_screen->screen, 16, 16, 32767, 32767);
rp = rrGetScrPriv(xwl_screen->screen);

View file

@ -56,8 +56,8 @@ struct xwl_output {
struct wl_output *output;
struct zxdg_output_v1 *xdg_output;
uint32_t server_output_id;
int32_t x, y, width, height, refresh, scale;
int32_t mode_width, mode_height;
int32_t logical_x, logical_y, logical_w, logical_h;
int32_t mode_width, mode_height, refresh, scale;
double xscale; /* Effective scale, can be fractional */
Rotation rotation;
Bool wl_output_done;
@ -111,6 +111,8 @@ 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);
void output_get_logical_extents(struct xwl_output *xwl_output, int *width, int *height);
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,

View file

@ -134,7 +134,7 @@ 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)
if (xwl_output->logical_x == 0 && xwl_output->logical_y == 0)
return xwl_output;
}

View file

@ -308,7 +308,7 @@ xwlVidModeSetViewPort(ScreenPtr pScreen, int x, int y)
return FALSE;
/* Support only default viewport */
return (x == xwl_output->x && y == xwl_output->y);
return (x == xwl_output->logical_x && y == xwl_output->logical_y);
}
static Bool
@ -321,8 +321,8 @@ xwlVidModeGetViewPort(ScreenPtr pScreen, int *x, int *y)
if (xwl_output == NULL)
return FALSE;
*x = xwl_output->x;
*y = xwl_output->y;
*x = xwl_output->logical_x;
*y = xwl_output->logical_y;
return TRUE;
}

View file

@ -443,30 +443,35 @@ xwl_window_enable_viewport_for_output(struct xwl_window *xwl_window,
struct xwl_emulated_mode *emulated_mode)
{
struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
int width, height;
int width, height, logical_width, logical_height;
if (xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180)) {
width = emulated_mode->width / xwl_screen->global_surface_scale;
height = emulated_mode->height / xwl_screen->global_surface_scale;
} else {
width = emulated_mode->height / xwl_screen->global_surface_scale;
height = emulated_mode->width / xwl_screen->global_surface_scale;
}
output_get_logical_extents(xwl_output, &logical_width, &logical_height);
if (!xwl_window_has_viewport_enabled(xwl_window)) {
DebugF("XWAYLAND: enabling viewport %dx%d -> %dx%d\n",
emulated_mode->width, emulated_mode->height,
xwl_output->width, xwl_output->height);
width, height, logical_width, logical_height);
xwl_window->viewport = wp_viewporter_get_viewport(xwl_window->xwl_screen->viewporter,
xwl_window->surface);
}
width = emulated_mode->width / xwl_screen->global_surface_scale;
height = emulated_mode->height / xwl_screen->global_surface_scale;
wp_viewport_set_source(xwl_window->viewport,
wl_fixed_from_int(0),
wl_fixed_from_int(0),
wl_fixed_from_int(width),
wl_fixed_from_int(height));
wp_viewport_set_destination(xwl_window->viewport,
xwl_output->width,
xwl_output->height);
logical_width, logical_height);
xwl_window->viewport_scale_x = (float) width / xwl_output->width;
xwl_window->viewport_scale_y = (float) height / xwl_output->height;
xwl_window->viewport_scale_x = (float) width / logical_width;
xwl_window->viewport_scale_y = (float) height / logical_height;
xwl_window_set_input_region(xwl_window, wInputShape(xwl_window->toplevel));
}
@ -524,7 +529,7 @@ is_output_suitable_for_fullscreen(struct xwl_output *xwl_output)
if (xwl_output == NULL)
return FALSE;
if (xwl_output->width == 0 || xwl_output->height == 0)
if (xwl_output->logical_w == 0 || xwl_output->logical_h == 0)
return FALSE;
return TRUE;
@ -602,14 +607,24 @@ xwl_window_should_enable_viewport(struct xwl_window *xwl_window,
* 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) {
int emulated_width, emulated_height;
emulated_mode = xwl_output_get_emulated_mode_for_client(xwl_output, owner);
if (!emulated_mode)
continue;
if (drawable->x == xwl_output->x &&
drawable->y == xwl_output->y &&
drawable->width == emulated_mode->width &&
drawable->height == emulated_mode->height) {
if (xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180)) {
emulated_width = emulated_mode->width;
emulated_height = emulated_mode->height;
} else {
emulated_width = emulated_mode->height;
emulated_height = emulated_mode->width;
}
if (drawable->x == xwl_output->logical_x &&
drawable->y == xwl_output->logical_y &&
drawable->width == emulated_width &&
drawable->height == emulated_height) {
memcpy(emulated_mode_ret, emulated_mode, sizeof(struct xwl_emulated_mode));
*xwl_output_ret = xwl_output;