Provide a better R/W base PixelBuffer class

Clearly separates the read API from the write API
and also from actual implementation.
diff --git a/common/rfb/ComparingUpdateTracker.cxx b/common/rfb/ComparingUpdateTracker.cxx
index 8d4311a..1d27f3c 100644
--- a/common/rfb/ComparingUpdateTracker.cxx
+++ b/common/rfb/ComparingUpdateTracker.cxx
@@ -157,6 +157,8 @@
     oldData += oldStrideBytes * BLOCK_SIZE;
   }
 
+  oldFb.commitBufferRW(r);
+
   if (!changedBlocks.empty()) {
     Region temp;
     temp.setOrderedRects(changedBlocks);
diff --git a/common/rfb/PixelBuffer.cxx b/common/rfb/PixelBuffer.cxx
index ea19d18..bcffa79 100644
--- a/common/rfb/PixelBuffer.cxx
+++ b/common/rfb/PixelBuffer.cxx
@@ -60,26 +60,24 @@
   }
 }
 
+// -=- Modifiable generic pixel buffer class
 
-FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
-                                           rdr::U8* data_, int stride_)
-  : PixelBuffer(pf, w, h), data(data_), stride(stride_)
+ModifiablePixelBuffer::ModifiablePixelBuffer(const PixelFormat& pf,
+                                             int w, int h)
+  : PixelBuffer(pf, w, h)
 {
 }
 
-FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
-
-FullFramePixelBuffer::~FullFramePixelBuffer() {}
-
-
-rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride_)
+ModifiablePixelBuffer::ModifiablePixelBuffer()
 {
-  *stride_ = stride;
-  return &data[(r.tl.x + (r.tl.y * stride)) * format.bpp/8];
 }
 
+ModifiablePixelBuffer::~ModifiablePixelBuffer()
+{
+}
 
-void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
+void ModifiablePixelBuffer::fillRect(const Rect& r, Pixel pix)
+{
   int stride;
   U8 *buf, pixbuf[4];
   int w, h, b;
@@ -99,9 +97,13 @@
     }
     buf += (stride - w) * b;
   }
+
+  commitBufferRW(r);
 }
 
-void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
+void ModifiablePixelBuffer::imageRect(const Rect& r,
+                                      const void* pixels, int srcStride)
+{
   int bytesPerPixel = getPF().bpp/8;
   int destStride;
   U8* dest = getBufferRW(r, &destStride);
@@ -116,9 +118,12 @@
     dest += bytesPerDestRow;
     src += bytesPerSrcRow;
   }
+  commitBufferRW(r);
 }
 
-void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
+void ModifiablePixelBuffer::maskRect(const Rect& r,
+                                     const void* pixels, const void* mask_)
+{
   Rect cr = getRect().intersect(r);
   if (cr.is_empty()) return;
   int stride;
@@ -154,9 +159,13 @@
     }
     mask += maskStride;
   }
+
+  commitBufferRW(cr);
 }
 
-void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
+void ModifiablePixelBuffer::maskRect(const Rect& r,
+                                     Pixel pixel, const void* mask_)
+{
   Rect cr = getRect().intersect(r);
   if (cr.is_empty()) return;
   int stride;
@@ -190,9 +199,13 @@
     }
     mask += maskStride;
   }
+
+  commitBufferRW(cr);
 }
 
-void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
+void ModifiablePixelBuffer::copyRect(const Rect &rect,
+                                     const Point &move_by_delta)
+{
   int stride;
   U8* data;
   unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
@@ -241,8 +254,30 @@
       src -= bytesPerRow;
     }
   }
+  commitBufferRW(getRect());
 }
 
+// -=- Simple pixel buffer with a continuous block of memory
+
+FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
+                                           rdr::U8* data_, int stride_)
+  : ModifiablePixelBuffer(pf, w, h), data(data_), stride(stride_)
+{
+}
+
+FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
+
+FullFramePixelBuffer::~FullFramePixelBuffer() {}
+
+rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride_)
+{
+  *stride_ = stride;
+  return &data[(r.tl.x + (r.tl.y * stride)) * format.bpp/8];
+}
+
+void FullFramePixelBuffer::commitBufferRW(const Rect& r)
+{
+}
 
 // -=- Managed pixel buffer class
 // Automatically allocates enough space for the specified format & area
diff --git a/common/rfb/PixelBuffer.h b/common/rfb/PixelBuffer.h
index 59d71c7..e2cc3e9 100644
--- a/common/rfb/PixelBuffer.h
+++ b/common/rfb/PixelBuffer.h
@@ -66,7 +66,6 @@
     //   The pointer is to the top-left pixel of the specified Rect.
     //   The buffer stride (in pixels) is returned.
     virtual const rdr::U8* getBuffer(const Rect& r, int* stride) = 0;
-    virtual rdr::U8* getBufferRW(const Rect& r, int* stride) = 0;
 
     // Get pixel data for a given part of the buffer
     //   Data is copied into the supplied buffer, with the specified
@@ -89,20 +88,31 @@
     int width_, height_;
   };
 
