]> source.dussan.org Git - tigervnc.git/commitdiff
Add back support for letting the client reconfigure the screen, this time
authorPierre Ossman <ossman@cendio.se>
Fri, 13 Jul 2012 10:29:15 +0000 (10:29 +0000)
committerPierre Ossman <ossman@cendio.se>
Fri, 13 Jul 2012 10:29:15 +0000 (10:29 +0000)
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
unix/xserver/hw/vnc/XserverDesktop.h
unix/xserver/hw/vnc/xf86vncModule.cc
unix/xserver/hw/vnc/xvnc.cc

index 45aa468a423034d5df28f52c8882ea36c5e862fd..921fd112b17802653bff6ae8db6414ddae577616 100644 (file)
@@ -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)
index da107bc8beec003044e1308932f87a16471fdf40..fb247b048e06f44ad1f61dbc27c20bce6fd65105 100644 (file)
@@ -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;
index b5fbe46923d9f24102428814818530a74bf6f4f7..547ab06fe67637981df2b0fc375772b586c94e58 100644 (file)
@@ -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;
 }
index adc61b214dbbcf9cbdb4feabf7123d728e342bcb..a65fc49bf19d14cb19e8486871586f4b80fb3c0c 100644 (file)
@@ -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