123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506 |
- /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
- #include <assert.h>
- #include <stdio.h>
-
- #include <rfb/msgTypes.h>
- #include <rdr/InStream.h>
- #include <rfb/Exception.h>
- #include <rfb/LogWriter.h>
- #include <rfb/util.h>
- #include <rfb/CMsgHandler.h>
- #include <rfb/CMsgReader.h>
-
- static rfb::LogWriter vlog("CMsgReader");
-
- using namespace rfb;
-
- CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
- : imageBufIdealSize(0), handler(handler_), is(is_),
- nUpdateRectsLeft(0)
- {
- }
-
- CMsgReader::~CMsgReader()
- {
- }
-
- void CMsgReader::readServerInit()
- {
- int width = is->readU16();
- int height = is->readU16();
- PixelFormat pf;
- pf.read(is);
- CharArray name(is->readString());
- handler->serverInit(width, height, pf, name.buf);
- }
-
- void CMsgReader::readMsg()
- {
- if (nUpdateRectsLeft == 0) {
- int type = is->readU8();
-
- switch (type) {
- case msgTypeSetColourMapEntries:
- readSetColourMapEntries();
- break;
- case msgTypeBell:
- readBell();
- break;
- case msgTypeServerCutText:
- readServerCutText();
- break;
- case msgTypeFramebufferUpdate:
- readFramebufferUpdate();
- break;
- case msgTypeServerFence:
- readFence();
- break;
- case msgTypeEndOfContinuousUpdates:
- readEndOfContinuousUpdates();
- break;
- default:
- vlog.error("unknown message type %d", type);
- throw 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 pseudoEncodingLastRect:
- nUpdateRectsLeft = 1; // this rectangle is the last one
- break;
- case pseudoEncodingXCursor:
- readSetXCursor(w, h, Point(x,y));
- break;
- case pseudoEncodingCursor:
- readSetCursor(w, h, Point(x,y));
- break;
- 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;
- case pseudoEncodingDesktopSize:
- handler->setDesktopSize(w, h);
- break;
- case pseudoEncodingExtendedDesktopSize:
- readExtendedDesktopSize(x, y, w, h);
- break;
- case pseudoEncodingLEDState:
- readLEDState();
- break;
- case pseudoEncodingVMwareLEDState:
- readVMwareLEDState();
- break;
- case pseudoEncodingQEMUKeyEvent:
- handler->supportsQEMUKeyEvent();
- break;
- default:
- readRect(Rect(x, y, x+w, y+h), encoding);
- break;
- };
-
- nUpdateRectsLeft--;
- if (nUpdateRectsLeft == 0)
- handler->framebufferUpdateEnd();
- }
- }
-
- void CMsgReader::readSetColourMapEntries()
- {
- is->skip(1);
- int firstColour = is->readU16();
- int nColours = is->readU16();
- rdr::U16Array rgbs(nColours * 3);
- for (int i = 0; i < nColours * 3; i++)
- rgbs.buf[i] = is->readU16();
- handler->setColourMapEntries(firstColour, nColours, rgbs.buf);
- }
-
- void CMsgReader::readBell()
- {
- handler->bell();
- }
-
- void CMsgReader::readServerCutText()
- {
- is->skip(3);
- rdr::U32 len = is->readU32();
- if (len > 256*1024) {
- is->skip(len);
- vlog.error("cut text too long (%d bytes) - ignoring",len);
- return;
- }
- CharArray ca(len);
- is->readBytes(ca.buf, len);
- CharArray filtered(convertLF(ca.buf, len));
- handler->serverCutText(filtered.buf);
- }
-
- void CMsgReader::readFence()
- {
- rdr::U32 flags;
- rdr::U8 len;
- char data[64];
-
- is->skip(3);
-
- flags = is->readU32();
-
- len = is->readU8();
- if (len > sizeof(data)) {
- vlog.error("Ignoring fence with too large payload");
- is->skip(len);
- return;
- }
-
- is->readBytes(data, len);
-
- handler->fence(flags, len, data);
- }
-
- void CMsgReader::readEndOfContinuousUpdates()
- {
- handler->endOfContinuousUpdates();
- }
-
- void CMsgReader::readFramebufferUpdate()
- {
- is->skip(1);
- nUpdateRectsLeft = is->readU16();
- handler->framebufferUpdateStart();
- }
-
- void CMsgReader::readRect(const Rect& r, int encoding)
- {
- if ((r.br.x > handler->server.width()) ||
- (r.br.y > handler->server.height())) {
- vlog.error("Rect too big: %dx%d at %d,%d exceeds %dx%d",
- r.width(), r.height(), r.tl.x, r.tl.y,
- handler->server.width(), handler->server.height());
- throw Exception("Rect too big");
- }
-
- if (r.is_empty())
- vlog.error("zero size rect");
-
- handler->dataRect(r, encoding);
- }
-
- void CMsgReader::readSetXCursor(int width, int height, const Point& hotspot)
- {
- if (width > maxCursorSize || height > maxCursorSize)
- throw Exception("Too big cursor");
-
- rdr::U8Array rgba(width*height*4);
-
- if (width * height > 0) {
- rdr::U8 pr, pg, pb;
- rdr::U8 sr, sg, sb;
- int data_len = ((width+7)/8) * height;
- int mask_len = ((width+7)/8) * height;
- rdr::U8Array data(data_len);
- rdr::U8Array mask(mask_len);
-
- int x, y;
- rdr::U8* out;
-
- pr = is->readU8();
- pg = is->readU8();
- pb = is->readU8();
-
- sr = is->readU8();
- sg = is->readU8();
- sb = is->readU8();
-
- is->readBytes(data.buf, data_len);
- is->readBytes(mask.buf, mask_len);
-
- int maskBytesPerRow = (width+7)/8;
- out = rgba.buf;
- for (y = 0;y < height;y++) {
- for (x = 0;x < width;x++) {
- int byte = y * maskBytesPerRow + x / 8;
- int bit = 7 - x % 8;
-
- if (data.buf[byte] & (1 << bit)) {
- out[0] = pr;
- out[1] = pg;
- out[2] = pb;
- } else {
- out[0] = sr;
- out[1] = sg;
- out[2] = sb;
- }
-
- if (mask.buf[byte] & (1 << bit))
- out[3] = 255;
- else
- out[3] = 0;
-
- out += 4;
- }
- }
- }
-
- handler->setCursor(width, height, hotspot, rgba.buf);
- }
-
- void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
- {
- if (width > maxCursorSize || height > maxCursorSize)
- throw Exception("Too big cursor");
-
- int data_len = width * height * (handler->server.pf().bpp/8);
- int mask_len = ((width+7)/8) * height;
- rdr::U8Array data(data_len);
- rdr::U8Array mask(mask_len);
-
- int x, y;
- rdr::U8Array rgba(width*height*4);
- rdr::U8* in;
- rdr::U8* out;
-
- is->readBytes(data.buf, data_len);
- is->readBytes(mask.buf, mask_len);
-
- int maskBytesPerRow = (width+7)/8;
- in = data.buf;
- out = rgba.buf;
- for (y = 0;y < height;y++) {
- for (x = 0;x < width;x++) {
- int byte = y * maskBytesPerRow + x / 8;
- int bit = 7 - x % 8;
-
- handler->server.pf().rgbFromBuffer(out, in, 1);
-
- if (mask.buf[byte] & (1 << bit))
- out[3] = 255;
- else
- out[3] = 0;
-
- in += handler->server.pf().bpp/8;
- out += 4;
- }
- }
-
- handler->setCursor(width, height, hotspot, rgba.buf);
- }
-
- void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot)
- {
- if (width > maxCursorSize || height > maxCursorSize)
- throw Exception("Too big cursor");
-
- int encoding;
-
- const PixelFormat rgbaPF(32, 32, false, true, 255, 255, 255, 16, 8, 0);
- ManagedPixelBuffer pb(rgbaPF, width, height);
- PixelFormat origPF;
-
- rdr::U8* buf;
- int stride;
-
- encoding = is->readS32();
-
- origPF = handler->server.pf();
- handler->server.setPF(rgbaPF);
- handler->readAndDecodeRect(pb.getRect(), encoding, &pb);
- handler->server.setPF(origPF);
-
- // On-wire data has pre-multiplied alpha, but we store it
- // non-pre-multiplied
- buf = pb.getBufferRW(pb.getRect(), &stride);
- assert(stride == width);
-
- for (int i = 0;i < pb.area();i++) {
- rdr::U8 alpha;
-
- alpha = buf[3];
- if (alpha == 0)
- alpha = 1; // Avoid division by zero
-
- buf[0] = (unsigned)buf[0] * 255/alpha;
- buf[1] = (unsigned)buf[1] * 255/alpha;
- buf[2] = (unsigned)buf[2] * 255/alpha;
-
- buf += 4;
- }
-
- pb.commitBufferRW(pb.getRect());
-
- handler->setCursor(width, height, hotspot,
- 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();
-
- if (x || y || w || h) {
- vlog.error("Ignoring DesktopName rect with non-zero position/size");
- } else {
- handler->setName(name);
- }
-
- delete [] name;
- }
-
- void CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h)
- {
- unsigned int screens, i;
- rdr::U32 id, flags;
- int sx, sy, sw, sh;
- ScreenSet layout;
-
- 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(Screen(id, sx, sy, sw, sh, flags));
- }
-
- handler->setExtendedDesktopSize(x, y, w, h, layout);
- }
-
- void CMsgReader::readLEDState()
- {
- rdr::U8 state;
-
- state = is->readU8();
-
- handler->setLEDState(state);
- }
-
- void CMsgReader::readVMwareLEDState()
- {
- rdr::U32 state;
-
- state = is->readU32();
-
- // As luck has it, this extension uses the same bit definitions,
- // so no conversion required
-
- handler->setLEDState(state);
- }
|