Merge branch 'clipfocus' of https://github.com/CendioOssman/tigervnc
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ca1e85b..a63288a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -68,6 +68,9 @@
 set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -UNDEBUG")
 set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -UNDEBUG")
 
+# Make sure we get a sane C version
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
+
 # Tell the compiler to be stringent
 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat=2")
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wformat=2")
diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake
index 06883c6..4b58b1d 100644
--- a/cmake/StaticBuild.cmake
+++ b/cmake/StaticBuild.cmake
@@ -109,6 +109,9 @@
     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()
 
diff --git a/common/rfb/CMsgWriter.cxx b/common/rfb/CMsgWriter.cxx
index 84a0d1f..44b73da 100644
--- a/common/rfb/CMsgWriter.cxx
+++ b/common/rfb/CMsgWriter.cxx
@@ -217,7 +217,7 @@
   endMsg();
 }
 
-void CMsgWriter::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
+void CMsgWriter::writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
 {
   if (!cp->supportsQEMUKeyEvent || !keycode) {
     /* This event isn't meaningful without a valid keysym */
@@ -240,7 +240,7 @@
 }
 
 
-void CMsgWriter::pointerEvent(const Point& pos, int buttonMask)
+void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask)
 {
   Point p(pos);
   if (p.x < 0) p.x = 0;
@@ -256,7 +256,7 @@
 }
 
 
