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@redhat.com>
Reviewed-by: Adam Jackson <ajax@redhat.com>
Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1338
This commit is contained in:
Olivier Fourdan 2022-04-26 15:28:50 +02:00
parent 5cc0319ca5
commit b0cee5e703
8 changed files with 309 additions and 63 deletions

View File

@ -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

View File

@ -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;
if (with_xrandr) {
snprintf(name, sizeof name, "XWAYLAND%d",
xwl_screen_get_next_output_serial(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);
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);
}
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);
if (xwl_output->randr_output)
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);
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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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