Minimal server side implementation of the extended desktop size protocol.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@3698 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/rfb/ConnParams.cxx b/common/rfb/ConnParams.cxx
index b328a1f..10b60fb 100644
--- a/common/rfb/ConnParams.cxx
+++ b/common/rfb/ConnParams.cxx
@@ -29,8 +29,10 @@
 ConnParams::ConnParams()
   : majorVersion(0), minorVersion(0), tightExtensionsEnabled(false),
     width(0), height(0), useCopyRect(false),
-    supportsLocalCursor(false), supportsLocalXCursor(false), supportsDesktopResize(true), supportsDesktopRename(false),
-    supportsLastRect(false), customCompressLevel(false), compressLevel(6),
+    supportsLocalCursor(false), supportsLocalXCursor(false),
+    supportsDesktopResize(false), supportsExtendedDesktopSize(false),
+    supportsDesktopRename(false), supportsLastRect(false),
+    customCompressLevel(false), compressLevel(6),
     noJpeg(false), qualityLevel(-1), 
     name_(0), nEncodings_(0), encodings_(0),
     currentEncoding_(encodingRaw), verStrPos(0)
@@ -92,6 +94,7 @@
   useCopyRect = false;
   supportsLocalCursor = false;
   supportsDesktopResize = false;
+  supportsExtendedDesktopSize = false;
   supportsLocalXCursor = false;
   supportsLastRect = false;
   customCompressLevel = false;
@@ -111,6 +114,8 @@
       supportsLocalXCursor = true;
     else if (encodings[i] == pseudoEncodingDesktopSize)
       supportsDesktopResize = true;
+    else if (encodings[i] == pseudoEncodingExtendedDesktopSize)
+      supportsExtendedDesktopSize = true;
     else if (encodings[i] == pseudoEncodingDesktopName)
       supportsDesktopRename = true;
     else if (encodings[i] == pseudoEncodingLastRect)
diff --git a/common/rfb/ConnParams.h b/common/rfb/ConnParams.h
index f00b1d6..bdd9cf2 100644
--- a/common/rfb/ConnParams.h
+++ b/common/rfb/ConnParams.h
@@ -73,6 +73,7 @@
     bool supportsLocalCursor;
     bool supportsLocalXCursor;
     bool supportsDesktopResize;
+    bool supportsExtendedDesktopSize;
     bool supportsDesktopRename;
     bool supportsLastRect;
 
diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx
index c55a8b7..0c74f0f 100644
--- a/common/rfb/SMsgHandler.cxx
+++ b/common/rfb/SMsgHandler.cxx
@@ -43,11 +43,13 @@
   supportsLocalCursor();
 }
 
-void SMsgHandler::framebufferUpdateRequest(const Rect& r, bool incremental)
+void SMsgHandler::supportsLocalCursor()
 {
 }
 
-void SMsgHandler::supportsLocalCursor()
+void SMsgHandler::setDesktopSize(int fb_width, int fb_height)
 {
+  cp.width = fb_width;
+  cp.height = fb_height;
 }
 
diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h
index 50a2dc4..78cff2f 100644
--- a/common/rfb/SMsgHandler.h
+++ b/common/rfb/SMsgHandler.h
@@ -38,14 +38,15 @@
 
     // The following methods are called as corresponding messages are read.  A
     // derived class should override these methods as desired.  Note that for
-    // the setPixelFormat() and setEncodings() methods, a derived class must
-    // call on to SMsgHandler's methods.
+    // the setPixelFormat(), setEncodings() and setDesktopSize() methods, a
+    // derived class must call on to SMsgHandler's methods.
 
     virtual void clientInit(bool shared);
 
     virtual void setPixelFormat(const PixelFormat& pf);
     virtual void setEncodings(int nEncodings, rdr::U32* encodings);
-    virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+    virtual void framebufferUpdateRequest(const Rect& r, bool incremental) = 0;
+    virtual void setDesktopSize(int fb_width, int fb_height) = 0;
 
     // InputHandler interface
     // The InputHandler methods will be called for the corresponding messages.
diff --git a/common/rfb/SMsgReaderV3.cxx b/common/rfb/SMsgReaderV3.cxx
index 8e870cd..1408fe6 100644
--- a/common/rfb/SMsgReaderV3.cxx
+++ b/common/rfb/SMsgReaderV3.cxx
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009 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
@@ -51,9 +52,30 @@
   case msgTypeKeyEvent:                 readKeyEvent(); break;
   case msgTypePointerEvent:             readPointerEvent(); break;
   case msgTypeClientCutText:            readClientCutText(); break;
+  case msgTypeSetDesktopSize:           readSetDesktopSize(); break;
 
   default:
     fprintf(stderr, "unknown message type %d\n", msgType);
     throw Exception("unknown message type");
   }
 }
