Xrandr doesn’t scale screen under Cygwin/X and Xming

cygwin;x-serverxmingxrandr

The preamble:

I have a very specific remote X client, that needs fixed resolution. 1280×1024, to say. And my notebook has 1440×900. And Windows aboard. So, the idea is to scale the output of the client to fit 900px output. Panning is not an option.

The first approach was Xserver, running under VirtualBox. But that didn't work as I wanted, scaling of the VB window only truncated the screen, with scrolling bars.

So, I tried Xming. Created the screen using this args:

-screen 0 640x512

It successfully started. Connected my X client, well, the image was truncated, but that was expected. Than I ran xrandr for created display and got the output:

$ xrandr
xrandr: Failed to get size of gamma for output default
Screen 0: minimum 0 x 0, current 640 x 512, maximum 32768 x 32768
default connected primary 640x512+0+0 0mm x 0 mm
   640x512        0.00*

Ok, a try to change the scale:

$ xrandr --output default --scale 2x2
xrandr: Failed to get size of gamma for output default
X Error of failed request: BadValue (integer parameter out of range for operation)
  Major opcode of failed request:  139 (RANDR)
  Minor opcode of failed request:  26 (RRSetCrtcTransform)
  Value in failed request:  0x3e
  Serial number of failed request:  21
  Current serial number in output stream:  22

And the scale didn't change. But the size of screen extended, showing all the client data inbound. If query the config again:

$ xrandr
xrandr: Failed to get size of gamma for output default
Screen 0: minimum 0 x 0, current 1280 x 1024, maximum 32768 x 32768
default connected primary 1280x1024+0+0 0mm x 0 mm
   1280x1024      0.00*

I tried both Xming and Cygwin/X, the same result.
"Value in failed requested" doesn't depend on requested scale value, and is always 0x3e.

New dimensions of the screen are always a requested scale multiplied by initial dimensions if the requested scale is bigger than 1. Otherwise, nothing changes, while the error message is still there.
If new dimensions are bigger than my client dimensions, all spare pixels are black.

I've found here
that such Xserver output is caused by xf86-video-vesa and older nvidia drivers. But I don't know if that's my case, and how to change something about driver inside Xming or Cygwin/X. Windows host drivers were updated, anyway.

I tried also x11vnc inside Cygwin, and scaling worked, both on side of x11vnc and vnc client. But that was slow, and is too tricky.

So the question is about xrandr, why that doesn't work as expected and declared in man pages?


Update:

Well, I went deeper, and looked through sources of RANDR extension. And the only case I've found, that produces BadValue in the reply to RRSetCrtcTransform request, is False value of boolean variable crtc->transforms, that is meant to be an indicator of driver transform support:

 /*
 * Set the pending CRTC transformation
 */

int
RRCrtcTransformSet(RRCrtcPtr crtc,
                   PictTransformPtr transform,
                   struct pixman_f_transform *f_transform,
                   struct pixman_f_transform *f_inverse,
                   char *filter_name,
                   int filter_len, xFixed * params, int nparams)
{
    PictFilterPtr filter = NULL;
    int width = 0, height = 0;

    if (!crtc->transforms)
        return BadValue;

    if (filter_len) {
        filter = PictureFindFilter(crtc->pScreen, filter_name, filter_len);
        if (!filter)
            return BadName;
        if (filter->ValidateParams) {
            if (!filter->ValidateParams(crtc->pScreen, filter->id,
                                        params, nparams, &width, &height))
                return BadMatch;
        }
        else {
            width = filter->width;
            height = filter->height;
        }
    }
    else {
        if (nparams)
            return BadMatch;
    }
    if (!RRTransformSetFilter(&crtc->client_pending_transform,
                              filter, params, nparams, width, height))
        return BadAlloc;

    crtc->client_pending_transform.transform = *transform;
    crtc->client_pending_transform.f_transform = *f_transform;
    crtc->client_pending_transform.f_inverse = *f_inverse;
    return Success;
}

The only line that modifies that value is a setter function:

/*
 * Set whether transforms are allowed on a CRTC
 */
void
RRCrtcSetTransformSupport(RRCrtcPtr crtc, Bool transforms)
{
    crtc->transforms = transforms;
}

But it is declared as export function in the header, and nobody calls it inside RANDR srcs.

Some further lookup shows that this value is reported in reply to the RRGetCrtcTransform request (line reply->hasTransforms = crtc->transforms;):

int
ProcRRGetCrtcTransform(ClientPtr client)
{
    REQUEST(xRRGetCrtcTransformReq);
    xRRGetCrtcTransformReply *reply;
    RRCrtcPtr crtc;
    int nextra;
    RRTransformPtr current, pending;
    char *extra;

    REQUEST_SIZE_MATCH(xRRGetCrtcTransformReq);
    VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess);

    pending = &crtc->client_pending_transform;
    current = &crtc->client_current_transform;

    nextra = (transform_filter_length(pending) +
              transform_filter_length(current));

    reply = calloc(1, sizeof(xRRGetCrtcTransformReply) + nextra);
    if (!reply)
        return BadAlloc;

    extra = (char *) (reply + 1);
    reply->type = X_Reply;
    reply->sequenceNumber = client->sequence;
    reply->length = bytes_to_int32(CrtcTransformExtra + nextra);

    reply->hasTransforms = crtc->transforms;

    transform_encode(client, &reply->pendingTransform, &pending->transform);
    extra += transform_filter_encode(client, extra,
                                     &reply->pendingNbytesFilter,
                                     &reply->pendingNparamsFilter, pending);

    transform_encode(client, &reply->currentTransform, &current->transform);
    extra += transform_filter_encode(client, extra,
                                     &reply->currentNbytesFilter,
                                     &reply->currentNparamsFilter, current);

    if (client->swapped) {
        swaps(&reply->sequenceNumber);
        swapl(&reply->length);
    }
    WriteToClient(client, sizeof(xRRGetCrtcTransformReply) + nextra, reply);
    free(reply);
    return Success;
}

