Merge the "V3" message classes into the normal ones

We have no need for this abstraction so let's keep things simple.
diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx
index 509ffbf..c21f8bc 100644
--- a/common/rfb/SMsgWriter.cxx
+++ b/common/rfb/SMsgWriter.cxx
@@ -1,5 +1,6 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright (C) 2011 D. R. Commander.  All Rights Reserved.
+ * Copyright 2009-2014 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
@@ -20,9 +21,12 @@
 #include <assert.h>
 #include <rdr/OutStream.h>
 #include <rfb/msgTypes.h>
+#include <rfb/fenceTypes.h>
+#include <rfb/Exception.h>
 #include <rfb/ColourMap.h>
 #include <rfb/ConnParams.h>
 #include <rfb/UpdateTracker.h>
+#include <rfb/Encoder.h>
 #include <rfb/SMsgWriter.h>
 #include <rfb/LogWriter.h>
 
@@ -31,8 +35,11 @@
 static LogWriter vlog("SMsgWriter");
 
 SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
-  : imageBufIdealSize(0), cp(cp_), os(os_), lenBeforeRect(0),
-    currentEncoding(0), updatesSent(0), rawBytesEquivalent(0),
+  : imageBufIdealSize(0), cp(cp_), os(os_), currentEncoding(0),
+    nRectsInUpdate(0), nRectsInHeader(0),
+    wsccb(0), needSetDesktopSize(false),
+    needExtendedDesktopSize(false), needSetDesktopName(false),
+    lenBeforeRect(0), updatesSent(0), rawBytesEquivalent(0),
     imageBuf(0), imageBufSize(0)
 {
   for (int i = 0; i <= encodingMax; i++) {
@@ -59,6 +66,15 @@
   delete [] imageBuf;
 }
 
+void SMsgWriter::writeServerInit()
+{
+  os->writeU16(cp->width);
+  os->writeU16(cp->height);
+  cp->pf().write(os);
+  os->writeString(cp->name());
+  endMsg();
+}
+
 void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
                                           ColourMap* cm)
 {
@@ -91,6 +107,35 @@
   endMsg();
 }
 
+void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
+{
+  if (!cp->supportsFence)
+    throw Exception("Client does not support fences");
+  if (len > 64)
+    throw Exception("Too large fence payload");
+  if ((flags & ~fenceFlagsSupported) != 0)
+    throw Exception("Unknown fence flags");
+
+  startMsg(msgTypeServerFence);
+  os->pad(3);
+
+  os->writeU32(flags);
+
+  os->writeU8(len);
+  os->writeBytes(data, len);
+
+  endMsg();
+}
+
+void SMsgWriter::writeEndOfContinuousUpdates()
+{
+  if (!cp->supportsContinuousUpdates)
+    throw Exception("Client does not support continuous updates");
+
+  startMsg(msgTypeEndOfContinuousUpdates);
+  endMsg();
+}
+
 void SMsgWriter::setupCurrentEncoder()
 {
   int encoding = cp->currentEncoding();
@@ -117,20 +162,133 @@
   return encoders[encoding]->getNumRects(r);
 }
 
