xserver/hw/xwayland/xwayland-vidmode.c
Olivier Fourdan b0cee5e703 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
2022-06-30 17:52:22 +02:00

507 lines
14 KiB
C

/*
* Copyright (c) 1999-2003 by The XFree86 Project, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of the copyright holder(s)
* and author(s) shall not be used in advertising or otherwise to promote
* the sale, use or other dealings in this Software without prior written
* authorization from the copyright holder(s) and author(s).
*/
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <X11/X.h>
#include "misc.h"
#include "os.h"
#include "extinit.h"
#ifdef XF86VIDMODE
#include "randrstr.h"
#include "vidmodestr.h"
#include "xwayland-screen.h"
#include "xwayland-vidmode.h"
static DevPrivateKeyRec xwlVidModePrivateKeyRec;
#define xwlVidModePrivateKey (&xwlVidModePrivateKeyRec)
/* Taken from xrandr, h sync frequency in KHz */
static double
mode_hsync(const xRRModeInfo *mode_info)
{
double rate;
if (mode_info->hTotal)
rate = (double) mode_info->dotClock / (double) mode_info->hTotal;
else
rate = 0.0;
return rate / 1000.0;
}
/* Taken from xrandr, v refresh frequency in Hz */
static double
mode_refresh(const xRRModeInfo *mode_info)
{
double rate;
double vTotal = mode_info->vTotal;
if (mode_info->modeFlags & RR_DoubleScan)
vTotal *= 2.0;
if (mode_info->modeFlags & RR_Interlace)
vTotal /= 2.0;
if (mode_info->hTotal > 0.0 && vTotal > 0.0)
rate = ((double) mode_info->dotClock /
((double) mode_info->hTotal * (double) vTotal));
else
rate = 0.0;
return rate;
}
static void
xwlRRModeToDisplayMode(RRModePtr rrmode, DisplayModePtr mode)
{
const xRRModeInfo *mode_info = &rrmode->mode;
mode->next = mode;
mode->prev = mode;
mode->name = "";
mode->VScan = 1;
mode->Private = NULL;
mode->HDisplay = mode_info->width;
mode->HSyncStart = mode_info->hSyncStart;
mode->HSyncEnd = mode_info->hSyncEnd;
mode->HTotal = mode_info->hTotal;
mode->HSkew = mode_info->hSkew;
mode->VDisplay = mode_info->height;
mode->VSyncStart = mode_info->vSyncStart;
mode->VSyncEnd = mode_info->vSyncEnd;
mode->VTotal = mode_info->vTotal;
mode->Flags = mode_info->modeFlags;
mode->Clock = mode_info->dotClock / 1000.0;
mode->VRefresh = mode_refresh(mode_info); /* Or RRVerticalRefresh() */
mode->HSync = mode_hsync(mode_info);
}
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_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
if (!xwl_output)
return NULL;
return xwl_output_find_mode(xwl_output, width, height);
}
static RRModePtr
xwlVidModeGetCurrentRRMode(ScreenPtr pScreen)
{
struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
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;
emulated_mode =
xwl_output_get_emulated_mode_for_client(xwl_output, GetCurrentClient());
if (emulated_mode) {
return xwl_output_find_mode(xwl_output,
emulated_mode->width,
emulated_mode->height);
} else {
return xwl_output_find_mode(xwl_output, -1, -1);
}
}
static Bool
xwlVidModeGetCurrentModeline(ScreenPtr pScreen, DisplayModePtr *mode, int *dotClock)
{
DisplayModePtr pMod;
RRModePtr rrmode;
pMod = dixLookupPrivate(&pScreen->devPrivates, xwlVidModePrivateKey);
if (pMod == NULL)
return FALSE;
rrmode = xwlVidModeGetCurrentRRMode(pScreen);
if (rrmode == NULL)
return FALSE;
xwlRRModeToDisplayMode(rrmode, pMod);
*mode = pMod;
if (dotClock != NULL)
*dotClock = pMod->Clock;
return TRUE;
}
static vidMonitorValue
xwlVidModeGetMonitorValue(ScreenPtr pScreen, int valtyp, int indx)
{
vidMonitorValue ret = { NULL, };
RRModePtr rrmode;
rrmode = xwlVidModeGetCurrentRRMode(pScreen);
if (rrmode == NULL)
return ret;
switch (valtyp) {
case VIDMODE_MON_VENDOR:
ret.ptr = XVENDORNAME;
break;
case VIDMODE_MON_MODEL:
ret.ptr = "XWAYLAND";
break;
case VIDMODE_MON_NHSYNC:
ret.i = 1;
break;
case VIDMODE_MON_NVREFRESH:
ret.i = 1;
break;
case VIDMODE_MON_HSYNC_LO:
case VIDMODE_MON_HSYNC_HI:
ret.f = mode_hsync(&rrmode->mode) * 100.0;
break;
case VIDMODE_MON_VREFRESH_LO:
case VIDMODE_MON_VREFRESH_HI:
ret.f = mode_refresh(&rrmode->mode) * 100.0;
break;
}
return ret;
}
static int
xwlVidModeGetDotClock(ScreenPtr pScreen, int Clock)
{
return Clock;
}
static int
xwlVidModeGetNumOfClocks(ScreenPtr pScreen, Bool *progClock)
{
/* We emulate a programmable clock, rather then a fixed set of clocks */
*progClock = TRUE;
return 0;
}
static Bool
xwlVidModeGetClocks(ScreenPtr pScreen, int *Clocks)
{
return FALSE; /* Programmable clock, no clock list */
}
/* GetFirstModeline and GetNextModeline are used from Xext/vidmode.c like this:
* if (pVidMode->GetFirstModeline(pScreen, &mode, &dotClock)) {
* do {
* ...
* if (...)
* break;
* } while (pVidMode->GetNextModeline(pScreen, &mode, &dotClock));
* }
* IOW our caller basically always loops over all the modes. There never is a
* return to the mainloop between GetFirstModeline and NextModeline calls where
* other parts of the server may change our state so we do not need to worry
* about xwl_output->randr_output->modes changing underneath us.
* Thus we can simply implement these two callbacks by storing the enumeration
* index in pVidMode->Next.
*/
static Bool
xwlVidModeGetNextModeline(ScreenPtr pScreen, DisplayModePtr *mode, int *dotClock)
{
struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
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 (pMod == NULL || pVidMode == NULL)
return FALSE;
index = (intptr_t)pVidMode->Next;
if (index >= xwl_output->randr_output->numModes)
return FALSE;
xwlRRModeToDisplayMode(xwl_output->randr_output->modes[index], pMod);
index++;
pVidMode->Next = (void *)index;
*mode = pMod;
if (dotClock != NULL)
*dotClock = pMod->Clock;
return TRUE;
}
static Bool
xwlVidModeGetFirstModeline(ScreenPtr pScreen, DisplayModePtr *mode, int *dotClock)
{
VidModePtr pVidMode;
intptr_t index = 0;
pVidMode = VidModeGetPtr(pScreen);
if (pVidMode == NULL)
return FALSE;
pVidMode->Next = (void *)index; /* 0 */
return xwlVidModeGetNextModeline(pScreen, mode, dotClock);
}
static Bool
xwlVidModeDeleteModeline(ScreenPtr pScreen, DisplayModePtr mode)
{
/* Unsupported */
return FALSE;
}
static Bool
xwlVidModeZoomViewport(ScreenPtr pScreen, int zoom)
{
/* Support only no zoom */
return (zoom == 1);
}
static Bool
xwlVidModeSetViewPort(ScreenPtr pScreen, int x, int y)
{
struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
struct xwl_output *xwl_output;
xwl_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
if (xwl_output == NULL)
return FALSE;
/* Support only default viewport */
return (x == xwl_output->x && y == xwl_output->y);
}
static Bool
xwlVidModeGetViewPort(ScreenPtr pScreen, int *x, int *y)
{
struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
struct xwl_output *xwl_output;
xwl_output = xwl_screen_get_fixed_or_first_output(xwl_screen);
if (xwl_output == NULL)
return FALSE;
*x = xwl_output->x;
*y = xwl_output->y;
return TRUE;
}
static Bool
xwlVidModeSwitchMode(ScreenPtr pScreen, DisplayModePtr mode)
{
struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
struct xwl_output *xwl_output;
RRModePtr rrmode;
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;
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;
}
static Bool
xwlVidModeLockZoom(ScreenPtr pScreen, Bool lock)
{
/* Unsupported for now, but pretend it works */
return TRUE;
}
static ModeStatus
xwlVidModeCheckModeForMonitor(ScreenPtr pScreen, DisplayModePtr mode)
{
RRModePtr rrmode;
rrmode = xwlVidModeGetRRMode(pScreen, mode->HDisplay, mode->VDisplay);
if (rrmode == NULL)
return MODE_ERROR;
/* Only support mode with the same HSync/VRefresh as we advertise */
if (mode->HSync == mode_hsync(&rrmode->mode) &&
mode->VRefresh == mode_refresh(&rrmode->mode))
return MODE_OK;
/* All the rest is unsupported - If we want to succeed, return MODE_OK instead */
return MODE_ONE_SIZE;
}
static ModeStatus
xwlVidModeCheckModeForDriver(ScreenPtr pScreen, DisplayModePtr mode)
{
RRModePtr rrmode;
rrmode = xwlVidModeGetRRMode(pScreen, mode->HDisplay, mode->VDisplay);
return rrmode ? MODE_OK : MODE_ERROR;
}
static void
xwlVidModeSetCrtcForMode(ScreenPtr pScreen, DisplayModePtr mode)
{
/* Unsupported */
return;
}
static Bool
xwlVidModeAddModeline(ScreenPtr pScreen, DisplayModePtr mode)
{
/* Unsupported */
return FALSE;
}
static int
xwlVidModeGetNumOfModes(ScreenPtr pScreen)
{
struct xwl_screen *xwl_screen = xwl_screen_get(pScreen);
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;
}
static Bool
xwlVidModeSetGamma(ScreenPtr pScreen, float red, float green, float blue)
{
/* Unsupported for now, but pretend it works */
return TRUE;
}
static Bool
xwlVidModeGetGamma(ScreenPtr pScreen, float *red, float *green, float *blue)
{
/* Unsupported for now, but pretend it works */
*red = *green = *blue = 1.0f;
return TRUE;
}
static Bool
xwlVidModeSetGammaRamp(ScreenPtr pScreen, int size, CARD16 *r, CARD16 *g, CARD16 *b)
{
/* Unsupported for now */
return FALSE;
}
static Bool
xwlVidModeGetGammaRamp(ScreenPtr pScreen, int size, CARD16 *r, CARD16 *g, CARD16 *b)
{
/* Unsupported for now */
return FALSE;
}
static int
xwlVidModeGetGammaRampSize(ScreenPtr pScreen)
{
/* Unsupported for now */
return 0;
}
static Bool
xwlVidModeInit(ScreenPtr pScreen)
{
VidModePtr pVidMode = NULL;
pVidMode = VidModeInit(pScreen);
if (!pVidMode)
return FALSE;
pVidMode->Flags = 0;
pVidMode->Next = NULL;
pVidMode->GetMonitorValue = xwlVidModeGetMonitorValue;
pVidMode->GetCurrentModeline = xwlVidModeGetCurrentModeline;
pVidMode->GetFirstModeline = xwlVidModeGetFirstModeline;
pVidMode->GetNextModeline = xwlVidModeGetNextModeline;
pVidMode->DeleteModeline = xwlVidModeDeleteModeline;
pVidMode->ZoomViewport = xwlVidModeZoomViewport;
pVidMode->GetViewPort = xwlVidModeGetViewPort;
pVidMode->SetViewPort = xwlVidModeSetViewPort;
pVidMode->SwitchMode = xwlVidModeSwitchMode;
pVidMode->LockZoom = xwlVidModeLockZoom;
pVidMode->GetNumOfClocks = xwlVidModeGetNumOfClocks;
pVidMode->GetClocks = xwlVidModeGetClocks;
pVidMode->CheckModeForMonitor = xwlVidModeCheckModeForMonitor;
pVidMode->CheckModeForDriver = xwlVidModeCheckModeForDriver;
pVidMode->SetCrtcForMode = xwlVidModeSetCrtcForMode;
pVidMode->AddModeline = xwlVidModeAddModeline;
pVidMode->GetDotClock = xwlVidModeGetDotClock;
pVidMode->GetNumOfModes = xwlVidModeGetNumOfModes;
pVidMode->SetGamma = xwlVidModeSetGamma;
pVidMode->GetGamma = xwlVidModeGetGamma;
pVidMode->SetGammaRamp = xwlVidModeSetGammaRamp;
pVidMode->GetGammaRamp = xwlVidModeGetGammaRamp;
pVidMode->GetGammaRampSize = xwlVidModeGetGammaRampSize;
return TRUE;
}
void
xwlVidModeExtensionInit(void)
{
int i;
Bool enabled = FALSE;
for (i = 0; i < screenInfo.numScreens; i++) {
if (xwlVidModeInit (screenInfo.screens[i]))
enabled = TRUE;
}
/* This means that the DDX doesn't want the vidmode extension enabled */
if (!enabled)
return;
if (!dixRegisterPrivateKey(xwlVidModePrivateKey, PRIVATE_SCREEN,
sizeof(DisplayModeRec)))
return;
VidModeAddExtension(FALSE);
}
#endif /* XF86VIDMODE */