diff options
author | Pierre Ossman <ossman@cendio.se> | 2018-04-10 08:11:46 +0200 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2018-04-10 08:11:46 +0200 |
commit | 382d3735f0b32792a2cc17be3bb9845dab953ab7 (patch) | |
tree | a9c1b88bd5313b57bce50998770594e86aa81433 | |
parent | 4ae358d561463f1a2dc763fec885be5e4932f394 (diff) | |
parent | 242c5b2c8af6d89a34ff83089e9e724e32dcb279 (diff) | |
download | tigervnc-382d3735f0b32792a2cc17be3bb9845dab953ab7.tar.gz tigervnc-382d3735f0b32792a2cc17be3bb9845dab953ab7.zip |
Merge branch 'x0vncserver-randr' of https://github.com/x11clone/x11clone
-rw-r--r-- | cmake/StaticBuild.cmake | 3 | ||||
-rw-r--r-- | unix/CMakeLists.txt | 2 | ||||
-rw-r--r-- | unix/common/CMakeLists.txt | 14 | ||||
-rw-r--r-- | unix/common/RandrGlue.h | 68 | ||||
-rw-r--r-- | unix/common/randr.cxx | 439 | ||||
-rw-r--r-- | unix/common/unixcommon.h | 48 | ||||
-rw-r--r-- | unix/x0vncserver/CMakeLists.txt | 11 | ||||
-rw-r--r-- | unix/x0vncserver/Geometry.cxx | 12 | ||||
-rw-r--r-- | unix/x0vncserver/Geometry.h | 1 | ||||
-rw-r--r-- | unix/x0vncserver/RandrGlue.c | 511 | ||||
-rw-r--r-- | unix/x0vncserver/XDesktop.cxx | 289 | ||||
-rw-r--r-- | unix/x0vncserver/XDesktop.h | 10 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/Makefile.am | 10 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/RandrGlue.c | 360 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/XorgGlue.c | 250 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/XorgGlue.h | 30 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/XserverDesktop.cc | 218 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/XserverDesktop.h | 5 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/vncExtInit.cc | 10 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/vncHooks.c | 18 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/vncModule.c | 41 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/xvnc.c | 78 |
22 files changed, 1872 insertions, 556 deletions
diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake index 06883c6f..4b58b1de 100644 --- a/cmake/StaticBuild.cmake +++ b/cmake/StaticBuild.cmake @@ -109,6 +109,9 @@ if(BUILD_STATIC) 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/unix/CMakeLists.txt b/unix/CMakeLists.txt index 8b2c70eb..7a1457df 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 00000000..611e1956 --- /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 00000000..5cc57003 --- /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 00000000..95c1f332 --- /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 00000000..43191fb6 --- /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 5930e32a..8beade7e 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 @@ add_executable(x0vncserver 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 @@ else() 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 48c18426..d9114713 100644 --- a/unix/x0vncserver/Geometry.cxx +++ b/unix/x0vncserver/Geometry.cxx @@ -35,10 +35,16 @@ StringParameter Geometry::m_geometryParam("Geometry", ""); 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 98bafb24..d938d63f 100644 --- a/unix/x0vncserver/Geometry.h +++ b/unix/x0vncserver/Geometry.h @@ -30,6 +30,7 @@ class Geometry { 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 00000000..2e477630 --- /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 748796be..59e25323 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 @@ XDesktop::XDesktop(Display* dpy_, Geometry *geometry_) } #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 @@ void XDesktop::start(VNCServer* vs) { 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::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) { 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 @@ bool XDesktop::handleGlobalEvent(XEvent* ev) { 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 c9106f8b..ff52c014 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 @@ public: 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 @@ protected: #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 24a40b3d..e8fa38e8 100644 --- a/unix/xserver/hw/vnc/Makefile.am +++ b/unix/xserver/hw/vnc/Makefile.am @@ -6,7 +6,8 @@ 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) +UNIXCOMMON_LIB=${top_builddir}/../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 @@ HDRS = vncExtInit.h vncHooks.h \ 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 @@ nodist_EXTRA_Xvnc_SOURCES = dummy.cxx 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 @@ libvnc_la_SOURCES = vncModule.c 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 00000000..82e85524 --- /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 141023a8..10ba98d6 100644 --- a/unix/xserver/hw/vnc/XorgGlue.c +++ b/unix/xserver/hw/vnc/XorgGlue.c @@ -114,253 +114,3 @@ int vncGetScreenY(int scrIdx) 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 63227ac3..5d019493 100644 --- a/unix/xserver/hw/vnc/XorgGlue.h +++ b/unix/xserver/hw/vnc/XorgGlue.h @@ -45,34 +45,14 @@ void vncGetScreenFormat(int scrIdx, int *depth, int *bpp, 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 c68a0775..080943d0 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 @@ void XserverDesktop::setFramebuffer(int w, int h, void* fbptr, int stride_) 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 @@ void XserverDesktop::clientCutText(const char* str, int len) 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 2a378ea1..f1c3e3e7 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 @@ protected: virtual bool handleTimeout(rfb::Timer* t); private: - rfb::ScreenSet computeScreenLayout(); int screenIndex; rfb::VNCServerST* server; @@ -132,10 +132,7 @@ private: 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 c47dfa72..54cca953 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 @@ void vncExtensionInit(void) 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 c0d9f7c6..f3c2520a 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 @@ typedef struct _vncHooksScreenRec { TriFanProcPtr TriFan; #endif #endif -#ifdef RANDR RRSetConfigProcPtr rrSetConfig; RRScreenSetSizeProcPtr rrScreenSetSize; RRCrtcSetProcPtr rrCrtcSet; -#endif } vncHooksScreenRec, *vncHooksScreenPtr; typedef struct _vncHooksGCRec { @@ -174,7 +170,6 @@ static void vncHooksTriFan(CARD8 op, PicturePtr pSrc, PicturePtr pDst, 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 @@ static Bool vncHooksRandRCrtcSet(ScreenPtr pScreen, RRCrtcPtr crtc, RRModePtr mode, int x, int y, Rotation rotation, int numOutputs, RROutputPtr *outputs); -#endif // GC "funcs" @@ -277,9 +271,7 @@ int vncHooksInit(int scrIdx) #ifdef RENDER PictureScreenPtr ps; #endif -#ifdef RANDR rrScrPrivPtr rp; -#endif pScreen = screenInfo.screens[scrIdx]; @@ -339,7 +331,6 @@ int vncHooksInit(int scrIdx) #endif } #endif -#ifdef RANDR rp = rrGetScrPriv(pScreen); if (rp) { /* Some RandR callbacks are optional */ @@ -350,7 +341,6 @@ int vncHooksInit(int scrIdx) if (rp->rrCrtcSet) wrap(vncHooksScreen, rp, rrCrtcSet, vncHooksRandRCrtcSet); } -#endif return TRUE; } @@ -473,9 +463,7 @@ static Bool vncHooksCloseScreen(ScreenPtr pScreen_) #ifdef RENDER PictureScreenPtr ps; #endif -#ifdef RANDR rrScrPrivPtr rp; -#endif SCREEN_PROLOGUE(pScreen_, CloseScreen); @@ -501,14 +489,12 @@ static Bool vncHooksCloseScreen(ScreenPtr pScreen_) #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 @@ static void vncHooksTriFan(CARD8 op, PicturePtr pSrc, PicturePtr pDst, #endif /* RENDER */ -#ifdef RANDR - // Unwrap and rewrap helpers #define RANDR_PROLOGUE(field) \ @@ -1275,8 +1259,6 @@ static Bool vncHooksRandRCrtcSet(ScreenPtr pScreen, RRCrtcPtr crtc, return TRUE; } -#endif /* RANDR */ - ///////////////////////////////////////////////////////////////////////////// // // GC "funcs" diff --git a/unix/xserver/hw/vnc/vncModule.c b/unix/xserver/hw/vnc/vncModule.c index 5b4db968..451f527a 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 @@ typedef pointer XF86OptionPtr; #include "vncExtInit.h" #include "RFBGlue.h" #include "XorgGlue.h" +#include "RandrGlue.h" static void vncModuleInit(INITARGS); @@ -111,29 +110,31 @@ void vncClientGone(int fd) { } -#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; - - /* - * 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. - */ + return 0; +} - output = out; +int vncRandRCanCreateModes(void) +{ + return 0; +} - 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]; - } +void* vncRandRCreateMode(void* output, int width, int height) +{ + return 0; +} - return NULL; +void* vncRandRSetPreferredMode(void* output, void* mode) +{ + /* + * We're not going to change which modes are preferred, + * so just return the incoming mode. + */ + return mode; } -#endif diff --git a/unix/xserver/hw/vnc/xvnc.c b/unix/xserver/hw/vnc/xvnc.c index 6924f65f..57152cd5 100644 --- a/unix/xserver/hw/vnc/xvnc.c +++ b/unix/xserver/hw/vnc/xvnc.c @@ -35,6 +35,7 @@ from the X Consortium. #include "vncExtInit.h" #include "RFBGlue.h" #include "XorgGlue.h" +#include "RandrGlue.h" #include "xorg-version.h" #ifdef WIN32 @@ -72,9 +73,7 @@ from the X Consortium. #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 @@ static miPointerScreenFuncRec vfbPointerCursorFuncs = { miPointerWarpCursor }; -#ifdef RANDR static Bool vncRandRGetInfo (ScreenPtr pScreen, Rotation *rotations) { @@ -1379,41 +1377,87 @@ static RRCrtcPtr vncRandRCrtcCreate(ScreenPtr pScreen) } /* 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; +} + +/* Creating and modifying modes, used by XserverDesktop and init here */ + +int vncRandRCanCreateModes() +{ + return 1; } -/* Used to create a preferred mode from various places */ -void *vncRandRCreatePreferredMode(void *out, int width, int height) +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 @@ static Bool vncRandRInit(ScreenPtr pScreen) 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 @@ static Bool vncRandRInit(ScreenPtr pScreen) return TRUE; } -#endif static Bool #if XORG < 113 @@ -1506,9 +1552,7 @@ vfbScreenInit(ScreenPtr pScreen, int argc, char **argv) int ret; void *pbits; -#ifdef RANDR rrScrPrivPtr rp; -#endif #if XORG >= 113 if (!dixRegisterPrivateKey(&cmapScrPrivateKeyRec, PRIVATE_SCREEN, 0)) @@ -1644,7 +1688,6 @@ vfbScreenInit(ScreenPtr pScreen, int argc, char **argv) pvfb->closeScreen = pScreen->CloseScreen; pScreen->CloseScreen = vfbCloseScreen; -#ifdef RANDR ret = RRScreenInit(pScreen); if (!ret) return FALSE; @@ -1659,7 +1702,6 @@ vfbScreenInit(ScreenPtr pScreen, int argc, char **argv) ret = vncRandRInit(pScreen); if (!ret) return FALSE; -#endif return TRUE; |