blob: a928eb1587ad29c06cf8dcd10b735ef072f5590c [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman6a1a0d02017-02-19 15:48:17 +01002 * Copyright 2009-2017 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
Pierre Ossmana4c0aac2017-02-19 15:50:29 +010019
20#include <assert.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000021#include <stdio.h>
Pierre Ossmana4c0aac2017-02-19 15:50:29 +010022
Pierre Ossman7638e9c2014-01-16 13:12:40 +010023#include <rfb/msgTypes.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024#include <rdr/InStream.h>
25#include <rfb/Exception.h>
Pierre Ossmane9e7da92016-04-20 09:38:06 +020026#include <rfb/LogWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000027#include <rfb/util.h>
28#include <rfb/CMsgHandler.h>
29#include <rfb/CMsgReader.h>
30
Pierre Ossmane9e7da92016-04-20 09:38:06 +020031static rfb::LogWriter vlog("CMsgReader");
32
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000033using namespace rfb;
34
35CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
36 : imageBufIdealSize(0), handler(handler_), is(is_),
Pierre Ossman7bfb73b2015-11-10 13:03:26 +010037 nUpdateRectsLeft(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000038{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000039}
40
41CMsgReader::~CMsgReader()
42{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000043}
44
Pierre Ossman7638e9c2014-01-16 13:12:40 +010045void CMsgReader::readServerInit()
46{
47 int width = is->readU16();
48 int height = is->readU16();
Pierre Ossman7638e9c2014-01-16 13:12:40 +010049 PixelFormat pf;
50 pf.read(is);
Pierre Ossman7638e9c2014-01-16 13:12:40 +010051 CharArray name(is->readString());
Pierre Ossmandd45b442018-10-31 17:08:59 +010052 handler->serverInit(width, height, pf, name.buf);
Pierre Ossman7638e9c2014-01-16 13:12:40 +010053}
54
55void CMsgReader::readMsg()
56{
57 if (nUpdateRectsLeft == 0) {
58 int type = is->readU8();
59
60 switch (type) {
61 case msgTypeSetColourMapEntries:
62 readSetColourMapEntries();
63 break;
64 case msgTypeBell:
65 readBell();
66 break;
67 case msgTypeServerCutText:
68 readServerCutText();
69 break;
70 case msgTypeFramebufferUpdate:
71 readFramebufferUpdate();
72 break;
73 case msgTypeServerFence:
74 readFence();
75 break;
76 case msgTypeEndOfContinuousUpdates:
77 readEndOfContinuousUpdates();
78 break;
79 default:
Pierre Ossmane9e7da92016-04-20 09:38:06 +020080 vlog.error("unknown message type %d", type);
Pierre Ossman7638e9c2014-01-16 13:12:40 +010081 throw Exception("unknown message type");
82 }
83 } else {
84 int x = is->readU16();
85 int y = is->readU16();
86 int w = is->readU16();
87 int h = is->readU16();
88 int encoding = is->readS32();
89
90 switch (encoding) {
91 case pseudoEncodingLastRect:
92 nUpdateRectsLeft = 1; // this rectangle is the last one
93 break;
Pierre Ossman6b68f972017-02-19 15:51:19 +010094 case pseudoEncodingXCursor:
95 readSetXCursor(w, h, Point(x,y));
96 break;
Pierre Ossman7638e9c2014-01-16 13:12:40 +010097 case pseudoEncodingCursor:
98 readSetCursor(w, h, Point(x,y));
99 break;
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100100 case pseudoEncodingCursorWithAlpha:
101 readSetCursorWithAlpha(w, h, Point(x,y));
102 break;
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100103 case pseudoEncodingVMwareCursor:
104 readSetVMwareCursor(w, h, Point(x,y));
105 break;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100106 case pseudoEncodingDesktopName:
107 readSetDesktopName(x, y, w, h);
108 break;
109 case pseudoEncodingDesktopSize:
110 handler->setDesktopSize(w, h);
111 break;
112 case pseudoEncodingExtendedDesktopSize:
113 readExtendedDesktopSize(x, y, w, h);
114 break;
Pierre Ossman2fa63f82016-12-05 15:26:21 +0100115 case pseudoEncodingLEDState:
116 readLEDState();
Pierre Ossman62b07862018-11-05 16:28:57 +0100117 break;
118 case pseudoEncodingVMwareLEDState:
119 readVMwareLEDState();
120 break;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200121 case pseudoEncodingQEMUKeyEvent:
122 handler->supportsQEMUKeyEvent();
Pierre Ossman2fa63f82016-12-05 15:26:21 +0100123 break;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100124 default:
125 readRect(Rect(x, y, x+w, y+h), encoding);
126 break;
127 };
128
129 nUpdateRectsLeft--;
130 if (nUpdateRectsLeft == 0)
131 handler->framebufferUpdateEnd();
132 }
133}
134
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000135void CMsgReader::readSetColourMapEntries()
136{
137 is->skip(1);
138 int firstColour = is->readU16();
139 int nColours = is->readU16();
140 rdr::U16Array rgbs(nColours * 3);
141 for (int i = 0; i < nColours * 3; i++)
142 rgbs.buf[i] = is->readU16();
143 handler->setColourMapEntries(firstColour, nColours, rgbs.buf);
144}
145
146void CMsgReader::readBell()
147{
148 handler->bell();
149}
150
151void CMsgReader::readServerCutText()
152{
153 is->skip(3);
Adam Tkacacf6c6b2009-02-13 12:42:05 +0000154 rdr::U32 len = is->readU32();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000155 if (len > 256*1024) {
156 is->skip(len);
Pierre Ossmane9e7da92016-04-20 09:38:06 +0200157 vlog.error("cut text too long (%d bytes) - ignoring",len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000158 return;
159 }
Pierre Ossman546b2ad2019-05-02 12:32:03 +0200160 CharArray ca(len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000161 is->readBytes(ca.buf, len);
Pierre Ossman546b2ad2019-05-02 12:32:03 +0200162 CharArray filtered(convertLF(ca.buf, len));
163 handler->serverCutText(filtered.buf, strlen(filtered.buf));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000164}
165
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100166void CMsgReader::readFence()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000167{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100168 rdr::U32 flags;
169 rdr::U8 len;
170 char data[64];
171
172 is->skip(3);
173
174 flags = is->readU32();
175
176 len = is->readU8();
177 if (len > sizeof(data)) {
Pierre Ossmane9e7da92016-04-20 09:38:06 +0200178 vlog.error("Ignoring fence with too large payload");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100179 is->skip(len);
180 return;
181 }
182
183 is->readBytes(data, len);
184
185 handler->fence(flags, len, data);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000186}
187
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100188void CMsgReader::readEndOfContinuousUpdates()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000189{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100190 handler->endOfContinuousUpdates();
191}
192
193void CMsgReader::readFramebufferUpdate()
194{
195 is->skip(1);
196 nUpdateRectsLeft = is->readU16();
197 handler->framebufferUpdateStart();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198}
199
Peter Ã…strand98fe98c2010-02-10 07:43:02 +0000200void CMsgReader::readRect(const Rect& r, int encoding)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000201{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200202 if ((r.br.x > handler->server.width()) ||
203 (r.br.y > handler->server.height())) {
Pierre Ossmane9e7da92016-04-20 09:38:06 +0200204 vlog.error("Rect too big: %dx%d at %d,%d exceeds %dx%d",
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000205 r.width(), r.height(), r.tl.x, r.tl.y,
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200206 handler->server.width(), handler->server.height());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207 throw Exception("Rect too big");
208 }
209
210 if (r.is_empty())
Pierre Ossmane9e7da92016-04-20 09:38:06 +0200211 vlog.error("zero size rect");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000212
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100213 handler->dataRect(r, encoding);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000214}
215
Pierre Ossman6b68f972017-02-19 15:51:19 +0100216void CMsgReader::readSetXCursor(int width, int height, const Point& hotspot)
217{
Michal Srbc26b4b32017-04-06 23:52:22 +0300218 if (width > maxCursorSize || height > maxCursorSize)
219 throw Exception("Too big cursor");
220
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100221 rdr::U8Array rgba(width*height*4);
Pierre Ossman6b68f972017-02-19 15:51:19 +0100222
Brian P. Hinz33d78322017-11-16 17:46:15 -0500223 if (width * height > 0) {
Pierre Ossman9c88e0d2018-09-13 12:30:30 +0200224 rdr::U8 pr, pg, pb;
225 rdr::U8 sr, sg, sb;
226 int data_len = ((width+7)/8) * height;
227 int mask_len = ((width+7)/8) * height;
228 rdr::U8Array data(data_len);
229 rdr::U8Array mask(mask_len);
230
231 int x, y;
232 rdr::U8* out;
233
Pierre Ossman6b68f972017-02-19 15:51:19 +0100234 pr = is->readU8();
235 pg = is->readU8();
236 pb = is->readU8();
237
238 sr = is->readU8();
239 sg = is->readU8();
240 sb = is->readU8();
241
242 is->readBytes(data.buf, data_len);
243 is->readBytes(mask.buf, mask_len);
Pierre Ossman6b68f972017-02-19 15:51:19 +0100244
Pierre Ossman9c88e0d2018-09-13 12:30:30 +0200245 int maskBytesPerRow = (width+7)/8;
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100246 out = rgba.buf;
Pierre Ossman9c88e0d2018-09-13 12:30:30 +0200247 for (y = 0;y < height;y++) {
248 for (x = 0;x < width;x++) {
249 int byte = y * maskBytesPerRow + x / 8;
250 int bit = 7 - x % 8;
Pierre Ossman6b68f972017-02-19 15:51:19 +0100251
Pierre Ossman9c88e0d2018-09-13 12:30:30 +0200252 if (data.buf[byte] & (1 << bit)) {
253 out[0] = pr;
254 out[1] = pg;
255 out[2] = pb;
256 } else {
257 out[0] = sr;
258 out[1] = sg;
259 out[2] = sb;
260 }
261
262 if (mask.buf[byte] & (1 << bit))
263 out[3] = 255;
264 else
265 out[3] = 0;
266
267 out += 4;
Pierre Ossman6b68f972017-02-19 15:51:19 +0100268 }
Pierre Ossman6b68f972017-02-19 15:51:19 +0100269 }
270 }
271
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100272 handler->setCursor(width, height, hotspot, rgba.buf);
Pierre Ossman6b68f972017-02-19 15:51:19 +0100273}
274
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000275void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
276{
Michal Srbc26b4b32017-04-06 23:52:22 +0300277 if (width > maxCursorSize || height > maxCursorSize)
278 throw Exception("Too big cursor");
279
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200280 int data_len = width * height * (handler->server.pf().bpp/8);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000281 int mask_len = ((width+7)/8) * height;
282 rdr::U8Array data(data_len);
283 rdr::U8Array mask(mask_len);
284
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100285 int x, y;
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100286 rdr::U8Array rgba(width*height*4);
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100287 rdr::U8* in;
288 rdr::U8* out;
289
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000290 is->readBytes(data.buf, data_len);
291 is->readBytes(mask.buf, mask_len);
292
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100293 int maskBytesPerRow = (width+7)/8;
294 in = data.buf;
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100295 out = rgba.buf;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100296 for (y = 0;y < height;y++) {
297 for (x = 0;x < width;x++) {
298 int byte = y * maskBytesPerRow + x / 8;
299 int bit = 7 - x % 8;
300
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200301 handler->server.pf().rgbFromBuffer(out, in, 1);
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100302
303 if (mask.buf[byte] & (1 << bit))
304 out[3] = 255;
305 else
306 out[3] = 0;
307
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200308 in += handler->server.pf().bpp/8;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100309 out += 4;
310 }
311 }
312
Pierre Ossmanbeb59a42018-11-07 21:36:05 +0100313 handler->setCursor(width, height, hotspot, rgba.buf);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000314}
315
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100316void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot)
317{
Michal Srbc26b4b32017-04-06 23:52:22 +0300318 if (width > maxCursorSize || height > maxCursorSize)
319 throw Exception("Too big cursor");
320
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100321 int encoding;
322
323 const PixelFormat rgbaPF(32, 32, false, true, 255, 255, 255, 16, 8, 0);
324 ManagedPixelBuffer pb(rgbaPF, width, height);
325 PixelFormat origPF;
326
327 rdr::U8* buf;
328 int stride;
329
330 encoding = is->readS32();
331
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200332 origPF = handler->server.pf();
333 handler->server.setPF(rgbaPF);
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100334 handler->readAndDecodeRect(pb.getRect(), encoding, &pb);
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200335 handler->server.setPF(origPF);
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100336
337 // On-wire data has pre-multiplied alpha, but we store it
338 // non-pre-multiplied
339 buf = pb.getBufferRW(pb.getRect(), &stride);
340 assert(stride == width);
341
342 for (int i = 0;i < pb.area();i++) {
343 rdr::U8 alpha;
344
345 alpha = buf[3];
346 if (alpha == 0)
347 alpha = 1; // Avoid division by zero
348
349 buf[0] = (unsigned)buf[0] * 255/alpha;
350 buf[1] = (unsigned)buf[1] * 255/alpha;
351 buf[2] = (unsigned)buf[2] * 255/alpha;
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100352
353 buf += 4;
354 }
355
356 pb.commitBufferRW(pb.getRect());
357
358 handler->setCursor(width, height, hotspot,
359 pb.getBuffer(pb.getRect(), &stride));
360}
361
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100362void CMsgReader::readSetVMwareCursor(int width, int height, const Point& hotspot)
363{
364 if (width > maxCursorSize || height > maxCursorSize)
365 throw Exception("Too big cursor");
366
367 rdr::U8 type;
368
369 type = is->readU8();
370 is->skip(1);
371
372 if (type == 0) {
373 int len = width * height * (handler->server.pf().bpp/8);
374 rdr::U8Array andMask(len);
375 rdr::U8Array xorMask(len);
376
377 rdr::U8Array data(width*height*4);
378
379 rdr::U8* andIn;
380 rdr::U8* xorIn;
381 rdr::U8* out;
382 int Bpp;
383
384 is->readBytes(andMask.buf, len);
385 is->readBytes(xorMask.buf, len);
386
387 andIn = andMask.buf;
388 xorIn = xorMask.buf;
389 out = data.buf;
390 Bpp = handler->server.pf().bpp/8;
391 for (int y = 0;y < height;y++) {
392 for (int x = 0;x < width;x++) {
393 Pixel andPixel, xorPixel;
394
395 andPixel = handler->server.pf().pixelFromBuffer(andIn);
396 xorPixel = handler->server.pf().pixelFromBuffer(xorIn);
397 andIn += Bpp;
398 xorIn += Bpp;
399
400 if (andPixel == 0) {
401 rdr::U8 r, g, b;
402
403 // Opaque pixel
404
405 handler->server.pf().rgbFromPixel(xorPixel, &r, &g, &b);
406 *out++ = r;
407 *out++ = g;
408 *out++ = b;
409 *out++ = 0xff;
410 } else if (xorPixel == 0) {
411 // Fully transparent pixel
412 *out++ = 0;
413 *out++ = 0;
414 *out++ = 0;
415 *out++ = 0;
416 } else if (andPixel == xorPixel) {
417 // Inverted pixel
418
419 // We don't really support this, so just turn the pixel black
420 // FIXME: Do an outline like WinVNC does?
421 *out++ = 0;
422 *out++ = 0;
423 *out++ = 0;
424 *out++ = 0xff;
425 } else {
426 // Partially transparent/inverted pixel
427
428 // We _really_ can't handle this, just make it black
429 *out++ = 0;
430 *out++ = 0;
431 *out++ = 0;
432 *out++ = 0xff;
433 }
434 }
435 }
436
437 handler->setCursor(width, height, hotspot, data.buf);
438 } else if (type == 1) {
439 rdr::U8Array data(width*height*4);
440
441 // FIXME: Is alpha premultiplied?
442 is->readBytes(data.buf, width*height*4);
443
444 handler->setCursor(width, height, hotspot, data.buf);
445 } else {
446 throw Exception("Unknown cursor type");
447 }
448}
449
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100450void CMsgReader::readSetDesktopName(int x, int y, int w, int h)
451{
452 char* name = is->readString();
453
454 if (x || y || w || h) {
Pierre Ossmane9e7da92016-04-20 09:38:06 +0200455 vlog.error("Ignoring DesktopName rect with non-zero position/size");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100456 } else {
457 handler->setName(name);
458 }
459
460 delete [] name;
461}
462
463void CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h)
464{
465 unsigned int screens, i;
466 rdr::U32 id, flags;
467 int sx, sy, sw, sh;
468 ScreenSet layout;
469
470 screens = is->readU8();
471 is->skip(3);
472
473 for (i = 0;i < screens;i++) {
474 id = is->readU32();
475 sx = is->readU16();
476 sy = is->readU16();
477 sw = is->readU16();
478 sh = is->readU16();
479 flags = is->readU32();
480
481 layout.add_screen(Screen(id, sx, sy, sw, sh, flags));
482 }
483
484 handler->setExtendedDesktopSize(x, y, w, h, layout);
485}
Pierre Ossman2fa63f82016-12-05 15:26:21 +0100486
487void CMsgReader::readLEDState()
488{
489 rdr::U8 state;
490
491 state = is->readU8();
492
493 handler->setLEDState(state);
494}
Pierre Ossman62b07862018-11-05 16:28:57 +0100495
496void CMsgReader::readVMwareLEDState()
497{
498 rdr::U32 state;
499
500 state = is->readU32();
501
502 // As luck has it, this extension uses the same bit definitions,
503 // so no conversion required
504
505 handler->setLEDState(state);
506}