Move update request handling in to CConnection

It's a generic client thing, so abstract it in to the common library.
Makes it easier to integrate with other common code.
diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx
index 14ef221..696a124 100644
--- a/common/rfb/CConnection.cxx
+++ b/common/rfb/CConnection.cxx
@@ -43,6 +43,10 @@
   : csecurity(0), is(0), os(0), reader_(0), writer_(0),
     shared(false),
     state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
+    pendingPFChange(false), preferredEncoding(encodingTight),
+    formatChange(false), encodingChange(false),
+    firstUpdate(true), pendingUpdate(false), continuousUpdates(false),
+    forceNonincremental(true),
     framebuffer(NULL), decoder(this)
 {
 }
@@ -324,6 +328,11 @@
   decoder.flush();
 
   CMsgHandler::setDesktopSize(w,h);
+
+  if (continuousUpdates)
+    writer()->writeEnableContinuousUpdates(true, 0, 0,
+                                           server.width(),
+                                           server.height());
 }
 
 void CConnection::setExtendedDesktopSize(unsigned reason,
@@ -334,6 +343,27 @@
   decoder.flush();
 
   CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
+
+  if (continuousUpdates)
+    writer()->writeEnableContinuousUpdates(true, 0, 0,
+                                           server.width(),
+                                           server.height());
+}
+
+void CConnection::endOfContinuousUpdates()
+{
+  CMsgHandler::endOfContinuousUpdates();
+
+  // We've gotten the marker for a format change, so make the pending
+  // one active
+  if (pendingPFChange) {
+    server.setPF(pendingPF);
+    pendingPFChange = false;
+
+    // We might have another change pending
+    if (formatChange)
+      requestNewUpdate();
+  }
 }
 
 void CConnection::serverInit(int width, int height,
@@ -349,6 +379,18 @@
   assert(framebuffer != NULL);
   assert(framebuffer->width() == server.width());
   assert(framebuffer->height() == server.height());
+
+  // We want to make sure we call SetEncodings at least once
+  encodingChange = true;
+
+  requestNewUpdate();
+
+  // This initial update request is a bit of a corner case, so we need
+  // to help out setting the correct format here.
+  if (pendingPFChange) {
+    server.setPF(pendingPF);
+    pendingPFChange = false;
+  }
 }
 
 void CConnection::readAndDecodeRect(const Rect& r, int encoding,
@@ -361,6 +403,13 @@
 void CConnection::framebufferUpdateStart()
 {
   CMsgHandler::framebufferUpdateStart();
+
+  assert(framebuffer != NULL);
+
+  // Note: This might not be true if continuous updates are supported
+  pendingUpdate = false;
+
+  requestNewUpdate();
 }
 
 void CConnection::framebufferUpdateEnd()
@@ -368,6 +417,25 @@
   decoder.flush();
 
   CMsgHandler::framebufferUpdateEnd();
+
+  // A format change has been scheduled and we are now past the update
+  // with the old format. Time to active the new one.
+  if (pendingPFChange && !continuousUpdates) {
+    server.setPF(pendingPF);
+    pendingPFChange = false;
+  }
+
+  if (firstUpdate) {
+    if (server.supportsContinuousUpdates) {
+      vlog.info("Enabling continuous updates");
+      continuousUpdates = true;
+      writer()->writeEnableContinuousUpdates(true, 0, 0,
+                                             server.width(),
+                                             server.height());
+    }
+
+    firstUpdate = false;
+  }
 }
 
 void CConnection::dataRect(const Rect& r, int encoding)
@@ -383,6 +451,57 @@
 {
 }
 
+void CConnection::refreshFramebuffer()
+{
+  forceNonincremental = true;
+
+  // Without continuous updates we have to make sure we only have a
+  // single update in flight, so we'll have to wait to do the refresh
+  if (continuousUpdates)
+    requestNewUpdate();
+}
+
+void CConnection::setPreferredEncoding(int encoding)
+{
+  if (preferredEncoding == encoding)
+    return;
+
+  preferredEncoding = encoding;
+  encodingChange = true;
+}
+
+int CConnection::getPreferredEncoding()
+{
+  return preferredEncoding;
+}
+
+void CConnection::setCompressLevel(int level)
+{
+  if (server.compressLevel == level)
+    return;
+
+  server.compressLevel = level;
+  encodingChange = true;
+}
+
+void CConnection::setQualityLevel(int level)
+{
+  if (server.qualityLevel == level)
+    return;
+
+  server.qualityLevel = level;
+  encodingChange = true;
+}
+
+void CConnection::setPF(const PixelFormat& pf)
+{
+  if (server.pf().equal(pf) && !formatChange)
+    return;
+
+  nextPF = pf;
+  formatChange = true;
+}
+
 void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
 {
   CMsgHandler::fence(flags, len, data);
@@ -395,3 +514,50 @@
 
   writer()->writeFence(flags, len, data);
 }
