From 27fa951e1adcdfc53fecf5aa88ac1f77357955ce Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 13 Jul 2012 10:29:15 +0000 Subject: [PATCH] Add back support for letting the client reconfigure the screen, this time with all the RandR 1.2 multi-head goodness. Tested with Xvnc, but should also work with libvnc.so, although it will only work for simpler cases there. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4933 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- unix/xserver/hw/vnc/XserverDesktop.cc | 225 +++++++++++++++++++++++++- unix/xserver/hw/vnc/XserverDesktop.h | 4 + unix/xserver/hw/vnc/xf86vncModule.cc | 14 +- unix/xserver/hw/vnc/xvnc.cc | 75 ++++++--- 4 files changed, 284 insertions(+), 34 deletions(-) diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 45aa468a..921fd112 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -233,9 +233,7 @@ ScreenSet XserverDesktop::computeScreenLayout() { ScreenSet layout; -#ifndef RANDR - layout.add_screen(Screen(0, 0, 0, pScreen->width, pScreen->height, 0)); -#else +#ifdef RANDR rrScrPrivPtr rp = rrGetScrPriv(pScreen); OutputIdMap newIdMap; @@ -280,9 +278,39 @@ ScreenSet XserverDesktop::computeScreenLayout() outputIdMap = newIdMap; #endif + /* + * Make sure we have something to display. Hopefully it's just temporary + * that we have no active outputs... + */ + if (layout.num_screens() == 0) + layout.add_screen(Screen(0, 0, 0, pScreen->width, pScreen->height, 0)); + return layout; } +#ifdef RANDR + +extern RRModePtr vncRandRModeGet(int width, int height); + +RRModePtr XserverDesktop::findRandRMode(RROutputPtr output, int width, int height) +{ + RRModePtr mode; + + for (int i;i < output->numModes;i++) { + if ((output->modes[i]->mode.width == width) && + (output->modes[i]->mode.height == height)) + return output->modes[i]; + } + + mode = vncRandRModeGet(width, height); + if (mode != NULL) + return mode; + + return NULL; +} + +#endif + char* XserverDesktop::substitute(const char* varName) { if (strcmp(varName, "$$") == 0) { @@ -790,14 +818,197 @@ void XserverDesktop::clientCutText(const char* str, int len) vncClientCutText(str, len); } -extern unsigned int vncSetScreenLayout(ScreenPtr pScreen, - int fb_width, int fb_height, - const rfb::ScreenSet& layout); +extern RROutputPtr vncRandROutputCreate(ScreenPtr pScreen); unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout) { - return vncSetScreenLayout(pScreen, fb_width, fb_height, layout); +#ifndef RANDR + return rfb::resultProhibited; +#else + int availableOutputs; + Bool ret; + + rrScrPrivPtr rp = rrGetScrPriv(pScreen); + + /* + * First check that we don't have any active clone modes. That's just + * too messy to deal with. + */ + for (int i = 0;i < rp->numCrtcs;i++) { + if (rp->crtcs[i]->numOutputs > 1) { + vlog.error("Clone mode active. Refusing to touch screen layout."); + return rfb::resultInvalid; + } + } + + /* Next count how many useful outputs we have... */ + availableOutputs = 0; + for (int i = 0;i < rp->numOutputs;i++) { + if (rp->outputs[i]->crtc != NULL) + availableOutputs++; + } + + /* Try to create more outputs if needed... (only works on Xvnc) */ + if (layout.num_screens() > availableOutputs) { + for (int i = 0;i < (layout.num_screens() - availableOutputs);i++) { + RROutputPtr output; + output = vncRandROutputCreate(pScreen); + if (output == NULL) { + vlog.error("Unable to create more screens, as needed by the new client layout."); + return rfb::resultInvalid; + } + } + } + + /* First we might need to resize the screen */ + if ((fb_width != pScreen->width) || (fb_height == pScreen->height)) { + /* Try to retain DPI when we resize */ + ret = RRScreenSizeSet(pScreen, fb_width, fb_height, + pScreen->mmWidth * fb_width / pScreen->width, + pScreen->mmHeight * fb_height / pScreen->height); + if (!ret) { + vlog.error("Failed to resize screen to %dx%d", fb_width, fb_height); + return rfb::resultInvalid; + } + } + + /* Next, reconfigure all known outputs, and turn off the other ones */ + for (int i = 0;i < rp->numOutputs;i++) { + RROutputPtr output; + RRCrtcPtr crtc; + RRModePtr mode; + + ScreenSet::const_iterator iter; + + output = rp->outputs[i]; + crtc = output->crtc; + + /* Useful output? */ + if (crtc == NULL) + continue; + + /* Known? */ + if (outputIdMap.count(output) == 0) + continue; + + /* Find the corresponding screen... */ + for (iter = layout.begin();iter != layout.end();++iter) { + if (iter->id == outputIdMap[output]) + break; + } + + /* Missing? */ + if (iter == layout.end()) { + /* Disable and move on... */ + ret = RRCrtcSet(crtc, NULL, crtc->x, crtc->y, crtc->rotation, + crtc->numOutputs, crtc->outputs); + if (!ret) { + vlog.error("Failed to disable unused CRTC for output '%s'", + output->name); + return rfb::resultInvalid; + } + outputIdMap.erase(output); + continue; + } + + /* Need to switch mode? */ + if ((crtc->mode->mode.width == iter->dimensions.width()) && + (crtc->mode->mode.height == iter->dimensions.height())) + mode = crtc->mode; + else { + mode = findRandRMode(output, iter->dimensions.width(), + iter->dimensions.height()); + if (!ret) { + vlog.error("Failed to find a suitable mode for %dx%d for output '%s'", + iter->dimensions.width(), iter->dimensions.height(), + output->name); + return rfb::resultInvalid; + } + } + + /* Reconfigure new mode and position */ + ret = RRCrtcSet(crtc, mode, iter->dimensions.tl.x, iter->dimensions.tl.y, + crtc->rotation, crtc->numOutputs, crtc->outputs); + if (!ret) { + vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d", + output->name, + iter->dimensions.width(), iter->dimensions.height(), + iter->dimensions.tl.x, iter->dimensions.tl.y); + return rfb::resultInvalid; + } + } + + /* Finally, allocate new outputs for new screens */ + ScreenSet::const_iterator iter; + for (iter = layout.begin();iter != layout.end();++iter) { + OutputIdMap::const_iterator oi; + + RROutputPtr output; + RRCrtcPtr crtc; + RRModePtr mode; + + int i; + + /* Does this screen have an output already? */ + for (oi = outputIdMap.begin();oi != outputIdMap.end();++oi) { + if (oi->second == iter->id) + break; + } + + if (oi != outputIdMap.end()) + continue; + + /* Find an unused output */ + for (i = 0;i < rp->numOutputs;i++) { + output = rp->outputs[i]; + crtc = output->crtc; + + /* Useful output? */ + if (crtc == NULL) + continue; + + /* In use? */ + if (outputIdMap.count(output) == 1) + continue; + + break; + } + + /* Shouldn't happen */ + if (i == rp->numOutputs) + return rfb::resultInvalid; + + mode = findRandRMode(output, iter->dimensions.width(), + iter->dimensions.height()); + if (!ret) { + vlog.error("Failed to find a suitable mode for %dx%d for output '%s'", + iter->dimensions.width(), iter->dimensions.height(), + output->name); + return rfb::resultInvalid; + } + + /* + * Make sure we already have an entry for this, or + * computeScreenLayout() will think it is a brand new output and + * assign it a random id. + */ + outputIdMap[output] = iter->id; + + /* Reconfigure new mode and position */ + ret = RRCrtcSet(crtc, mode, iter->dimensions.tl.x, iter->dimensions.tl.y, + crtc->rotation, crtc->numOutputs, crtc->outputs); + if (!ret) { + vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d", + output->name, + iter->dimensions.width(), iter->dimensions.height(), + iter->dimensions.tl.x, iter->dimensions.tl.y); + return rfb::resultInvalid; + } + } + + return rfb::resultSuccess; +#endif } void XserverDesktop::grabRegion(const rfb::Region& region) diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index da107bc8..fb247b04 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -128,6 +128,10 @@ public: private: void setColourMapEntries(int firstColour, int nColours); rfb::ScreenSet computeScreenLayout(); +#ifdef RANDR + RRModePtr findRandRMode(RROutputPtr output, int width, int height); +#endif + ScreenPtr pScreen; InputDevice *inputDevice; rfb::VNCServerST* server; diff --git a/unix/xserver/hw/vnc/xf86vncModule.cc b/unix/xserver/hw/vnc/xf86vncModule.cc index b5fbe469..547ab06f 100644 --- a/unix/xserver/hw/vnc/xf86vncModule.cc +++ b/unix/xserver/hw/vnc/xf86vncModule.cc @@ -35,6 +35,9 @@ extern "C" { #define new c_new #include "xf86.h" #include "xf86Module.h" +#ifdef RANDR +#include "randrstr.h" +#endif /* RANDR */ #undef class #undef private #undef bool @@ -101,9 +104,12 @@ static void vncExtensionInitWithParams(INITARGS) } } -unsigned int vncSetScreenLayout(ScreenPtr pScreen, - int fb_width, int fb_height, - const rfb::ScreenSet& layout) +RRModePtr vncRandRModeGet(int width, int height) { - return rfb::resultProhibited; + return NULL; +} + +RROutputPtr vncRandROutputCreate(ScreenPtr pScreen) +{ + return NULL; } diff --git a/unix/xserver/hw/vnc/xvnc.cc b/unix/xserver/hw/vnc/xvnc.cc index adc61b21..a65fc49b 100644 --- a/unix/xserver/hw/vnc/xvnc.cc +++ b/unix/xserver/hw/vnc/xvnc.cc @@ -1013,7 +1013,7 @@ xf86SetRootClip (ScreenPtr pScreen, Bool enable) FlushAllOutput (); } -static RRModePtr vncRandRModeGet(int width, int height); +RRModePtr vncRandRModeGet(int width, int height); static Bool vncRandRScreenSetSize(ScreenPtr pScreen, CARD16 width, CARD16 height, @@ -1166,12 +1166,13 @@ static void vncRandRModeDestroy(ScreenPtr pScreen, RRModePtr mode) /* We haven't allocated anything so nothing to destroy */ } -static const char vncRandROutputName[] = "VNC"; - static const int vncRandRWidths[] = { 1920, 1920, 1600, 1680, 1400, 1360, 1280, 1280, 1280, 1280, 1024, 800, 640 }; static const int vncRandRHeights[] = { 1200, 1080, 1200, 1050, 1050, 768, 1024, 960, 800, 720, 768, 600, 480 }; -static RRModePtr vncRandRModeGet(int width, int height) +static int vncRandRIndex = 0; + +/* This is a global symbol since XserverDesktop also uses it */ +RRModePtr vncRandRModeGet(int width, int height) { xRRModeInfo modeInfo; char name[100]; @@ -1193,29 +1194,31 @@ static RRModePtr vncRandRModeGet(int width, int height) return mode; } -static Bool vncRandRInit(ScreenPtr pScreen) +static RRCrtcPtr vncRandRCrtcCreate(ScreenPtr pScreen) { RRCrtcPtr crtc; RROutputPtr output; RRModePtr mode; + char name[100]; - if (!RRInit()) - return FALSE; - - /* These are completely arbitrary */ - RRScreenSetSizeRange(pScreen, 32, 32, 32768, 32768); - - /* Start with a single CRTC with a single output */ - crtc = RRCrtcCreate(pScreen, 0 /* id */); + /* First we create the CRTC... */ + crtc = RRCrtcCreate(pScreen, NULL); /* We don't actually support gamma, but xrandr complains when it is missing */ RRCrtcGammaSetSize (crtc, 256); - output = RROutputCreate(pScreen, vncRandROutputName, - sizeof(vncRandROutputName), NULL); + /* Then we create a dummy output for it... */ + sprintf(name, "VNC-%d", vncRandRIndex); + vncRandRIndex++; + + output = RROutputCreate(pScreen, name, strlen(name), NULL); + RROutputSetCrtcs(output, &crtc, 1); RROutputSetConnection(output, RR_Connected); + /* Make sure the CRTC has this output set */ + RRCrtcNotify(crtc, NULL, 0, 0, RR_Rotate_0, 1, &output); + /* Populate a list of default modes */ RRModePtr modes[sizeof(vncRandRWidths)/sizeof(*vncRandRWidths)]; int num_modes; @@ -1231,23 +1234,49 @@ static Bool vncRandRInit(ScreenPtr pScreen) RROutputSetModes(output, modes, num_modes, 0); + return crtc; +} + +/* Used from XserverDesktop when it needs more outputs... */ +RROutputPtr vncRandROutputCreate(ScreenPtr pScreen) +{ + RRCrtcPtr crtc; + + crtc = vncRandRCrtcCreate(pScreen); + if (crtc == NULL) + return NULL; + + return crtc->outputs[0]; +} + +static Bool vncRandRInit(ScreenPtr pScreen) +{ + RRCrtcPtr crtc; + RRModePtr mode; + + if (!RRInit()) + return FALSE; + + /* These are completely arbitrary */ + RRScreenSetSizeRange(pScreen, 32, 32, 32768, 32768); + + /* + * Start with a single CRTC with a single output. More will be + * allocated as needed... + */ + crtc = vncRandRCrtcCreate(pScreen); + /* Make sure the current screen size is the active mode */ mode = vncRandRModeGet(pScreen->width, pScreen->height); if (mode == NULL) return FALSE; - RRCrtcNotify(crtc, mode, 0, 0, RR_Rotate_0, 1, &output); + RRCrtcNotify(crtc, mode, 0, 0, RR_Rotate_0, + crtc->numOutputs, crtc->outputs); return TRUE; } -unsigned int vncSetScreenLayout(ScreenPtr pScreen, - int fb_width, int fb_height, - const rfb::ScreenSet& layout) -{ - return rfb::resultProhibited; -} - #endif static Bool -- 2.39.5