]> source.dussan.org Git - tigervnc.git/commitdiff
Add support for VMware cursors
authorPierre Ossman <ossman@cendio.se>
Mon, 5 Nov 2018 15:28:18 +0000 (16:28 +0100)
committerPierre Ossman <ossman@cendio.se>
Mon, 10 Dec 2018 20:19:26 +0000 (21:19 +0100)
common/rfb/CConnection.cxx
common/rfb/CMsgReader.cxx
common/rfb/CMsgReader.h
common/rfb/ClientParams.cxx
common/rfb/SMsgWriter.cxx
common/rfb/SMsgWriter.h
common/rfb/encodings.h

index 3ea217fa1bfe59052e44ceaf86483bff7363b209..3a6b74484df56908be34d1a123120a94d5de989e 100644 (file)
@@ -596,6 +596,7 @@ void CConnection::updateEncodings()
 
   if (supportsLocalCursor) {
     encodings.push_back(pseudoEncodingCursorWithAlpha);
+    encodings.push_back(pseudoEncodingVMwareCursor);
     encodings.push_back(pseudoEncodingCursor);
     encodings.push_back(pseudoEncodingXCursor);
   }
index 17152ab71164ba205fa603b114af5c267442ea25..55191663b432a70f3b4d40938ccbf93f0a46745a 100644 (file)
@@ -97,6 +97,9 @@ void CMsgReader::readMsg()
     case pseudoEncodingCursorWithAlpha:
       readSetCursorWithAlpha(w, h, Point(x,y));
       break;
+    case pseudoEncodingVMwareCursor:
+      readSetVMwareCursor(w, h, Point(x,y));
+      break;
     case pseudoEncodingDesktopName:
       readSetDesktopName(x, y, w, h);
       break;
@@ -349,6 +352,94 @@ void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hots
                      pb.getBuffer(pb.getRect(), &stride));
 }
 
+void CMsgReader::readSetVMwareCursor(int width, int height, const Point& hotspot)
+{
+  if (width > maxCursorSize || height > maxCursorSize)
+    throw Exception("Too big cursor");
+
+  rdr::U8 type;
+
+  type = is->readU8();
+  is->skip(1);
+
+  if (type == 0) {
+    int len = width * height * (handler->server.pf().bpp/8);
+    rdr::U8Array andMask(len);
+    rdr::U8Array xorMask(len);
+
+    rdr::U8Array data(width*height*4);
+
+    rdr::U8* andIn;
+    rdr::U8* xorIn;
+    rdr::U8* out;
+    int Bpp;
+
+    is->readBytes(andMask.buf, len);
+    is->readBytes(xorMask.buf, len);
+
+    andIn = andMask.buf;
+    xorIn = xorMask.buf;
+    out = data.buf;
+    Bpp = handler->server.pf().bpp/8;
+    for (int y = 0;y < height;y++) {
+      for (int x = 0;x < width;x++) {
+        Pixel andPixel, xorPixel;
+
+        andPixel = handler->server.pf().pixelFromBuffer(andIn);
+        xorPixel = handler->server.pf().pixelFromBuffer(xorIn);
+        andIn += Bpp;
+        xorIn += Bpp;
+
+        if (andPixel == 0) {
+          rdr::U8 r, g, b;
+
+          // Opaque pixel
+
+          handler->server.pf().rgbFromPixel(xorPixel, &r, &g, &b);
+          *out++ = r;
+          *out++ = g;
+          *out++ = b;
+          *out++ = 0xff;
+        } else if (xorPixel == 0) {
+          // Fully transparent pixel
+          *out++ = 0;
+          *out++ = 0;
+          *out++ = 0;
+          *out++ = 0;
+        } else if (andPixel == xorPixel) {
+          // Inverted pixel
+
+          // We don't really support this, so just turn the pixel black
+          // FIXME: Do an outline like WinVNC does?
+          *out++ = 0;
+          *out++ = 0;
+          *out++ = 0;
+          *out++ = 0xff;
+        } else {
+          // Partially transparent/inverted pixel
+
+          // We _really_ can't handle this, just make it black
+          *out++ = 0;
+          *out++ = 0;
+          *out++ = 0;
+          *out++ = 0xff;
+        }
+      }
+    }
+
+    handler->setCursor(width, height, hotspot, data.buf);
+  } else if (type == 1) {
+    rdr::U8Array data(width*height*4);
+
+    // FIXME: Is alpha premultiplied?
+    is->readBytes(data.buf, width*height*4);
+
+    handler->setCursor(width, height, hotspot, data.buf);
+  } else {
+    throw Exception("Unknown cursor type");
+  }
+}
+
 void CMsgReader::readSetDesktopName(int x, int y, int w, int h)
 {
   char* name = is->readString();
index 99638276d9f0763100f3d62b7f1c3d2198eb5703..029e95c170e6d11e5ce2bcc5d4c3971e242d23b9 100644 (file)
@@ -63,6 +63,7 @@ namespace rfb {
     void readSetXCursor(int width, int height, const Point& hotspot);
     void readSetCursor(int width, int height, const Point& hotspot);
     void readSetCursorWithAlpha(int width, int height, const Point& hotspot);
+    void readSetVMwareCursor(int width, int height, const Point& hotspot);
     void readSetDesktopName(int x, int y, int w, int h);
     void readExtendedDesktopSize(int x, int y, int w, int h);
     void readLEDState();
index 2f8783bbd0b0390f58ab47417027fe263ec5e40c..bf2caa4702498f5c83402d9fab438c4b10f901a9 100644 (file)
@@ -140,6 +140,8 @@ bool ClientParams::supportsLocalCursor() const
 {
   if (supportsEncoding(pseudoEncodingCursorWithAlpha))
     return true;
+  if (supportsEncoding(pseudoEncodingVMwareCursor))
+    return true;
   if (supportsEncoding(pseudoEncodingCursor))
     return true;
   if (supportsEncoding(pseudoEncodingXCursor))
index a12a6d40d2b2a8e12e3b9c040103e3d55ac6751d..2d59dde3f0222f119ca8e57652929bbd7cc201cd 100644 (file)
@@ -144,7 +144,8 @@ void SMsgWriter::writeCursor()
 {
   if (!client->supportsEncoding(pseudoEncodingCursor) &&
       !client->supportsEncoding(pseudoEncodingXCursor) &&
-      !client->supportsEncoding(pseudoEncodingCursorWithAlpha))
+      !client->supportsEncoding(pseudoEncodingCursorWithAlpha) &&
+      !client->supportsEncoding(pseudoEncodingVMwareCursor))
     throw Exception("Client does not support local cursor");
 
   needCursor = true;
@@ -299,6 +300,10 @@ void SMsgWriter::writePseudoRects()
       writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
                                   cursor.hotspot().x, cursor.hotspot().y,
                                   cursor.getBuffer());
+    } else if (client->supportsEncoding(pseudoEncodingVMwareCursor)) {
+      writeSetVMwareCursorRect(cursor.width(), cursor.height(),
+                               cursor.hotspot().x, cursor.hotspot().y,
+                               cursor.getBuffer());
     } else if (client->supportsEncoding(pseudoEncodingCursor)) {
       rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
       rdr::U8Array mask(cursor.getMask());
@@ -503,6 +508,28 @@ void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
   }
 }
 
