if(X11_Xdamage_LIB)
set(X11_Xdamage_LIB "-Wl,-Bstatic -lXdamage -Wl,-Bdynamic")
endif()
+ if(X11_Xrandr_LIB)
+ set(X11_Xrandr_LIB "-Wl,-Bstatic -lXrandr -lXrender -Wl,-Bdynamic")
+ endif()
endif()
endif()
include_directories(${X11_INCLUDE_DIR})
+include_directories(${CMAKE_SOURCE_DIR}/unix/common)
include_directories(${CMAKE_SOURCE_DIR}/unix/tx)
include_directories(${CMAKE_SOURCE_DIR}/unix)
include_directories(${CMAKE_SOURCE_DIR}/common)
x0vncserver.cxx
XPixelBuffer.cxx
XDesktop.cxx
+ RandrGlue.c
../vncconfig/QueryConnectDialog.cxx
)
-target_link_libraries(x0vncserver tx rfb network rdr)
+target_link_libraries(x0vncserver tx rfb network rdr unixcommon)
if(X11_FOUND AND X11_XTest_LIB)
add_definitions(-DHAVE_XTEST)
message(WARNING "No XFIXES extension. x0vncserver will not be able to show cursors.")
endif()
+if(X11_FOUND AND X11_Xrandr_LIB)
+ add_definitions(-DHAVE_XRANDR)
+ target_link_libraries(x0vncserver ${X11_Xrandr_LIB})
+else()
+ message(WARNING "No Xrandr extension. x0vncserver will not be able to resize session.")
+endif()
+
target_link_libraries(x0vncserver ${X11_LIBRARIES})
install(TARGETS x0vncserver DESTINATION ${BIN_DIR})
"");
Geometry::Geometry(int fullWidth, int fullHeight)
- : m_fullWidth(fullWidth),
- m_fullHeight(fullHeight),
- m_rect(0, 0, fullWidth, fullHeight)
{
+ recalc(fullWidth, fullHeight);
+}
+
+void Geometry::recalc(int fullWidth, int fullHeight)
+{
+ m_fullWidth = fullWidth;
+ m_fullHeight = fullHeight;
+ m_rect.setXYWH(0, 0, fullWidth, fullHeight);
+
// Parse geometry specification and save the result in m_rect.
const char *param = m_geometryParam.getData();
bool geometrySpecified = (strlen(param) > 0);
{
public:
Geometry(int fullWidth, int fullHeight);
+ void recalc(int fullWidth, int fullHeight);
// Return coordinates and dimensions that specify a rectangular part
// of the desktop that would be shown to RFB clients. This
--- /dev/null
+/* Copyright 2018 Peter Astrand <astrand@cendio.se> for Cendio AB
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifdef HAVE_XRANDR
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "RandrGlue.h"
+
+typedef struct _vncGlueContext {
+ Display *dpy;
+ XRRScreenResources *res;
+} vncGlueContext;
+
+static vncGlueContext randrGlueContext;
+
+void vncSetGlueContext(Display *dpy, void *res)
+{
+ randrGlueContext.dpy = dpy;
+ randrGlueContext.res = (XRRScreenResources *)res;
+}
+
+static RRMode vncRandRGetMatchingMode(XRROutputInfo *output,
+ unsigned int width, unsigned int height)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+
+ /*
+ * We're not going to change which modes are preferred, but let's
+ * see if we can at least find a mode with matching dimensions.
+ */
+
+ if (output->crtc) {
+ XRRCrtcInfo *crtc;
+ unsigned int swap;
+
+ crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
+ if (!crtc)
+ return None;
+
+ switch (crtc->rotation) {
+ case RR_Rotate_90:
+ case RR_Rotate_270:
+ swap = width;
+ width = height;
+ height = swap;
+ break;
+ }
+
+ XRRFreeCrtcInfo(crtc);
+ }
+
+ for (int i = 0; i < ctx->res->nmode; i++) {
+ for (int j = 0; j < output->nmode; j++) {
+ if ((output->modes[j] == ctx->res->modes[i].id) &&
+ (ctx->res->modes[i].width == width) &&
+ (ctx->res->modes[i].height == height)) {
+ return ctx->res->modes[i].id;
+ }
+ }
+ }
+
+ return None;
+}
+
+int vncGetScreenWidth(void)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ return DisplayWidth(ctx->dpy, DefaultScreen(ctx->dpy));
+}
+
+int vncGetScreenHeight(void)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ return DisplayHeight(ctx->dpy, DefaultScreen(ctx->dpy));
+}
+
+int vncRandRIsValidScreenSize(int width, int height)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ /* Assert size ranges */
+ int minwidth, minheight, maxwidth, maxheight;
+ int ret = XRRGetScreenSizeRange(ctx->dpy, DefaultRootWindow(ctx->dpy),
+ &minwidth, &minheight,
+ &maxwidth, &maxheight);
+ if (!ret) {
+ return 0;
+ }
+ if (width < minwidth || maxwidth < width) {
+ return 0;
+ }
+ if (height < minheight || maxheight < height) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int vncRandRResizeScreen(int width, int height)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+
+ int xwidth = DisplayWidth(ctx->dpy, DefaultScreen(ctx->dpy));
+ int xheight = DisplayHeight(ctx->dpy, DefaultScreen(ctx->dpy));
+ int xwidthmm = DisplayWidthMM(ctx->dpy, DefaultScreen(ctx->dpy));
+ int xheightmm = DisplayHeightMM(ctx->dpy, DefaultScreen(ctx->dpy));
+
+ /* Try to retain DPI when we resize */
+ XRRSetScreenSize(ctx->dpy, DefaultRootWindow(ctx->dpy), width, height,
+ xwidthmm * width / xwidth,
+ xheightmm * height / xheight);
+
+ return 1;
+}
+
+void vncRandRUpdateSetTime(void)
+{
+
+}
+
+int vncRandRHasOutputClones(void)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ for (int i = 0; i < ctx->res->ncrtc; i++) {
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, ctx->res->crtcs[i]);
+ if (!crtc) {
+ return 0;
+ }
+ if (crtc->noutput > 1) {
+ XRRFreeCrtcInfo (crtc);
+ return 1;
+ }
+ XRRFreeCrtcInfo (crtc);
+ }
+ return 0;
+}
+
+int vncRandRGetOutputCount(void)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ return ctx->res->noutput;
+}
+
+int vncRandRGetAvailableOutputs(void)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+
+ int availableOutputs;
+ RRCrtc *usedCrtcs;
+ int numUsed;
+
+ int i, j, k;
+
+ usedCrtcs = (RRCrtc*)malloc(sizeof(RRCrtc) * ctx->res->ncrtc);
+ if (usedCrtcs == NULL)
+ return 0;
+
+ /*
+ * This gets slightly complicated because we might need to hook a CRTC
+ * up to the output, but also check that we don't try to use the same
+ * CRTC for multiple outputs.
+ */
+ availableOutputs = 0;
+ numUsed = 0;
+ for (i = 0;i < ctx->res->noutput; i++) {
+ XRROutputInfo *output;
+
+ output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[i]);
+ if (!output) {
+ continue;
+ }
+
+ if (output->crtc != None)
+ availableOutputs++;
+ else {
+ for (j = 0;j < output->ncrtc;j++) {
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[j]);
+ if (!crtc) {
+ continue;
+ }
+ if (crtc->noutput != 0) {
+ XRRFreeCrtcInfo(crtc);
+ continue;
+ }
+ XRRFreeCrtcInfo(crtc);
+
+ for (k = 0;k < numUsed;k++) {
+ if (usedCrtcs[k] == output->crtcs[j])
+ break;
+ }
+ if (k != numUsed)
+ continue;
+
+ availableOutputs++;
+
+ usedCrtcs[numUsed] = output->crtcs[j];
+ numUsed++;
+
+ break;
+ }
+ }
+ XRRFreeOutputInfo(output);
+ }
+
+ free(usedCrtcs);
+
+ return availableOutputs;
+}
+
+char *vncRandRGetOutputName(int outputIdx)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
+ if (!output) {
+ return strdup("");
+ }
+ char *ret = strdup(output->name);
+ XRRFreeOutputInfo(output);
+ return ret;
+}
+
+int vncRandRIsOutputEnabled(int outputIdx)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
+ if (!output) {
+ return 0;
+ }
+
+ if (output->crtc == None) {
+ XRRFreeOutputInfo(output);
+ return 0;
+ }
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
+ XRRFreeOutputInfo(output);
+ if (!crtc) {
+ return 0;
+ }
+ if (crtc->mode == None) {
+ XRRFreeCrtcInfo(crtc);
+ return 0;
+ }
+ XRRFreeCrtcInfo(crtc);
+ return 1;
+}
+
+int vncRandRIsOutputUsable(int outputIdx)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+
+ XRROutputInfo *output;
+ int i;
+
+ output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
+ if (!output) {
+ return 0;
+ }
+
+ if (output->crtc != None) {
+ XRRFreeOutputInfo(output);
+ return 1;
+ }
+
+ /* Any unused CRTCs? */
+ for (i = 0;i < output->ncrtc;i++) {
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[i]);
+ if (crtc->noutput == 0) {
+ XRRFreeOutputInfo(output);
+ XRRFreeCrtcInfo(crtc);
+ return 1;
+ }
+ XRRFreeCrtcInfo(crtc);
+ }
+
+ XRRFreeOutputInfo(output);
+ return 0;
+}
+
+int vncRandRIsOutputConnected(int outputIdx)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ XRROutputInfo *output;
+
+ output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
+ if (!output) {
+ return 0;
+ }
+
+ int ret = (output->connection == RR_Connected);
+ XRRFreeOutputInfo(output);
+ return ret;
+}
+
+int vncRandRCheckOutputMode(int outputIdx, int width, int height)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ XRROutputInfo *output;
+ RRMode mode;
+
+ output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
+ if (!output)
+ return 0;
+
+ /* Make sure we have the mode we want */
+ mode = vncRandRGetMatchingMode(output, width, height);
+ XRRFreeOutputInfo(output);
+
+ if (mode == None)
+ return 0;
+
+ return 1;
+}
+
+int vncRandRDisableOutput(int outputIdx)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ RRCrtc crtcid;
+ int i;
+ int move = 0;
+
+ XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
+ if (!output) {
+ return 0;
+ }
+
+ crtcid = output->crtc;
+ if (crtcid == 0) {
+ XRRFreeOutputInfo(output);
+ return 1;
+ }
+
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
+ XRRFreeOutputInfo(output);
+ if (!crtc) {
+ return 0;
+ }
+
+ /* Remove this output from the CRTC configuration */
+ for (i = 0; i < crtc->noutput; i++) {
+ if (ctx->res->outputs[outputIdx] == crtc->outputs[i]) {
+ crtc->noutput -= 1;
+ move = 1;
+ }
+ if (move && i < crtc->noutput) {
+ crtc->outputs[i] = crtc->outputs[i+1];
+ }
+ }
+ if (crtc->noutput == 0) {
+ crtc->mode = None;
+ crtc->outputs = NULL;
+ }
+
+ int ret = XRRSetCrtcConfig(ctx->dpy,
+ ctx->res,
+ crtcid,
+ CurrentTime,
+ crtc->x, crtc->y,
+ crtc->mode, crtc->rotation,
+ crtc->outputs, crtc->noutput);
+
+ XRRFreeCrtcInfo(crtc);
+
+ return (ret == RRSetConfigSuccess);
+}
+
+unsigned int vncRandRGetOutputId(int outputIdx)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ return ctx->res->outputs[outputIdx];
+}
+
+int vncRandRGetOutputDimensions(int outputIdx,
+ int *x, int *y, int *width, int *height)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+ int swap;
+ *x = *y = *width = *height = 0;
+
+ XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
+ if (!output) {
+ return 1;
+ }
+
+ if (!output->crtc) {
+ XRRFreeOutputInfo(output);
+ return 1;
+ }
+
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
+ XRRFreeOutputInfo(output);
+ if (!crtc) {
+ return 1;
+ }
+ if (crtc->mode == None) {
+ XRRFreeCrtcInfo(crtc);
+ return 1;
+ }
+
+ *x = crtc->x;
+ *y = crtc->y;
+ for (int m = 0; m < ctx->res->nmode; m++) {
+ if (crtc->mode == ctx->res->modes[m].id) {
+ *width = ctx->res->modes[m].width;
+ *height = ctx->res->modes[m].height;
+ }
+ }
+
+ switch (crtc->rotation) {
+ case RR_Rotate_90:
+ case RR_Rotate_270:
+ swap = *width;
+ *width = *height;
+ *height = swap;
+ break;
+ }
+
+ XRRFreeCrtcInfo(crtc);
+ return 0;
+}
+
+int vncRandRReconfigureOutput(int outputIdx, int x, int y,
+ int width, int height)
+{
+ vncGlueContext *ctx = &randrGlueContext;
+
+ XRROutputInfo *output;
+ RRCrtc crtcid;
+ RRMode mode;
+ XRRCrtcInfo *crtc = NULL;
+
+ int i, ret;
+
+ output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
+ if (!output) {
+ return 0;
+ }
+
+ crtcid = output->crtc;
+
+ /* Need a CRTC? */
+ if (crtcid == None) {
+ for (i = 0;i < output->ncrtc;i++) {
+ crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[i]);
+ if (!crtc) {
+ continue;
+ }
+
+ if (crtc->noutput != 0) {
+ XRRFreeCrtcInfo(crtc);
+ continue;
+ }
+
+ crtcid = output->crtcs[i];
+ break;
+ }
+ } else {
+ crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, crtcid);
+ }
+
+ /* Couldn't find one... */
+ if (crtc == NULL) {
+ XRRFreeOutputInfo(output);
+ return 0;
+ }
+
+ /* Make sure we have the mode we want */
+ mode = vncRandRGetMatchingMode(output, width, height);
+ if (mode == None) {
+ XRRFreeCrtcInfo(crtc);
+ XRRFreeOutputInfo(output);
+ return 0;
+ }
+
+ /* Reconfigure new mode and position */
+ ret = XRRSetCrtcConfig (ctx->dpy, ctx->res, crtcid, CurrentTime, x, y,
+ mode, crtc->rotation, ctx->res->outputs+outputIdx, 1);
+
+ XRRFreeCrtcInfo(crtc);
+ XRRFreeOutputInfo(output);
+
+ return (ret == RRSetConfigSuccess);
+}
+
+int vncRandRCanCreateOutputs(int extraOutputs)
+{
+ return 0;
+}
+
+int vncRandRCreateOutputs(int extraOutputs)
+{
+ return 0;
+}
+
+#endif /* HAVE_XRANDR */
#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif
-
+#ifdef HAVE_XRANDR
+#include <X11/extensions/Xrandr.h>
+#include <RandrGlue.h>
+extern "C" {
+void vncSetGlueContext(Display *dpy, void *res);
+}
+#endif
#include <x0vncserver/Geometry.h>
#include <x0vncserver/XPixelBuffer.h>
}
#endif
+#ifdef HAVE_XRANDR
+ int xrandrErrorBase;
+
+ randrSyncSerial = 0;
+ if (XRRQueryExtension(dpy, &xrandrEventBase, &xrandrErrorBase)) {
+ XRRSelectInput(dpy, DefaultRootWindow(dpy),
+ RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask);
+ /* Override TXWindow::init input mask */
+ XSelectInput(dpy, DefaultRootWindow(dpy),
+ PropertyChangeMask | StructureNotifyMask | ExposureMask);
+ } else {
+#endif
+ vlog.info("RANDR extension not present");
+ vlog.info("Will not be able to handle session resize");
+#ifdef HAVE_XRANDR
+ }
+#endif
+
TXWindow::setGlobalEventHandler(this);
}
vlog.info("Allocated %s", pb->getImage()->classDesc());
server = (VNCServerST *)vs;
- server->setPixelBuffer(pb);
+ server->setPixelBuffer(pb, computeScreenLayout());
#ifdef HAVE_XDAMAGE
if (haveDamage) {
void XDesktop::clientCutText(const char* str, int len) {
}
+ScreenSet XDesktop::computeScreenLayout()
+{
+ ScreenSet layout;
+
+#ifdef HAVE_XRANDR
+ XRRScreenResources *res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
+ if (!res) {
+ vlog.error("XRRGetScreenResources failed");
+ return layout;
+ }
+ vncSetGlueContext(dpy, res);
+
+ layout = ::computeScreenLayout(&outputIdMap);
+ XRRFreeScreenResources(res);
+#endif
+
+ return layout;
+}
+
+#ifdef HAVE_XRANDR
+/* Get the biggest mode which is equal or smaller to requested
+ size. If no such mode exists, return the smallest. */
+static void GetSmallerMode(XRRScreenResources *res,
+ XRROutputInfo *output,
+ unsigned int *width, unsigned int *height)
+{
+ XRRModeInfo best = {};
+ XRRModeInfo smallest = {};
+ smallest.width = -1;
+ smallest.height = -1;
+
+ for (int i = 0; i < res->nmode; i++) {
+ for (int j = 0; j < output->nmode; j++) {
+ if (output->modes[j] == res->modes[i].id) {
+ if ((res->modes[i].width > best.width && res->modes[i].width <= *width) &&
+ (res->modes[i].height > best.height && res->modes[i].height <= *height)) {
+ best = res->modes[i];
+ }
+ if ((res->modes[i].width < smallest.width) && res->modes[i].height < smallest.height) {
+ smallest = res->modes[i];
+ }
+ }
+ }
+ }
+
+ if (best.id == 0 && smallest.id != 0) {
+ best = smallest;
+ }
+
+ *width = best.width;
+ *height = best.height;
+}
+#endif /* HAVE_XRANDR */
+
+unsigned int XDesktop::setScreenLayout(int fb_width, int fb_height,
+ const rfb::ScreenSet& layout)
+{
+#ifdef HAVE_XRANDR
+ char buffer[2048];
+ vlog.debug("Got request for framebuffer resize to %dx%d",
+ fb_width, fb_height);
+ layout.print(buffer, sizeof(buffer));
+ vlog.debug("%s", buffer);
+
+ XRRScreenResources *res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
+ if (!res) {
+ vlog.error("XRRGetScreenResources failed");
+ return rfb::resultProhibited;
+ }
+ vncSetGlueContext(dpy, res);
+
+ /* The client may request a screen layout which is not supported by
+ the Xserver. This happens, for example, when adjusting the size
+ of a non-fullscreen vncviewer window. To handle this and other
+ cases, we first call tryScreenLayout. If this fails, we try to
+ adjust the request to one screen with a smaller mode. */
+ vlog.debug("Testing screen layout");
+ unsigned int tryresult = ::tryScreenLayout(fb_width, fb_height, layout, &outputIdMap);
+ rfb::ScreenSet adjustedLayout;
+ if (tryresult == rfb::resultSuccess) {
+ adjustedLayout = layout;
+ } else {
+ vlog.debug("Impossible layout - trying to adjust");
+
+ ScreenSet::const_iterator firstscreen = layout.begin();
+ adjustedLayout.add_screen(*firstscreen);
+ ScreenSet::iterator iter = adjustedLayout.begin();
+ RROutput outputId = None;
+
+ for (int i = 0;i < vncRandRGetOutputCount();i++) {
+ unsigned int oi = vncRandRGetOutputId(i);
+
+ /* Known? */
+ if (outputIdMap.count(oi) == 0)
+ continue;
+
+ /* Find the corresponding screen... */
+ if (iter->id == outputIdMap[oi]) {
+ outputId = oi;
+ } else {
+ outputIdMap.erase(oi);
+ }
+ }
+
+ /* New screen */
+ if (outputId == None) {
+ int i = getPreferredScreenOutput(&outputIdMap, std::set<unsigned int>());
+ if (i != -1) {
+ outputId = vncRandRGetOutputId(i);
+ }
+ }
+ if (outputId == None) {
+ vlog.debug("Resize adjust: Could not find corresponding screen");
+ XRRFreeScreenResources(res);
+ return rfb::resultInvalid;
+ }
+ XRROutputInfo *output = XRRGetOutputInfo(dpy, res, outputId);
+ if (!output) {
+ vlog.debug("Resize adjust: XRRGetOutputInfo failed");
+ XRRFreeScreenResources(res);
+ return rfb::resultInvalid;
+ }
+ if (!output->crtc) {
+ vlog.debug("Resize adjust: Selected output has no CRTC");
+ XRRFreeScreenResources(res);
+ XRRFreeOutputInfo(output);
+ return rfb::resultInvalid;
+ }
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo(dpy, res, output->crtc);
+ if (!crtc) {
+ vlog.debug("Resize adjust: XRRGetCrtcInfo failed");
+ XRRFreeScreenResources(res);
+ XRRFreeOutputInfo(output);
+ return rfb::resultInvalid;
+ }
+
+ unsigned int swidth = iter->dimensions.width();
+ unsigned int sheight = iter->dimensions.height();
+
+ switch (crtc->rotation) {
+ case RR_Rotate_90:
+ case RR_Rotate_270:
+ unsigned int swap = swidth;
+ swidth = sheight;
+ sheight = swap;
+ break;
+ }
+
+ GetSmallerMode(res, output, &swidth, &sheight);
+ XRRFreeOutputInfo(output);
+
+ switch (crtc->rotation) {
+ case RR_Rotate_90:
+ case RR_Rotate_270:
+ unsigned int swap = swidth;
+ swidth = sheight;
+ sheight = swap;
+ break;
+ }
+
+ XRRFreeCrtcInfo(crtc);
+
+ if (sheight != 0 && swidth != 0) {
+ vlog.debug("Adjusted resize request to %dx%d", swidth, sheight);
+ iter->dimensions.setXYWH(0, 0, swidth, sheight);
+ fb_width = swidth;
+ fb_height = sheight;
+ } else {
+ vlog.error("Failed to find smaller or equal screen size");
+ XRRFreeScreenResources(res);
+ return rfb::resultInvalid;
+ }
+ }
+
+ vlog.debug("Changing screen layout");
+ unsigned int ret = ::setScreenLayout(fb_width, fb_height, adjustedLayout, &outputIdMap);
+ XRRFreeScreenResources(res);
+
+ /* Send a dummy event to the root window. When this event is seen,
+ earlier change events (ConfigureNotify and/or CrtcChange) have
+ been processed. An Expose event is used for simplicity; does not
+ require any Atoms, and will not affect other applications. */
+ unsigned long serial = XNextRequest(dpy);
+ XExposeEvent ev = {}; /* zero x, y, width, height, count */
+ ev.type = Expose;
+ ev.display = dpy;
+ ev.window = DefaultRootWindow(dpy);
+ if (XSendEvent(dpy, DefaultRootWindow(dpy), False, ExposureMask, (XEvent*)&ev)) {
+ while (randrSyncSerial < serial) {
+ TXWindow::handleXEvents(dpy);
+ }
+ } else {
+ vlog.error("XSendEvent failed");
+ }
+
+ /* The protocol requires that an error is returned if the requested
+ layout could not be set. This is checked by
+ VNCSConnectionST::setDesktopSize. Another ExtendedDesktopSize
+ with reason=0 will be sent in response to the changes seen by the
+ event handler. */
+ if (adjustedLayout != layout) {
+ return rfb::resultInvalid;
+ } else {
+ return ret;
+ }
+
+#else
+ return rfb::resultProhibited;
+#endif /* HAVE_XRANDR */
+}
+
bool XDesktop::handleGlobalEvent(XEvent* ev) {
if (ev->type == xkbEventBase + XkbEventCode) {
return false;
return setCursor();
+#endif
+#ifdef HAVE_XRANDR
+ } else if (ev->type == Expose) {
+ XExposeEvent* eev = (XExposeEvent*)ev;
+ randrSyncSerial = eev->serial;
+
+ return false;
+
+ } else if (ev->type == ConfigureNotify) {
+ XConfigureEvent* cev = (XConfigureEvent*)ev;
+
+ if (cev->window != DefaultRootWindow(dpy)) {
+ return false;
+ }
+
+ XRRUpdateConfiguration(ev);
+ geometry->recalc(cev->width, cev->height);
+
+ if (!running) {
+ return false;
+ }
+
+ if ((cev->width != pb->width() || (cev->height != pb->height()))) {
+ // Recreate pixel buffer
+ ImageFactory factory((bool)useShm);
+ delete pb;
+ pb = new XPixelBuffer(dpy, factory, geometry->getRect());
+ server->setPixelBuffer(pb, computeScreenLayout());
+
+ // Mark entire screen as changed
+ server->add_changed(rfb::Region(Rect(0, 0, cev->width, cev->height)));
+ }
+
+ return true;
+
+ } else if (ev->type == xrandrEventBase + RRNotify) {
+ XRRNotifyEvent* rev = (XRRNotifyEvent*)ev;
+
+ if (rev->window != DefaultRootWindow(dpy)) {
+ return false;
+ }
+
+ if (!running)
+ return false;
+
+ if (rev->subtype == RRNotify_CrtcChange) {
+ server->setScreenLayout(computeScreenLayout());
+ }
+
+ return true;
#endif
}
#include <rfb/VNCServerST.h>
#include <tx/TXWindow.h>
+#include <unixcommon.h>
#include <X11/XKBlib.h>
#ifdef HAVE_XDAMAGE
KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down);
virtual void clientCutText(const char* str, int len);
+ virtual unsigned int setScreenLayout(int fb_width, int fb_height,
+ const rfb::ScreenSet& layout);
+
// -=- TXGlobalEventHandler interface
virtual bool handleGlobalEvent(XEvent* ev);
int xkbEventBase;
#ifdef HAVE_XFIXES
int xfixesEventBase;
+#endif
+#ifdef HAVE_XRANDR
+ int xrandrEventBase;
+ OutputIdMap outputIdMap;
+ unsigned long randrSyncSerial;
#endif
int ledMasks[XDESKTOP_N_LEDS];
unsigned ledState;
const unsigned short *codeMap;
unsigned codeMapLen;
bool setCursor();
+ rfb::ScreenSet computeScreenLayout();
};
#endif // __XDESKTOP_H__