diff options
-rw-r--r-- | common/rfb/SDesktop.h | 8 | ||||
-rw-r--r-- | common/rfb/SMsgWriter.h | 10 | ||||
-rw-r--r-- | common/rfb/SMsgWriterV3.cxx | 67 | ||||
-rw-r--r-- | common/rfb/SMsgWriterV3.h | 15 | ||||
-rw-r--r-- | common/rfb/ScreenSet.h | 29 | ||||
-rw-r--r-- | common/rfb/VNCSConnectionST.cxx | 49 | ||||
-rw-r--r-- | common/rfb/VNCSConnectionST.h | 1 | ||||
-rw-r--r-- | common/rfb/VNCServer.h | 13 | ||||
-rw-r--r-- | common/rfb/VNCServerST.cxx | 113 | ||||
-rw-r--r-- | common/rfb/VNCServerST.h | 4 |
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; |