Server-side support for the XCursor encoding


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@188 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/rfb/ConnParams.cxx b/rfb/ConnParams.cxx
index 7dcfdcc..4a8e534 100644
--- a/rfb/ConnParams.cxx
+++ b/rfb/ConnParams.cxx
@@ -27,7 +27,7 @@
 
 ConnParams::ConnParams()
   : majorVersion(0), minorVersion(0), width(0), height(0), useCopyRect(false),
-    supportsLocalCursor(false), supportsDesktopResize(false), supportsLastRect(false), 
+    supportsLocalCursor(false), supportsLocalXCursor(false), supportsDesktopResize(false), supportsLastRect(false), 
     customCompressLevel(false), compressLevel(6), noJpeg(false), qualityLevel(-1), 
     name_(0), nEncodings_(0), encodings_(0), 
     currentEncoding_(encodingRaw), verStrPos(0)
@@ -88,6 +88,7 @@
   nEncodings_ = nEncodings;
   useCopyRect = false;
   supportsLocalCursor = false;
+  supportsLocalXCursor = false;
   supportsLastRect = false;
   customCompressLevel = false;
   compressLevel = -1;
@@ -102,6 +103,8 @@
       useCopyRect = true;
     else if (encodings[i] == pseudoEncodingCursor)
       supportsLocalCursor = true;
+    else if (encodings[i] == pseudoEncodingXCursor)
+      supportsLocalXCursor = true;
     else if (encodings[i] == pseudoEncodingDesktopSize)
       supportsDesktopResize = true;
     else if (encodings[i] == pseudoEncodingLastRect)
diff --git a/rfb/ConnParams.h b/rfb/ConnParams.h
index 70578ea..09e79c2 100644
--- a/rfb/ConnParams.h
+++ b/rfb/ConnParams.h
@@ -70,6 +70,7 @@
     bool useCopyRect;
 
     bool supportsLocalCursor;
+    bool supportsLocalXCursor;
     bool supportsDesktopResize;
     bool supportsLastRect;
 
diff --git a/rfb/SMsgWriter.h b/rfb/SMsgWriter.h
index 72bc10a..6dc272c 100644
--- a/rfb/SMsgWriter.h
+++ b/rfb/SMsgWriter.h
@@ -82,6 +82,8 @@
     virtual void cursorChange(WriteSetCursorCallback* cb)=0;
     virtual void writeSetCursor(int width, int height, int hotspotX,
                                 int hotspotY, void* data, void* mask)=0;
+    virtual void writeSetXCursor(int width, int height, int hotspotX,
+                                int hotspotY, void* data, void* mask)=0;
 
     // needFakeUpdate() returns true when an immediate update is needed in
     // order to flush out setDesktopSize or setCursor pseudo-rectangles to the
diff --git a/rfb/SMsgWriterV3.cxx b/rfb/SMsgWriterV3.cxx
index 20a7280..c34f216 100644
--- a/rfb/SMsgWriterV3.cxx
+++ b/rfb/SMsgWriterV3.cxx
@@ -84,6 +84,32 @@
   os->writeBytes(mask, (width+7)/8 * height);
 }
 
+void SMsgWriterV3::writeSetXCursor(int width, int height, int hotspotX,
+				   int hotspotY, void* data, void* mask)
+{
+  if (!wsccb) return;
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriterV3::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. 
+  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);
+}
+
 void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
 {
   startMsg(msgTypeFramebufferUpdate);
diff --git a/rfb/SMsgWriterV3.h b/rfb/SMsgWriterV3.h
index 0bad8f1..fbd07d6 100644
--- a/rfb/SMsgWriterV3.h
+++ b/rfb/SMsgWriterV3.h
@@ -35,6 +35,8 @@
     virtual void cursorChange(WriteSetCursorCallback* cb);
     virtual void writeSetCursor(int width, int height, int hotspotX,
                                 int hotspotY, void* data, void* mask);
+    virtual void writeSetXCursor(int width, int height, int hotspotX,
+				 int hotspotY, void* data, void* mask);
     virtual void writeFramebufferUpdateStart(int nRects);
     virtual void writeFramebufferUpdateStart();
     virtual void writeFramebufferUpdateEnd();
diff --git a/rfb/VNCSConnectionST.cxx b/rfb/VNCSConnectionST.cxx
index 426ecd4..a20ec01 100644
--- a/rfb/VNCSConnectionST.cxx
+++ b/rfb/VNCSConnectionST.cxx
@@ -277,7 +277,7 @@
 bool VNCSConnectionST::needRenderedCursor()
 {
   return (state() == RFBSTATE_NORMAL
-          && (!cp.supportsLocalCursor
+          && (!cp.supportsLocalCursor && !cp.supportsLocalXCursor
               || (!server->cursorPos.equals(pointerEventPos) &&
                   (time(0) - pointerEventTime) > 0)));
 }
@@ -478,7 +478,7 @@
 
 void VNCSConnectionST::supportsLocalCursor()
 {
-  if (cp.supportsLocalCursor) {
+  if (cp.supportsLocalCursor || cp.supportsLocalXCursor) {
     removeRenderedCursor = true;
     drawRenderedCursor = false;
     setCursor();
@@ -487,15 +487,37 @@
 
 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* transData = writer()->getImageBuf(server->cursor.area());
   image_getter.translatePixels(server->cursor.data, transData,
-                               server->cursor.area());
-
+			       server->cursor.area());
   writer()->writeSetCursor(server->cursor.width(),
-                           server->cursor.height(),
-                           server->cursor.hotspot.x,
-                           server->cursor.hotspot.y,
-                           transData, server->cursor.mask.buf);
+			   server->cursor.height(),
+			   server->cursor.hotspot.x,
+			   server->cursor.hotspot.y,
+			   transData, server->cursor.mask.buf);
 }
 
 
diff --git a/rfb/encodings.h b/rfb/encodings.h
index a40af82..550440d 100644
--- a/rfb/encodings.h
+++ b/rfb/encodings.h
@@ -30,6 +30,7 @@
 
   const unsigned int encodingMax = 255;
 
+  const unsigned int pseudoEncodingXCursor = 0xffffff10;
   const unsigned int pseudoEncodingCursor = 0xffffff11;
   const unsigned int pseudoEncodingDesktopSize = 0xffffff21;