summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/rfb/SDesktop.h8
-rw-r--r--common/rfb/SMsgWriter.h10
-rw-r--r--common/rfb/SMsgWriterV3.cxx67
-rw-r--r--common/rfb/SMsgWriterV3.h15
-rw-r--r--common/rfb/ScreenSet.h29
-rw-r--r--common/rfb/VNCSConnectionST.cxx49
-rw-r--r--common/rfb/VNCSConnectionST.h1
-rw-r--r--common/rfb/VNCServer.h13
-rw-r--r--common/rfb/VNCServerST.cxx113
-rw-r--r--common/rfb/VNCServerST.h4
10 files changed, 245 insertions, 64 deletions
diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h
index 7b054e37..4a53d538 100644
--- a/common/rfb/SDesktop.h
+++ b/common/rfb/SDesktop.h
@@ -41,6 +41,7 @@
#include <rfb/VNCServer.h>
#include <rfb/InputHandler.h>
#include <rfb/Exception.h>
+#include <rfb/screenTypes.h>
namespace rfb {
@@ -75,6 +76,13 @@ namespace rfb {
virtual Point getFbSize() = 0;
+ // setScreenLayout() requests to reconfigure the framebuffer and/or
+ // the layout of screens.
+ virtual unsigned int setScreenLayout(int fb_width, int fb_height,
+ const ScreenSet& layout) {
+ return resultProhibited;
+ }
+
// InputHandler interface
// pointerEvent(), keyEvent() and clientCutText() are called in response to
// the relevant RFB protocol messages from clients.
diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h
index 3c6da95a..5df72704 100644
--- a/common/rfb/SMsgWriter.h
+++ b/common/rfb/SMsgWriter.h
@@ -27,6 +27,7 @@
#include <rfb/screenTypes.h>
#include <rfb/Encoder.h>
#include <rfb/PixelBuffer.h>
+#include <rfb/ScreenSet.h>
namespace rdr { class OutStream; }
@@ -75,8 +76,13 @@ namespace rfb {
// writeSetDesktopSize() on a V3 writer won't actually write immediately,
// but will write the relevant pseudo-rectangle as part of the next update.
virtual bool writeSetDesktopSize()=0;
- // Same thing for the extended version
- virtual bool writeExtendedDesktopSize(rdr::U16 error = resultUnsolicited)=0;
+ // Same thing for the extended version. The first version queues up a
+ // generic update of the current server state, but the second queues a
+ // specific message.
+ virtual bool writeExtendedDesktopSize()=0;
+ virtual bool writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
+ int fb_width, int fb_height,
+ const ScreenSet& layout)=0;
virtual bool writeSetDesktopName()=0;
diff --git a/common/rfb/SMsgWriterV3.cxx b/common/rfb/SMsgWriterV3.cxx
index de093964..6a8316f1 100644
--- a/common/rfb/SMsgWriterV3.cxx
+++ b/common/rfb/SMsgWriterV3.cxx
@@ -66,12 +66,27 @@ bool SMsgWriterV3::writeSetDesktopSize() {
return true;
}
-bool SMsgWriterV3::writeExtendedDesktopSize(rdr::U16 error) {
+bool SMsgWriterV3::writeExtendedDesktopSize() {
if (!cp->supportsExtendedDesktopSize) return false;
- if (error == resultUnsolicited)
- needExtendedDesktopSize = true;
- else
- edsErrors.push_back(error);
+ needExtendedDesktopSize = true;
+ return true;
+}
+
+bool SMsgWriterV3::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
+ int fb_width, int fb_height,
+ const ScreenSet& layout) {
+ ExtendedDesktopSizeMsg msg;
+
+ if (!cp->supportsExtendedDesktopSize) return false;
+
+ msg.reason = reason;
+ msg.result = result;
+ msg.fb_width = fb_width;
+ msg.fb_height = fb_height;
+ msg.layout = layout;
+
+ extendedDesktopSizeMsgs.push_back(msg);
+
return true;
}
@@ -136,7 +151,7 @@ void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
if (wsccb) nRects++;
if (needSetDesktopSize) nRects++;
if (needExtendedDesktopSize) nRects++;
- if (!edsErrors.empty()) nRects += edsErrors.size();
+ if (!extendedDesktopSizeMsgs.empty()) nRects += extendedDesktopSizeMsgs.size();
if (needSetDesktopName) nRects++;
os->writeU16(nRects);
nRectsInUpdate = 0;
@@ -157,26 +172,37 @@ void SMsgWriterV3::writeFramebufferUpdateStart()
void SMsgWriterV3::writeFramebufferUpdateEnd()
{
- /* Start with responses to SetDesktopSize messages */
- if (!edsErrors.empty()) {
- std::list<rdr::U16>::const_iterator iter;
+ /* Start with specific ExtendedDesktopSize messages */
+ if (!extendedDesktopSizeMsgs.empty()) {
+ std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
+ ScreenSet::const_iterator si;
if (!cp->supportsExtendedDesktopSize)
throw Exception("Client does not support extended desktop resize");
- if ((nRectsInUpdate += edsErrors.size()) > nRectsInHeader && nRectsInHeader)
- throw Exception("SMsgWriterV3 setExtendedDesktopSize: nRects out of sync");
-
- for (iter = edsErrors.begin();iter != edsErrors.end();iter++) {
- os->writeU16(1);
- os->writeU16(*iter);
- os->writeU16(0);
- os->writeU16(0);
+ if ((nRectsInUpdate += extendedDesktopSizeMsgs.size()) > nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriterV3 SetDesktopSize reply: nRects out of sync");
+
+ for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
+ os->writeU16(ri->reason);
+ os->writeU16(ri->result);
+ os->writeU16(ri->fb_width);
+ os->writeU16(ri->fb_height);
os->writeU32(pseudoEncodingExtendedDesktopSize);
- os->writeU8(0); // # screens
+
+ os->writeU8(ri->layout.num_screens());
os->pad(3);
+
+ for (si = ri->layout.begin();si != ri->layout.end();++si) {
+ os->writeU32(si->id);
+ os->writeU16(si->dimensions.tl.x);
+ os->writeU16(si->dimensions.tl.y);
+ os->writeU16(si->dimensions.width());
+ os->writeU16(si->dimensions.height());
+ os->writeU32(si->flags);
+ }
}
- edsErrors.clear();
+ extendedDesktopSizeMsgs.clear();
}
/* Send this before SetDesktopSize to make life easier on the clients */
@@ -250,7 +276,8 @@ void SMsgWriterV3::writeFramebufferUpdateEnd()
bool SMsgWriterV3::needFakeUpdate()
{
- return wsccb || needSetDesktopSize || needSetDesktopName;
+ return wsccb || needSetDesktopSize || needExtendedDesktopSize ||
+ !extendedDesktopSizeMsgs.empty() || needSetDesktopName;
}
void SMsgWriterV3::startRect(const Rect& r, unsigned int encoding)
diff --git a/common/rfb/SMsgWriterV3.h b/common/rfb/SMsgWriterV3.h
index 509bfdcb..e86c828c 100644
--- a/common/rfb/SMsgWriterV3.h
+++ b/common/rfb/SMsgWriterV3.h
@@ -20,6 +20,7 @@
#define __RFB_SMSGWRITERV3_H__
#include <list>
+#include <utility>
#include <rfb/SMsgWriter.h>
@@ -35,7 +36,10 @@ namespace rfb {
virtual void startMsg(int type);
virtual void endMsg();
virtual bool writeSetDesktopSize();
- virtual bool writeExtendedDesktopSize(rdr::U16 error);
+ virtual bool writeExtendedDesktopSize();
+ virtual bool writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
+ int fb_width, int fb_height,
+ const ScreenSet& layout);
virtual bool writeSetDesktopName();
virtual void cursorChange(WriteSetCursorCallback* cb);
virtual void writeSetCursor(int width, int height, const Point& hotspot,
@@ -57,9 +61,16 @@ namespace rfb {
WriteSetCursorCallback* wsccb;
bool needSetDesktopSize;
bool needExtendedDesktopSize;
- std::list<rdr::U16> edsErrors;
bool needSetDesktopName;
bool needLastRect;
+
+ typedef struct {
+ rdr::U16 reason, result;
+ int fb_width, fb_height;
+ ScreenSet layout;
+ } ExtendedDesktopSizeMsg;
+ std::list<ExtendedDesktopSizeMsg> extendedDesktopSizeMsgs;
+
};
}
#endif
diff --git a/common/rfb/ScreenSet.h b/common/rfb/ScreenSet.h
index 55abb509..6f5188ca 100644
--- a/common/rfb/ScreenSet.h
+++ b/common/rfb/ScreenSet.h
@@ -21,6 +21,8 @@
#ifndef __RFB_SCREENSET_INCLUDED__
#define __RFB_SCREENSET_INCLUDED__
+#include <stdio.h>
+
#include <rfb/Rect.h>
#include <list>
#include <set>
@@ -36,6 +38,17 @@ namespace rfb {
Screen(void) : id(0), flags(0) {};
Screen(rdr::U32 id_, int x_, int y_, int w_, int h_, rdr::U32 flags_) :
id(id_), dimensions(x_, y_, x_+w_, y_+h_), flags(flags_) {};
+
+ inline bool operator==(const Screen& r) const {
+ if (id != r.id)
+ return false;
+ if (!dimensions.equals(r.dimensions))
+ return false;
+ if (flags != r.flags)
+ return false;
+ return true;
+ }
+
rdr::U32 id;
Rect dimensions;
rdr::U32 flags;
@@ -93,6 +106,22 @@ namespace rfb {
return true;
};
+ inline void debug_print(void) const {
+ std::list<Screen>::const_iterator iter;
+ fprintf(stderr, "%d screens\n", num_screens());
+ for (iter = screens.begin();iter != screens.end();++iter) {
+ fprintf(stderr, " %10d (0x%08x): %dx%d+%d+%d (flags 0x%08x)\n",
+ (int)iter->id, (unsigned)iter->id,
+ iter->dimensions.width(), iter->dimensions.height(),
+ iter->dimensions.tl.x, iter->dimensions.tl.y,
+ (unsigned)iter->flags);
+ }
+ };
+
+ // FIXME: List order shouldn't matter
+ inline bool operator==(const ScreenSet& r) const { return screens == r.screens; }
+ inline bool operator!=(const ScreenSet& r) const { return screens != r.screens; }
+
std::list<Screen> screens;
};
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index 29c62746..fdcea883 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -191,6 +191,25 @@ void VNCSConnectionST::pixelBufferChange()
}
}
+void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
+{
+ try {
+ if (!authenticated())
+ return;
+
+ cp.screenLayout = server->screenLayout;
+ if (state() == RFBSTATE_NORMAL) {
+ writer()->writeExtendedDesktopSize(reason, 0, cp.width, cp.height,
+ cp.screenLayout);
+ }
+
+ if (writer()->needFakeUpdate())
+ writeFramebufferUpdate();
+ } catch(rdr::Exception &e) {
+ close(e.str());
+ }
+}
+
void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
{
try {
@@ -512,8 +531,34 @@ void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height,
const ScreenSet& layout)
{
- vlog.info("Rejecting client request to change desktop size");
- writer()->writeExtendedDesktopSize(resultProhibited);
+ unsigned int result;
+
+ // Don't bother the desktop with an invalid configuration
+ if (!layout.validate(fb_width, fb_height)) {
+ writer()->writeExtendedDesktopSize(reasonClient, resultInvalid,
+ fb_width, fb_height, layout);
+ if (writer()->needFakeUpdate())
+ writeFramebufferUpdate();
+ return;
+ }
+
+ // FIXME: the desktop will call back to VNCServerST and an extra set
+ // of ExtendedDesktopSize messages will be sent. This is okay
+ // protocol-wise, but unnecessary.
+ result = server->desktop->setScreenLayout(fb_width, fb_height, layout);
+
+ // Always send back a reply to the requesting client
+ writer()->writeExtendedDesktopSize(reasonClient, result,
+ fb_width, fb_height, layout);
+ if (writer()->needFakeUpdate())
+ writeFramebufferUpdate();
+
+ // But only notify other clients on success
+ if (result == resultSuccess) {
+ if (server->screenLayout != layout)
+ throw Exception("Desktop configured a different screen layout than requested");
+ server->notifyScreenLayoutChange(this);
+ }
}
void VNCSConnectionST::setInitialColourMap()
diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h
index c1750b92..c9e4ac8d 100644
--- a/common/rfb/VNCSConnectionST.h
+++ b/common/rfb/VNCSConnectionST.h
@@ -63,6 +63,7 @@ namespace rfb {
void writeFramebufferUpdateOrClose();
void pixelBufferChange();
+ void screenLayoutChange(rdr::U16 reason);
void setColourMapEntriesOrClose(int firstColour, int nColours);
void bell();
void serverCutText(const char *str, int len);
diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h
index 1e7acc63..850dbd3c 100644
--- a/common/rfb/VNCServer.h
+++ b/common/rfb/VNCServer.h
@@ -24,17 +24,24 @@
#include <rfb/UpdateTracker.h>
#include <rfb/SSecurity.h>
+#include <rfb/ScreenSet.h>
namespace rfb {
class VNCServer : public UpdateTracker {
public:
- // setPixelBuffer() tells the server to use the given pixel buffer. If
- // this differs in size from the previous pixel buffer, this may result in
- // protocol messages being sent, or clients being disconnected.
+ // setPixelBuffer() tells the server to use the given pixel buffer (and
+ // optionally a modified screen layout). If this differs in size from
+ // the previous pixel buffer, this may result in protocol messages being
+ // sent, or clients being disconnected.
+ virtual void setPixelBuffer(PixelBuffer* pb, const ScreenSet& layout) = 0;
virtual void setPixelBuffer(PixelBuffer* pb) = 0;
+ // setScreenLayout() modifies the current screen layout without changing
+ // the pixelbuffer. Clients will be notified of the new layout.
+ virtual void setScreenLayout(const ScreenSet& layout) = 0;
+
// getPixelBuffer() returns a pointer to the PixelBuffer object.
virtual PixelBuffer* getPixelBuffer() const = 0;
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index 7edac56b..ec01074e 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -252,50 +252,83 @@ int VNCServerST::checkTimeouts()
// VNCServer methods
-void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
+void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
{
pb = pb_;
delete comparer;
comparer = 0;
- if (pb) {
- comparer = new ComparingUpdateTracker(pb);
- cursor.setPF(pb->getPF());
- renderedCursor.setPF(pb->getPF());
-
- // Check that the screen layout is still valid
- if (!screenLayout.validate(pb->width(), pb->height())) {
- Rect fbRect;
- ScreenSet::iterator iter, iter_next;
-
- fbRect.setXYWH(0, 0, pb->width(), pb->height());
-
- for (iter = screenLayout.begin();iter != screenLayout.end();iter = iter_next) {
- iter_next = iter; ++iter_next;
- if (iter->dimensions.enclosed_by(fbRect))
- continue;
- iter->dimensions = iter->dimensions.intersect(fbRect);
- if (iter->dimensions.is_empty()) {
- slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
- (int)iter->id, (unsigned)iter->id);
- screenLayout.remove_screen(iter->id);
- }
- }
- }
+ screenLayout = layout;
- if (screenLayout.num_screens() == 0) {
- // Boot strap the screen layout
- screenLayout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
- }
+ if (!pb) {
+ if (desktopStarted)
+ throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
+ return;
+ }
- std::list<VNCSConnectionST*>::iterator ci, ci_next;
- for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
- ci_next = ci; ci_next++;
- (*ci)->pixelBufferChange();
- }
- } else {
+ comparer = new ComparingUpdateTracker(pb);
+ cursor.setPF(pb->getPF());
+ renderedCursor.setPF(pb->getPF());
+
+ // Make sure that we have at least one screen
+ if (screenLayout.num_screens() == 0)
+ screenLayout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
+
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->pixelBufferChange();
+ // Since the new pixel buffer means an ExtendedDesktopSize needs to
+ // be sent anyway, we don't need to call screenLayoutChange.
+ }
+}
+
+void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
+{
+ ScreenSet layout;
+
+ if (!pb_) {
if (desktopStarted)
throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
+ return;
+ }
+
+ layout = screenLayout;
+
+ // Check that the screen layout is still valid
+ if (!layout.validate(pb_->width(), pb_->height())) {
+ Rect fbRect;
+ ScreenSet::iterator iter, iter_next;
+
+ fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
+
+ for (iter = layout.begin();iter != layout.end();iter = iter_next) {
+ iter_next = iter; ++iter_next;
+ if (iter->dimensions.enclosed_by(fbRect))
+ continue;
+ iter->dimensions = iter->dimensions.intersect(fbRect);
+ if (iter->dimensions.is_empty()) {
+ slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
+ (int)iter->id, (unsigned)iter->id);
+ layout.remove_screen(iter->id);
+ }
+ }
+ }
+
+ setPixelBuffer(pb_, layout);
+}
+
+void VNCServerST::setScreenLayout(const ScreenSet& layout)
+{
+ if (!pb)
+ throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
+ if (!layout.validate(pb->width(), pb->height()))
+ throw Exception("setScreenLayout: invalid screen layout");
+
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->screenLayoutChange(reasonServer);
}
}
@@ -571,3 +604,13 @@ void VNCServerST::setConnStatus(ListConnInfo* listConn)
}
}
+void VNCServerST::notifyScreenLayoutChange(VNCSConnectionST* requester)
+{
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+ for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+ ci_next = ci; ci_next++;
+ if ((*ci) == requester)
+ continue;
+ (*ci)->screenLayoutChange(reasonOtherClient);
+ }
+}
diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h
index 8e98ba33..320db2f4 100644
--- a/common/rfb/VNCServerST.h
+++ b/common/rfb/VNCServerST.h
@@ -78,7 +78,9 @@ namespace rfb {
// Methods overridden from VNCServer
+ virtual void setPixelBuffer(PixelBuffer* pb, const ScreenSet& layout);
virtual void setPixelBuffer(PixelBuffer* pb);
+ virtual void setScreenLayout(const ScreenSet& layout);
virtual PixelBuffer* getPixelBuffer() const { return pb; }
virtual void setColourMapEntries(int firstColour=0, int nColours=0);
virtual void serverCutText(const char* str, int len);
@@ -225,6 +227,8 @@ namespace rfb {
bool needRenderedCursor();
void checkUpdate();
+ void notifyScreenLayoutChange(VNCSConnectionST *requester);
+
SSecurityFactory* securityFactory;
QueryConnectionHandler* queryConnectionHandler;
KeyRemapper* keyRemapper;