+
+void SMsgReaderV3::readSetDesktopSize()
+{
+  int width, height;
+  int screens, i;
+
+  is->skip(1);
+
+  width = is->readU16();
+  height = is->readU16();
+
+  screens = is->readU8();
+  is->skip(1);
+
+  // XXX: We don't support this command properly yet
+  is->skip(screens * 16);
+
+  handler->setDesktopSize(width, height);
+}
+
diff --git a/common/rfb/SMsgReaderV3.h b/common/rfb/SMsgReaderV3.h
index c6b7bf4..8b52632 100644
--- a/common/rfb/SMsgReaderV3.h
+++ b/common/rfb/SMsgReaderV3.h
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009 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
@@ -27,6 +28,8 @@
     virtual ~SMsgReaderV3();
     virtual void readClientInit();
     virtual void readMsg();
+  protected:
+    virtual void readSetDesktopSize();
   };
 }
 #endif
diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h
index bab9a1a..3c6da95 100644
--- a/common/rfb/SMsgWriter.h
+++ b/common/rfb/SMsgWriter.h
@@ -24,6 +24,7 @@
 
 #include <rdr/types.h>
 #include <rfb/encodings.h>
+#include <rfb/screenTypes.h>
 #include <rfb/Encoder.h>
 #include <rfb/PixelBuffer.h>
 
@@ -74,6 +75,8 @@
     // 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;
 
     virtual bool writeSetDesktopName()=0;
 
diff --git a/common/rfb/SMsgWriterV3.cxx b/common/rfb/SMsgWriterV3.cxx
index 1271619..ca6f3f0 100644
--- a/common/rfb/SMsgWriterV3.cxx
+++ b/common/rfb/SMsgWriterV3.cxx
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009 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
@@ -18,6 +19,7 @@
 #include <rdr/OutStream.h>
 #include <rdr/MemOutStream.h>
 #include <rfb/msgTypes.h>
+#include <rfb/screenTypes.h>
 #include <rfb/Exception.h>
 #include <rfb/ConnParams.h>
 #include <rfb/SMsgWriterV3.h>
@@ -64,6 +66,15 @@
   return true;
 }
 
