From 1173637739194be70e4158e911a31dccf39edeb0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Peter=20=C3=85strand=20=28astrand=29?= Date: Wed, 7 Mar 2018 09:29:00 +0100 Subject: [PATCH] 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. --- unix/CMakeLists.txt | 2 +- unix/common/CMakeLists.txt | 14 ++ unix/common/RandrGlue.h | 64 ++++++ unix/common/randr.cxx | 249 ++++++++++++++++++++++ unix/common/unixcommon.h | 40 ++++ unix/xserver/hw/vnc/Makefile.am | 10 +- unix/xserver/hw/vnc/RandrGlue.c | 283 ++++++++++++++++++++++++++ unix/xserver/hw/vnc/XorgGlue.c | 251 ----------------------- unix/xserver/hw/vnc/XorgGlue.h | 25 --- unix/xserver/hw/vnc/XserverDesktop.cc | 217 +------------------- unix/xserver/hw/vnc/XserverDesktop.h | 3 +- unix/xserver/hw/vnc/vncExtInit.cc | 1 + unix/xserver/hw/vnc/vncModule.c | 1 + unix/xserver/hw/vnc/xvnc.c | 1 + 14 files changed, 664 insertions(+), 497 deletions(-) create mode 100644 unix/common/CMakeLists.txt create mode 100644 unix/common/RandrGlue.h create mode 100644 unix/common/randr.cxx create mode 100644 unix/common/unixcommon.h create mode 100644 unix/xserver/hw/vnc/RandrGlue.c 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..40d2d2e7 --- /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 00000000..44e46de7 --- /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 +#endif + +#include + +#include +#include +#include +#include +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 00000000..66fe84c2 --- /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 +#endif + +#include + +#include + +typedef std::map 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 */ 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..9f770fa9 --- /dev/null +++ b/unix/xserver/hw/vnc/RandrGlue.c @@ -0,0 +1,283 @@ +/* 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 +#endif + +#include +#include + +#include "scrnintstr.h" +#ifdef RANDR +#include "randrstr.h" +#endif + +#include "RandrGlue.h" + +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 +} + +char *vncRandRGetOutputName(int scrIdx, int outputIdx) +{ +#ifdef RANDR + rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); + return strdup(rp->outputs[outputIdx]->name); +#else + return strdup(""); +#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 +} + +unsigned int vncRandRGetOutputId(int scrIdx, int outputIdx) +{ +#ifdef RANDR + rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); + return rp->outputs[outputIdx]->id; +#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.c b/unix/xserver/hw/vnc/XorgGlue.c index 3a2fce9f..10ba98d6 100644 --- a/unix/xserver/hw/vnc/XorgGlue.c +++ b/unix/xserver/hw/vnc/XorgGlue.c @@ -22,7 +22,6 @@ #endif #include -#include #include "scrnintstr.h" #ifdef RANDR @@ -115,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 -} - -char *vncRandRGetOutputName(int scrIdx, int outputIdx) -{ -#ifdef RANDR - rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); - return strdup(rp->outputs[outputIdx]->name); -#else - return strdup(""); -#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 -} - -unsigned int vncRandRGetOutputId(int scrIdx, int outputIdx) -{ -#ifdef RANDR - rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); - return rp->outputs[outputIdx]->id; -#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 9bdb25e7..8cf29350 100644 --- a/unix/xserver/hw/vnc/XorgGlue.h +++ b/unix/xserver/hw/vnc/XorgGlue.h @@ -45,34 +45,9 @@ 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); - -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); // 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); #ifdef __cplusplus } diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index c9b70389..19a09fc9 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -185,68 +185,14 @@ void XserverDesktop::setFramebuffer(int w, int h, void* fbptr, int stride_) data = (rdr::U8*)fbptr; stride = stride_; - layout = computeScreenLayout(); + layout = ::computeScreenLayout(screenIndex, &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++) { - 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(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; + server->setScreenLayout(::computeScreenLayout(screenIndex, &outputIdMap)); } char* XserverDesktop::substitute(const char* varName) @@ -605,170 +551,13 @@ 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++) { - unsigned int 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) { - 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 */ - 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; + return ::setScreenLayout(screenIndex, 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 b611402a..8866f187 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -37,6 +37,7 @@ #include #include #include +#include #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; @@ -133,7 +133,6 @@ private: rfb::Timer queryConnectTimer; #ifdef RANDR - typedef std::map OutputIdMap; OutputIdMap outputIdMap; #endif diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index c47dfa72..52e270a3 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -41,6 +41,7 @@ #include "vncBlockHandler.h" #include "vncSelection.h" #include "XorgGlue.h" +#include "RandrGlue.h" #include "xorg-version.h" using namespace rfb; diff --git a/unix/xserver/hw/vnc/vncModule.c b/unix/xserver/hw/vnc/vncModule.c index 5b4db968..3fb07769 100644 --- a/unix/xserver/hw/vnc/vncModule.c +++ b/unix/xserver/hw/vnc/vncModule.c @@ -39,6 +39,7 @@ typedef pointer XF86OptionPtr; #include "vncExtInit.h" #include "RFBGlue.h" #include "XorgGlue.h" +#include "RandrGlue.h" static void vncModuleInit(INITARGS); diff --git a/unix/xserver/hw/vnc/xvnc.c b/unix/xserver/hw/vnc/xvnc.c index 6924f65f..56a6ca58 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 -- 2.39.5