blob: 0141e6b40f791d4a51402e7db13e5bc3ba9d08b1 [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>
26#include <rfb/util.h>
27#include <rfb/CMsgHandler.h>
28#include <rfb/CMsgReader.h>
29
30using namespace rfb;
31
32CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
33 : imageBufIdealSize(0), handler(handler_), is(is_),
Pierre Ossman7bfb73b2015-11-10 13:03:26 +010034 nUpdateRectsLeft(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000035{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036}
37
38CMsgReader::~CMsgReader()
39{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000040}
41
Pierre Ossman7638e9c2014-01-16 13:12:40 +010042void CMsgReader::readServerInit()
43{
44 int width = is->readU16();
45 int height = is->readU16();
Pierre Ossman7638e9c2014-01-16 13:12:40 +010046 PixelFormat pf;
47 pf.read(is);
Pierre Ossman7638e9c2014-01-16 13:12:40 +010048 CharArray name(is->readString());
Pierre Ossmandd45b442018-10-31 17:08:59 +010049 handler->serverInit(width, height, pf, name.buf);
Pierre Ossman7638e9c2014-01-16 13:12:40 +010050}
51
52void CMsgReader::readMsg()
53{
54 if (nUpdateRectsLeft == 0) {
55 int type = is->readU8();
56
57 switch (type) {
58 case msgTypeSetColourMapEntries:
59 readSetColourMapEntries();
60 break;
61 case msgTypeBell:
62 readBell();
63 break;
64 case msgTypeServerCutText:
65 readServerCutText();
66 break;
67 case msgTypeFramebufferUpdate:
68 readFramebufferUpdate();
69 break;
70 case msgTypeServerFence:
71 readFence();
72 break;
73 case msgTypeEndOfContinuousUpdates:
74 readEndOfContinuousUpdates();
75 break;
76 default:
77 fprintf(stderr, "unknown message type %d\n", type);
78 throw Exception("unknown message type");
79 }
80 } else {
81 int x = is->readU16();
82 int y = is->readU16();
83 int w = is->readU16();
84 int h = is->readU16();
85 int encoding = is->readS32();
86
87 switch (encoding) {
88 case pseudoEncodingLastRect:
89 nUpdateRectsLeft = 1; // this rectangle is the last one
90 break;
Pierre Ossman6b68f972017-02-19 15:51:19 +010091 case pseudoEncodingXCursor:
92 readSetXCursor(w, h, Point(x,y));
93 break;
Pierre Ossman7638e9c2014-01-16 13:12:40 +010094 case pseudoEncodingCursor:
95 readSetCursor(w, h, Point(x,y));
96 break;
Pierre Ossmana4c0aac2017-02-19 15:50:29 +010097 case pseudoEncodingCursorWithAlpha:
98 readSetCursorWithAlpha(w, h, Point(x,y));
99 break;
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100100 case pseudoEncodingVMwareCursor:
101 readSetVMwareCursor(w, h, Point(x,y));
102 break;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100103 case pseudoEncodingDesktopName:
104 readSetDesktopName(x, y, w, h);
105 break;
106 case pseudoEncodingDesktopSize:
107 handler->setDesktopSize(w, h);
108 break;
109 case pseudoEncodingExtendedDesktopSize:
110 readExtendedDesktopSize(x, y, w, h);
111 break;
Pierre Ossman2fa63f82016-12-05 15:26:21 +0100112 case pseudoEncodingLEDState:
113 readLEDState();
Pierre Ossman62b07862018-11-05 16:28:57 +0100114 break;
115 case pseudoEncodingVMwareLEDState:
116 readVMwareLEDState();
117 break;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200118 case pseudoEncodingQEMUKeyEvent:
119 handler->supportsQEMUKeyEvent();
Pierre Ossman2fa63f82016-12-05 15:26:21 +0100120 break;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100121 default:
122 readRect(Rect(x, y, x+w, y+h), encoding);
123 break;
124 };
125
126 nUpdateRectsLeft--;
127 if (nUpdateRectsLeft == 0)
128 handler->framebufferUpdateEnd();
129 }
130}
131
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000132void CMsgReader::readSetColourMapEntries()
133{
134 is->skip(1);
135 int firstColour = is->readU16();
136 int nColours = is->readU16();
137 rdr::U16Array rgbs(nColours * 3);
138 for (int i = 0; i < nColours * 3; i++)
139 rgbs.buf[i] = is->readU16();
140 handler->setColourMapEntries(firstColour, nColours, rgbs.buf);
141}
142
143void CMsgReader::readBell()
144{
145 handler->bell();
146}
147
148void CMsgReader::readServerCutText()
149{
150 is->skip(3);
Adam Tkacacf6c6b2009-02-13 12:42:05 +0000151 rdr::U32 len = is->readU32();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000152 if (len > 256*1024) {
153 is->skip(len);
154 fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
155 return;
156 }
157 CharArray ca(len+1);
158 ca.buf[len] = 0;
159 is->readBytes(ca.buf, len);
160 handler->serverCutText(ca.buf, len);
161}
162
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100163void CMsgReader::readFence()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000164{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100165 rdr::U32 flags;
166 rdr::U8 len;
167 char data[64];
168
169 is->skip(3);
170
171 flags = is->readU32();
172
173 len = is->readU8();
174 if (len > sizeof(data)) {
175 fprintf(stderr, "Ignoring fence with too large payload\n");
176 is->skip(len);
177 return;
178 }
179
180 is->readBytes(data, len);
181
182 handler->fence(flags, len, data);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000183}
184
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100185void CMsgReader::readEndOfContinuousUpdates()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000186{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100187 handler->endOfContinuousUpdates();
188}
189
190void CMsgReader::readFramebufferUpdate()
191{
192 is->skip(1);
193 nUpdateRectsLeft = is->readU16();
194 handler->framebufferUpdateStart();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000195}
196
Peter Ã…strand98fe98c2010-02-10 07:43:02 +0000197void CMsgReader::readRect(const Rect& r, int encoding)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200199 if ((r.br.x > handler->server.width()) ||
200 (r.br.y > handler->server.height())) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000201 fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n",
202 r.width(), r.height(), r.tl.x, r.tl.y,
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200203 handler->server.width(), handler->server.height());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000204 throw Exception("Rect too big");
205 }
206
207 if (r.is_empty())
208 fprintf(stderr, "Warning: zero size rect\n");
209
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100210 handler->dataRect(r, encoding);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000211}
212
Pierre Ossman6b68f972017-02-19 15:51:19 +0100213void CMsgReader::readSetXCursor(int width, int height, const Point& hotspot)
214{
Michal Srbc26b4b32017-04-06 23:52:22 +0300215 if (width > maxCursorSize || height > maxCursorSize)
216 throw Exception("Too big cursor");
217
Pierre Ossman6b68f972017-02-19 15:51:19 +0100218 rdr::U8 buf[width*height*4];
Pierre Ossman6b68f972017-02-19 15:51:19 +0100219
Brian P. Hinz33d78322017-11-16 17:46:15 -0500220 if (width * height > 0) {
Pierre Ossman9c88e0d2018-09-13 12:30:30 +0200221 rdr::U8 pr, pg, pb;
222 rdr::U8 sr, sg, sb;
223 int data_len = ((width+7)/8) * height;
224 int mask_len = ((width+7)/8) * height;
225 rdr::U8Array data(data_len);
226 rdr::U8Array mask(mask_len);
227
228 int x, y;
229 rdr::U8* out;
230
Pierre Ossman6b68f972017-02-19 15:51:19 +0100231 pr = is->readU8();
232 pg = is->readU8();
233 pb = is->readU8();
234
235 sr = is->readU8();
236 sg = is->readU8();
237 sb = is->readU8();
238
239 is->readBytes(data.buf, data_len);
240 is->readBytes(mask.buf, mask_len);
Pierre Ossman6b68f972017-02-19 15:51:19 +0100241
Pierre Ossman9c88e0d2018-09-13 12:30:30 +0200242 int maskBytesPerRow = (width+7)/8;
243 out = buf;
244 for (y = 0;y < height;y++) {
245 for (x = 0;x < width;x++) {
246 int byte = y * maskBytesPerRow + x / 8;
247 int bit = 7 - x % 8;
Pierre Ossman6b68f972017-02-19 15:51:19 +0100248
Pierre Ossman9c88e0d2018-09-13 12:30:30 +0200249 if (data.buf[byte] & (1 << bit)) {
250 out[0] = pr;
251 out[1] = pg;
252 out[2] = pb;
253 } else {
254 out[0] = sr;
255 out[1] = sg;
256 out[2] = sb;
257 }
258
259 if (mask.buf[byte] & (1 << bit))
260 out[3] = 255;
261 else
262 out[3] = 0;
263
264 out += 4;
Pierre Ossman6b68f972017-02-19 15:51:19 +0100265 }
Pierre Ossman6b68f972017-02-19 15:51:19 +0100266 }
267 }
268
269 handler->setCursor(width, height, hotspot, buf);
270}
271
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000272void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
273{
Michal Srbc26b4b32017-04-06 23:52:22 +0300274 if (width > maxCursorSize || height > maxCursorSize)
275 throw Exception("Too big cursor");
276
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200277 int data_len = width * height * (handler->server.pf().bpp/8);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000278 int mask_len = ((width+7)/8) * height;
279 rdr::U8Array data(data_len);
280 rdr::U8Array mask(mask_len);
281
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100282 int x, y;
283 rdr::U8 buf[width*height*4];
284 rdr::U8* in;
285 rdr::U8* out;
286
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000287 is->readBytes(data.buf, data_len);
288 is->readBytes(mask.buf, mask_len);
289
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100290 int maskBytesPerRow = (width+7)/8;
291 in = data.buf;
292 out = buf;
293 for (y = 0;y < height;y++) {
294 for (x = 0;x < width;x++) {
295 int byte = y * maskBytesPerRow + x / 8;
296 int bit = 7 - x % 8;
297
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200298 handler->server.pf().rgbFromBuffer(out, in, 1);
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100299
300 if (mask.buf[byte] & (1 << bit))
301 out[3] = 255;
302 else
303 out[3] = 0;
304
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200305 in += handler->server.pf().bpp/8;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100306 out += 4;
307 }
308 }
309
310 handler->setCursor(width, height, hotspot, buf);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000311}
312
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100313void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot)
314{
Michal Srbc26b4b32017-04-06 23:52:22 +0300315 if (width > maxCursorSize || height > maxCursorSize)
316 throw Exception("Too big cursor");
317
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100318 int encoding;
319
320 const PixelFormat rgbaPF(32, 32, false, true, 255, 255, 255, 16, 8, 0);
321 ManagedPixelBuffer pb(rgbaPF, width, height);
322 PixelFormat origPF;
323
324 rdr::U8* buf;
325 int stride;
326
327 encoding = is->readS32();
328
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200329 origPF = handler->server.pf();
330 handler->server.setPF(rgbaPF);
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100331 handler->readAndDecodeRect(pb.getRect(), encoding, &pb);
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200332 handler->server.setPF(origPF);
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100333
334 // On-wire data has pre-multiplied alpha, but we store it
335 // non-pre-multiplied
336 buf = pb.getBufferRW(pb.getRect(), &stride);
337 assert(stride == width);
338
339 for (int i = 0;i < pb.area();i++) {
340 rdr::U8 alpha;
341
342 alpha = buf[3];
343 if (alpha == 0)
344 alpha = 1; // Avoid division by zero
345
346 buf[0] = (unsigned)buf[0] * 255/alpha;
347 buf[1] = (unsigned)buf[1] * 255/alpha;
348 buf[2] = (unsigned)buf[2] * 255/alpha;
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100349
350 buf += 4;
351 }
352
353 pb.commitBufferRW(pb.getRect());
354
355 handler->setCursor(width, height, hotspot,
356 pb.getBuffer(pb.getRect(), &stride));
357}
358
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100359void CMsgReader::readSetVMwareCursor(int width, int height, const Point& hotspot)
360{
361 if (width > maxCursorSize || height > maxCursorSize)
362 throw Exception("Too big cursor");
363
364 rdr::U8 type;
365
366 type = is->readU8();
367 is->skip(1);
368
369 if (type == 0) {
370 int len = width * height * (handler->server.pf().bpp/8);
371 rdr::U8Array andMask(len);
372 rdr::U8Array xorMask(len);
373
374 rdr::U8Array data(width*height*4);
375
376 rdr::U8* andIn;
377 rdr::U8* xorIn;
378 rdr::U8* out;
379 int Bpp;
380
381 is->readBytes(andMask.buf, len);
382 is->readBytes(xorMask.buf, len);
383
384 andIn = andMask.buf;
385 xorIn = xorMask.buf;
386 out = data.buf;
387 Bpp = handler->server.pf().bpp/8;
388 for (int y = 0;y < height;y++) {
389 for (int x = 0;x < width;x++) {
390 Pixel andPixel, xorPixel;
391
392 andPixel = handler->server.pf().pixelFromBuffer(andIn);
393 xorPixel = handler->server.pf().pixelFromBuffer(xorIn);
394 andIn += Bpp;
395 xorIn += Bpp;
396
397 if (andPixel == 0) {
398 rdr::U8 r, g, b;
399
400 // Opaque pixel
401
402 handler->server.pf().rgbFromPixel(xorPixel, &r, &g, &b);
403 *out++ = r;
404 *out++ = g;
405 *out++ = b;
406 *out++ = 0xff;
407 } else if (xorPixel == 0) {
408 // Fully transparent pixel
409 *out++ = 0;
410 *out++ = 0;
411 *out++ = 0;
412 *out++ = 0;
413 } else if (andPixel == xorPixel) {
414 // Inverted pixel
415
416 // We don't really support this, so just turn the pixel black
417 // FIXME: Do an outline like WinVNC does?
418 *out++ = 0;
419 *out++ = 0;
420 *out++ = 0;
421 *out++ = 0xff;
422 } else {
423 // Partially transparent/inverted pixel
424
425 // We _really_ can't handle this, just make it black
426 *out++ = 0;
427 *out++ = 0;
428 *out++ = 0;
429 *out++ = 0xff;
430 }
431 }
432 }
433
434 handler->setCursor(width, height, hotspot, data.buf);
435 } else if (type == 1) {
436 rdr::U8Array data(width*height*4);
437
438 // FIXME: Is alpha premultiplied?
439 is->readBytes(data.buf, width*height*4);
440
441 handler->setCursor(width, height, hotspot, data.buf);
442 } else {
443 throw Exception("Unknown cursor type");
444 }
445}
446
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100447void CMsgReader::readSetDesktopName(int x, int y, int w, int h)
448{
449 char* name = is->readString();
450
451 if (x || y || w || h) {
452 fprintf(stderr, "Ignoring DesktopName rect with non-zero position/size\n");
453 } else {
454 handler->setName(name);
455 }
456
457 delete [] name;
458}
459
460void CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h)
461{
462 unsigned int screens, i;
463 rdr::U32 id, flags;
464 int sx, sy, sw, sh;
465 ScreenSet layout;
466
467 screens = is->readU8();
468 is->skip(3);
469
470 for (i = 0;i < screens;i++) {
471 id = is->readU32();
472 sx = is->readU16();
473 sy = is->readU16();
474 sw = is->readU16();
475 sh = is->readU16();
476 flags = is->readU32();
477
478 layout.add_screen(Screen(id, sx, sy, sw, sh, flags));
479 }
480
481 handler->setExtendedDesktopSize(x, y, w, h, layout);
482}
Pierre Ossman2fa63f82016-12-05 15:26:21 +0100483
484void CMsgReader::readLEDState()
485{
486 rdr::U8 state;
487
488 state = is->readU8();
489
490 handler->setLEDState(state);
491}
Pierre Ossman62b07862018-11-05 16:28:57 +0100492
493void CMsgReader::readVMwareLEDState()
494{
495 rdr::U32 state;
496
497 state = is->readU32();
498
499 // As luck has it, this extension uses the same bit definitions,
500 // so no conversion required
501
502 handler->setLEDState(state);
503}