-void CMsgWriter::clientCutText(const char* str, int len)
+void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len)
 {
   startMsg(msgTypeClientCutText);
   os->pad(3);
diff --git a/common/rfb/CMsgWriter.h b/common/rfb/CMsgWriter.h
index eb9b112..1322186 100644
--- a/common/rfb/CMsgWriter.h
+++ b/common/rfb/CMsgWriter.h
@@ -25,8 +25,6 @@
 
 #include <rdr/types.h>
 
-#include <rfb/InputHandler.h>
-
 namespace rdr { class OutStream; }
 
 namespace rfb {
@@ -34,9 +32,10 @@
   class PixelFormat;
   class ConnParams;
   struct ScreenSet;
+  struct Point;
   struct Rect;
 
-  class CMsgWriter : public InputHandler {
+  class CMsgWriter {
   public:
     CMsgWriter(ConnParams* cp, rdr::OutStream* os);
     virtual ~CMsgWriter();
@@ -53,11 +52,9 @@
 
     void writeFence(rdr::U32 flags, unsigned len, const char data[]);
 
-    // InputHandler implementation
-
-    virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
-    virtual void pointerEvent(const Point& pos, int buttonMask);
-    virtual void clientCutText(const char* str, int len);
+    void writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
+    void writePointerEvent(const Point& pos, int buttonMask);
+    void writeClientCutText(const char* str, rdr::U32 len);
 
   protected:
     void startMsg(int type);
diff --git a/common/rfb/ConnParams.cxx b/common/rfb/ConnParams.cxx
index 23f02ed..80b4a5a 100644
--- a/common/rfb/ConnParams.cxx
+++ b/common/rfb/ConnParams.cxx
@@ -147,6 +147,7 @@
       break;
     case pseudoEncodingLEDState:
       supportsLEDState = true;
+      break;
     case pseudoEncodingQEMUKeyEvent:
       supportsQEMUKeyEvent = true;
       break;
diff --git a/common/rfb/util.cxx b/common/rfb/util.cxx
index 4fd84eb..f52213b 100644
--- a/common/rfb/util.cxx
+++ b/common/rfb/util.cxx
@@ -16,20 +16,6 @@
  * USA.
  */
 
-/*
- * The following applies to stcasecmp and strncasecmp implementations:
- *
- * Copyright (c) 1987 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that this notice is preserved and that due credit is given
- * to the University of California at Berkeley. The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific written prior permission. This software
- * is provided ``as is'' without express or implied warranty.
- */
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt
index 8b2c70e..7a1457d 100644
--- a/unix/CMakeLists.txt
+++ b/unix/CMakeLists.txt
@@ -1,5 +1,5 @@
 add_subdirectory(tx)
-
+add_subdirectory(common)
 add_subdirectory(vncconfig)
 add_subdirectory(vncpasswd)
 add_subdirectory(x0vncserver)
diff --git a/unix/common/CMakeLists.txt b/unix/common/CMakeLists.txt
new file mode 100644
index 0000000..611e195
--- /dev/null
+++ b/unix/common/CMakeLists.txt
@@ -0,0 +1,14 @@
+include_directories(${CMAKE_SOURCE_DIR}/common)
+include_directories(${CMAKE_SOURCE_DIR}/unix/common)
+
+add_library(unixcommon STATIC
+  randr.cxx)
+
+if(UNIX)
+  libtool_create_control_file(unixcommon)
+endif()
+
+if(NOT WIN32)
+  set_target_properties(unixcommon
+    PROPERTIES COMPILE_FLAGS -fPIC)
+endif()
diff --git a/unix/common/RandrGlue.h b/unix/common/RandrGlue.h
new file mode 100644
index 0000000..5cc5700
--- /dev/null
+++ b/unix/common/RandrGlue.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2011-2015 Pierre Ossman 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.
+ */
+
+/*
+  This header defines an interface for X RandR operations. It is
+  implemented by a corresponding RandrGlue.c, either with internal
+  calls (for Xvnc/vncmodule.so) or Xlib calls (x0vncserver).
+ */
+
+#ifndef RANDR_GLUE_H
+#define RANDR_GLUE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int vncGetScreenWidth(void);
+int vncGetScreenHeight(void);
+
+int vncRandRIsValidScreenSize(int width, int height);
+int vncRandRResizeScreen(int width, int height);
+void vncRandRUpdateSetTime(void);
+
+int vncRandRHasOutputClones(void);
+
+int vncRandRGetOutputCount(void);
+int vncRandRGetAvailableOutputs(void);
+
+char *vncRandRGetOutputName(int outputIdx);
+
+int vncRandRIsOutputEnabled(int outputIdx);
+int vncRandRIsOutputUsable(int outputIdx);
+int vncRandRIsOutputConnected(int outputIdx);
+
+int vncRandRCheckOutputMode(int outputIdx, int width, int height);
+
+int vncRandRDisableOutput(int outputIdx);
+int vncRandRReconfigureOutput(int outputIdx, int x, int y,
+                              int width, int height);
+
+unsigned int vncRandRGetOutputId(int outputIdx);
+int vncRandRGetOutputDimensions(int outputIdx,
+                                 int *x, int *y, int *width, int *height);
+
+int vncRandRCanCreateOutputs(int extraOutputs);
+int vncRandRCreateOutputs(int extraOutputs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/unix/common/randr.cxx b/unix/common/randr.cxx
new file mode 100644
index 0000000..95c1f33
--- /dev/null
+++ b/unix/common/randr.cxx
@@ -0,0 +1,439 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009-2017 Pierre Ossman for Cendio AB
+ * Copyright 2018 Peter Astrand <astrand@cendio.se> for Cendio AB
+ * Copyright 2014 Brian P. Hinz
+ * 
+ * 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_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <unixcommon.h>
+#include <rfb/screenTypes.h>
+#include <rfb/LogWriter.h>
+#include <RandrGlue.h>
+static rfb::LogWriter vlog("RandR");
+
+static int ResizeScreen(bool dryrun, int fb_width, int fb_height,
+                        std::set<unsigned int>* disabledOutputs)
+{
+  vlog.debug("Resizing screen framebuffer to %dx%d", fb_width, fb_height);
+
+  /*
+   * Disable outputs which are larger than the target size
+   */
+  for (int i = 0;i < vncRandRGetOutputCount();i++) {
+    int x, y, width, height;
+    if (vncRandRGetOutputDimensions(i, &x, &y, &width, &height) == 0) {
+      if (x + width > fb_width || y + height > fb_height) {
+        char *name = vncRandRGetOutputName(i);
+        vlog.debug("Temporarily disabling output '%s'", name);
+        free(name);
+        if (!dryrun) {
+          /* Currently ignoring errors */
+          /* FIXME: Save output rotation and restore when configuring output */
+          vncRandRDisableOutput(i);
+          disabledOutputs->insert(vncRandRGetOutputId(i));
+        }
+      }
+    }
+  }
+
+  if (!vncRandRIsValidScreenSize(fb_width, fb_height))
+    return 0;
+
+  if (dryrun)
+    return 1;
+
+  return vncRandRResizeScreen(fb_width, fb_height);
+}
+
+
+/* Return output index of preferred output, -1 on failure */
+int getPreferredScreenOutput(OutputIdMap *outputIdMap,
+                             const std::set<unsigned int>& disabledOutputs)
+{
+  int firstDisabled = -1;
+  int firstEnabled = -1;
+  int firstConnected = -1;
+  int firstUsable = -1;
+
+  for (int i = 0;i < vncRandRGetOutputCount();i++) {
+    unsigned int output = vncRandRGetOutputId(i);
+
+    /* In use? */
+    if (outputIdMap->count(output) == 1) {
+      continue;
+    }
+
+    /* Can it be used? */
+    if (!vncRandRIsOutputUsable(i)) {
+      continue;
+    }
+
+    /* Temporarily disabled? */
+    if (disabledOutputs.count(output)) {
+      if (firstDisabled == -1) firstDisabled = i;
+    }
+
+    /* Enabled? */
+    if (vncRandRIsOutputEnabled(i)) {
+      if (firstEnabled == -1) firstEnabled = i;
+    }
+
+    /* Connected? */
+    if (vncRandRIsOutputConnected(i)) {
+      if (firstConnected == -1) firstConnected = i;
+    }
+
+    if (firstUsable == -1) firstUsable = i;
+  }
+
+  if (firstEnabled != -1) {
+    return firstEnabled;
+  } else if (firstDisabled != -1) {
+    return firstDisabled;
+  } else if (firstConnected != -1) {
+    return firstConnected;
+  } else {
+    return firstUsable; /* Possibly -1 */
+  }
+}
+
+
+rfb::ScreenSet computeScreenLayout(OutputIdMap *outputIdMap)
+{
+  rfb::ScreenSet layout;
+  OutputIdMap newIdMap;
+
+  for (int i = 0;i < vncRandRGetOutputCount();i++) {
+    unsigned int outputId;
+    int x, y, width, height;
+
+    /* Disabled? */
+    if (!vncRandRIsOutputEnabled(i))
+      continue;
+
+    outputId = vncRandRGetOutputId(i);
+
+    /* Known output? */
+    if (outputIdMap->count(outputId) == 1)
+      newIdMap[outputId] = (*outputIdMap)[outputId];
+    else {
+      rdr::U32 id;
+      OutputIdMap::const_iterator iter;
+
+      while (true) {
+        id = rand();
+        for (iter = outputIdMap->begin();iter != outputIdMap->end();++iter) {
+          if (iter->second == id)
+            break;
+        }
+        if (iter == outputIdMap->end())
+          break;
+      }
+
+      newIdMap[outputId] = id;
+    }
+
+    if (vncRandRGetOutputDimensions(i, &x, &y, &width, &height) == 0) {
+      layout.add_screen(rfb::Screen(newIdMap[outputId], x, y, width, height, 0));
+    }
+  }
+
+  /* Only keep the entries that are currently active */
+  *outputIdMap = newIdMap;
+
+  /*
+   * 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(rfb::Screen(0, 0, 0, vncGetScreenWidth(),
+                                  vncGetScreenHeight(), 0));
+
+  return layout;
+}
+
+static unsigned int _setScreenLayout(bool dryrun,
+                                     int fb_width, int fb_height, const rfb::ScreenSet& layout,
+                                     OutputIdMap *outputIdMap)
+{
+  int ret;
+  int availableOutputs;
+  std::set<unsigned int> disabledOutputs;
+  /* Printing errors in the dryrun pass might be confusing */
+  const bool logErrors = !dryrun || vlog.getLevel() >= vlog.LEVEL_DEBUG;
+
+  // RandR support?
+  if (vncRandRGetOutputCount() == 0)
+    return rfb::resultProhibited;
+
+  /*
+   * First check that we don't have any active clone modes. That's just
+   * too messy to deal with.
+   */
+  if (vncRandRHasOutputClones()) {
+    if (logErrors) {
+      vlog.error("Clone mode active. Refusing to touch screen layout.");
+    }
+    return rfb::resultInvalid;
+  }
+
+  /* Next count how many useful outputs we have... */
+  availableOutputs = vncRandRGetAvailableOutputs();
+
+  /* Try to create more outputs if needed... (only works on Xvnc) */
+  if (layout.num_screens() > availableOutputs) {
+    vlog.debug("Insufficient screens. Need to create %d more.",
+               layout.num_screens() - availableOutputs);
+
+    if (!vncRandRCanCreateOutputs(layout.num_screens() - availableOutputs)) {
+      if (logErrors)
+        vlog.error("Unable to create more screens, as needed by the new client layout.");
+      return rfb::resultInvalid;
+    }
+
+    if (!dryrun) {
+      ret = vncRandRCreateOutputs(layout.num_screens() - availableOutputs);
+      if (!ret) {
+        if (logErrors)
+          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 != vncGetScreenWidth()) ||
+      (fb_height != vncGetScreenHeight())) {
+    ret = ResizeScreen(dryrun, fb_width, fb_height, &disabledOutputs);
+    if (!ret) {
+      if (logErrors) {
+        vlog.error("Failed to resize screen to %dx%d", fb_width, fb_height);
+      }
+      return rfb::resultInvalid;
+    }
+  }
+
+  /* Next, reconfigure all known outputs */
+  for (int i = 0;i < vncRandRGetOutputCount();i++) {
+    unsigned int output;
+
+    rfb::ScreenSet::const_iterator iter;
+
+    output = vncRandRGetOutputId(i);
+
+    /* 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()) {
+      outputIdMap->erase(output);
+      continue;
+    }
+
+    /* Probably not needed, but let's be safe */
+    if (!vncRandRIsOutputUsable(i)) {
+      if (logErrors) {
+        char *name = vncRandRGetOutputName(i);
+        vlog.error("Required output '%s' cannot be used", name);
+        free(name);
+      }
+      return rfb::resultInvalid;
+    }
+
+    /* Possible mode? */
+    if (!vncRandRCheckOutputMode(i, iter->dimensions.width(),
+                                 iter->dimensions.height())) {
+      if (logErrors) {
+        char *name = vncRandRGetOutputName(i);
+        vlog.error("Output '%s' does not support required mode %dx%d", name,
+                   iter->dimensions.width(), iter->dimensions.height());
+        free(name);
+      }
+      return rfb::resultInvalid;
+    }
+
+    char *name = vncRandRGetOutputName(i);
+    vlog.debug("Reconfiguring output '%s' to %dx%d+%d+%d", name,
+               iter->dimensions.width(), iter->dimensions.height(),
+               iter->dimensions.tl.x, iter->dimensions.tl.y);
+    free(name);
+
+    if (dryrun)
+      continue;
+
+    /* Reconfigure new mode and position */
+    ret = vncRandRReconfigureOutput(i,
+                                    iter->dimensions.tl.x,
+                                    iter->dimensions.tl.y,
+                                    iter->dimensions.width(),
+                                    iter->dimensions.height());
+    if (!ret) {
+      if (logErrors) {
+        char *name = vncRandRGetOutputName(i);
+        vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d", name,
+                   iter->dimensions.width(), iter->dimensions.height(),
+                   iter->dimensions.tl.x, iter->dimensions.tl.y);
+        free(name);
+      }
+      return rfb::resultInvalid;
+    }
+  }
+
+  /* Allocate new outputs for new screens */
+  rfb::ScreenSet::const_iterator iter;
+  for (iter = layout.begin();iter != layout.end();++iter) {
+    OutputIdMap::const_iterator oi;
+    unsigned int output;
+    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 */
+    i = getPreferredScreenOutput(outputIdMap, disabledOutputs);
+
+    /* Shouldn't happen */
+    if (i == -1)
+      return rfb::resultInvalid;
+    output = vncRandRGetOutputId(i);
+
+    /*
+     * 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;
+
+    /* Probably not needed, but let's be safe */
+    if (!vncRandRIsOutputUsable(i)) {
+      if (logErrors) {
+        char *name = vncRandRGetOutputName(i);
+        vlog.error("Required new output '%s' cannot be used", name);
+        free(name);
+      }
+      return rfb::resultInvalid;
+    }
+
+    /* Possible mode? */
+    if (!vncRandRCheckOutputMode(i, iter->dimensions.width(),
+                                 iter->dimensions.height())) {
+      if (logErrors) {
+        char *name = vncRandRGetOutputName(i);
+        vlog.error("New output '%s' does not support required mode %dx%d", name,
+                   iter->dimensions.width(), iter->dimensions.height());
+        free(name);
+      }
+      return rfb::resultInvalid;
+    }
+
+    char *name = vncRandRGetOutputName(i);
+    vlog.debug("Reconfiguring new output '%s' to %dx%d+%d+%d", name,
+               iter->dimensions.width(), iter->dimensions.height(),
+               iter->dimensions.tl.x, iter->dimensions.tl.y);
+    free(name);
+
+    if (dryrun)
+      continue;
+
+    /* Reconfigure new mode and position */
+    ret = vncRandRReconfigureOutput(i,
+                                    iter->dimensions.tl.x,
+                                    iter->dimensions.tl.y,
+                                    iter->dimensions.width(),
+                                    iter->dimensions.height());
+    if (!ret) {
+      if (logErrors) {
+        char *name = vncRandRGetOutputName(i);
+        vlog.error("Failed to reconfigure new output '%s' to %dx%d+%d+%d", name,
+                   iter->dimensions.width(), iter->dimensions.height(),
+                   iter->dimensions.tl.x, iter->dimensions.tl.y);
+        free(name);
+      }
+      return rfb::resultInvalid;
+    }
+  }
+
+  /* Turn off unused outputs */
+  for (int i = 0;i < vncRandRGetOutputCount();i++) {
+    unsigned int output = vncRandRGetOutputId(i);
+
+    /* Known? */
+    if (outputIdMap->count(output) == 1)
+      continue;
+
+    /* Enabled? */
+    if (!vncRandRIsOutputEnabled(i))
+      continue;
+
+    /* Disable and move on... */
+    ret = vncRandRDisableOutput(i);
+    char *name = vncRandRGetOutputName(i);
+    if (ret) {
+      vlog.debug("Disabled unused output '%s'", name);
+    } else {
+      if (logErrors) {
+        vlog.error("Failed to disable unused output '%s'", name);
+      }
+      free(name);
+      return rfb::resultInvalid;
+    }
+    free(name);
+  }
+
+  /*
+   * Update timestamp for when screen layout was last changed.
+   * This is normally done in the X11 request handlers, which is
+   * why we have to deal with it manually here.
+   */
+  vncRandRUpdateSetTime();
+
+  return rfb::resultSuccess;
+}
+
+
+unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout,
+                             OutputIdMap *outputIdMap)
+{
+    return _setScreenLayout(false, fb_width, fb_height, layout, outputIdMap);
+}
+
+
+unsigned int tryScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout,
+                             OutputIdMap *outputIdMap)
+{
+    OutputIdMap dryrunIdMap = *outputIdMap;
+    return _setScreenLayout(true, fb_width, fb_height, layout, &dryrunIdMap);
+}
diff --git a/unix/common/unixcommon.h b/unix/common/unixcommon.h
new file mode 100644
index 0000000..43191fb
--- /dev/null
+++ b/unix/common/unixcommon.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009-2015 Pierre Ossman 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.
+ */
+
+#ifndef UNIXCOMMON_H
+#define UNIXCOMMON_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <map>
+
+#include <rfb/ScreenSet.h>
+
+typedef std::map<unsigned int, rdr::U32> OutputIdMap;
+
+rfb::ScreenSet computeScreenLayout(OutputIdMap *outputIdMap);
+
+unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout,
+                             OutputIdMap *outputIdMap);
+
+unsigned int tryScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout,
+                             OutputIdMap *outputIdMap);
+
+/*
+ * FIXME: This is only exposed because we still have logic in XDesktop
+ *        that we haven't integrated in setScreenLayout()
+ */
+int getPreferredScreenOutput(OutputIdMap *outputIdMap,
+                             const std::set<unsigned int>& disabledOutputs);
+
+#endif /* UNIXCOMMON_H */
diff --git a/unix/x0vncserver/CMakeLists.txt b/unix/x0vncserver/CMakeLists.txt
index 5930e32..8beade7 100644
--- a/unix/x0vncserver/CMakeLists.txt
+++ b/unix/x0vncserver/CMakeLists.txt
@@ -1,4 +1,5 @@
 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)
