123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2011-2019 Brian P. Hinz
- * Copyright (C) 2017 Pierre Ossman for Cendio AB
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
- //
- // CMsgReader - class for reading RFB messages on the client side
- // (i.e. messages from server to client).
- //
-
- package com.tigervnc.rfb;
-
- import java.awt.image.*;
- import java.nio.ByteBuffer;
- import java.nio.CharBuffer;
- import java.nio.charset.Charset;
-
- import com.tigervnc.rdr.*;
-
- public class CMsgReader {
-
- static LogWriter vlog = new LogWriter("CMsgReader");
-
- protected CMsgReader(CMsgHandler handler_, InStream is_)
- {
- imageBufIdealSize = 0;
- handler = handler_;
- is = is_;
- nUpdateRectsLeft = 0;
- imageBuf = null;
- imageBufSize = 0;
- }
-
- public void readServerInit()
- {
- int width = is.readU16();
- int height = is.readU16();
- PixelFormat pf = new PixelFormat();
- pf.read(is);
- String name = is.readString();
- handler.serverInit(width, height, pf, name);
- }
-
- public void readMsg()
- {
- if (nUpdateRectsLeft == 0) {
- int type = is.readU8();
-
- switch (type) {
- case MsgTypes.msgTypeSetColourMapEntries:
- readSetColourMapEntries();
- break;
- case MsgTypes.msgTypeBell:
- readBell();
- break;
- case MsgTypes.msgTypeServerCutText:
- readServerCutText();
- break;
- case MsgTypes.msgTypeFramebufferUpdate:
- readFramebufferUpdate();
- break;
- case MsgTypes.msgTypeServerFence:
- readFence();
- break;
- case MsgTypes.msgTypeEndOfContinuousUpdates:
- readEndOfContinuousUpdates();
- break;
- default:
- vlog.error("unknown message type "+type);
- throw new Exception("unknown message type");
- }
- } else {
- int x = is.readU16();
- int y = is.readU16();
- int w = is.readU16();
- int h = is.readU16();
- int encoding = is.readS32();
-
- switch (encoding) {
- case Encodings.pseudoEncodingLastRect:
- nUpdateRectsLeft = 1; // this rectangle is the last one
- break;
- case Encodings.pseudoEncodingXCursor:
- readSetXCursor(w, h, new Point(x,y));
- break;
- case Encodings.pseudoEncodingCursor:
- readSetCursor(w, h, new Point(x,y));
- break;
- 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;
- case Encodings.pseudoEncodingDesktopSize:
- handler.setDesktopSize(w, h);
- break;
- case Encodings.pseudoEncodingExtendedDesktopSize:
- readExtendedDesktopSize(x, y, w, h);
- break;
- case Encodings.pseudoEncodingClientRedirect:
- nUpdateRectsLeft = 0;
- readClientRedirect(x, y, w, h);
- return;
- default:
- readRect(new Rect(x, y, x+w, y+h), encoding);
- break;
- };
-
- nUpdateRectsLeft--;
- if (nUpdateRectsLeft == 0)
- handler.framebufferUpdateEnd();
- }
- }
-
- protected void readSetColourMapEntries()
- {
- is.skip(1);
- int firstColour = is.readU16();
- int nColours = is.readU16();
- int[] rgbs = new int[nColours * 3];
- for (int i = 0; i < nColours * 3; i++)
- rgbs[i] = is.readU16();
- handler.setColourMapEntries(firstColour, nColours, rgbs);
- }
-
- protected void readBell()
- {
- handler.bell();
- }
-
- protected void readServerCutText()
- {
- is.skip(3);
- int len = is.readU32();
-
- if (len > 256*1024) {
- is.skip(len);
- vlog.error("cut text too long ("+len+" bytes) - ignoring");
- return;
- }
-
- ByteBuffer buf = ByteBuffer.allocate(len);
- is.readBytes(buf, len);
- Charset latin1 = Charset.forName("ISO-8859-1");
- CharBuffer chars = latin1.decode(buf.compact());
- handler.serverCutText(chars.toString(), len);
- }
-
- protected void readFence()
- {
- int flags;
- int len;
- ByteBuffer data = ByteBuffer.allocate(64);
-
- is.skip(3);
-
- flags = is.readU32();
-
- len = is.readU8();
- if (len > data.capacity()) {
- System.out.println("Ignoring fence with too large payload\n");
- is.skip(len);
- return;
- }
-
- is.readBytes(data, len);
-
- handler.fence(flags, len, data.array());
- }
-
- protected void readEndOfContinuousUpdates()
- {
- handler.endOfContinuousUpdates();
- }
-
- protected void readFramebufferUpdate()
- {
- is.skip(1);
- nUpdateRectsLeft = is.readU16();
- handler.framebufferUpdateStart();
- }
-
- protected void readRect(Rect r, int encoding)
- {
- if ((r.br.x > handler.server.width()) || (r.br.y > handler.server.height())) {
- vlog.error("Rect too big: "+r.width()+"x"+r.height()+" at "+
- r.tl.x+","+r.tl.y+" exceeds "+handler.server.width()+"x"+
- handler.server.height());
- throw new Exception("Rect too big");
- }
-
- if (r.is_empty())
- vlog.error("Ignoring zero size rect");
-
- handler.dataRect(r, encoding);
- }
-
- protected void readSetXCursor(int width, int height, Point hotspot)
- {
- byte pr, pg, pb;
- byte sr, sg, sb;
- int data_len = ((width+7)/8) * height;
- int mask_len = ((width+7)/8) * height;
- ByteBuffer data = ByteBuffer.allocate(data_len);
- ByteBuffer mask = ByteBuffer.allocate(mask_len);
-
- int x, y;
- byte[] buf = new byte[width*height*4];
- ByteBuffer out;
-
- if (width * height == 0)
- return;
-
- pr = (byte)is.readU8();
- pg = (byte)is.readU8();
- pb = (byte)is.readU8();
-
- sr = (byte)is.readU8();
- sg = (byte)is.readU8();
- sb = (byte)is.readU8();
-
- is.readBytes(data, data_len);
- is.readBytes(mask, mask_len);
-
- int maskBytesPerRow = (width+7)/8;
- out = ByteBuffer.wrap(buf);
- for (y = 0;y < height;y++) {
- for (x = 0;x < width;x++) {
- int byte_ = y * maskBytesPerRow + x / 8;
- int bit = 7 - x % 8;
-
- // NOTE: BufferedImage needs ARGB, rather than RGBA
- if ((mask.get(byte_) & (1 << bit)) > 0)
- out.put(out.position(), (byte)255);
- else
- out.put(out.position(), (byte)0);
-
- if ((data.get(byte_) & (1 << bit)) > 0) {
- out.put(out.position() + 1, pr);
- out.put(out.position() + 2, pg);
- out.put(out.position() + 3, pb);
- } else {
- out.put(out.position() + 1, sr);
- out.put(out.position() + 2, sg);
- out.put(out.position() + 3, sb);
- }
-
- out.position(out.position() + 4);
- }
- }
-
- handler.setCursor(width, height, hotspot, buf);
- }
-
- protected void readSetCursor(int width, int height, Point hotspot)
- {
- int data_len = width * height * (handler.server.pf().bpp/8);
- int mask_len = ((width+7)/8) * height;
- ByteBuffer data = ByteBuffer.allocate(data_len);
- ByteBuffer mask = ByteBuffer.allocate(mask_len);
-
- int x, y;
- byte[] buf = new byte[width*height*4];
- ByteBuffer in;
- ByteBuffer out;
-
- is.readBytes(data, data_len);
- is.readBytes(mask, mask_len);
-
- int maskBytesPerRow = (width+7)/8;
- in = ByteBuffer.wrap(data.array());
- out = ByteBuffer.wrap(buf);
- for (y = 0;y < height;y++) {
- for (x = 0;x < width;x++) {
- int byte_ = y * maskBytesPerRow + x / 8;
- int bit = 7 - x % 8;
-
- // NOTE: BufferedImage needs ARGB, rather than RGBA
- if ((mask.get(byte_) & (1 << bit)) != 0)
- out.put((byte)255);
- else
- out.put((byte)0);
-
- handler.server.pf().rgbFromBuffer(out.duplicate(), in.duplicate(), 1);
-
- in.position(in.position() + handler.server.pf().bpp/8);
- out.position(out.position() + 3);
- }
- }
-
- handler.setCursor(width, height, hotspot, buf);
- }
-
- protected void readSetCursorWithAlpha(int width, int height, Point hotspot)
- {
- int encoding;
-
- PixelFormat rgbaPF =
- new PixelFormat(32, 32, false, true, 255, 255, 255, 16, 8, 0);
- ManagedPixelBuffer pb =
- new ManagedPixelBuffer(rgbaPF, width, height);
- PixelFormat origPF;
-
- ByteBuffer buf =
- ByteBuffer.allocate(pb.area()*4).order(rgbaPF.getByteOrder());;
-
- encoding = is.readS32();
-
- origPF = handler.server.pf();
- handler.server.setPF(rgbaPF);
- handler.readAndDecodeRect(pb.getRect(), encoding, pb);
- handler.server.setPF(origPF);
-
- // ARGB with pre-multiplied alpha works best for BufferedImage
- if (pb.area() > 0) {
- // Sometimes a zero width or height cursor is sent.
- DataBuffer db = pb.getBuffer(pb.getRect()).getDataBuffer();
- for (int i = 0;i < pb.area();i++)
- buf.asIntBuffer().put(i, db.getElem(i));
- }
-
- for (int i = 0;i < pb.area();i++) {
- byte alpha = buf.get(buf.position()+3);
-
- buf.put(i*4+3, buf.get(i*4+2));
- buf.put(i*4+2, buf.get(i*4+1));
- buf.put(i*4+1, buf.get(i*4+0));
- buf.put(i*4+0, alpha);
-
- buf.position(buf.position() + 4);
- }
-
- 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();
-
- if (x != 0 || y != 0 || w != 0 || h != 0) {
- vlog.error("Ignoring DesktopName rect with non-zero position/size");
- } else {
- handler.setName(name);
- }
-
- }
-
- protected void readExtendedDesktopSize(int x, int y, int w, int h)
- {
- int screens, i;
- int id, flags;
- int sx, sy, sw, sh;
- ScreenSet layout = new ScreenSet();
-
- screens = is.readU8();
- is.skip(3);
-
- for (i = 0;i < screens;i++) {
- id = is.readU32();
- sx = is.readU16();
- sy = is.readU16();
- sw = is.readU16();
- sh = is.readU16();
- flags = is.readU32();
-
- layout.add_screen(new Screen(id, sx, sy, sw, sh, flags));
- }
-
- handler.setExtendedDesktopSize(x, y, w, h, layout);
- }
-
- protected void readClientRedirect(int x, int y, int w, int h)
- {
- int port = is.readU16();
- String host = is.readString();
- String x509subject = is.readString();
-
- if (x != 0 || y != 0 || w != 0 || h != 0)
- vlog.error("Ignoring ClientRedirect rect with non-zero position/size");
- else
- handler.clientRedirect(port, host, x509subject);
- }
-
- public int[] getImageBuf(int required) { return getImageBuf(required, 0, 0); }
-
- public int[] getImageBuf(int required, int requested, int nPixels)
- {
- int requiredBytes = required;
- int requestedBytes = requested;
- int size = requestedBytes;
- if (size > imageBufIdealSize) size = imageBufIdealSize;
-
- if (size < requiredBytes)
- size = requiredBytes;
-
- if (imageBufSize < size) {
- imageBufSize = size;
- imageBuf = new int[imageBufSize];
- }
- if (nPixels != 0)
- nPixels = imageBufSize / (handler.server.pf().bpp / 8);
- return imageBuf;
- }
-
- public InStream getInStream() { return is; }
-
- public int imageBufIdealSize;
-
- protected CMsgHandler handler;
- protected InStream is;
- protected int nUpdateRectsLeft;
- protected final int maxCursorSize = 256;
- protected int[] imageBuf;
- protected int imageBufSize;
- }
|