void XserverDesktop::setFramebuffer(int w, int h, void* fbptr, int stride)
{
+ ScreenSet layout;
+
width_ = w;
height_ = h;
data = (rdr::U8*)fbptr;
stride_ = stride;
- server->setPixelBuffer(this);
+ layout = computeScreenLayout();
+
+ server->setPixelBuffer(this, layout);
+}
+
+void XserverDesktop::refreshScreenLayout()
+{
+ server->setScreenLayout(computeScreenLayout());
+}
+
+ScreenSet XserverDesktop::computeScreenLayout()
+{
+ ScreenSet layout;
+
+#ifndef RANDR
+ layout.add_screen(Screen(0, 0, 0, pScreen->width, pScreen->height, 0));
+#else
+ rrScrPrivPtr rp = rrGetScrPriv(pScreen);
+ CrtcIdMap newIdMap;
+
+ for (int i = 0;i < rp->numCrtcs;i++) {
+ RRCrtcPtr crtc;
+
+ crtc = rp->crtcs[i];
+
+ /* Disabled? */
+ if (crtc->mode == NULL)
+ continue;
+
+ /* Known CRTC? */
+ if (crtcIdMap.count(crtc) == 1)
+ newIdMap[crtc] = crtcIdMap[crtc];
+ else {
+ rdr::U32 id;
+ CrtcIdMap::const_iterator iter;
+
+ while (true) {
+ id = rand();
+ for (iter = crtcIdMap.begin();iter != crtcIdMap.end();++iter) {
+ if (iter->second == id)
+ break;
+ }
+ if (iter == crtcIdMap.end())
+ break;
+ }
+
+ newIdMap[crtc] = id;
+ }
+
+ layout.add_screen(Screen(newIdMap[crtc], crtc->x, crtc->y,
+ crtc->mode->mode.width,
+ crtc->mode->mode.height,
+ 0));
+ }
+
+ /* Only keep the entries that are currently active */
+ crtcIdMap = newIdMap;
+#endif
+
+ return layout;
}
char* XserverDesktop::substitute(const char* varName)
vncClientCutText(str, len);
}
-#ifdef RANDR
+extern unsigned int vncSetScreenLayout(ScreenPtr pScreen,
+ int fb_width, int fb_height,
+ const rfb::ScreenSet& layout);
+
unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout)
{
- int i;
- Bool ret;
- RRScreenSizePtr pSize;
- RROutputPtr output;
- RRModePtr mode;
-
- // Make sure all RandR tables are properly populated
-#if XORG == 15
- ret = RRGetInfo(pScreen);
-#else
- ret = RRGetInfo(pScreen, FALSE);
-#endif
- if (!ret)
- return resultNoResources;
-
- // Register a new size, or get a reference to the existing one
- pSize = RRRegisterSize(pScreen, fb_width, fb_height,
- pScreen->mmWidth, pScreen->mmHeight);
- if (!pSize) {
- vlog.error("setScreenLayout: Could not get register new resolution");
- return resultNoResources;
- }
- ret = RRRegisterRate(pScreen, pSize, 60);
- if (!ret) {
- vlog.error("setScreenLayout: Could not register a rate for the resolution");
- return resultNoResources;
- }
-
- // Then we have to call RRGetInfo again for it to copy the RandR
- // 1.0 information to the 1.2 structures.
-#if XORG == 15
- ret = RRGetInfo(pScreen);
-#else
- ret = RRGetInfo(pScreen, FALSE);
-#endif
- if (!ret)
- return resultNoResources;
-
- // Go via RandR to set the resolution in order for X11 notifications
- // to be sent out properly. We currently only do RandR 1.0, but Xorg
- // has dropped support for that API. So we have to emulate it via the
- // same method ProcRRSetScreenConfig() uses.
- //
- // FIXME: This will cause setPixelBuffer() to be called, resulting in
- // an unnecessary ExtendedDesktopSize to be sent.
-
- // We'll just reconfigure the first output
- output = RRFirstOutput(pScreen);
- if (!output) {
- vlog.error("setScreenLayout: Could not get first output");
- return resultNoResources;
- }
-
- // Find first mode with matching size
- mode = NULL;
- for (i = 0;i < output->numModes;i++) {
- if ((output->modes[i]->mode.width == fb_width) &&
- (output->modes[i]->mode.height == fb_height)) {
- mode = output->modes[i];
- break;
- }
- }
- if (!mode) {
- vlog.error("setScreenLayout: Could not find a matching mode");
- return resultNoResources;
- }
-
- // Adjust screen size
- ret = RRScreenSizeSet(pScreen, fb_width, fb_height,
- pScreen->mmWidth, pScreen->mmHeight);
- if (!ret) {
- vlog.error("setScreenLayout: Could not adjust screen size");
- return resultNoResources;
- }
-
- // And then the CRTC
- ret = RRCrtcSet(output->crtc, mode, 0, 0, RR_Rotate_0, 1, &output);
- if (!ret) {
- vlog.error("setScreenLayout: Could not adjust CRTC");
- return resultNoResources;
- }
-
- // RandR 1.0 doesn't carry any screen layout information, so we need
- // to update that manually. This results in another unnecessary
- // ExtendedDesktopSize.
- server->setScreenLayout(layout);
-
- return resultSuccess;
+ return vncSetScreenLayout(pScreen, fb_width, fb_height, layout);
}
-#endif // RANDR
void XserverDesktop::grabRegion(const rfb::Region& region)
{
#include <dix-config.h>
#endif
+#include <map>
+
#include <rfb/SDesktop.h>
#include <rfb/HTTPServer.h>
#include <rfb/PixelBuffer.h>
#define class c_class
#include <scrnintstr.h>
#include <os.h>
+#ifdef RANDR
+#include <randrstr.h>
+#endif
#undef class
}
void blockUpdates();
void unblockUpdates();
void setFramebuffer(int w, int h, void* fbptr, int stride);
+ void refreshScreenLayout();
void setColormap(ColormapPtr cmap);
void setColourMapEntries(ColormapPtr pColormap, int ndef, xColorItem* pdef);
void bell();
virtual void keyEvent(rdr::U32 key, bool down);
virtual void clientCutText(const char* str, int len);
virtual rfb::Point getFbSize() { return rfb::Point(width(), height()); }
-#ifdef RANDR
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout);
-#endif
// rfb::PixelBuffer callbacks
virtual void grabRegion(const rfb::Region& r);
private:
void setColourMapEntries(int firstColour, int nColours);
+ rfb::ScreenSet computeScreenLayout();
ScreenPtr pScreen;
InputDevice *inputDevice;
rfb::VNCServerST* server;
void* queryConnectId;
rfb::CharArray queryConnectAddress;
rfb::CharArray queryConnectUsername;
+
+#ifdef RANDR
+ typedef std::map<RRCrtcPtr, rdr::U32> CrtcIdMap;
+ CrtcIdMap crtcIdMap;
+#endif
};
#endif
#endif
#ifdef RANDR
RRSetConfigProcPtr RandRSetConfig;
+ RRScreenSetSizeProcPtr RandRScreenSetSize;
+ RRCrtcSetProcPtr RandRCrtcSet;
#endif
} vncHooksScreenRec, *vncHooksScreenPtr;
#ifdef RANDR
static Bool vncHooksRandRSetConfig(ScreenPtr pScreen, Rotation rotation,
int rate, RRScreenSizePtr pSize);
+static Bool vncHooksRandRScreenSetSize(ScreenPtr pScreen,
+ CARD16 width, CARD16 height,
+ CARD32 mmWidth, CARD32 mmHeight);
+static Bool vncHooksRandRCrtcSet(ScreenPtr pScreen, RRCrtcPtr crtc,
+ RRModePtr mode, int x, int y,
+ Rotation rotation, int numOutputs,
+ RROutputPtr *outputs);
#endif
// GC "funcs"
rp = rrGetScrPriv(pScreen);
if (rp) {
vncHooksScreen->RandRSetConfig = rp->rrSetConfig;
+ vncHooksScreen->RandRScreenSetSize = rp->rrScreenSetSize;
+ vncHooksScreen->RandRCrtcSet = rp->rrCrtcSet;
}
#endif
#endif
#ifdef RANDR
if (rp) {
- rp->rrSetConfig = vncHooksRandRSetConfig;
+ /* Some RandR callbacks are optional */
+ if (rp->rrSetConfig)
+ rp->rrSetConfig = vncHooksRandRSetConfig;
+ if (rp->rrScreenSetSize)
+ rp->rrScreenSetSize = vncHooksRandRScreenSetSize;
+ if (rp->rrCrtcSet)
+ rp->rrCrtcSet = vncHooksRandRCrtcSet;
}
#endif
rp = rrGetScrPriv(pScreen);
if (rp) {
rp->rrSetConfig = vncHooksScreen->RandRSetConfig;
+ rp->rrScreenSetSize = vncHooksScreen->RandRScreenSetSize;
+ rp->rrCrtcSet = vncHooksScreen->RandRCrtcSet;
}
#endif
#ifdef RANDR
+static void vncPreScreenResize(ScreenPtr pScreen)
+{
+ vncHooksScreenPtr vncHooksScreen = vncHooksScreenPrivate(pScreen);
+
+ // We need to prevent the RFB core from accessing the framebuffer
+ // for a while as there might be updates thrown our way inside
+ // the routines that change the screen (i.e. before we have a
+ // pointer to the new framebuffer).
+ vncHooksScreen->desktop->blockUpdates();
+}
+
+static void vncPostScreenResize(ScreenPtr pScreen, Bool success)
+{
+ vncHooksScreenPtr vncHooksScreen = vncHooksScreenPrivate(pScreen);
+
+ RegionRec reg;
+ BoxRec box;
+
+ if (success) {
+ // Let the RFB core know of the new dimensions and framebuffer
+ vncHooksScreen->desktop->setFramebuffer(pScreen->width, pScreen->height,
+ vncFbptr[pScreen->myNum],
+ vncFbstride[pScreen->myNum]);
+ }
+
+ vncHooksScreen->desktop->unblockUpdates();
+
+ if (success) {
+ // Mark entire screen as changed
+ box.x1 = 0;
+ box.y1 = 0;
+ box.x2 = pScreen->width;
+ box.y2 = pScreen->height;
+ REGION_INIT(pScreen, ®, &box, 1);
+
+ vncHooksScreen->desktop->add_changed(®);
+ }
+}
+
static Bool vncHooksRandRSetConfig(ScreenPtr pScreen, Rotation rotation,
int rate, RRScreenSizePtr pSize)
{
vncHooksScreenPtr vncHooksScreen = vncHooksScreenPrivate(pScreen);
rrScrPrivPtr rp = rrGetScrPriv(pScreen);
Bool ret;
- RegionRec reg;
- BoxRec box;
- // We need to prevent the RFB core from accessing the framebuffer
- // for a while as there might be updates thrown our way inside
- // rrSetConfig (i.e. before we have a pointer to the new framebuffer).
- vncHooksScreen->desktop->blockUpdates();
+ vncPreScreenResize(pScreen);
rp->rrSetConfig = vncHooksScreen->RandRSetConfig;
ret = (*rp->rrSetConfig)(pScreen, rotation, rate, pSize);
rp->rrSetConfig = vncHooksRandRSetConfig;
+ vncPostScreenResize(pScreen, ret);
+
if (!ret)
return FALSE;
- // Let the RFB core know of the new dimensions and framebuffer
- vncHooksScreen->desktop->setFramebuffer(pScreen->width, pScreen->height,
- vncFbptr[pScreen->myNum],
- vncFbstride[pScreen->myNum]);
+ return TRUE;
+}
- vncHooksScreen->desktop->unblockUpdates();
+static Bool vncHooksRandRScreenSetSize(ScreenPtr pScreen,
+ CARD16 width, CARD16 height,
+ CARD32 mmWidth, CARD32 mmHeight)
+{
+ vncHooksScreenPtr vncHooksScreen = vncHooksScreenPrivate(pScreen);
+ rrScrPrivPtr rp = rrGetScrPriv(pScreen);
+ Bool ret;
+
+ vncPreScreenResize(pScreen);
+
+ rp->rrScreenSetSize = vncHooksScreen->RandRScreenSetSize;
+ ret = (*rp->rrScreenSetSize)(pScreen, width, height, mmWidth, mmHeight);
+ rp->rrScreenSetSize = vncHooksRandRScreenSetSize;
+
+ vncPostScreenResize(pScreen, ret);
+
+ if (!ret)
+ return FALSE;
- // Mark entire screen as changed
- box.x1 = 0;
- box.y1 = 0;
- box.x2 = pScreen->width;
- box.y2 = pScreen->height;
- REGION_INIT(pScreen, ®, &box, 1);
+ return TRUE;
+}
+
+static Bool vncHooksRandRCrtcSet(ScreenPtr pScreen, RRCrtcPtr crtc,
+ RRModePtr mode, int x, int y,
+ Rotation rotation, int num_outputs,
+ RROutputPtr *outputs)
+{
+ vncHooksScreenPtr vncHooksScreen = vncHooksScreenPrivate(pScreen);
+ rrScrPrivPtr rp = rrGetScrPriv(pScreen);
+ Bool ret;
+
+ rp->rrCrtcSet = vncHooksScreen->RandRCrtcSet;
+ ret = (*rp->rrCrtcSet)(pScreen, crtc, mode, x, y, rotation,
+ num_outputs, outputs);
+ rp->rrCrtcSet = vncHooksRandRCrtcSet;
+
+ if (!ret)
+ return FALSE;
- vncHooksScreen->desktop->add_changed(®);
+ vncHooksScreen->desktop->refreshScreenLayout();
return TRUE;
}
#include <rfb/Configuration.h>
#include <rfb/Logger_stdio.h>
#include <rfb/LogWriter.h>
+#include <rfb/ScreenSet.h>
+#include <rfb/screenTypes.h>
extern "C" {
#define class c_class
vncExtensionInit();
}
}
+
+unsigned int vncSetScreenLayout(ScreenPtr pScreen,
+ int fb_width, int fb_height,
+ const rfb::ScreenSet& layout)
+{
+ return rfb::resultProhibited;
+}
static Bool vncRandRGetInfo (ScreenPtr pScreen, Rotation *rotations)
{
- Bool ret, gotCurrent = FALSE;
- int i;
-
- const int widths[] = { 1920, 1920, 1600, 1680, 1400, 1360, 1280, 1280, 1280, 1280, 1024, 800, 640 };
- const int heights[] = { 1200, 1080, 1200, 1050, 1050, 768, 1024, 960, 800, 720, 768, 600, 480 };
-
- for (i = 0;i < sizeof(widths)/sizeof(*widths);i++) {
- RRScreenSizePtr pSize;
-
- pSize = RRRegisterSize(pScreen, widths[i], heights[i],
- pScreen->mmWidth, pScreen->mmHeight);
- if (!pSize)
- return FALSE;
-
- ret = RRRegisterRate(pScreen, pSize, 60);
- if (!ret)
- return FALSE;
-
- if ((widths[i] == pScreen->width) && (heights[i] == pScreen->height)) {
- RRSetCurrentConfig(pScreen, RR_Rotate_0, 60, pSize);
- gotCurrent = TRUE;
- }
- }
-
- if (!gotCurrent) {
- RRScreenSizePtr pSize;
-
- pSize = RRRegisterSize(pScreen, pScreen->width, pScreen->height,
- pScreen->mmWidth, pScreen->mmHeight);
- if (!pSize)
- return FALSE;
-
- RRRegisterRate(pScreen, pSize, 60);
-
- RRSetCurrentConfig(pScreen, RR_Rotate_0, 60, pSize);
- }
-
- *rotations = RR_Rotate_0;
-
+ // We update all information right away, so there is nothing to
+ // do here.
return TRUE;
}
FlushAllOutput ();
}
-static Bool vncRandRSetConfig (ScreenPtr pScreen, Rotation rotation,
- int rate, RRScreenSizePtr pSize)
+static RRModePtr vncRandRModeGet(int width, int height);
+
+static Bool vncRandRScreenSetSize(ScreenPtr pScreen,
+ CARD16 width, CARD16 height,
+ CARD32 mmWidth, CARD32 mmHeight)
{
vfbScreenInfoPtr pvfb = &vfbScreens[pScreen->myNum];
vfbFramebufferInfo fb;
+ rrScrPrivPtr rp = rrGetScrPriv(pScreen);
PixmapPtr rootPixmap = pScreen->GetScreenPixmap(pScreen);
void *pbits;
Bool ret;
int oldwidth, oldheight, oldmmWidth, oldmmHeight;
- int dpix, dpiy;
/* Prevent updates while we fiddle */
xf86SetRootClip(pScreen, FALSE);
oldmmWidth = pScreen->mmWidth;
oldmmHeight = pScreen->mmHeight;
- /* Compute the current DPI (for use later) */
- dpix = (pScreen->width * 254 + pScreen->mmWidth * 5) / (pScreen->mmWidth * 10);
- dpiy = (pScreen->height * 254 + pScreen->mmHeight * 5) / (pScreen->mmHeight * 10);
-
/* Then set the new dimensions */
- pScreen->width = pSize->width;
- pScreen->height = pSize->height;
-
- /* Try to keep the same DPI as we do not have a physical screen */
- pScreen->mmWidth = (pScreen->width * 254 + dpix * 5) / (dpix * 10);
- pScreen->mmHeight = (pScreen->height * 254 + dpiy * 5) / (dpiy * 10);
+ pScreen->width = width;
+ pScreen->height = height;
+ pScreen->mmWidth = mmWidth;
+ pScreen->mmHeight = mmHeight;
/* Allocate a new framebuffer */
memset(&fb, 0, sizeof(vfbFramebufferInfo));
/* Restore ability to update screen, now with new dimensions */
xf86SetRootClip(pScreen, TRUE);
+ /*
+ * Let RandR know we changed something (it doesn't assume that
+ * TRUE means something changed for some reason...).
+ */
+ RRScreenSizeNotify(pScreen);
+
+ /* Crop all CRTCs to the new screen */
+ for (int i = 0;i < rp->numCrtcs;i++) {
+ RRCrtcPtr crtc;
+ RRModePtr mode;
+
+ crtc = rp->crtcs[i];
+
+ /* Disabled? */
+ if (crtc->mode == NULL)
+ continue;
+
+ /* Fully inside? */
+ if ((crtc->x + crtc->mode->mode.width <= width) &&
+ (crtc->y + crtc->mode->mode.height <= height))
+ continue;
+
+ /* Fully outside? */
+ if ((crtc->x >= width) || (crtc->y >= height)) {
+ /* Disable it */
+ ret = RRCrtcNotify(crtc, NULL, crtc->x, crtc->y, crtc->rotation,
+ crtc->numOutputs, crtc->outputs);
+ if (!ret)
+ ErrorF("Warning: Unable to disable CRTC that is outside of new screen dimensions");
+ continue;
+ }
+
+ /* Just needs to be resized */
+ mode = vncRandRModeGet(width - crtc->x, height - crtc->y);
+ if (mode == NULL) {
+ ErrorF("Warning: Unable to create custom mode for %dx%d",
+ width - crtc->x, height - crtc->y);
+ continue;
+ }
+
+ ret = RRCrtcNotify(crtc, mode, crtc->x, crtc->y, crtc->rotation,
+ crtc->numOutputs, crtc->outputs);
+ RRModeDestroy(mode);
+ if (!ret)
+ ErrorF("Warning: Unable to crop CRTC to new screen dimensions");
+ }
+
+ return TRUE;
+}
+
+static Bool vncRandRCrtcSet(ScreenPtr pScreen, RRCrtcPtr crtc, RRModePtr mode,
+ int x, int y, Rotation rotation, int num_outputs,
+ RROutputPtr *outputs)
+{
+ Bool ret;
+
+ /* Let RandR know we approve, and let it update its internal state */
+ ret = RRCrtcNotify(crtc, mode, x, y, rotation, num_outputs, outputs);
+ if (!ret)
+ return FALSE;
+
+ return TRUE;
+}
+
+static Bool vncRandROutputValidateMode(ScreenPtr pScreen,
+ RROutputPtr output, RRModePtr mode)
+{
+ /* We have no hardware so any mode works */
return TRUE;
}
+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)
+{
+ xRRModeInfo modeInfo;
+ char name[100];
+ RRModePtr mode;
+
+ memset(&modeInfo, 0, sizeof(modeInfo));
+ sprintf(name, "%dx%d", width, height);
+
+ modeInfo.width = width;
+ modeInfo.height = height;
+ modeInfo.hTotal = width;
+ modeInfo.vTotal = height;
+ modeInfo.dotClock = ((CARD32)width * (CARD32)height * 60);
+ modeInfo.nameLength = strlen(name);
+ mode = RRModeGet(&modeInfo, name);
+ if (mode == NULL)
+ return NULL;
+
+ return mode;
+}
+
+static Bool vncRandRInit(ScreenPtr pScreen)
+{
+ RRCrtcPtr crtc;
+ RROutputPtr output;
+ 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 */
+ 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);
+ RROutputSetCrtcs(output, &crtc, 1);
+ RROutputSetConnection(output, RR_Connected);
+
+ /* Populate a list of default modes */
+ RRModePtr modes[sizeof(vncRandRWidths)/sizeof(*vncRandRWidths)];
+ int num_modes;
+
+ num_modes = 0;
+ for (int i = 0;i < sizeof(vncRandRWidths)/sizeof(*vncRandRWidths);i++) {
+ mode = vncRandRModeGet(vncRandRWidths[i], vncRandRHeights[i]);
+ if (mode != NULL) {
+ modes[num_modes] = mode;
+ num_modes++;
+ }
+ }
+
+ RROutputSetModes(output, modes, num_modes, 0);
+
+ /* 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);
+
+ return TRUE;
+}
+
+unsigned int vncSetScreenLayout(ScreenPtr pScreen,
+ int fb_width, int fb_height,
+ const rfb::ScreenSet& layout)
+{
+ return rfb::resultProhibited;
+}
+
#endif
static Bool
if (!ret) return FALSE;
rp = rrGetScrPriv(pScreen);
+
rp->rrGetInfo = vncRandRGetInfo;
- rp->rrSetConfig = vncRandRSetConfig;
+ rp->rrSetConfig = NULL;
+ rp->rrScreenSetSize = vncRandRScreenSetSize;
+ rp->rrCrtcSet = vncRandRCrtcSet;
+ rp->rrOutputValidateMode = vncRandROutputValidateMode;
+ rp->rrModeDestroy = vncRandRModeDestroy;
+
+ ret = vncRandRInit(pScreen);
+ if (!ret) return FALSE;
#endif