Move computeScreenLayout/setScreenLayout to unixcommon lib
These two code blocks are not specific to Xvnc/vnc.so, but useful for
x0vncserver as well. RandrGlue.h defines the interface on which
unixcommon depends on.
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..40d2d2e
--- /dev/null
+++ b/unix/common/RandrGlue.h
@@ -0,0 +1,64 @@
+/* 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(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);
+
+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);
+
+unsigned int vncRandRGetOutputId(int scrIdx, int outputIdx);
+void vncRandRGetOutputDimensions(int scrIdx, int outputIdx,
+ int *x, int *y, int *width, int *height);
+
+int vncRandRCreateOutputs(int scrIdx, int extraOutputs);
+void *vncRandRCreatePreferredMode(void *output, int width, int height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/unix/common/randr.cxx b/unix/common/randr.cxx
new file mode 100644
index 0000000..44e46de
--- /dev/null
+++ b/unix/common/randr.cxx
@@ -0,0 +1,249 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2009-2017 Pierre Ossman 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");
+
+rfb::ScreenSet computeScreenLayout(int screenIndex, OutputIdMap *outputIdMap)
+{
+ rfb::ScreenSet layout;
+ OutputIdMap newIdMap;
+
+ for (int i = 0;i < vncRandRGetOutputCount(screenIndex);i++) {
+ unsigned int 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(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(screenIndex),
+ vncGetScreenHeight(screenIndex), 0));
+
+ return layout;
+}
+
+unsigned int setScreenLayout(int screenIndex,
+ int fb_width, int fb_height, const rfb::ScreenSet& layout,
+ OutputIdMap *outputIdMap)
+{
+ int ret;
+ int availableOutputs;
+
+ // RandR support?
+ if (vncRandRGetOutputCount(screenIndex) == 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(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++) {
+ unsigned int output;
+
+ rfb::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) {
+ char *name = vncRandRGetOutputName(screenIndex, i);
+ vlog.error("Failed to disable unused output '%s'",
+ name);
+ free(name);
+ 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) {
+ char *name = vncRandRGetOutputName(screenIndex, 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;
+ }
+ }
+
+ /* Finally, 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 */
+ 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) {
+ char *name = vncRandRGetOutputName(screenIndex, 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;
+ }
+ }
+
+ /*
+ * 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;
+}
diff --git a/unix/common/unixcommon.h b/unix/common/unixcommon.h
new file mode 100644
index 0000000..66fe84c
--- /dev/null
+++ b/unix/common/unixcommon.h
@@ -0,0 +1,40 @@
+/* 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(int screenIndex, OutputIdMap *outputIdMap);
+
+unsigned int setScreenLayout(int screenIndex,
+ int fb_width, int fb_height, const rfb::ScreenSet& layout,
+ OutputIdMap *outputIdMap);
+
+
+#endif /* UNIXCOMMON_H */