summaryrefslogtreecommitdiffstats
path: root/unix
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2012-07-13 10:29:15 +0000
committerPierre Ossman <ossman@cendio.se>2012-07-13 10:29:15 +0000
commit27fa951e1adcdfc53fecf5aa88ac1f77357955ce (patch)
treee5ed687132eed7136441341ae88b9ad38ed1c7c8 /unix
parent1d3be39bcd8ed3b061f2d354454fb7ed0f174b40 (diff)
downloadtigervnc-27fa951e1adcdfc53fecf5aa88ac1f77357955ce.tar.gz
tigervnc-27fa951e1adcdfc53fecf5aa88ac1f77357955ce.zip
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
Diffstat (limited to 'unix')
-rw-r--r--unix/xserver/hw/vnc/XserverDesktop.cc225
-rw-r--r--unix/xserver/hw/vnc/XserverDesktop.h4
-rw-r--r--unix/xserver/hw/vnc/xf86vncModule.cc14
-rw-r--r--unix/xserver/hw/vnc/xvnc.cc75
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