+void SMsgWriter::writeSetVMwareCursorRect(int width, int height,
+                                          int hotspotX, int hotspotY,
+                                          const rdr::U8* data)
+{
+  if (!client->supportsEncoding(pseudoEncodingVMwareCursor))
+    throw Exception("Client does not support local cursors");
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
+
+  os->writeS16(hotspotX);
+  os->writeS16(hotspotY);
+  os->writeU16(width);
+  os->writeU16(height);
+  os->writeU32(pseudoEncodingVMwareCursor);
+
+  os->writeU8(1); // Alpha cursor
+  os->pad(1);
+
+  // FIXME: Should alpha be premultiplied?
+  os->writeBytes(data, width*height*4);
+}
+
 void SMsgWriter::writeLEDStateRect(rdr::U8 state)
 {
   if (!client->supportsEncoding(pseudoEncodingLEDState))
index 80f6de9126d21605436faad4d1837d1b71057bb8..4f4c9cc05cb1c76382b04f4ca6fb02b614fea9e4 100644 (file)
@@ -130,6 +130,9 @@ namespace rfb {
     void writeSetCursorWithAlphaRect(int width, int height,
                                      int hotspotX, int hotspotY,
                                      const rdr::U8* data);
+    void writeSetVMwareCursorRect(int width, int height,
+                                  int hotspotX, int hotspotY,
+                                  const rdr::U8* data);
     void writeLEDStateRect(rdr::U8 state);
     void writeQEMUKeyEventRect();
 
index 122afe7f65b27fb571526b9d538e3c5aea136ba4..315ff54cc9d3ea9f6b68b432c68bbb2e46a79aeb 100644 (file)
@@ -59,6 +59,9 @@ namespace rfb {
   const int pseudoEncodingSubsamp8X = -764;
   const int pseudoEncodingSubsamp16X = -763;
 
+  // VMware-specific
+  const int pseudoEncodingVMwareCursor = 0x574d5664;
+
   int encodingNum(const char* name);
   const char* encodingName(int num);
 }