Store the mouse cursor in the ConnParams object

Like we do for everything else. This also gets rid
of the callback, which is a bit out of place
compared to everything else.
diff --git a/common/rfb/ConnParams.cxx b/common/rfb/ConnParams.cxx
index 6fd6668..36f6daa 100644
--- a/common/rfb/ConnParams.cxx
+++ b/common/rfb/ConnParams.cxx
@@ -86,6 +86,21 @@
   name_ = strDup(name);
 }
 
+void ConnParams::setCursor(const Cursor& other)
+{
+  const rdr::U8* data;
+  int stride;
+
+  cursor_.hotspot = other.hotspot;
+  cursor_.setPF(other.getPF());
+  cursor_.setSize(other.width(), other.height());
+
+  data = other.getBuffer(other.getRect(), &stride);
+  cursor_.imageRect(cursor_.getRect(), data, stride);
+
+  memcpy(cursor_.mask.buf, other.mask.buf, cursor_.maskLen());
+}
+
 void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
 {
   useCopyRect = false;
diff --git a/common/rfb/ConnParams.h b/common/rfb/ConnParams.h
index 43267ff..7a6d60c 100644
--- a/common/rfb/ConnParams.h
+++ b/common/rfb/ConnParams.h
@@ -24,6 +24,7 @@
 #define __RFB_CONNPARAMS_H__
 
 #include <rdr/types.h>
+#include <rfb/Cursor.h>
 #include <rfb/PixelFormat.h>
 #include <rfb/ScreenSet.h>
 
@@ -74,6 +75,9 @@
     const char* name() { return name_; }
     void setName(const char* name);
 
+    const Cursor& cursor() { return cursor_; }
+    void setCursor(const Cursor& cursor);
+
     rdr::S32 currentEncoding() { return currentEncoding_; }
 
     void setEncodings(int nEncodings, const rdr::S32* encodings);
@@ -100,6 +104,7 @@
 
     PixelFormat pf_;
     char* name_;
+    Cursor cursor_;
     int currentEncoding_;
     char verStr[13];
     int verStrPos;
diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx
index 40e6d7f..46c4138 100644
--- a/common/rfb/SMsgWriter.cxx
+++ b/common/rfb/SMsgWriter.cxx
@@ -36,8 +36,8 @@
 SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
   : imageBufIdealSize(0), cp(cp_), os(os_), currentEncoding(0),
     nRectsInUpdate(0), nRectsInHeader(0),
-    wsccb(0), needSetDesktopSize(false),
-    needExtendedDesktopSize(false), needSetDesktopName(false),
+    needSetDesktopSize(false), needExtendedDesktopSize(false),
+    needSetDesktopName(false), needSetCursor(false), needSetXCursor(false),
     lenBeforeRect(0), updatesSent(0), rawBytesEquivalent(0),
     imageBuf(0), imageBufSize(0)
 {
@@ -179,69 +179,46 @@
   return true;
 }
 
-void SMsgWriter::cursorChange(WriteSetCursorCallback* cb)
+bool SMsgWriter::writeSetCursor()
 {
-  wsccb = cb;
+  if (!cp->supportsLocalCursor)
+    return false;
+
+  needSetCursor = true;
+
+  return true;
 }
 
-void SMsgWriter::writeSetCursor(int width, int height, const Point& hotspot,
-                                void* data, void* mask)
+bool SMsgWriter::writeSetXCursor()
 {
-  if (!wsccb)
-    return;
+  if (!cp->supportsLocalXCursor)
+    return false;
 
-  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
-    throw Exception("SMsgWriter::writeSetCursor: nRects out of sync");
+  needSetXCursor = true;
 
-  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);
-  }
+  return true;
 }
 
 bool SMsgWriter::needFakeUpdate()
 {
-  return wsccb || needSetDesktopName || needNoDataUpdate();
+  if (needSetDesktopName)
+    return true;
+  if (needSetCursor || needSetXCursor)
+    return true;
+  if (needNoDataUpdate())
+    return true;
+
+  return false;
 }
 
 bool SMsgWriter::needNoDataUpdate()
 {
-  return needSetDesktopSize || needExtendedDesktopSize ||
-         !extendedDesktopSizeMsgs.empty();
+  if (needSetDesktopSize)
+    return true;
+  if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty())
+    return true;
+
+  return false;
 }
 
 void SMsgWriter::writeNoDataUpdate()