+bool SMsgWriter::writeSetDesktopSize() {
+  if (!cp->supportsDesktopResize)
+    return false;
+
+  needSetDesktopSize = true;
+
+  return true;
+}
+
+bool SMsgWriter::writeExtendedDesktopSize() {
+  if (!cp->supportsExtendedDesktopSize)
+    return false;
+
+  needExtendedDesktopSize = true;
+
+  return true;
+}
+
+bool SMsgWriter::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;
+}
+
+bool SMsgWriter::writeSetDesktopName() {
+  if (!cp->supportsDesktopRename)
+    return false;
+
+  needSetDesktopName = true;
+
+  return true;
+}
+
+void SMsgWriter::cursorChange(WriteSetCursorCallback* cb)
+{
+  wsccb = cb;
+}
+
+void SMsgWriter::writeSetCursor(int width, int height, const Point& hotspot,
+                                void* data, void* mask)
+{
+  if (!wsccb)
+    return;
+
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriter::writeSetCursor: nRects out of sync");
+
+  os->writeS16(hotspot.x);
+  os->writeS16(hotspot.y);
+  os->writeU16(width);
+  os->writeU16(height);
+  os->writeU32(pseudoEncodingCursor);
+  os->writeBytes(data, width * height * (cp->pf().bpp/8));
+  os->writeBytes(mask, (width+7)/8 * height);
+}
+
+void SMsgWriter::writeSetXCursor(int width, int height, int hotspotX,
+                                 int hotspotY, void* data, void* mask)
+{
+  if (!wsccb)
+    return;
+
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriter::writeSetXCursor: nRects out of sync");
+
+  os->writeS16(hotspotX);
+  os->writeS16(hotspotY);
+  os->writeU16(width);
+  os->writeU16(height);
+  os->writeU32(pseudoEncodingXCursor);
+  // FIXME: We only support black and white cursors, currently. We
+  // could pass the correct color by using the pix0/pix1 values
+  // returned from getBitmap, in writeSetCursorCallback. However, we
+  // would then need to undo the conversion from rgb to Pixel that is
+  // done by FakeAllocColor.
+  if (width * height) {
+    os->writeU8(0);
+    os->writeU8(0);
+    os->writeU8(0);
+    os->writeU8(255);
+    os->writeU8(255);
+    os->writeU8(255);
+    os->writeBytes(data, (width+7)/8 * height);
+    os->writeBytes(mask, (width+7)/8 * height);
+  }
+}
+
 bool SMsgWriter::needFakeUpdate()
 {
-  return false;
+  return wsccb || needSetDesktopName || needNoDataUpdate();
 }
 
 bool SMsgWriter::needNoDataUpdate()
 {
-  return false;
+  return needSetDesktopSize || needExtendedDesktopSize ||
+         !extendedDesktopSizeMsgs.empty();
 }
 
 void SMsgWriter::writeNoDataUpdate()
 {
-  // This class has no pseudo-rectangles so there is nothing to do here
-  vlog.error("writeNoDataUpdate() called");
+  int nRects;
+
+  nRects = 0;
+
+  if (needSetDesktopSize)
+    nRects++;
+  if (needExtendedDesktopSize)
+    nRects++;
+  if (!extendedDesktopSizeMsgs.empty())
+    nRects += extendedDesktopSizeMsgs.size();
+
+  writeFramebufferUpdateStart(nRects);
+  writeNoDataRects();
+  writeFramebufferUpdateEnd();
 }
 
 void SMsgWriter::writeRects(const UpdateInfo& ui, TransImageGetter* ig,
@@ -155,6 +313,48 @@
   }
 }
 
+void SMsgWriter::writeFramebufferUpdateStart(int nRects)
+{
+  startMsg(msgTypeFramebufferUpdate);
+  os->pad(1);
+
+  if (nRects != 0xFFFF) {
+    if (wsccb)
+      nRects++;
+    if (needSetDesktopName)
+      nRects++;
+  }
+
+  os->writeU16(nRects);
+
+  nRectsInUpdate = 0;
+  if (nRects == 0xFFFF)
+    nRectsInHeader = 0;
+  else
+    nRectsInHeader = nRects;
+
+  writePseudoRects();
+}
+
+void SMsgWriter::writeFramebufferUpdateEnd()
+{
+  if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
+                    "nRects out of sync");
+
+  if (nRectsInHeader == 0) {
+    // Send last rect. marker
+    os->writeS16(0);
+    os->writeS16(0);
+    os->writeU16(0);
+    os->writeU16(0);
+    os->writeU32(pseudoEncodingLastRect);
+  }
+
+  updatesSent++;
+  endMsg();
+}
+
 bool SMsgWriter::writeRect(const Rect& r, TransImageGetter* ig, Rect* actual)
 {
   return writeRect(r, cp->currentEncoding(), ig, actual);
@@ -178,6 +378,31 @@
   endRect();
 }
 