@@ -15,10 +16,11 @@
   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)
@@ -41,6 +43,13 @@
   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})
diff --git a/unix/x0vncserver/Geometry.cxx b/unix/x0vncserver/Geometry.cxx
index 48c1842..d911471 100644
--- a/unix/x0vncserver/Geometry.cxx
+++ b/unix/x0vncserver/Geometry.cxx
@@ -35,10 +35,16 @@
   "");
 
 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);
diff --git a/unix/x0vncserver/Geometry.h b/unix/x0vncserver/Geometry.h
index 98bafb2..d938d63 100644
--- a/unix/x0vncserver/Geometry.h
+++ b/unix/x0vncserver/Geometry.h
@@ -30,6 +30,7 @@
 {
 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
diff --git a/unix/x0vncserver/RandrGlue.c b/unix/x0vncserver/RandrGlue.c
new file mode 100644
index 0000000..2e47763
--- /dev/null
+++ b/unix/x0vncserver/RandrGlue.c
@@ -0,0 +1,511 @@
+/* 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 */
diff --git a/unix/x0vncserver/XDesktop.cxx b/unix/x0vncserver/XDesktop.cxx
index 748796b..59e2532 100644
--- a/unix/x0vncserver/XDesktop.cxx
+++ b/unix/x0vncserver/XDesktop.cxx
@@ -30,7 +30,13 @@
 #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>
 
@@ -162,6 +168,24 @@
   }
 #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);
 }
 
@@ -202,7 +226,7 @@
   vlog.info("Allocated %s", pb->getImage()->classDesc());
 
   server = (VNCServerST *)vs;
-  server->setPixelBuffer(pb);
+  server->setPixelBuffer(pb, computeScreenLayout());
 
 #ifdef HAVE_XDAMAGE
   if (haveDamage) {
@@ -331,6 +355,217 @@
 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) {
@@ -379,6 +614,56 @@
 
     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
   }
 
   return false;
diff --git a/unix/x0vncserver/XDesktop.h b/unix/x0vncserver/XDesktop.h
index c9106f8..ff52c01 100644
--- a/unix/x0vncserver/XDesktop.h
+++ b/unix/x0vncserver/XDesktop.h
@@ -23,6 +23,7 @@
 
 #include <rfb/VNCServerST.h>
 #include <tx/TXWindow.h>
+#include <unixcommon.h>
 
 #include <X11/XKBlib.h>
 #ifdef HAVE_XDAMAGE
@@ -49,6 +50,9 @@
   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);
 
@@ -71,11 +75,17 @@
 #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__
diff --git a/unix/xserver/hw/vnc/Makefile.am b/unix/xserver/hw/vnc/Makefile.am
index 24a40b3..470424b 100644
--- a/unix/xserver/hw/vnc/Makefile.am
+++ b/unix/xserver/hw/vnc/Makefile.am
@@ -1,12 +1,13 @@
 TIGERVNC_SRCDIR=${top_srcdir}/../..
-LIB_DIR=${top_builddir}/../../common
+TIGERVNC_BUILDDIR=${TIGERVNC_SRCDIR}
 
-RFB_LIB=$(LIB_DIR)/rfb/librfb.la
-RDR_LIB=$(LIB_DIR)/rdr/librdr.la
-OS_LIB=$(LIB_DIR)/os/libos.la
-NETWORK_LIB=$(LIB_DIR)/network/libnetwork.la
-XREGION_LIB=$(LIB_DIR)/Xregion/libXregion.la
-COMMON_LIBS=$(NETWORK_LIB) $(RFB_LIB) $(RDR_LIB) $(XREGION_LIB) $(OS_LIB)
+RFB_LIB=$(TIGERVNC_BUILDDIR)/common/rfb/librfb.la
+RDR_LIB=$(TIGERVNC_BUILDDIR)/common/rdr/librdr.la
+OS_LIB=$(TIGERVNC_BUILDDIR)/common/os/libos.la
+NETWORK_LIB=$(TIGERVNC_BUILDDIR)/common/network/libnetwork.la
+XREGION_LIB=$(TIGERVNC_BUILDDIR)/common/Xregion/libXregion.la
+UNIXCOMMON_LIB=$(TIGERVNC_BUILDDIR)/unix/common/libunixcommon.la
+COMMON_LIBS=$(NETWORK_LIB) $(RFB_LIB) $(RDR_LIB) $(XREGION_LIB) $(OS_LIB) $(UNIXCOMMON_LIB)
 
 noinst_LTLIBRARIES = libvnccommon.la
 