@@ -268,10 +245,12 @@
   os->pad(1);
 
   if (nRects != 0xFFFF) {
-    if (wsccb)
-      nRects++;
     if (needSetDesktopName)
       nRects++;
+    if (needSetCursor)
+      nRects++;
+    if (needSetXCursor)
+      nRects++;
   }
 
   os->writeU16(nRects);
@@ -369,9 +348,41 @@
 
 void SMsgWriter::writePseudoRects()
 {
-  if (wsccb) {
-    wsccb->writeSetCursorCallback();
-    wsccb = 0;
+  if (needSetCursor) {
+    rdr::U8* data;
+    int stride;
+
+    const Cursor& cursor = cp->cursor();
+
+    data = new rdr::U8[cursor.area() * cp->pf().bpp/8];
+    cursor.getImage(cp->pf(), data, cursor.getRect());
+
+    writeSetCursorRect(cursor.width(), cursor.height(),
+                       cursor.hotspot.x, cursor.hotspot.y,
+                       data, cursor.mask.buf);
+    needSetCursor = false;
+
+    delete [] data;
+  }
+
+  if (needSetXCursor) {
+    const Cursor& cursor = cp->cursor();
+    Pixel pix0, pix1;
+    rdr::U8 rgb0[3], rgb1[3];
+    rdr::U8Array bitmap(cursor.getBitmap(&pix0, &pix1));
+
+    if (!bitmap.buf) {
+      // FIXME: We could reduce to two colors.
+      throw Exception("SMsgWriter::writePseudoRects: Unable to send multicolor cursor: RichCursor not supported by client");
+    }
+
+    cp->pf().rgbFromPixel(pix0, &rgb0[0], &rgb0[1], &rgb0[2]);
+    cp->pf().rgbFromPixel(pix1, &rgb1[0], &rgb1[1], &rgb1[2]);
+
+    writeSetXCursorRect(cursor.width(), cursor.height(),
+                        cursor.hotspot.x, cursor.hotspot.y,
+                        rgb0, rgb1, bitmap.buf, cursor.mask.buf);
+    needSetXCursor = false;
   }
 
   if (needSetDesktopName) {
@@ -469,3 +480,49 @@
   os->writeU32(pseudoEncodingDesktopName);
   os->writeString(name);
 }
+
+void SMsgWriter::writeSetCursorRect(int width, int height,
+                                    int hotspotX, int hotspotY,
+                                    const void* data, const void* mask)
+{
+  if (!cp->supportsLocalCursor)
+    throw Exception("Client does not support local cursors");
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
+
+  os->writeS16(hotspotX);
+  os->writeS16(hotspotY);
+  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::writeSetXCursorRect(int width, int height,
+                                     int hotspotX, int hotspotY,
+                                     const rdr::U8 pix0[],
+                                     const rdr::U8 pix1[],
+                                     const void* data, const void* mask)
+{
+  if (!cp->supportsLocalXCursor)
+    throw Exception("Client does not support local cursors");
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
+
+  os->writeS16(hotspotX);
+  os->writeS16(hotspotY);
+  os->writeU16(width);
+  os->writeU16(height);
+  os->writeU32(pseudoEncodingXCursor);
+  if (width * height) {
+    os->writeU8(pix0[0]);
+    os->writeU8(pix0[1]);
+    os->writeU8(pix0[2]);
+    os->writeU8(pix1[0]);
+    os->writeU8(pix1[1]);
+    os->writeU8(pix1[2]);
+    os->writeBytes(data, (width+7)/8 * height);
+    os->writeBytes(mask, (width+7)/8 * height);
+  }
+}
diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h
index c3ee457..e9fc5a4 100644
--- a/common/rfb/SMsgWriter.h
+++ b/common/rfb/SMsgWriter.h
@@ -34,11 +34,6 @@
   class ConnParams;
   class ScreenSet;
 
-  class WriteSetCursorCallback {
-  public:
-    virtual void writeSetCursorCallback() = 0;
-  };
-
   class SMsgWriter {
   public:
     SMsgWriter(ConnParams* cp, rdr::OutStream* os);
@@ -81,16 +76,10 @@
 
     bool writeSetDesktopName();
 
-    // Like setDesktopSize, we can't just write out a setCursor message
-    // immediately. Instead of calling writeSetCursor() directly,
-    // you must call cursorChange(), and then invoke writeSetCursor()
-    // in response to the writeSetCursorCallback() callback. This will
-    // happen when the next update is sent.
-    void cursorChange(WriteSetCursorCallback* cb);
-    void writeSetCursor(int width, int height, const Point& hotspot,
-                        void* data, void* mask);
-    void writeSetXCursor(int width, int height, int hotspotX, int hotspotY,
-                         void* data, void* mask);
+    // Like setDesktopSize, we can't just write out a cursor message
+    // immediately. 
+    bool writeSetCursor();
+    bool writeSetXCursor();
 
     // needFakeUpdate() returns true when an immediate update is needed in
     // order to flush out pseudo-rectangles to the client.
@@ -141,6 +130,13 @@
                                       int fb_width, int fb_height,
                                       const ScreenSet& layout);
     void writeSetDesktopNameRect(const char *name);
+    void writeSetCursorRect(int width, int height,
+                            int hotspotX, int hotspotY,
+                            const void* data, const void* mask);
+    void writeSetXCursorRect(int width, int height,
+                             int hotspotX, int hotspotY,
+                             const rdr::U8 pix0[], const rdr::U8 pix1[],
+                             const void* data, const void* mask);
 
     ConnParams* cp;
     rdr::OutStream* os;
@@ -150,12 +146,12 @@
     int nRectsInUpdate;
     int nRectsInHeader;
 
-    WriteSetCursorCallback* wsccb;
-
     bool needSetDesktopSize;
     bool needExtendedDesktopSize;
     bool needSetDesktopName;
     bool needLastRect;
+    bool needSetCursor;
+    bool needSetXCursor;
 
     int lenBeforeRect;
     int updatesSent;
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index f8674de..e712300 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -712,46 +712,6 @@
   writer()->writeEndOfContinuousUpdates();
 }
 
-void VNCSConnectionST::writeSetCursorCallback()
-{
-  if (cp.supportsLocalXCursor) {
-    Pixel pix0, pix1;
-    rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1));
-    if (bitmap.buf) {
-      // The client supports XCursor and the cursor only has two
-      // colors. Use the XCursor encoding.
-      writer()->writeSetXCursor(server->cursor.width(),
-				server->cursor.height(),
-				server->cursor.hotspot.x,
-				server->cursor.hotspot.y,
-				bitmap.buf, server->cursor.mask.buf);
-      return;
-    } else {
-      // More than two colors
-      if (!cp.supportsLocalCursor) {
-	// FIXME: We could reduce to two colors. 
-	vlog.info("Unable to send multicolor cursor: RichCursor not supported by client");
-	return;
-      }
-    }
-  }
-
-  // Use RichCursor
-  rdr::U8* transBuffer;
-  int stride;
-  const rdr::U8* buffer;
-
-  transBuffer = writer()->getImageBuf(server->cursor.area());
-
-  buffer = server->cursor.getBuffer(server->cursor.getRect(), &stride);
-  image_getter.translatePixels(buffer, transBuffer, server->cursor.area());
-
-  writer()->writeSetCursor(server->cursor.width(),
-                           server->cursor.height(),
-                           server->cursor.hotspot,
-                           transBuffer, server->cursor.mask.buf);
-}
-
 
 bool VNCSConnectionST::handleTimeout(Timer* t)
 {
@@ -1166,10 +1126,16 @@
 {
   if (state() != RFBSTATE_NORMAL)
     return;
-  if (!cp.supportsLocalCursor)
-    return;
 
-  writer()->cursorChange(this);
+  cp.setCursor(server->cursor);
+
+  if (!writer()->writeSetCursor()) {
+    if (!writer()->writeSetXCursor()) {
+      // No client support
+      return;
+    }
+  }
+
   writeFramebufferUpdate();
 }
 
diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h
index ca5c4f0..c918c59 100644
--- a/common/rfb/VNCSConnectionST.h
+++ b/common/rfb/VNCSConnectionST.h
@@ -40,7 +40,6 @@
   class Encoder;
 
   class VNCSConnectionST : public SConnection,
-                           public WriteSetCursorCallback,
                            public Timer::Callback {
   public:
     VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse);
@@ -152,9 +151,6 @@
     // default access settings and the connection's access settings.
     virtual void setAccessRights(AccessRights ar) {accessRights=ar;}
 
-    // WriteSetCursorCallback
-    virtual void writeSetCursorCallback();
-
     // Timer callbacks
     virtual bool handleTimeout(Timer* t);