+void SMsgWriter::startRect(const Rect& r, int encoding)
+{
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriter::startRect: nRects out of sync");
+
+  currentEncoding = encoding;
+  lenBeforeRect = os->length();
+  if (encoding != encodingCopyRect)
+    rawBytesEquivalent += 12 + r.width() * r.height() * (bpp()/8);
+
+  os->writeS16(r.tl.x);
+  os->writeS16(r.tl.y);
+  os->writeU16(r.width());
+  os->writeU16(r.height());
+  os->writeU32(encoding);
+}
+
+void SMsgWriter::endRect()
+{
+  if (currentEncoding <= encodingMax) {
+    bytesSent[currentEncoding] += os->length() - lenBeforeRect;
+    rectsSent[currentEncoding]++;
+  }
+}
+
 rdr::U8* SMsgWriter::getImageBuf(int required, int requested, int* nPixels)
 {
   int requiredBytes = required * (cp->pf().bpp / 8);
@@ -202,3 +427,119 @@
 {
   return cp->pf().bpp;
 }
+
+void SMsgWriter::startMsg(int type)
+{
+  os->writeU8(type);
+}
+
+void SMsgWriter::endMsg()
+{
+  os->flush();
+}
+
+void SMsgWriter::writePseudoRects()
+{
+  if (wsccb) {
+    wsccb->writeSetCursorCallback();
+    wsccb = 0;
+  }
+
+  if (needSetDesktopName) {
+    if (!cp->supportsDesktopRename)
+      throw Exception("Client does not support desktop rename");
+    if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+      throw Exception("SMsgWriter::setDesktopName: nRects out of sync");
+
+    os->writeS16(0);
+    os->writeS16(0);
+    os->writeU16(0);
+    os->writeU16(0);
+    os->writeU32(pseudoEncodingDesktopName);
+    os->writeString(cp->name());
+
+    needSetDesktopName = false;
+  }
+}
+
+void SMsgWriter::writeNoDataRects()
+{
+  // 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 += extendedDesktopSizeMsgs.size()) > nRectsInHeader && nRectsInHeader)
+      throw Exception("SMsgWriter::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(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);
+      }
+    }
+
+    extendedDesktopSizeMsgs.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("SMsgWriter::setExtendedDesktopSize: nRects out of sync");
+
+    os->writeU16(0);
+    os->writeU16(0);
+    os->writeU16(cp->width);
+    os->writeU16(cp->height);
+    os->writeU32(pseudoEncodingExtendedDesktopSize);
+
+    os->writeU8(cp->screenLayout.num_screens());
+    os->pad(3);
+
+    ScreenSet::const_iterator iter;
+    for (iter = cp->screenLayout.begin();iter != cp->screenLayout.end();++iter) {
+      os->writeU32(iter->id);
+      os->writeU16(iter->dimensions.tl.x);
+      os->writeU16(iter->dimensions.tl.y);
+      os->writeU16(iter->dimensions.width());
+      os->writeU16(iter->dimensions.height());
+      os->writeU32(iter->flags);
+    }
+
+    needExtendedDesktopSize = false;
+  }
+
+  // Some clients assume this is the last rectangle so don't send anything
+  // more after this
+  if (needSetDesktopSize) {
+    if (!cp->supportsDesktopResize)
+      throw Exception("Client does not support desktop resize");
+    if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+      throw Exception("SMsgWriter::setDesktopSize: nRects out of sync");
+
+    os->writeS16(0);
+    os->writeS16(0);
+    os->writeU16(cp->width);
+    os->writeU16(cp->height);
+    os->writeU32(pseudoEncodingDesktopSize);
+
+    needSetDesktopSize = false;
+  }
+}