]> source.dussan.org Git - tigervnc.git/commitdiff
Implement server side support for SetDesktopSize.
authorPierre Ossman <ossman@cendio.se>
Mon, 23 Mar 2009 16:57:07 +0000 (16:57 +0000)
committerPierre Ossman <ossman@cendio.se>
Mon, 23 Mar 2009 16:57:07 +0000 (16:57 +0000)
It has some warts, but should be feature complete. Most of the magic happens
in the desktop class though.

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@3713 3789f03b-4d11-0410-bbf8-ca57d06f2519

common/rfb/SDesktop.h
common/rfb/SMsgWriter.h
common/rfb/SMsgWriterV3.cxx
common/rfb/SMsgWriterV3.h
common/rfb/ScreenSet.h
common/rfb/VNCSConnectionST.cxx
common/rfb/VNCSConnectionST.h
common/rfb/VNCServer.h
common/rfb/VNCServerST.cxx
common/rfb/VNCServerST.h

index 7b054e37868a5c4f0c29d744caf95953f9510237..4a53d53834cb92f4e275f1664a07366be5435d68 100644 (file)
@@ -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.
index 3c6da95a2264463445aa9b4b4fcf2a6cbd02d841..5df72704d9bb19f871b402943de5b7b1b9580454 100644 (file)
@@ -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;
 
index de093964dd5daeab647ad3c1b9039f66f578acea..6a8316f17467deb202c3a9ff5d088f8b7b5a019e 100644 (file)
@@ -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)
index 509bfdcb6ad8c271a27253c4c21c0f27a0893bac..e86c828c6b63f0b82929f022f64fe23227420571 100644 (file)
@@ -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
index 55abb5098955376b46ee8bb03383ea8c17f9139e..6f5188ca23b0f506d7bee239dd30fdfbf56edb56 100644 (file)
@@ -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;
   };
 
index 29c62746095664c8d20d546783353ef5cf751ac0..fdcea883d1ebc15e7d323f8c2ba6e5e026b6819c 100644 (file)
@@ -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()
index c1750b92a3b3afade89aa477029b5e4fe3b77502..c9e4ac8d7574f60eccad02b0f79433ac03587920 100644 (file)
@@ -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);
index 1e7acc63f72f6adcd578384a15744820bb2637da..850dbd3c0f5a3bd02a7a9a74c457fe6b2c26a9a2 100644 (file)
 
 #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;
 
index 7edac56b74bd8b359ffbcf0347d96e142820cbc2..ec01074eab3e7695e2b23b4de6598f0b23136230 100644 (file)
@@ -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);
+  }
+}
index 8e98ba33ac5225b8b8cfc25d45baeaedb21ecce5..320db2f488b566ba84941577ab30a439ae40f446 100644 (file)
@@ -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;