Revision: 4933
          http://tigervnc.svn.sourceforge.net/tigervnc/?rev=4933&view=rev
Author:   ossman_
Date:     2012-07-13 10:29:15 +0000 (Fri, 13 Jul 2012)
Log Message:
-----------
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.

Modified Paths:
--------------
    trunk/unix/xserver/hw/vnc/XserverDesktop.cc
    trunk/unix/xserver/hw/vnc/XserverDesktop.h
    trunk/unix/xserver/hw/vnc/xf86vncModule.cc
    trunk/unix/xserver/hw/vnc/xvnc.cc

Modified: trunk/unix/xserver/hw/vnc/XserverDesktop.cc
===================================================================
--- trunk/unix/xserver/hw/vnc/XserverDesktop.cc 2012-07-12 12:40:50 UTC (rev 
4932)
+++ trunk/unix/xserver/hw/vnc/XserverDesktop.cc 2012-07-13 10:29:15 UTC (rev 
4933)
@@ -233,9 +233,7 @@
 {
   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 @@
   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 @@
   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)

Modified: trunk/unix/xserver/hw/vnc/XserverDesktop.h
===================================================================
--- trunk/unix/xserver/hw/vnc/XserverDesktop.h  2012-07-12 12:40:50 UTC (rev 
4932)
+++ trunk/unix/xserver/hw/vnc/XserverDesktop.h  2012-07-13 10:29:15 UTC (rev 
4933)
@@ -128,6 +128,10 @@
 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;

Modified: trunk/unix/xserver/hw/vnc/xf86vncModule.cc
===================================================================
--- trunk/unix/xserver/hw/vnc/xf86vncModule.cc  2012-07-12 12:40:50 UTC (rev 
4932)
+++ trunk/unix/xserver/hw/vnc/xf86vncModule.cc  2012-07-13 10:29:15 UTC (rev 
4933)
@@ -35,6 +35,9 @@
 #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 @@
 }
 }
 
-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;
+}

Modified: trunk/unix/xserver/hw/vnc/xvnc.cc
===================================================================
--- trunk/unix/xserver/hw/vnc/xvnc.cc   2012-07-12 12:40:50 UTC (rev 4932)
+++ trunk/unix/xserver/hw/vnc/xvnc.cc   2012-07-13 10:29:15 UTC (rev 4933)
@@ -1013,7 +1013,7 @@
     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 @@
     /* 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 @@
     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;
+    /* First we create the CRTC... */
+    crtc = RRCrtcCreate(pScreen, NULL);
 
-    /* These are completely arbitrary */
-    RRScreenSetSizeRange(pScreen, 32, 32, 32768, 32768);
-
-    /* Start with a single CRTC with a single output */
-    crtc = RRCrtcCreate(pScreen, 0 /* id */);
-
     /* 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 @@
 
     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

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and 
threat landscape has changed and how IT managers can respond. Discussions 
will include endpoint security, mobile security and the latest in malware 
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
Tigervnc-commits mailing list
Tigervnc-commits@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tigervnc-commits

Reply via email to