]> source.dussan.org Git - tigervnc.git/commitdiff
Add support for VMwareCursor pseudo encoding to Java client
authorBrian P. Hinz <bphinz@users.sf.net>
Thu, 28 Nov 2019 19:22:33 +0000 (14:22 -0500)
committerBrian P. Hinz <bphinz@users.sf.net>
Thu, 28 Nov 2019 19:24:03 +0000 (14:24 -0500)
java/com/tigervnc/rfb/CConnection.java
java/com/tigervnc/rfb/CMsgReader.java
java/com/tigervnc/rfb/Encodings.java

index aaa2f49041e9f63603daaa3a3320a509d95a23b7..3b60de577268b323d3d67adac0864e1a77fe41f4 100644 (file)
@@ -667,6 +667,7 @@ abstract public class CConnection extends CMsgHandler {
       // JRE on Windows does not support alpha cursor
       if (!osName.contains("windows"))
         encodings.add(Encodings.pseudoEncodingCursorWithAlpha);
+      encodings.add(Encodings.pseudoEncodingVMwareCursor);
       encodings.add(Encodings.pseudoEncodingCursor);
       encodings.add(Encodings.pseudoEncodingXCursor);
     }
index 56b761e47de26910b4d89360064851d031e34f0a..c71a1e02f6d1be67e311e4d077663a67cd2fd68d 100644 (file)
@@ -104,6 +104,9 @@ public class CMsgReader {
       case Encodings.pseudoEncodingCursorWithAlpha:
         readSetCursorWithAlpha(w, h, new Point(x,y));
         break;
+      case Encodings.pseudoEncodingVMwareCursor:
+        readSetVMwareCursor(w, h, new Point(x,y));
+        break;
       case Encodings.pseudoEncodingDesktopName:
         readSetDesktopName(x, y, w, h);
         break;
@@ -349,6 +352,103 @@ public class CMsgReader {
     handler.setCursor(width, height, hotspot, buf.array());
   }
 
+  protected void readSetVMwareCursor(int width, int height, Point hotspot)
+  {
+    // VMware cursor sends RGBA, java BufferedImage needs ARGB
+    if (width > maxCursorSize || height > maxCursorSize)
+      throw new Exception("Too big cursor");
+
+    byte type;
+
+    type = (byte)is.readU8();
+    is.skip(1);
+
+    if (type == 0) {
+      int len = width * height * (handler.server.pf().bpp/8);
+      ByteBuffer andMask = ByteBuffer.allocate(len);
+      ByteBuffer xorMask = ByteBuffer.allocate(len);
+
+      ByteBuffer data = ByteBuffer.allocate(width*height*4);
+
+      ByteBuffer andIn;
+      ByteBuffer xorIn;
+      ByteBuffer out;
+      int Bpp;
+
+      is.readBytes(andMask, len);
+      is.readBytes(xorMask, len);
+
+      andIn = ByteBuffer.wrap(andMask.array());
+      xorIn = ByteBuffer.wrap(xorMask.array());
+      out = ByteBuffer.wrap(data.array());
+      Bpp = handler.server.pf().bpp/8;
+      for (int y = 0;y < height;y++) {
+        for (int x = 0;x < width;x++) {
+          int andPixel, xorPixel;
+
+          andPixel = handler.server.pf().pixelFromBuffer(andIn.duplicate());
+          xorPixel = handler.server.pf().pixelFromBuffer(xorIn.duplicate());
+          andIn.position(andIn.position() + Bpp);
+          xorIn.position(xorIn.position() + Bpp);
+
+          if (andPixel == 0) {
+            byte r, g, b;
+
+            // Opaque pixel
+
+            r = (byte)handler.server.pf().getColorModel().getRed(xorPixel);
+            g = (byte)handler.server.pf().getColorModel().getGreen(xorPixel);
+            b = (byte)handler.server.pf().getColorModel().getBlue(xorPixel);
+            out.put((byte)0xff);
+            out.put(r);
+            out.put(g);
+            out.put(b);
+          } else if (xorPixel == 0) {
+            // Fully transparent pixel
+            out.put((byte)0);
+            out.put((byte)0);
+            out.put((byte)0);
+            out.put((byte)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.put((byte)0xff);
+            out.put((byte)0);
+            out.put((byte)0);
+            out.put((byte)0);
+          } else {
+            // Partially transparent/inverted pixel
+
+            // We _really_ can't handle this, just make it black
+            out.put((byte)0xff);
+            out.put((byte)0);
+            out.put((byte)0);
+            out.put((byte)0);
+          }
+        }
+      }
+
+      handler.setCursor(width, height, hotspot, data.array());
+    } else if (type == 1) {
+      ByteBuffer data = ByteBuffer.allocate(width*height*4);
+
+      // FIXME: Is alpha premultiplied?
+      ByteBuffer buf = ByteBuffer.allocate(4);
+      for (int i=0;i < width*height*4;i+=4) {
+        is.readBytes(buf,4);
+        data.put(buf.array(),3,1);
+        data.put(buf.array(),0,3);
+        buf.clear();
+      }
+
+      handler.setCursor(width, height, hotspot, data.array());
+    } else {
+      throw new Exception("Unknown cursor type");
+    }
+  }
+
   protected void readSetDesktopName(int x, int y, int w, int h)
   {
     String name = is.readString();
@@ -425,6 +525,7 @@ public class CMsgReader {
   protected CMsgHandler handler;
   protected InStream is;
   protected int nUpdateRectsLeft;
+  protected final int maxCursorSize = 256;
   protected int[] imageBuf;
   protected int imageBufSize;
 }
index 8a499e03e967601c056c171a0be720b4e808d0f9..d9b713bc2a49ec5ea397c43f50595d1853e64ef1 100644 (file)
@@ -59,6 +59,9 @@ public class Encodings {
   public static final int pseudoEncodingSubsamp8X = -764;
   public static final int pseudoEncodingSubsamp16X = -763;
 
+  // VMware-specific
+  public static final int pseudoEncodingVMwareCursor = 0x574d5664;
+
   public static int encodingNum(String name) {
     if (name.equalsIgnoreCase("raw"))      return encodingRaw;
     if (name.equalsIgnoreCase("copyRect")) return encodingCopyRect;