@@ -17,10 +18,10 @@
 
 libvnccommon_la_SOURCES = $(HDRS) \
 	vncExt.c vncExtInit.cc vncHooks.c vncSelection.c \
-	vncBlockHandler.c XorgGlue.c RFBGlue.cc XserverDesktop.cc \
+	vncBlockHandler.c XorgGlue.c RandrGlue.c RFBGlue.cc XserverDesktop.cc \
 	Input.c InputXKB.c qnum_to_xorgevdev.c qnum_to_xorgkbd.c
 
-libvnccommon_la_CPPFLAGS = -DVENDOR_RELEASE="$(VENDOR_RELEASE)" \
+libvnccommon_la_CPPFLAGS = -DVENDOR_RELEASE="$(VENDOR_RELEASE)" -I$(TIGERVNC_SRCDIR)/unix/common \
 	-DVENDOR_STRING="\"$(VENDOR_STRING)\"" -I$(TIGERVNC_SRCDIR)/common -UHAVE_CONFIG_H \
 	-I$(TIGERVNC_SRCDIR)/unix/vncconfig $(XVNC_CPPFLAGS) ${XSERVERLIBS_CFLAGS} -I$(includedir) \
 	-I$(top_srcdir)/include
@@ -41,7 +42,7 @@
 Xvnc_CPPFLAGS = $(XVNC_CPPFLAGS) -DTIGERVNC -DNO_MODULE_EXTS \
 	-UHAVE_CONFIG_H \
 	-DXFree86Server -DVENDOR_RELEASE="$(VENDOR_RELEASE)" \
-	-DVENDOR_STRING="\"$(VENDOR_STRING)\"" -I$(TIGERVNC_SRCDIR)/common \
+	-DVENDOR_STRING="\"$(VENDOR_STRING)\"" -I$(TIGERVNC_SRCDIR)/common -I$(TIGERVNC_SRCDIR)/unix/common \
 	-I$(top_srcdir)/include ${XSERVERLIBS_CFLAGS} -I$(includedir)
 
 Xvnc_LDADD = $(XVNC_LIBS) libvnccommon.la $(COMMON_LIBS) \
@@ -58,6 +59,7 @@
 nodist_EXTRA_libvnc_la_SOURCES = dummy.cxx
 
 libvnc_la_CPPFLAGS = $(XVNC_CPPFLAGS) -I$(TIGERVNC_SRCDIR)/common -UHAVE_CONFIG_H \
+	-I$(TIGERVNC_SRCDIR)/unix/common \
 	-I$(top_srcdir)/hw/xfree86/common \
 	-I$(top_srcdir)/hw/xfree86/os-support \
 	-I$(top_srcdir)/hw/xfree86/os-support/bus \
diff --git a/unix/xserver/hw/vnc/RandrGlue.c b/unix/xserver/hw/vnc/RandrGlue.c
new file mode 100644
index 0000000..82e8552
--- /dev/null
+++ b/unix/xserver/hw/vnc/RandrGlue.c
@@ -0,0 +1,360 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2011-2015 Pierre Ossman 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_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include "scrnintstr.h"
+#include "randrstr.h"
+
+#include "RandrGlue.h"
+#include "XorgGlue.h"
+
+static int scrIdx;
+
+void vncSetGlueContext(int screenIndex);
+
+void vncSetGlueContext(int screenIndex)
+{
+  scrIdx = screenIndex;
+}
+
+int vncGetScreenWidth(void)
+{
+  return screenInfo.screens[scrIdx]->width;
+}
+
+int vncGetScreenHeight(void)
+{
+  return screenInfo.screens[scrIdx]->height;
+}
+
+int vncRandRIsValidScreenSize(int width, int height)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+
+  if (width < rp->minWidth || rp->maxWidth < width)
+    return 0;
+  if (height < rp->minHeight || rp->maxHeight < height)
+    return 0;
+
+  return 1;
+}
+
+int vncRandRResizeScreen(int width, int height)
+{
+  ScreenPtr pScreen = screenInfo.screens[scrIdx];
+
+  /* Try to retain DPI when we resize */
+  return RRScreenSizeSet(pScreen, width, height,
+                         pScreen->mmWidth * width / pScreen->width,
+                         pScreen->mmHeight * height / pScreen->height);
+}
+
+void vncRandRUpdateSetTime(void)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+  rp->lastSetTime = currentTime;
+}
+
+int vncRandRHasOutputClones(void)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+  for (int i = 0;i < rp->numCrtcs;i++) {
+    if (rp->crtcs[i]->numOutputs > 1)
+      return 1;
+  }
+  return 0;
+}
+
+int vncRandRGetOutputCount(void)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+  return rp->numOutputs;
+}
+
+int vncRandRGetAvailableOutputs(void)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+
+  int availableOutputs;
+  RRCrtcPtr *usedCrtcs;
+  int numUsed;
+
+  int i, j, k;
+
+  usedCrtcs = malloc(sizeof(RRCrtcPtr) * rp->numCrtcs);
+  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 < rp->numOutputs;i++) {
+    RROutputPtr output;
+
+    output = rp->outputs[i];
+
+    if (output->crtc != NULL)
+      availableOutputs++;
+    else {
+      for (j = 0;j < output->numCrtcs;j++) {
+        if (output->crtcs[j]->numOutputs != 0)
+          continue;
+
+        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;
+      }
+    }
+  }
+
+  free(usedCrtcs);
+
+  return availableOutputs;
+}
+
+char *vncRandRGetOutputName(int outputIdx)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+  return strdup(rp->outputs[outputIdx]->name);
+}
+
+int vncRandRIsOutputEnabled(int outputIdx)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+
+  if (rp->outputs[outputIdx]->crtc == NULL)
+    return 0;
+  if (rp->outputs[outputIdx]->crtc->mode == NULL)
+    return 0;
+
+  return 1;
+}
+
+int vncRandRIsOutputUsable(int outputIdx)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+
+  RROutputPtr output;
+  int i;
+
+  output = rp->outputs[outputIdx];
+  if (output->crtc != NULL)
+    return 1;
+
+  /* Any unused CRTCs? */
+  for (i = 0;i < output->numCrtcs;i++) {
+    if (output->crtcs[i]->numOutputs == 0)
+      return 1;
+  }
+
+  return 0;
+}
+
+int vncRandRIsOutputConnected(int outputIdx)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+
+  RROutputPtr output;
+
+  output = rp->outputs[outputIdx];
+  return (output->connection == RR_Connected);
+}
+
+static RRModePtr vncRandRGetMatchingMode(int outputIdx, int width, int height)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+
+  RROutputPtr output;
+
+  output = rp->outputs[outputIdx];
+
+  if (output->crtc != NULL) {
+    unsigned int swap;
+    switch (output->crtc->rotation) {
+    case RR_Rotate_90:
+    case RR_Rotate_270:
+      swap = width;
+      width = height;
+      height = swap;
+      break;
+    }
+  }
+
+  for (int i = 0; i < output->numModes; i++) {
+    if ((output->modes[i]->mode.width == width) &&
+        (output->modes[i]->mode.height == height))
+      return output->modes[i];
+  }
+
+  return NULL;
+}
+
+int vncRandRCheckOutputMode(int outputIdx, int width, int height)
+{
+  if (vncRandRGetMatchingMode(outputIdx, width, height) != NULL)
+    return 1;
+  if (vncRandRCanCreateModes())
+    return 1;
+  return 0;
+}
+
+int vncRandRDisableOutput(int outputIdx)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+  RRCrtcPtr crtc;
+  int i;
+  RROutputPtr *outputs;
+  int numOutputs = 0;
+  RRModePtr mode;
+  int ret;
+
+  crtc = rp->outputs[outputIdx]->crtc;
+  if (crtc == NULL)
+    return 1;
+
+  /* Remove this output from the CRTC configuration */
+  outputs = malloc(crtc->numOutputs * sizeof(RROutputPtr));
+  if (!outputs) {
+    return 0;
+  }
+
+  for (i = 0; i < crtc->numOutputs; i++) {
+    if (rp->outputs[outputIdx] != crtc->outputs[i]) {
+      outputs[numOutputs++] = crtc->outputs[i];
+    }
+  }
+
+  if (numOutputs == 0) {
+    mode = NULL;
+  } else {
+    mode = crtc->mode;
+  }
+
+  ret = RRCrtcSet(crtc, mode, crtc->x, crtc->y, crtc->rotation, numOutputs, outputs);
+  free(outputs);
+  return ret;
+}
+
+unsigned int vncRandRGetOutputId(int outputIdx)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+  return rp->outputs[outputIdx]->id;
+}
+
+int vncRandRGetOutputDimensions(int outputIdx,
+                            int *x, int *y, int *width, int *height)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+  RRCrtcPtr crtc;
+  int swap;
+  *x = *y = *width = *height = 0;
+
+  crtc = rp->outputs[outputIdx]->crtc;
+  if (crtc == NULL || !crtc->mode)
+    return 1;
+
+  *x = crtc->x;
+  *y = crtc->y;
+  *width = crtc->mode->mode.width;
+  *height = crtc->mode->mode.height;
+
+  switch (crtc->rotation & 0xf) {
+  case RR_Rotate_90:
+  case RR_Rotate_270:
+    swap = *width;
+    *width = *height;
+    *height = swap;
+    break;
+  }
+  return 0;
+}
+
+int vncRandRReconfigureOutput(int outputIdx, int x, int y,
+                              int width, int height)
+{
+  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
+
+  RROutputPtr output;
+  RRCrtcPtr crtc;
+  RRModePtr mode;
+
+  int i;
+
+  output = rp->outputs[outputIdx];
+  crtc = output->crtc;
+
+  /* Need a CRTC? */
+  if (crtc == NULL) {
+    for (i = 0;i < output->numCrtcs;i++) {
+      if (output->crtcs[i]->numOutputs != 0)
+        continue;
+
+      crtc = output->crtcs[i];
+      break;
+    }
+
+    /* Couldn't find one... */
+    if (crtc == NULL)
+      return 0;
+  }
+
+  /* Make sure we have the mode we want */
+  mode = vncRandRGetMatchingMode(outputIdx, width, height);
+  if (mode == NULL) {
+    mode = vncRandRCreateMode(output, width, height);
+    if (mode == NULL)
+      return 0;
+  }
+  mode = vncRandRSetPreferredMode(output, mode);
+  if (mode == NULL)
+    return 0;
+
+  /* Reconfigure new mode and position */
+  return RRCrtcSet(crtc, mode, x, y, crtc->rotation, 1, &output);
+}
+
+int vncRandRCanCreateOutputs(int extraOutputs)
+{
+  return vncRandRCanCreateScreenOutputs(scrIdx, extraOutputs);
+}
+
+int vncRandRCreateOutputs(int extraOutputs)
+{
+  return vncRandRCreateScreenOutputs(scrIdx, extraOutputs);
+}
diff --git a/unix/xserver/hw/vnc/XorgGlue.c b/unix/xserver/hw/vnc/XorgGlue.c
index 141023a..10ba98d 100644
--- a/unix/xserver/hw/vnc/XorgGlue.c
+++ b/unix/xserver/hw/vnc/XorgGlue.c
@@ -114,253 +114,3 @@
   return screenInfo.screens[scrIdx]->y;
 }
 
