From 44e99643dc5b2e519dc2393eadcd4812ce8b3dec Mon Sep 17 00:00:00 2001 From: Constantin Kaplinsky Date: Sat, 20 May 2006 12:58:38 +0000 Subject: [PATCH] Added support for new `Geometry' option which allows serving an arbitrary regtangular screen area instead of the whole screen. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@569 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- x0vncserver/Geometry.cxx | 73 ++++++++++++++++++++++++++++++++++ x0vncserver/Geometry.h | 50 +++++++++++++++++++++++ x0vncserver/Makefile.in | 1 + x0vncserver/PollingManager.cxx | 43 ++++++-------------- x0vncserver/PollingManager.h | 37 ++++++++++++++++- x0vncserver/x0vncserver.cxx | 32 +++++++++------ 6 files changed, 193 insertions(+), 43 deletions(-) create mode 100644 x0vncserver/Geometry.cxx create mode 100644 x0vncserver/Geometry.h diff --git a/x0vncserver/Geometry.cxx b/x0vncserver/Geometry.cxx new file mode 100644 index 00000000..f2321703 --- /dev/null +++ b/x0vncserver/Geometry.cxx @@ -0,0 +1,73 @@ +/* Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved. + * + * 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. + */ + +// +// Geometry.cxx +// + +#include + +#include +#include +#include + +static LogWriter vlog("Geometry"); + +StringParameter Geometry::m_geometryParam("Geometry", + "Screen area shown to VNC clients. " + "Format is x++, " + "more information in man X, section GEOMETRY SPECIFICATIONS. " + "If the argument is empty, full screen is shown to VNC clients.", + ""); + +Geometry::Geometry(int fullWidth, int fullHeight) + : m_width(fullWidth), m_height(fullHeight), + m_offsetLeft(0), m_offsetTop(0) +{ + const char *param = m_geometryParam.getData(); + if (strlen(param) != 0) { + int w, h; + int x = 0, y = 0; + char sign_x[2] = "+"; + char sign_y[2] = "+"; + int n = sscanf(param, "%dx%d%1[+-]%d%1[+-]%d", + &w, &h, sign_x, &x, sign_y, &y); + if ((n == 2 || n == 6) && w > 0 && h > 0 && x >= 0 && y >= 0) { + if (sign_x[0] == '-') + x = fullWidth - w - x; + if (sign_y[0] == '-') + y = fullHeight - h - y; + Rect fullRect(0, 0, fullWidth, fullHeight); + Rect partRect(x, y, x + w, y + h); + Rect r = partRect.intersect(fullRect); + if (r.area() > 0) { + m_width = r.width(); + m_height = r.height(); + m_offsetLeft = r.tl.x; + m_offsetTop = r.tl.y; + } else { + vlog.error("Requested area is out of the desktop boundaries"); + } + } else { + vlog.error("Wrong argument format"); + } + } + vlog.info("Desktop geometry is %dx%d+%d+%d", + m_width, m_height, m_offsetLeft, m_offsetTop); +} + diff --git a/x0vncserver/Geometry.h b/x0vncserver/Geometry.h new file mode 100644 index 00000000..95059e7d --- /dev/null +++ b/x0vncserver/Geometry.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved. + * + * 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. + */ + +// +// Geometry.h +// + +#ifndef __GEOMETRY_H__ +#define __GEOMETRY_H__ + +#include + +using namespace rfb; + +class Geometry +{ +public: + Geometry(int fullWidth, int fullHeight); + + int width() const { return m_width; } + int height() const { return m_height; } + int offsetLeft() const { return m_offsetLeft; } + int offsetTop() const { return m_offsetTop; } + +protected: + static StringParameter m_geometryParam; + + int m_width; + int m_height; + int m_offsetLeft; + int m_offsetTop; +}; + +#endif // __GEOMETRY_H__ + diff --git a/x0vncserver/Makefile.in b/x0vncserver/Makefile.in index e69b89ad..cbb9fed6 100644 --- a/x0vncserver/Makefile.in +++ b/x0vncserver/Makefile.in @@ -1,5 +1,6 @@ SRCS = Image.cxx TimeMillis.cxx PollingScheduler.cxx PollingManager.cxx \ + Geometry.cxx \ x0vncserver.cxx ../vncconfig_unix/QueryConnectDialog.cxx OBJS = $(SRCS:.cxx=.o) diff --git a/x0vncserver/PollingManager.cxx b/x0vncserver/PollingManager.cxx index c0670107..3c6be4f8 100644 --- a/x0vncserver/PollingManager.cxx +++ b/x0vncserver/PollingManager.cxx @@ -58,9 +58,11 @@ const int PollingManager::m_pollingOrder[32] = { // PollingManager::PollingManager(Display *dpy, Image *image, - ImageFactory *factory) - : m_dpy(dpy), m_server(0), m_image(image), m_pointerPosKnown(false), - m_pollingStep(0) + ImageFactory *factory, + int offsetLeft, int offsetTop) + : m_dpy(dpy), m_server(0), m_image(image), + m_offsetLeft(offsetLeft), m_offsetTop(offsetTop), + m_pointerPosKnown(false), m_pollingStep(0) { // Save width and height of the screen (and the image). m_width = m_image->xim->width; @@ -241,7 +243,7 @@ bool PollingManager::poll_DetectVideo() if (scanLine >= tile_h) break; int scan_y = y * 32 + scanLine; - m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y); + getRow(scan_y); char *ptr_old = m_image->xim->data + scan_y * bytesPerLine; char *ptr_new = m_rowImage->xim->data; for (int x = 0; x * 32 < m_width; x++) { @@ -262,12 +264,7 @@ bool PollingManager::poll_DetectVideo() m_changedFlags[idx] |= wasChanged; if ( m_changedFlags[idx] && (!m_videoFlags[idx] || grandStep) ) { - if (tile_w == 32 && tile_h == 32) { - m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32); - } else { - m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32, - tile_w, tile_h); - } + getTile32(x, y, tile_w, tile_h); m_image->updateRect(m_tileImage, x * 32, y * 32); rect.setXYWH(x * 32, y * 32, tile_w, tile_h); m_server->add_changed(rect); @@ -311,7 +308,7 @@ bool PollingManager::poll_SkipCycles() if (scanLine >= tile_h) scanLine %= tile_h; int scan_y = y * 32 + scanLine; - m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y); + getRow(scan_y); char *ptr_old = m_image->xim->data + scan_y * bytesPerLine; char *ptr_new = m_rowImage->xim->data; for (int x = 0; x * 32 < m_width; x++) { @@ -323,12 +320,7 @@ bool PollingManager::poll_SkipCycles() true : (memcmp(ptr_old, ptr_new, nBytes) != 0); if (wasChanged) { if (grandStep || *pstatus == NOT_CHANGED) { - if (tile_w == 32 && tile_h == 32) { - m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32); - } else { - m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32, - tile_w, tile_h); - } + getTile32(x, y, tile_w, tile_h); m_image->updateRect(m_tileImage, x * 32, y * 32); rect.setXYWH(x * 32, y * 32, tile_w, tile_h); m_server->add_changed(rect); @@ -367,19 +359,14 @@ bool PollingManager::poll_Traditional() if (scanLine >= tile_h) break; int scan_y = y * 32 + scanLine; - m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y); + getRow(scan_y); char *ptr_old = m_image->xim->data + scan_y * bytesPerLine; char *ptr_new = m_rowImage->xim->data; for (int x = 0; x * 32 < m_width; x++) { int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32; int nBytes = tile_w * bytesPerPixel; if (memcmp(ptr_old, ptr_new, nBytes)) { - if (tile_w == 32 && tile_h == 32) { - m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32); - } else { - m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32, - tile_w, tile_h); - } + getTile32(x, y, tile_w, tile_h); m_image->updateRect(m_tileImage, x * 32, y * 32); rect.setXYWH(x * 32, y * 32, tile_w, tile_h); m_server->add_changed(rect); @@ -402,7 +389,7 @@ bool PollingManager::poll_Dumb() if (!m_server) return false; - m_image->get(DefaultRootWindow(m_dpy)); + getScreen(); Rect rect(0, 0, m_width, m_height); m_server->add_changed(rect); @@ -456,11 +443,7 @@ bool PollingManager::pollPointerArea() int w = r.width(), h = r.height(); // Get new pixels. - if (w == 128 && h == 128) { - m_areaImage->get(DefaultRootWindow(m_dpy), x, y); - } else { - m_areaImage->get(DefaultRootWindow(m_dpy), x, y, w, h); - } + getArea128(x, y, w, h); // Now, try to minimize the rectangle by cutting out unchanged // borders (at top and bottom). diff --git a/x0vncserver/PollingManager.h b/x0vncserver/PollingManager.h index 797f3f68..b8eef509 100644 --- a/x0vncserver/PollingManager.h +++ b/x0vncserver/PollingManager.h @@ -38,7 +38,8 @@ class PollingManager { public: - PollingManager(Display *dpy, Image *image, ImageFactory *factory); + PollingManager(Display *dpy, Image *image, ImageFactory *factory, + int offsetLeft = 0, int offsetTop = 0); virtual ~PollingManager(); void setVNCServer(VNCServer *s); @@ -71,6 +72,8 @@ protected: VNCServer *m_server; Image *m_image; + int m_offsetLeft; + int m_offsetTop; int m_width; int m_height; int m_widthTiles; @@ -83,6 +86,38 @@ protected: private: + inline void getScreen() { + m_image->get(DefaultRootWindow(m_dpy), m_offsetLeft, m_offsetTop); + } + + inline void getRow(int y) { + m_rowImage->get(DefaultRootWindow(m_dpy), m_offsetLeft, m_offsetTop + y); + } + + inline void getTile32(int tx, int ty, int w, int h) { + if (w == 32 && h == 32) { + // This version of get() may be better optimized. + m_tileImage->get(DefaultRootWindow(m_dpy), + m_offsetLeft + tx * 32, m_offsetTop + ty * 32); + } else { + // Generic version of get() for arbitrary width and height. + m_tileImage->get(DefaultRootWindow(m_dpy), + m_offsetLeft + tx * 32, m_offsetTop + ty * 32, w, h); + } + } + + inline void getArea128(int x, int y, int w, int h) { + if (w == 128 && h == 128) { + // This version of get() may be better optimized. + m_areaImage->get(DefaultRootWindow(m_dpy), + m_offsetLeft + x, m_offsetTop + y); + } else { + // Generic version of get() for arbitrary width and height. + m_areaImage->get(DefaultRootWindow(m_dpy), + m_offsetLeft + x, m_offsetTop + y, w, h); + } + } + void adjustVideoArea(); // Additional images used in polling algorithms. diff --git a/x0vncserver/x0vncserver.cxx b/x0vncserver/x0vncserver.cxx index 4ee44e04..70a5361b 100644 --- a/x0vncserver/x0vncserver.cxx +++ b/x0vncserver/x0vncserver.cxx @@ -44,6 +44,7 @@ #include #endif +#include #include #include #include @@ -130,8 +131,8 @@ private: class XDesktop : public SDesktop, public ColourMap { public: - XDesktop(Display* dpy_) - : dpy(dpy_), pb(0), server(0), image(0), pollmgr(0), + XDesktop(Display* dpy_, Geometry *geometry_) + : dpy(dpy_), geometry(geometry_), pb(0), server(0), image(0), pollmgr(0), oldButtonMask(0), haveXtest(false), maxButtons(0), running(false) { #ifdef HAVE_XTEST @@ -168,14 +169,15 @@ public: vlog.info("Enabling %d button%s of X pointer device", maxButtons, (maxButtons != 1) ? "s" : ""); - int dpyWidth = DisplayWidth(dpy, DefaultScreen(dpy)); - int dpyHeight = DisplayHeight(dpy, DefaultScreen(dpy)); - ImageFactory factory((bool)useShm, (bool)useOverlay); - image = factory.newImage(dpy, dpyWidth, dpyHeight); - image->get(DefaultRootWindow(dpy)); - - pollmgr = new PollingManager(dpy, image, &factory); + image = factory.newImage(dpy, geometry->width(), geometry->height()); + image->get(DefaultRootWindow(dpy), + geometry->offsetLeft(), geometry->offsetTop()); + + // FIXME: Duplication in using offsets above and here: + pollmgr = new PollingManager(dpy, image, &factory, + geometry->offsetLeft(), + geometry->offsetTop()); pollmgr->setVNCServer(vs); pf.bpp = image->xim->bits_per_pixel; @@ -189,7 +191,7 @@ public: pf.greenMax = image->xim->green_mask >> pf.greenShift; pf.blueMax = image->xim->blue_mask >> pf.blueShift; - pb = new FullFramePixelBuffer(pf, dpyWidth, dpyHeight, + pb = new FullFramePixelBuffer(pf, geometry->width(), geometry->height(), (rdr::U8*)image->xim->data, this); server = vs; server->setPixelBuffer(pb); @@ -222,7 +224,10 @@ public: pollmgr->setPointerPos(pos); #ifdef HAVE_XTEST if (!haveXtest) return; - XTestFakeMotionEvent(dpy, DefaultScreen(dpy), pos.x, pos.y, CurrentTime); + XTestFakeMotionEvent(dpy, DefaultScreen(dpy), + geometry->offsetLeft() + pos.x, + geometry->offsetTop() + pos.y, + CurrentTime); if (buttonMask != oldButtonMask) { for (int i = 0; i < maxButtons; i++) { if ((buttonMask ^ oldButtonMask) & (1<