But the request itself is produced by XRRGetCrtcTransform client function, and there is no way to get from its output the information about scaling support. The function simply ignores that value (rep.hasTransforms) in the reply:

Status
XRRGetCrtcTransform (Display    *dpy,
             RRCrtc crtc,
             XRRCrtcTransformAttributes **attributes)
{
    XExtDisplayInfo     *info = XRRFindDisplay(dpy);
    xRRGetCrtcTransformReply    rep;
    xRRGetCrtcTransformReq  *req;
    int             major_version, minor_version;
    XRRCrtcTransformAttributes  *attr;
    char            *extra = NULL, *e;
    int             p;

    *attributes = NULL;

    RRCheckExtension (dpy, info, False);

    if (!XRRQueryVersion (dpy, &major_version, &minor_version) ||
    !_XRRHasTransform (major_version, minor_version))
    {
    /* For pre-1.3 servers, just report identity matrices everywhere */
    rep.pendingTransform = identity;
    rep.pendingNbytesFilter = 0;
    rep.pendingNparamsFilter = 0;
    rep.currentTransform = identity;
    rep.currentNbytesFilter = 0;
    rep.currentNparamsFilter = 0;
    }
    else
    {
    LockDisplay (dpy);
    GetReq (RRGetCrtcTransform, req);
    req->reqType = info->codes->major_opcode;
    req->randrReqType = X_RRGetCrtcTransform;
    req->crtc = crtc;

    if (!_XReply (dpy, (xReply *) &rep, CrtcTransformExtra >> 2, xFalse))
    {
        rep.pendingTransform = identity;
        rep.pendingNbytesFilter = 0;
        rep.pendingNparamsFilter = 0;
        rep.currentTransform = identity;
        rep.currentNbytesFilter = 0;
        rep.currentNparamsFilter = 0;
    }
    else
    {
        int extraBytes = rep.length * 4 - CrtcTransformExtra;
        extra = Xmalloc (extraBytes);
        if (!extra) {
        _XEatDataWords (dpy, rep.length - (CrtcTransformExtra >> 2));
        UnlockDisplay (dpy);
        SyncHandle ();
        return False;
        }
        _XRead (dpy, extra, extraBytes);
    }

    UnlockDisplay (dpy);
    SyncHandle ();
    }

    attr = Xmalloc (sizeof (XRRCrtcTransformAttributes) +
            rep.pendingNparamsFilter * sizeof (XFixed) +
            rep.currentNparamsFilter * sizeof (XFixed) +
            rep.pendingNbytesFilter + 1 +
            rep.currentNbytesFilter + 1);

    if (!attr) {
    XFree (extra);
    return False;
    }
    XTransform_from_xRenderTransform (&attr->pendingTransform, &rep.pendingTransform);
    XTransform_from_xRenderTransform (&attr->currentTransform, &rep.currentTransform);

    attr->pendingParams = (XFixed *) (attr + 1);
    attr->currentParams = attr->pendingParams + rep.pendingNparamsFilter;
    attr->pendingFilter = (char *) (attr->currentParams + rep.currentNparamsFilter);
    attr->currentFilter = attr->pendingFilter + rep.pendingNbytesFilter + 1;

    e = extra;

    memcpy (attr->pendingFilter, e, rep.pendingNbytesFilter);
    attr->pendingFilter[rep.pendingNbytesFilter] = '\0';
    e += (rep.pendingNbytesFilter + 3) & ~3;
    for (p = 0; p < rep.pendingNparamsFilter; p++) {
    INT32   f;
    memcpy (&f, e, 4);
    e += 4;
    attr->pendingParams[p] = (XFixed) f;
    }
    attr->pendingNparams = rep.pendingNparamsFilter;

    memcpy (attr->currentFilter, e, rep.currentNbytesFilter);
    attr->currentFilter[rep.currentNbytesFilter] = '\0';
    e += (rep.currentNbytesFilter + 3) & ~3;
    for (p = 0; p < rep.currentNparamsFilter; p++) {
    INT32   f;
    memcpy (&f, e, 4);
    e += 4;
    attr->currentParams[p] = (XFixed) f;
    }
    attr->currentNparams = rep.currentNparamsFilter;

    if (extra)
    XFree (extra);
    *attributes = attr;

    return True;
}

At this moment here are the facts:

  • The X server driver inside Cygwin or Xming doesn't support transforms;
  • There is no way to get this information using XRandr client-side API, though the server-side sends such info (should it be considered as a defect?)

Ok, the reason is found, but how to make that driver support transforms?

Best Answer

I can't comment, so I have to give a solution:

Suggested solution: I would go back to VirtualBox. I may have to do this as well.

Comment: Was there any resolution on xrandr on cygwin?

With a 3000x2000 high dpi screen, old unix apps are not usable. I even tried the magnifier tool in Windows, but it is a bear.

Are there commercial x servers for windows that support app scaling? I have not found one yet.

Related Question