-int vncGetScreenWidth(int scrIdx)
-{
-  return screenInfo.screens[scrIdx]->width;
-}
-
-int vncGetScreenHeight(int scrIdx)
-{
-  return screenInfo.screens[scrIdx]->height;
-}
-
-int vncRandRResizeScreen(int scrIdx, int width, int height)
-{
-#ifdef RANDR
-  ScreenPtr pScreen = screenInfo.screens[scrIdx];
-  /* Try to retain DPI when we resize */
-  return RRScreenSizeSet(pScreen, width, height,
-                         pScreen->mmWidth * width / pScreen->width,
-                         pScreen->mmHeight * height / pScreen->height);
-#else
-  return -1;
-#endif
-}
-
-void vncRandRUpdateSetTime(int scrIdx)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-  rp->lastSetTime = currentTime;
-#endif
-}
-
-int vncRandRHasOutputClones(int scrIdx)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-  for (int i = 0;i < rp->numCrtcs;i++) {
-    if (rp->crtcs[i]->numOutputs > 1)
-      return 1;
-  }
-#endif
-  return 0;
-}
-
-int vncRandRGetOutputCount(int scrIdx)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-  return rp->numOutputs;
-#else
-  return 0;
-#endif
-}
-
-int vncRandRGetAvailableOutputs(int scrIdx)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-
-  int availableOutputs;
-  RRCrtcPtr *usedCrtcs;
-  int numUsed;
-
-  int i, j, k;
-
-  usedCrtcs = malloc(sizeof(RRCrtcPtr) * rp->numCrtcs);
-  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 < rp->numOutputs;i++) {
-    RROutputPtr output;
-
-    output = rp->outputs[i];
-
-    if (output->crtc != NULL)
-      availableOutputs++;
-    else {
-      for (j = 0;j < output->numCrtcs;j++) {
-        if (output->crtcs[j]->numOutputs != 0)
-          continue;
-
-        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;
-      }
-    }
-  }
-
-  free(usedCrtcs);
-
-  return availableOutputs;
-#else
-  return 0;
-#endif
-}
-
-const char *vncRandRGetOutputName(int scrIdx, int outputIdx)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-  return rp->outputs[outputIdx]->name;
-#else
-  return "";
-#endif
-}
-
-int vncRandRIsOutputEnabled(int scrIdx, int outputIdx)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-
-  if (rp->outputs[outputIdx]->crtc == NULL)
-    return 0;
-  if (rp->outputs[outputIdx]->crtc->mode == NULL)
-    return 0;
-
-  return 1;
-#else
-  return 0;
-#endif
-}
-
-int vncRandRIsOutputUsable(int scrIdx, int outputIdx)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-
-  RROutputPtr output;
-  int i;
-
-  output = rp->outputs[outputIdx];
-  if (output->crtc != NULL)
-    return 1;
-
-  /* Any unused CRTCs? */
-  for (i = 0;i < output->numCrtcs;i++) {
-    if (output->crtcs[i]->numOutputs == 0)
-      return 1;
-  }
-#endif
-
-  return 0;
-}
-
-int vncRandRDisableOutput(int scrIdx, int outputIdx)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-  RRCrtcPtr crtc;
-
-  crtc = rp->outputs[outputIdx]->crtc;
-  if (crtc == NULL)
-    return 0;
-
-  return RRCrtcSet(crtc, NULL, crtc->x, crtc->y, crtc->rotation, 0, NULL);
-#else
-  return -1;
-#endif
-}
-
-intptr_t vncRandRGetOutputId(int scrIdx, int outputIdx)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-  return (intptr_t)rp->outputs[outputIdx];
-#else
-  return 0;
-#endif
-}
-
-void vncRandRGetOutputDimensions(int scrIdx, int outputIdx,
-                            int *x, int *y, int *width, int *height)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-  int swap;
-
-  *x = rp->outputs[outputIdx]->crtc->x;
-  *y = rp->outputs[outputIdx]->crtc->y;
-  *width = rp->outputs[outputIdx]->crtc->mode->mode.width;
-  *height = rp->outputs[outputIdx]->crtc->mode->mode.height;
-
-  switch (rp->outputs[outputIdx]->crtc->rotation & 0xf) {
-  case RR_Rotate_90:
-  case RR_Rotate_270:
-    swap = *width;
-    *width = *height;
-    *height = swap;
-    break;
-  }
-#endif
-}
-
-int vncRandRReconfigureOutput(int scrIdx, int outputIdx, int x, int y,
-                              int width, int height)
-{
-#ifdef RANDR
-  rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
-
-  RROutputPtr output;
-  RRCrtcPtr crtc;
-  RRModePtr mode;
-
-  int i;
-
-  output = rp->outputs[outputIdx];
-  crtc = output->crtc;
-
-  /* Need a CRTC? */
-  if (crtc == NULL) {
-    for (i = 0;i < output->numCrtcs;i++) {
-      if (output->crtcs[i]->numOutputs != 0)
-        continue;
-
-      crtc = output->crtcs[i];
-      break;
-    }
-
-    /* Couldn't find one... */
-    if (crtc == NULL)
-      return -1;
-  }
-
-  /* Make sure we have the mode we want */
-  mode = vncRandRCreatePreferredMode(output, width, height);
-  if (mode == NULL)
-    return -1;
-
-  /* Reconfigure new mode and position */
-  return RRCrtcSet(crtc, mode, x, y, crtc->rotation, 1, &output);
-#else
-  return -1;
-#endif
-}
diff --git a/unix/xserver/hw/vnc/XorgGlue.h b/unix/xserver/hw/vnc/XorgGlue.h
index 63227ac..5d01949 100644
--- a/unix/xserver/hw/vnc/XorgGlue.h
+++ b/unix/xserver/hw/vnc/XorgGlue.h
@@ -45,34 +45,14 @@
 
 int vncGetScreenX(int scrIdx);
 int vncGetScreenY(int scrIdx);