+bool SMsgWriterV3::writeExtendedDesktopSize(rdr::U16 error) {
+  if (!cp->supportsExtendedDesktopSize) return false;
+  if (error == resultUnsolicited)
+    needExtendedDesktopSize = true;
+  else
+    edsErrors.push_back(error);
+  return true;
+}
+
 bool SMsgWriterV3::writeSetDesktopName() {
   if (!cp->supportsDesktopRename) return false;
   needSetDesktopName = true;
@@ -124,6 +135,8 @@
   os->pad(1);
   if (wsccb) nRects++;
   if (needSetDesktopSize) nRects++;
+  if (needExtendedDesktopSize) nRects++;
+  if (!edsErrors.empty()) nRects += edsErrors.size();
   if (needSetDesktopName) nRects++;
   os->writeU16(nRects);
   nRectsInUpdate = 0;
@@ -144,6 +157,50 @@
 
 void SMsgWriterV3::writeFramebufferUpdateEnd()
 {
+  /* Start with responses to SetDesktopSize messages */
+  if (!edsErrors.empty()) {
+    std::list<rdr::U16>::const_iterator iter;
+
+    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);
+      os->writeU32(pseudoEncodingExtendedDesktopSize);
+      os->writeU8(0);             // # screens
+      os->pad(3);
+    }
+
+    edsErrors.clear();
+  }
+
+  /* Send this before SetDesktopSize to make life easier on the clients */
+  if (needExtendedDesktopSize) {
+    if (!cp->supportsExtendedDesktopSize)
+      throw Exception("Client does not support extended desktop resize");
+    if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+      throw Exception("SMsgWriterV3 setExtendedDesktopSize: nRects out of sync");
+    os->writeU16(0);
+    os->writeU16(0);
+    os->writeU16(cp->width);
+    os->writeU16(cp->height);
+    os->writeU32(pseudoEncodingExtendedDesktopSize);
+    os->writeU8(1);             // # screens
+    os->pad(3);
+    os->writeU32(1);            // id
+    os->writeU16(0);            // x-pos
+    os->writeU16(0);            // y-pos
+    os->writeU16(cp->width);    // width
+    os->writeU16(cp->height);   // height
+    os->writeU32(0);            // flags
+    needExtendedDesktopSize = false;
+  }
+
   if (needSetDesktopSize) {
     if (!cp->supportsDesktopResize)
       throw Exception("Client does not support desktop resize");
diff --git a/common/rfb/SMsgWriterV3.h b/common/rfb/SMsgWriterV3.h
index d2c13df..509bfdc 100644
--- a/common/rfb/SMsgWriterV3.h
+++ b/common/rfb/SMsgWriterV3.h
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009 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
@@ -18,6 +19,8 @@
 #ifndef __RFB_SMSGWRITERV3_H__
 #define __RFB_SMSGWRITERV3_H__
 
+#include <list>
+
 #include <rfb/SMsgWriter.h>
 
 namespace rdr { class MemOutStream; }
@@ -32,6 +35,7 @@
     virtual void startMsg(int type);
     virtual void endMsg();
     virtual bool writeSetDesktopSize();
+    virtual bool writeExtendedDesktopSize(rdr::U16 error);
     virtual bool writeSetDesktopName();
     virtual void cursorChange(WriteSetCursorCallback* cb);
     virtual void writeSetCursor(int width, int height, const Point& hotspot,
@@ -52,6 +56,8 @@
     int nRectsInHeader;
     WriteSetCursorCallback* wsccb;
     bool needSetDesktopSize;
+    bool needExtendedDesktopSize;
+    std::list<rdr::U16> edsErrors;
     bool needSetDesktopName;
     bool needLastRect;
   };
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index e021aa6..3b4448b 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009 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
@@ -19,6 +20,7 @@
 #include <rfb/VNCSConnectionST.h>
 #include <rfb/LogWriter.h>
 #include <rfb/secTypes.h>
+#include <rfb/screenTypes.h>
 #include <rfb/ServerCore.h>
 #include <rfb/ComparingUpdateTracker.h>
 #include <rfb/KeyRemapper.h>
@@ -168,7 +170,8 @@
       cp.width = server->pb->width();
       cp.height = server->pb->height();
       if (state() == RFBSTATE_NORMAL) {
-        if (!writer()->writeSetDesktopSize()) {
+        if (!writer()->writeSetDesktopSize() &&
+            !writer()->writeExtendedDesktopSize()) {
           close("Client does not support desktop resize");
           return;
         }
@@ -490,9 +493,18 @@
     // Non-incremental update - treat as if area requested has changed
     updates.add_changed(reqRgn);
     server->comparer->add_changed(reqRgn);
+    // And update the clients view of screen layout
+    writer()->writeSetDesktopSize();
+    writer()->writeExtendedDesktopSize();
   }
 }
 
+void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height)
+{
+  vlog.info("Rejecting client request to change desktop size");
+  writer()->writeExtendedDesktopSize(resultProhibited);
+}
+
 void VNCSConnectionST::setInitialColourMap()
 {
   setColourMapEntries(0, 0);
diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h
index 2121150..d5c7a36 100644
--- a/common/rfb/VNCSConnectionST.h
+++ b/common/rfb/VNCSConnectionST.h
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2009 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
@@ -124,6 +125,7 @@
     virtual void keyEvent(rdr::U32 key, bool down);
     virtual void clientCutText(const char* str, int len);
     virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+    virtual void setDesktopSize(int fb_width, int fb_height);
     virtual void setInitialColourMap();
     virtual void supportsLocalCursor();
 
diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h
index 1021be9..2f3b430 100644
--- a/common/rfb/encodings.h
+++ b/common/rfb/encodings.h
@@ -33,6 +33,7 @@
   const unsigned int pseudoEncodingXCursor = 0xffffff10;
   const unsigned int pseudoEncodingCursor = 0xffffff11;
   const unsigned int pseudoEncodingDesktopSize = 0xffffff21;
+  const unsigned int pseudoEncodingExtendedDesktopSize = 0xfffffecb; // FIXME: Unofficial
   const unsigned int pseudoEncodingDesktopName = 0xfffffecdl;
 
   // TightVNC-specific
diff --git a/common/rfb/msgTypes.h b/common/rfb/msgTypes.h
index 2b24f3c..6bda345 100644
--- a/common/rfb/msgTypes.h
+++ b/common/rfb/msgTypes.h
@@ -39,5 +39,7 @@
   const int msgTypeClientCutText = 6;
 
   const int msgTypeEnableContinuousUpdates = 150;
+
+  const int msgTypeSetDesktopSize = 251; // FIXME: Unofficial
 }
 #endif
diff --git a/common/rfb/screenTypes.h b/common/rfb/screenTypes.h
new file mode 100644
index 0000000..70971e4
--- /dev/null
+++ b/common/rfb/screenTypes.h
@@ -0,0 +1,37 @@
+/* Copyright 2009 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 __RFB_SCREENTYPES_H__
+#define __RFB_SCREENTYPES_H__
+
+namespace rfb {
+
+  // Reasons
+  const unsigned int reasonServer = 0;
+  const unsigned int reasonClient = 1;
+  const unsigned int reasonOtherClient = 2;
+
+  // Result codes
+  const unsigned int resultSuccess = 0;
+  const unsigned int resultProhibited = 1;
+  const unsigned int resultNoResources = 2;
+  const unsigned int resultInvalid = 3;
+
+  const int resultUnsolicited = 0xffff; // internal code used for server changes
+
+}
+#endif