-  // FullFramePixelBuffer
-
-  class FullFramePixelBuffer : public PixelBuffer {
+  // ModifiablePixelBuffer
+  class ModifiablePixelBuffer : public PixelBuffer {
   public:
-    FullFramePixelBuffer(const PixelFormat& pf, int width, int height,
-                         rdr::U8* data, int stride);
-    virtual ~FullFramePixelBuffer();
+    ModifiablePixelBuffer(const PixelFormat& pf, int width, int height);
+    virtual ~ModifiablePixelBuffer();
 
-  public:
-    // Get a pointer to specified pixel data
+    ///////////////////////////////////////////////
+    // Access to pixel data
+    //
+
+    // Get a writeable pointer into the buffer
+    //   Like getBuffer(), the pointer is to the top-left pixel of the
+    //   specified Rect and the stride in pixels is returned.
+    virtual rdr::U8* getBufferRW(const Rect& r, int* stride) = 0;
+    // Commit the modified contents
+    //   Ensures that the changes to the specified Rect is properly
+    //   stored away and any temporary buffers are freed. The Rect given
+    //   here needs to match the Rect given to the earlier call to
+    //   getBufferRW().
+    virtual void commitBufferRW(const Rect& r) = 0;
+
+    // Default trivial handling of read-only access
     virtual const rdr::U8* getBuffer(const Rect& r, int* stride) {
       return getBufferRW(r, stride);
     }
-    virtual rdr::U8* getBufferRW(const Rect& r, int* stride);
 
     ///////////////////////////////////////////////
     // Basic rendering operations
@@ -128,6 +138,22 @@
     void maskRect(const Rect& r, Pixel pixel, const void* mask_);
 
   protected:
+    ModifiablePixelBuffer();
+  };
+
+  // FullFramePixelBuffer
+
+  class FullFramePixelBuffer : public ModifiablePixelBuffer {
+  public:
+    FullFramePixelBuffer(const PixelFormat& pf, int width, int height,
+                         rdr::U8* data_, int stride);
+    virtual ~FullFramePixelBuffer();
+
+  public:
+    virtual rdr::U8* getBufferRW(const Rect& r, int* stride);
+    virtual void commitBufferRW(const Rect& r);
+
+  protected:
     FullFramePixelBuffer();
 
     rdr::U8* data;
diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx
index db1a08a..c78bb89 100644
--- a/vncviewer/CConn.cxx
+++ b/vncviewer/CConn.cxx
@@ -470,7 +470,7 @@
   return desktop->getBufferRW(r, stride);
 }
 void CConn::releaseRawBuffer(const rfb::Rect& r) {
-  desktop->damageRect(r);
+  desktop->commitBufferRW(r);
 }
 
 
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index 3e9b57e..a64f02a 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -232,8 +232,8 @@
   return viewport->getBufferRW(r, stride);
 }
 
-void DesktopWindow::damageRect(const rfb::Rect& r) {
-  viewport->damageRect(r);
+void DesktopWindow::commitBufferRW(const rfb::Rect& r) {
+  viewport->commitBufferRW(r);
 }
 
 
diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h
index 08a6652..83a8c76 100644
--- a/vncviewer/DesktopWindow.h
+++ b/vncviewer/DesktopWindow.h
@@ -55,7 +55,7 @@
   void copyRect(const rfb::Rect& r, int srcX, int srcY);
 
   rdr::U8* getBufferRW(const rfb::Rect& r, int* stride);
-  void damageRect(const rfb::Rect& r);
+  void commitBufferRW(const rfb::Rect& r);
 
   void resizeFramebuffer(int new_w, int new_h);
 
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index d1d5162..70964b7 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -229,6 +229,7 @@
     pixelTrans->translateRect(pixels, r.width(),
                               rfb::Rect(0, 0, r.width(), r.height()),
                               buffer, stride, rfb::Point(0, 0));
+    frameBuffer->commitBufferRW(r);
   } else {
     frameBuffer->imageRect(r, pixels);
   }
@@ -244,6 +245,11 @@
   return frameBuffer->getBufferRW(r, stride);
 }
 
+void Viewport::commitBufferRW(const rfb::Rect& r) {
+  frameBuffer->commitBufferRW(r);
+  damageRect(r);
+}
+
 void Viewport::damageRect(const rfb::Rect& r) {
   damage.assign_union(rfb::Region(r));
   if (!Fl::has_timeout(handleUpdateTimeout, this))
diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h
index bd17655..e112efd 100644
--- a/vncviewer/Viewport.h
+++ b/vncviewer/Viewport.h
@@ -56,8 +56,7 @@
   void copyRect(const rfb::Rect& r, int srcX, int srcY);
 
   rdr::U8* getBufferRW(const rfb::Rect& r, int* stride);
-
-  void damageRect(const rfb::Rect& r);
+  void commitBufferRW(const rfb::Rect& r);
 
   void setCursor(int width, int height, const rfb::Point& hotspot,
                  void* data, void* mask);
@@ -72,6 +71,8 @@
 
 private:
 
+  void damageRect(const rfb::Rect& r);
+
   PlatformPixelBuffer* createFramebuffer(int w, int h);
 
   static void handleUpdateTimeout(void *data);
diff --git a/win/rfb_win32/DeviceFrameBuffer.cxx b/win/rfb_win32/DeviceFrameBuffer.cxx
index 0ad06e9..d075c25 100644
--- a/win/rfb_win32/DeviceFrameBuffer.cxx
+++ b/win/rfb_win32/DeviceFrameBuffer.cxx
@@ -220,6 +220,7 @@
           }
         }
       }
+      cursorBm.commitBufferRW(cursorBm.getRect());
     }
 
     // Finally invert the AND mask so it's suitable for RFB and pack it into