-int vncGetScreenWidth(int scrIdx);
-int vncGetScreenHeight(int scrIdx);
-
-int vncRandRResizeScreen(int scrIdx, int width, int height);
-void vncRandRUpdateSetTime(int scrIdx);
-
-int vncRandRHasOutputClones(int scrIdx);
-
-int vncRandRGetOutputCount(int scrIdx);
-int vncRandRGetAvailableOutputs(int scrIdx);
-
-const char *vncRandRGetOutputName(int scrIdx, int outputIdx);
-
-int vncRandRIsOutputEnabled(int scrIdx, int outputIdx);
-int vncRandRIsOutputUsable(int scrIdx, int outputIdx);
-
-int vncRandRDisableOutput(int scrIdx, int outputIdx);
-int vncRandRReconfigureOutput(int scrIdx, int outputIdx, int x, int y,
-                              int width, int height);
-
-intptr_t vncRandRGetOutputId(int scrIdx, int outputIdx);
-void vncRandRGetOutputDimensions(int scrIdx, int outputIdx,
-                                 int *x, int *y, int *width, int *height);
 
 // These hide in xvnc.c or vncModule.c
 void vncClientGone(int fd);
-int vncRandRCreateOutputs(int scrIdx, int extraOutputs);
-void *vncRandRCreatePreferredMode(void *output, int width, int height);
+int vncRandRCanCreateScreenOutputs(int scrIdx, int extraOutputs);
+int vncRandRCreateScreenOutputs(int scrIdx, int extraOutputs);
+int vncRandRCanCreateModes(void);
+void* vncRandRCreateMode(void* output, int width, int height);
+void* vncRandRSetPreferredMode(void* output, void* mode);
 
 #ifdef __cplusplus
 }
diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc
index c68a077..080943d 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.cc
+++ b/unix/xserver/hw/vnc/XserverDesktop.cc
@@ -48,6 +48,10 @@
 #include "XorgGlue.h"
 #include "Input.h"
 
+extern "C" {
+void vncSetGlueContext(int screenIndex);
+}
+
 using namespace rfb;
 using namespace network;
 
@@ -185,68 +189,16 @@
   data = (rdr::U8*)fbptr;
   stride = stride_;
 
-  layout = computeScreenLayout();
+  vncSetGlueContext(screenIndex);
+  layout = ::computeScreenLayout(&outputIdMap);
 
   server->setPixelBuffer(this, layout);
 }
 
 void XserverDesktop::refreshScreenLayout()
 {
-  server->setScreenLayout(computeScreenLayout());
-}
-
-ScreenSet XserverDesktop::computeScreenLayout()
-{
-  ScreenSet layout;
-  OutputIdMap newIdMap;
-
-  for (int i = 0;i < vncRandRGetOutputCount(screenIndex);i++) {
-      intptr_t outputId;
-      int x, y, width, height;
-
-      /* Disabled? */
-      if (!vncRandRIsOutputEnabled(screenIndex, i))
-          continue;
-
-      outputId = vncRandRGetOutputId(screenIndex, i);
-
-      /* Known output? */
-      if (outputIdMap.count(outputId) == 1)
-        newIdMap[outputId] = outputIdMap[outputId];
-      else {
-        rdr::U32 id;
-        OutputIdMap::const_iterator iter;
-
-        while (true) {
-          id = rand();
-          for (iter = outputIdMap.begin();iter != outputIdMap.end();++iter) {
-            if (iter->second == id)
-              break;
-          }
-          if (iter == outputIdMap.end())
-            break;
-        }
-
-        newIdMap[outputId] = id;
-      }
-
-      vncRandRGetOutputDimensions(screenIndex, i, &x, &y, &width, &height);
-
-      layout.add_screen(Screen(newIdMap[outputId], x, y, width, height, 0));
-  }
-
-  /* Only keep the entries that are currently active */
-  outputIdMap = newIdMap;
-
-  /*
-   * 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, vncGetScreenWidth(screenIndex),
-                             vncGetScreenHeight(screenIndex), 0));
-
-  return layout;
+  vncSetGlueContext(screenIndex);
+  server->setScreenLayout(::computeScreenLayout(&outputIdMap));
 }
 
 char* XserverDesktop::substitute(const char* varName)
@@ -605,164 +557,14 @@
 unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
                                              const rfb::ScreenSet& layout)
 {
-  int ret;
-  int availableOutputs;
-
-  // RandR support?
-  if (vncRandRGetOutputCount(screenIndex) == 0)
-    return rfb::resultProhibited;
-
   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);
 
-  /*
-   * First check that we don't have any active clone modes. That's just
-   * too messy to deal with.
-   */
-  if (vncRandRHasOutputClones(screenIndex)) {
-    vlog.error("Clone mode active. Refusing to touch screen layout.");
-    return rfb::resultInvalid;
-  }
-
-  /* Next count how many useful outputs we have... */
-  availableOutputs = vncRandRGetAvailableOutputs(screenIndex);
-
-  /* Try to create more outputs if needed... (only works on Xvnc) */
-  if (layout.num_screens() > availableOutputs) {
-    vlog.debug("Insufficient screens. Need to create %d more.",
-               layout.num_screens() - availableOutputs);
-    ret = vncRandRCreateOutputs(screenIndex,
-                                layout.num_screens() - availableOutputs);
-    if (ret < 0) {
-      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 != vncGetScreenWidth(screenIndex)) ||
-      (fb_height != vncGetScreenHeight(screenIndex))) {
-    ret = vncRandRResizeScreen(screenIndex, fb_width, fb_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 < vncRandRGetOutputCount(screenIndex);i++) {
-    intptr_t output;
-
-    ScreenSet::const_iterator iter;
-
-    output = vncRandRGetOutputId(screenIndex, i);
-
-    /* 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 = vncRandRDisableOutput(screenIndex, i);
-      if (!ret) {
-        vlog.error("Failed to disable unused output '%s'",
-                   vncRandRGetOutputName(screenIndex, i));
-        return rfb::resultInvalid;
-      }
-      outputIdMap.erase(output);
-      continue;
-    }
-
-    /* Reconfigure new mode and position */
-    ret = vncRandRReconfigureOutput(screenIndex, i,
-                                    iter->dimensions.tl.x,
-                                    iter->dimensions.tl.y,
-                                    iter->dimensions.width(),
-                                    iter->dimensions.height());
-    if (!ret) {
-      vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d",
-                 vncRandRGetOutputName(screenIndex, i),
-                 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;
-    intptr_t output;
-    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 < vncRandRGetOutputCount(screenIndex);i++) {
-      output = vncRandRGetOutputId(screenIndex, i);
-
-      /* In use? */
-      if (outputIdMap.count(output) == 1)
-        continue;
-
-      /* Can it be used? */
-      if (!vncRandRIsOutputUsable(screenIndex, i))
-        continue;
-
-      break;
-    }
-
-    /* Shouldn't happen */
-    if (i == vncRandRGetOutputCount(screenIndex))
-        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 = vncRandRReconfigureOutput(screenIndex, i,
-                                    iter->dimensions.tl.x,
-                                    iter->dimensions.tl.y,
-                                    iter->dimensions.width(),
-                                    iter->dimensions.height());
-    if (!ret) {
-      vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d",
-                 vncRandRGetOutputName(screenIndex, i),
-                 iter->dimensions.width(), iter->dimensions.height(),
-                 iter->dimensions.tl.x, iter->dimensions.tl.y);
-      return rfb::resultInvalid;
-    }
-  }
-
-  /*
-   * Update timestamp for when screen layout was last changed.
-   * This is normally done in the X11 request handlers, which is
-   * why we have to deal with it manually here.
-   */
-  vncRandRUpdateSetTime(screenIndex);
-
-  return rfb::resultSuccess;
+  vncSetGlueContext(screenIndex);
+  return ::setScreenLayout(fb_width, fb_height, layout, &outputIdMap);
 }
 
 void XserverDesktop::grabRegion(const rfb::Region& region)
diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h
index 2a378ea..f1c3e3e 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.h
+++ b/unix/xserver/hw/vnc/XserverDesktop.h
@@ -37,6 +37,7 @@
 #include <rfb/Configuration.h>
 #include <rfb/VNCServerST.h>
 #include <rdr/SubstitutingInStream.h>
+#include <unixcommon.h>
 #include "Input.h"
 
 namespace rfb {
@@ -117,7 +118,6 @@
   virtual bool handleTimeout(rfb::Timer* t);
 
 private:
-  rfb::ScreenSet computeScreenLayout();
 
   int screenIndex;
   rfb::VNCServerST* server;
@@ -132,10 +132,7 @@
   rfb::CharArray queryConnectUsername;
   rfb::Timer queryConnectTimer;
 
-#ifdef RANDR
-  typedef std::map<intptr_t, rdr::U32> OutputIdMap;
   OutputIdMap outputIdMap;
-#endif
 
   rfb::Point oldCursorPos;
 };
diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc
index c47dfa7..54cca95 100644
--- a/unix/xserver/hw/vnc/vncExtInit.cc
+++ b/unix/xserver/hw/vnc/vncExtInit.cc
@@ -41,8 +41,13 @@
 #include "vncBlockHandler.h"
 #include "vncSelection.h"
 #include "XorgGlue.h"
+#include "RandrGlue.h"
 #include "xorg-version.h"
 
+extern "C" {
+void vncSetGlueContext(int screenIndex);
+}
+
 using namespace rfb;
 
 static rfb::LogWriter vlog("vncext");
@@ -211,13 +216,14 @@
         CharArray desktopNameStr(desktopName.getData());
         PixelFormat pf = vncGetPixelFormat(scr);
 
+        vncSetGlueContext(scr);
         desktop[scr] = new XserverDesktop(scr,
                                           listeners,
                                           httpListeners,
                                           desktopNameStr.buf,
                                           pf,
-                                          vncGetScreenWidth(scr),
-                                          vncGetScreenHeight(scr),
+                                          vncGetScreenWidth(),
+                                          vncGetScreenHeight(),
                                           vncFbptr[scr],
                                           vncFbstride[scr]);
         vlog.info("created VNC server for screen %d", scr);
diff --git a/unix/xserver/hw/vnc/vncHooks.c b/unix/xserver/hw/vnc/vncHooks.c
index c0d9f7c..f3c2520 100644
--- a/unix/xserver/hw/vnc/vncHooks.c
+++ b/unix/xserver/hw/vnc/vncHooks.c
@@ -38,9 +38,7 @@
 #ifdef RENDER
 #include "picturestr.h"
 #endif
-#ifdef RANDR
 #include "randrstr.h"
-#endif
 
 #define DBGPRINT(x) //(fprintf x)
 
@@ -79,11 +77,9 @@
   TriFanProcPtr                TriFan;
 #endif
 #endif
-#ifdef RANDR
   RRSetConfigProcPtr           rrSetConfig;
   RRScreenSetSizeProcPtr       rrScreenSetSize;
   RRCrtcSetProcPtr             rrCrtcSet;
-#endif
 } vncHooksScreenRec, *vncHooksScreenPtr;
 
 typedef struct _vncHooksGCRec {
@@ -174,7 +170,6 @@
             int npoint, xPointFixed * points);
 #endif
 #endif
-#ifdef RANDR
 static Bool vncHooksRandRSetConfig(ScreenPtr pScreen, Rotation rotation,
                                    int rate, RRScreenSizePtr pSize);
 static Bool vncHooksRandRScreenSetSize(ScreenPtr pScreen,
@@ -184,7 +179,6 @@
                                  RRModePtr mode, int x, int y,
                                  Rotation rotation, int numOutputs,
                                  RROutputPtr *outputs);
-#endif
 
 // GC "funcs"
 
@@ -277,9 +271,7 @@
 #ifdef RENDER
   PictureScreenPtr ps;
 #endif
-#ifdef RANDR
   rrScrPrivPtr rp;
-#endif
 
   pScreen = screenInfo.screens[scrIdx];
 
@@ -339,7 +331,6 @@
 #endif
   }
 #endif
-#ifdef RANDR
   rp = rrGetScrPriv(pScreen);
   if (rp) {
     /* Some RandR callbacks are optional */
@@ -350,7 +341,6 @@
     if (rp->rrCrtcSet)
       wrap(vncHooksScreen, rp, rrCrtcSet, vncHooksRandRCrtcSet);
   }
-#endif
 
   return TRUE;
 }
@@ -473,9 +463,7 @@
 #ifdef RENDER
   PictureScreenPtr ps;
 #endif
-#ifdef RANDR
   rrScrPrivPtr rp;
-#endif
 
   SCREEN_PROLOGUE(pScreen_, CloseScreen);
 
@@ -501,14 +489,12 @@
 #endif
   }
 #endif
-#ifdef RANDR
   rp = rrGetScrPriv(pScreen);
   if (rp) {
     unwrap(vncHooksScreen, rp, rrSetConfig);
     unwrap(vncHooksScreen, rp, rrScreenSetSize);
     unwrap(vncHooksScreen, rp, rrCrtcSet);
   }
-#endif
 
   DBGPRINT((stderr,"vncHooksCloseScreen: unwrapped screen functions\n"));
 
@@ -1199,8 +1185,6 @@
 
 #endif /* RENDER */
 
-#ifdef RANDR
-
 // Unwrap and rewrap helpers
 
 #define RANDR_PROLOGUE(field)                                             \
@@ -1275,8 +1259,6 @@
   return TRUE;
 }
 
-#endif /* RANDR */
-
 /////////////////////////////////////////////////////////////////////////////
 //
 // GC "funcs"
diff --git a/unix/xserver/hw/vnc/vncModule.c b/unix/xserver/hw/vnc/vncModule.c
index 5b4db96..451f527 100644
--- a/unix/xserver/hw/vnc/vncModule.c
+++ b/unix/xserver/hw/vnc/vncModule.c
@@ -24,9 +24,7 @@
 #endif
 
 #include "opaque.h"
-#ifdef RANDR
 #include "randrstr.h"
-#endif
 
 #include "xorg-version.h"
 #if XORG <= 111
@@ -39,6 +37,7 @@
 #include "vncExtInit.h"
 #include "RFBGlue.h"
 #include "XorgGlue.h"
+#include "RandrGlue.h"
 
 static void vncModuleInit(INITARGS);
 
@@ -111,29 +110,31 @@
 {
 }
 
-#ifdef RANDR
-int vncRandRCreateOutputs(int scrIdx, int extraOutputs)
+int vncRandRCanCreateScreenOutputs(int scrIdx, int extraOutputs)
 {
-  return -1;
+  return 0;
 }
 
-void *vncRandRCreatePreferredMode(void *out, int width, int height)
+int vncRandRCreateScreenOutputs(int scrIdx, int extraOutputs)
 {
-  RROutputPtr output;
+  return 0;
+}
 
+int vncRandRCanCreateModes(void)
+{
+  return 0;
+}
+
+void* vncRandRCreateMode(void* output, int width, int height)
+{
+  return 0;
+}
+
+void* vncRandRSetPreferredMode(void* output, void* mode)
+{
   /*
-   * 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.
+   * We're not going to change which modes are preferred,
+   * so just return the incoming mode.
    */
-
-  output = out;
-
-  for (int i = 0;i < output->numModes;i++) {
-    if ((output->modes[i]->mode.width == width) &&
-        (output->modes[i]->mode.height == height))
-      return output->modes[i];
-  }
-
-  return NULL;
+  return mode;
 }