+
+// requestNewUpdate() requests an update from the server, having set the
+// format and encoding appropriately.
+void CConnection::requestNewUpdate()
+{
+  if (formatChange && !pendingPFChange) {
+    /* Catch incorrect requestNewUpdate calls */
+    assert(!pendingUpdate || continuousUpdates);
+
+    // We have to make sure we switch the internal format at a safe
+    // time. For continuous updates we temporarily disable updates and
+    // look for a EndOfContinuousUpdates message to see when to switch.
+    // For classical updates we just got a new update right before this
+    // function was called, so we need to make sure we finish that
+    // update before we can switch.
+
+    pendingPFChange = true;
+    pendingPF = nextPF;
+
+    if (continuousUpdates)
+      writer()->writeEnableContinuousUpdates(false, 0, 0, 0, 0);
+
+    writer()->writeSetPixelFormat(pendingPF);
+
+    if (continuousUpdates)
+      writer()->writeEnableContinuousUpdates(true, 0, 0,
+                                             server.width(),
+                                             server.height());
+
+    formatChange = false;
+  }
+
+  if (encodingChange) {
+    writer()->writeSetEncodings(preferredEncoding, true);
+    encodingChange = false;
+  }
+
+  if (forceNonincremental || !continuousUpdates) {
+    pendingUpdate = true;
+    writer()->writeFramebufferUpdateRequest(Rect(0, 0,
+                                                 server.width(),
+                                                 server.height()),
+                                            !forceNonincremental);
+  }
+
+  forceNonincremental = false;
+}
diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h
index 7623c02..5f953ae 100644
--- a/common/rfb/CConnection.h
+++ b/common/rfb/CConnection.h
@@ -100,6 +100,8 @@
                                         int w, int h,
                                         const ScreenSet& layout);
 
+    virtual void endOfContinuousUpdates();
+
     virtual void serverInit(int width, int height,
                             const PixelFormat& pf,
                             const char* name);
@@ -128,6 +130,24 @@
 
     // Other methods
 
+    // refreshFramebuffer() forces a complete refresh of the entire
+    // framebuffer
+    void refreshFramebuffer();
+
+    // setPreferredEncoding()/getPreferredEncoding() adjusts which
+    // encoding is listed first as a hint to the server that it is the
+    // preferred one
+    void setPreferredEncoding(int encoding);
+    int getPreferredEncoding();
+    // setCompressLevel()/setQualityLevel() controls the encoding hints
+    // sent to the server
+    void setCompressLevel(int level);
+    void setQualityLevel(int level);
+    // setPF() controls the pixel format requested from the server.
+    // server.pf() will automatically be adjusted once the new format
+    // is active.
+    void setPF(const PixelFormat& pf);
+
     CMsgReader* reader() { return reader_; }
     CMsgWriter* writer() { return writer_; }
 
@@ -180,6 +200,8 @@
     void throwConnFailedException();
     void securityCompleted();
 
+    void requestNewUpdate();
+
     rdr::InStream* is;
     rdr::OutStream* os;
     CMsgReader* reader_;
@@ -192,6 +214,21 @@
 
     bool useProtocol3_3;
 
+    bool pendingPFChange;
+    rfb::PixelFormat pendingPF;
+
+    int preferredEncoding;
+
+    bool formatChange;
+    rfb::PixelFormat nextPF;
+    bool encodingChange;
+
+    bool firstUpdate;
+    bool pendingUpdate;
+    bool continuousUpdates;
+
+    bool forceNonincremental;
+
     ModifiablePixelBuffer* framebuffer;
     DecodeManager decoder;
   };