-#endif
diff --git a/unix/xserver/hw/vnc/xvnc.c b/unix/xserver/hw/vnc/xvnc.c
index 6924f65..57152cd 100644
--- a/unix/xserver/hw/vnc/xvnc.c
+++ b/unix/xserver/hw/vnc/xvnc.c
@@ -35,6 +35,7 @@
 #include "vncExtInit.h"
 #include "RFBGlue.h"
 #include "XorgGlue.h"
+#include "RandrGlue.h"
 #include "xorg-version.h"
 
 #ifdef WIN32
@@ -72,9 +73,7 @@
 #include "os.h"
 #include "miline.h"
 #include "inputstr.h"
-#ifdef RANDR
 #include "randrstr.h"
-#endif /* RANDR */
 #ifdef DPMSExtension
 #include "dpmsproc.h"
 #endif
@@ -952,7 +951,6 @@
     miPointerWarpCursor
 };
 
-#ifdef RANDR
 
 static Bool vncRandRGetInfo (ScreenPtr pScreen, Rotation *rotations)
 {
@@ -1379,41 +1377,87 @@
 }
 
 /* Used from XserverDesktop when it needs more outputs... */
-int vncRandRCreateOutputs(int scrIdx, int extraOutputs)
+
+int vncRandRCanCreateScreenOutputs(int scrIdx, int extraOutputs)
+{
+    return 1;
+}
+
+int vncRandRCreateScreenOutputs(int scrIdx, int extraOutputs)
 {
     RRCrtcPtr crtc;
 
     while (extraOutputs > 0) {
         crtc = vncRandRCrtcCreate(screenInfo.screens[scrIdx]);
         if (crtc == NULL)
-            return -1;
+            return 0;
         extraOutputs--;
     }
 
-    return 0;
+    return 1;
 }
 
-/* Used to create a preferred mode from various places */
-void *vncRandRCreatePreferredMode(void *out, int width, int height)
+/* Creating and modifying modes, used by XserverDesktop and init here */
+
+int vncRandRCanCreateModes()
+{
+    return 1;
+}
+
+void* vncRandRCreateMode(void* out, int width, int height)
 {
     RROutputPtr output;
 
     output = out;
 
+    /* Do we already have the mode? */
+    for (int i = 0; i < output->numModes; i++) {
+        if ((output->modes[i]->mode.width == width) &&
+            (output->modes[i]->mode.height == height))
+            return output->modes[i];
+    }
+
+    /* Just recreate the entire list */
+    vncRandRSetModes(output, width, height);
+
+    /* Find the new mode */
+    for (int i = 0; i < output->numModes; i++) {
+        if ((output->modes[i]->mode.width == width) &&
+            (output->modes[i]->mode.height == height))
+            return output->modes[i];
+    }
+
+    /* Something went horribly wrong */
+    return NULL;
+}
+
+void* vncRandRSetPreferredMode(void* out, void* m)
+{
+    RRModePtr mode;
+    RROutputPtr output;
+    int width, height;
+
+    mode = m;
+    output = out;
+
+    width = mode->mode.width;
+    height = mode->mode.height;
+
     /* Already the preferred mode? */
     if ((output->numModes >= 1) && (output->numPreferred == 1) &&
-        (output->modes[0]->mode.width == width) &&
-        (output->modes[0]->mode.height == height))
-        return output->modes[0];
+        (output->modes[0] == mode))
+        return mode;
 
     /* Recreate the list, with the mode we want as preferred */
     vncRandRSetModes(output, width, height);
 
+    /* Sanity check */
     if ((output->numModes >= 1) && (output->numPreferred == 1) &&
         (output->modes[0]->mode.width == width) &&
         (output->modes[0]->mode.height == height))
         return output->modes[0];
 
+    /* Something went horribly wrong */
     return NULL;
 }
 
@@ -1436,8 +1480,11 @@
     crtc = vncRandRCrtcCreate(pScreen);
 
     /* Make sure the current screen size is the active mode */
-    mode = vncRandRCreatePreferredMode(crtc->outputs[0],
-                                       pScreen->width, pScreen->height);
+    mode = vncRandRCreateMode(crtc->outputs[0],
+                              pScreen->width, pScreen->height);
+    if (mode == NULL)
+        return FALSE;
+    mode = vncRandRSetPreferredMode(crtc->outputs[0], mode);
     if (mode == NULL)
         return FALSE;
 
@@ -1449,7 +1496,6 @@
     return TRUE;
 }
 
-#endif
 
 static Bool
 #if XORG < 113
@@ -1506,9 +1552,7 @@
     int ret;
     void *pbits;
 
-#ifdef RANDR
     rrScrPrivPtr rp;
-#endif
 
 #if XORG >= 113
     if (!dixRegisterPrivateKey(&cmapScrPrivateKeyRec, PRIVATE_SCREEN, 0))
@@ -1644,7 +1688,6 @@
     pvfb->closeScreen = pScreen->CloseScreen;
     pScreen->CloseScreen = vfbCloseScreen;
 
-#ifdef RANDR
     ret = RRScreenInit(pScreen);
     if (!ret) return FALSE;
 
@@ -1659,7 +1702,6 @@
 
     ret = vncRandRInit(pScreen);
     if (!ret) return FALSE;
-#endif
 
 
   return TRUE;
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 6c10f35..4a44f40 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -559,7 +559,7 @@
     vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(buffer));
 
     try {
-      cc->writer()->clientCutText(buffer, ret);
+      cc->writer()->writeClientCutText(buffer, ret);
     } catch (rdr::Exception& e) {
       vlog.error("%s", e.str());
       exit_vncviewer(e.str());
@@ -743,7 +743,7 @@
   if (pendingClientCutText) {
     size_t len = strlen(pendingClientCutText);
     vlog.debug("Sending pending clipboard data (%d bytes)", (int)len);
-    cc->writer()->clientCutText(pendingClientCutText, len);
+    cc->writer()->writeClientCutText(pendingClientCutText, len);
   }
 
   clearPendingClipboard();
@@ -755,7 +755,7 @@
   if (!viewOnly) {
     if (pointerEventInterval == 0 || buttonMask != lastButtonMask) {
       try {
-        cc->writer()->pointerEvent(pos, buttonMask);
+        cc->writer()->writePointerEvent(pos, buttonMask);
       } catch (rdr::Exception& e) {
         vlog.error("%s", e.str());
         exit_vncviewer(e.str());
@@ -778,7 +778,8 @@
   assert(self);
 
   try {
-    self->cc->writer()->pointerEvent(self->lastPointerPos, self->lastButtonMask);
+    self->cc->writer()->writePointerEvent(self->lastPointerPos,
+                                          self->lastButtonMask);
   } catch (rdr::Exception& e) {
     vlog.error("%s", e.str());
     exit_vncviewer(e.str());
@@ -845,9 +846,9 @@
   try {
     // Fake keycode?
     if (keyCode > 0xff)
-      cc->writer()->keyEvent(keySym, 0, true);
+      cc->writer()->writeKeyEvent(keySym, 0, true);
     else
-      cc->writer()->keyEvent(keySym, keyCode, true);
+      cc->writer()->writeKeyEvent(keySym, keyCode, true);
   } catch (rdr::Exception& e) {
     vlog.error("%s", e.str());
     exit_vncviewer(e.str());
@@ -879,9 +880,9 @@
 
   try {
     if (keyCode > 0xff)
-      cc->writer()->keyEvent(iter->second, 0, false);
+      cc->writer()->writeKeyEvent(iter->second, 0, false);
     else
-      cc->writer()->keyEvent(iter->second, keyCode, false);
+      cc->writer()->writeKeyEvent(iter->second, keyCode, false);
   } catch (rdr::Exception& e) {
     vlog.error("%s", e.str());
     exit_vncviewer(e.str());
diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h
index 7bfc418..d9d73a8 100644
--- a/vncviewer/Viewport.h
+++ b/vncviewer/Viewport.h
@@ -22,6 +22,8 @@
 
 #include <map>
 
+#include <rfb/Rect.h>
+
 #include <FL/Fl_Widget.H>
 
 class Fl_Menu_Button;