blob: bc3f43987c09ad9e68fc221e863c7792e4a07c64 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
DRCb4a83232011-08-19 04:57:18 +00002 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Pierre Ossman6a1a0d02017-02-19 15:48:17 +01003 * Copyright 2009-2017 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00004 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20#include <stdio.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000021#include <rdr/OutStream.h>
22#include <rfb/msgTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010023#include <rfb/fenceTypes.h>
24#include <rfb/Exception.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000025#include <rfb/ConnParams.h>
26#include <rfb/UpdateTracker.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010027#include <rfb/Encoder.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028#include <rfb/SMsgWriter.h>
29#include <rfb/LogWriter.h>
30
31using namespace rfb;
32
33static LogWriter vlog("SMsgWriter");
34
35SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
Pierre Ossman20dd2a92015-02-11 17:43:15 +010036 : cp(cp_), os(os_),
Pierre Ossman7638e9c2014-01-16 13:12:40 +010037 nRectsInUpdate(0), nRectsInHeader(0),
Pierre Ossman126e5642014-02-13 14:40:25 +010038 needSetDesktopSize(false), needExtendedDesktopSize(false),
Pierre Ossman8053c8e2017-02-21 12:59:04 +010039 needSetDesktopName(false), needSetCursor(false),
40 needSetXCursor(false), needSetCursorWithAlpha(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000041{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000042}
43
44SMsgWriter::~SMsgWriter()
45{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046}
47
Pierre Ossman7638e9c2014-01-16 13:12:40 +010048void SMsgWriter::writeServerInit()
49{
50 os->writeU16(cp->width);
51 os->writeU16(cp->height);
52 cp->pf().write(os);
53 os->writeString(cp->name());
54 endMsg();
55}
56
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010058 const rdr::U16 red[],
59 const rdr::U16 green[],
60 const rdr::U16 blue[])
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000061{
62 startMsg(msgTypeSetColourMapEntries);
63 os->pad(1);
64 os->writeU16(firstColour);
65 os->writeU16(nColours);
66 for (int i = firstColour; i < firstColour+nColours; i++) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010067 os->writeU16(red[i]);
68 os->writeU16(green[i]);
69 os->writeU16(blue[i]);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000070 }
71 endMsg();
72}
73
74void SMsgWriter::writeBell()
75{
76 startMsg(msgTypeBell);
77 endMsg();
78}
79
80void SMsgWriter::writeServerCutText(const char* str, int len)
81{
82 startMsg(msgTypeServerCutText);
83 os->pad(3);
84 os->writeU32(len);
85 os->writeBytes(str, len);
86 endMsg();
87}
88
Pierre Ossman7638e9c2014-01-16 13:12:40 +010089void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
90{
91 if (!cp->supportsFence)
92 throw Exception("Client does not support fences");
93 if (len > 64)
94 throw Exception("Too large fence payload");
95 if ((flags & ~fenceFlagsSupported) != 0)
96 throw Exception("Unknown fence flags");
97
98 startMsg(msgTypeServerFence);
99 os->pad(3);
100
101 os->writeU32(flags);
102
103 os->writeU8(len);
Michal Srbf3afa242017-03-27 19:02:15 +0300104
105 if (len > 0)
106 os->writeBytes(data, len);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100107
108 endMsg();
109}
110
111void SMsgWriter::writeEndOfContinuousUpdates()
112{
113 if (!cp->supportsContinuousUpdates)
114 throw Exception("Client does not support continuous updates");
115
116 startMsg(msgTypeEndOfContinuousUpdates);
117 endMsg();
118}
119
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100120bool SMsgWriter::writeSetDesktopSize() {
121 if (!cp->supportsDesktopResize)
122 return false;
123
124 needSetDesktopSize = true;
125
126 return true;
127}
128
129bool SMsgWriter::writeExtendedDesktopSize() {
130 if (!cp->supportsExtendedDesktopSize)
131 return false;
132
133 needExtendedDesktopSize = true;
134
135 return true;
136}
137
138bool SMsgWriter::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
139 int fb_width, int fb_height,
140 const ScreenSet& layout) {
141 ExtendedDesktopSizeMsg msg;
142
143 if (!cp->supportsExtendedDesktopSize)
144 return false;
145
146 msg.reason = reason;
147 msg.result = result;
148 msg.fb_width = fb_width;
149 msg.fb_height = fb_height;
150 msg.layout = layout;
151
152 extendedDesktopSizeMsgs.push_back(msg);
153
154 return true;
155}
156
157bool SMsgWriter::writeSetDesktopName() {
158 if (!cp->supportsDesktopRename)
159 return false;
160
161 needSetDesktopName = true;
162
163 return true;
164}
165
Pierre Ossman126e5642014-02-13 14:40:25 +0100166bool SMsgWriter::writeSetCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100167{
Pierre Ossman126e5642014-02-13 14:40:25 +0100168 if (!cp->supportsLocalCursor)
169 return false;
170
171 needSetCursor = true;
172
173 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100174}
175
Pierre Ossman126e5642014-02-13 14:40:25 +0100176bool SMsgWriter::writeSetXCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100177{
Pierre Ossman126e5642014-02-13 14:40:25 +0100178 if (!cp->supportsLocalXCursor)
179 return false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100180
Pierre Ossman126e5642014-02-13 14:40:25 +0100181 needSetXCursor = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100182
Pierre Ossman126e5642014-02-13 14:40:25 +0100183 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100184}
185
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100186bool SMsgWriter::writeSetCursorWithAlpha()
187{
188 if (!cp->supportsLocalCursorWithAlpha)
189 return false;
190
191 needSetCursorWithAlpha = true;
192
193 return true;
194}
195
Pierre Ossmane9962f72009-04-23 12:31:42 +0000196bool SMsgWriter::needFakeUpdate()
197{
Pierre Ossman126e5642014-02-13 14:40:25 +0100198 if (needSetDesktopName)
199 return true;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100200 if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
Pierre Ossman126e5642014-02-13 14:40:25 +0100201 return true;
202 if (needNoDataUpdate())
203 return true;
204
205 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000206}
207
Pierre Ossmane9962f72009-04-23 12:31:42 +0000208bool SMsgWriter::needNoDataUpdate()
209{
Pierre Ossman126e5642014-02-13 14:40:25 +0100210 if (needSetDesktopSize)
211 return true;
212 if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty())
213 return true;
214
215 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000216}
217
218void SMsgWriter::writeNoDataUpdate()
219{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100220 int nRects;
221
222 nRects = 0;
223
224 if (needSetDesktopSize)
225 nRects++;
226 if (needExtendedDesktopSize)
227 nRects++;
228 if (!extendedDesktopSizeMsgs.empty())
229 nRects += extendedDesktopSizeMsgs.size();
230
231 writeFramebufferUpdateStart(nRects);
232 writeNoDataRects();
233 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000234}
235
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100236void SMsgWriter::writeFramebufferUpdateStart(int nRects)
237{
238 startMsg(msgTypeFramebufferUpdate);
239 os->pad(1);
240
241 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100242 if (needSetDesktopName)
243 nRects++;
Pierre Ossman126e5642014-02-13 14:40:25 +0100244 if (needSetCursor)
245 nRects++;
246 if (needSetXCursor)
247 nRects++;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100248 if (needSetCursorWithAlpha)
249 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100250 }
251
252 os->writeU16(nRects);
253
254 nRectsInUpdate = 0;
255 if (nRects == 0xFFFF)
256 nRectsInHeader = 0;
257 else
258 nRectsInHeader = nRects;
259
260 writePseudoRects();
261}
262
263void SMsgWriter::writeFramebufferUpdateEnd()
264{
265 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
266 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
267 "nRects out of sync");
268
269 if (nRectsInHeader == 0) {
270 // Send last rect. marker
271 os->writeS16(0);
272 os->writeS16(0);
273 os->writeU16(0);
274 os->writeU16(0);
275 os->writeU32(pseudoEncodingLastRect);
276 }
277
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100278 endMsg();
279}
280
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000281void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
282{
283 startRect(r,encodingCopyRect);
284 os->writeU16(srcX);
285 os->writeU16(srcY);
286 endRect();
287}
288
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100289void SMsgWriter::startRect(const Rect& r, int encoding)
290{
291 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
292 throw Exception("SMsgWriter::startRect: nRects out of sync");
293
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100294 os->writeS16(r.tl.x);
295 os->writeS16(r.tl.y);
296 os->writeU16(r.width());
297 os->writeU16(r.height());
298 os->writeU32(encoding);
299}
300
301void SMsgWriter::endRect()
302{
Pierre Ossman35294682016-04-29 14:27:08 +0200303 os->flush();
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100304}
305
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100306void SMsgWriter::startMsg(int type)
307{
308 os->writeU8(type);
309}
310
311void SMsgWriter::endMsg()
312{
313 os->flush();
314}
315
316void SMsgWriter::writePseudoRects()
317{
Pierre Ossman126e5642014-02-13 14:40:25 +0100318 if (needSetCursor) {
Pierre Ossman126e5642014-02-13 14:40:25 +0100319 const Cursor& cursor = cp->cursor();
320
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100321 rdr::U8Array data(cursor.width()*cursor.height() * cp->pf().bpp/8);
322 rdr::U8Array mask(cursor.getMask());
323
324 const rdr::U8* in;
325 rdr::U8* out;
326
327 in = cursor.getBuffer();
328 out = data.buf;
329 for (int i = 0;i < cursor.width()*cursor.height();i++) {
330 cp->pf().bufferFromRGB(out, in, 1);
331 in += 4;
332 out += cp->pf().bpp/8;
333 }
Pierre Ossman126e5642014-02-13 14:40:25 +0100334
335 writeSetCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100336 cursor.hotspot().x, cursor.hotspot().y,
337 data.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100338 needSetCursor = false;
Pierre Ossman126e5642014-02-13 14:40:25 +0100339 }
340
341 if (needSetXCursor) {
342 const Cursor& cursor = cp->cursor();
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100343 rdr::U8Array bitmap(cursor.getBitmap());
344 rdr::U8Array mask(cursor.getMask());
Pierre Ossman126e5642014-02-13 14:40:25 +0100345
346 writeSetXCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100347 cursor.hotspot().x, cursor.hotspot().y,
348 bitmap.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100349 needSetXCursor = false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100350 }
351
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100352 if (needSetCursorWithAlpha) {
353 const Cursor& cursor = cp->cursor();
354
355 writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
356 cursor.hotspot().x, cursor.hotspot().y,
357 cursor.getBuffer());
358 needSetCursorWithAlpha = false;
359 }
360
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100361 if (needSetDesktopName) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100362 writeSetDesktopNameRect(cp->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100363 needSetDesktopName = false;
364 }
365}
366
367void SMsgWriter::writeNoDataRects()
368{
369 // Start with specific ExtendedDesktopSize messages
370 if (!extendedDesktopSizeMsgs.empty()) {
371 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100372
373 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100374 writeExtendedDesktopSizeRect(ri->reason, ri->result,
375 ri->fb_width, ri->fb_height, ri->layout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100376 }
377
378 extendedDesktopSizeMsgs.clear();
379 }
380
381 // Send this before SetDesktopSize to make life easier on the clients
382 if (needExtendedDesktopSize) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100383 writeExtendedDesktopSizeRect(0, 0, cp->width, cp->height,
384 cp->screenLayout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100385 needExtendedDesktopSize = false;
386 }
387
388 // Some clients assume this is the last rectangle so don't send anything
389 // more after this
390 if (needSetDesktopSize) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100391 writeSetDesktopSizeRect(cp->width, cp->height);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100392 needSetDesktopSize = false;
393 }
394}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100395
396void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
397{
398 if (!cp->supportsDesktopResize)
399 throw Exception("Client does not support desktop resize");
400 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
401 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
402
403 os->writeS16(0);
404 os->writeS16(0);
405 os->writeU16(width);
406 os->writeU16(height);
407 os->writeU32(pseudoEncodingDesktopSize);
408}
409
410void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
411 rdr::U16 result,
412 int fb_width,
413 int fb_height,
414 const ScreenSet& layout)
415{
416 ScreenSet::const_iterator si;
417
418 if (!cp->supportsExtendedDesktopSize)
419 throw Exception("Client does not support extended desktop resize");
420 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
421 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
422
423 os->writeU16(reason);
424 os->writeU16(result);
425 os->writeU16(fb_width);
426 os->writeU16(fb_height);
427 os->writeU32(pseudoEncodingExtendedDesktopSize);
428
429 os->writeU8(layout.num_screens());
430 os->pad(3);
431
432 for (si = layout.begin();si != layout.end();++si) {
433 os->writeU32(si->id);
434 os->writeU16(si->dimensions.tl.x);
435 os->writeU16(si->dimensions.tl.y);
436 os->writeU16(si->dimensions.width());
437 os->writeU16(si->dimensions.height());
438 os->writeU32(si->flags);
439 }
440}
441
442void SMsgWriter::writeSetDesktopNameRect(const char *name)
443{
444 if (!cp->supportsDesktopRename)
445 throw Exception("Client does not support desktop rename");
446 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
447 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
448
449 os->writeS16(0);
450 os->writeS16(0);
451 os->writeU16(0);
452 os->writeU16(0);
453 os->writeU32(pseudoEncodingDesktopName);
454 os->writeString(name);
455}
Pierre Ossman126e5642014-02-13 14:40:25 +0100456
457void SMsgWriter::writeSetCursorRect(int width, int height,
458 int hotspotX, int hotspotY,
459 const void* data, const void* mask)
460{
461 if (!cp->supportsLocalCursor)
462 throw Exception("Client does not support local cursors");
463 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
464 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
465
466 os->writeS16(hotspotX);
467 os->writeS16(hotspotY);
468 os->writeU16(width);
469 os->writeU16(height);
470 os->writeU32(pseudoEncodingCursor);
471 os->writeBytes(data, width * height * (cp->pf().bpp/8));
472 os->writeBytes(mask, (width+7)/8 * height);
473}
474
475void SMsgWriter::writeSetXCursorRect(int width, int height,
476 int hotspotX, int hotspotY,
Pierre Ossman126e5642014-02-13 14:40:25 +0100477 const void* data, const void* mask)
478{
479 if (!cp->supportsLocalXCursor)
480 throw Exception("Client does not support local cursors");
481 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
482 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
483
484 os->writeS16(hotspotX);
485 os->writeS16(hotspotY);
486 os->writeU16(width);
487 os->writeU16(height);
488 os->writeU32(pseudoEncodingXCursor);
489 if (width * height) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100490 os->writeU8(255);
491 os->writeU8(255);
492 os->writeU8(255);
493 os->writeU8(0);
494 os->writeU8(0);
495 os->writeU8(0);
Pierre Ossman126e5642014-02-13 14:40:25 +0100496 os->writeBytes(data, (width+7)/8 * height);
497 os->writeBytes(mask, (width+7)/8 * height);
498 }
499}
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100500
501void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
502 int hotspotX, int hotspotY,
503 const rdr::U8* data)
504{
505 if (!cp->supportsLocalCursorWithAlpha)
506 throw Exception("Client does not support local cursors");
507 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
508 throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
509
510 os->writeS16(hotspotX);
511 os->writeS16(hotspotY);
512 os->writeU16(width);
513 os->writeU16(height);
514 os->writeU32(pseudoEncodingCursorWithAlpha);
515
516 // FIXME: Use an encoder with compression?
517 os->writeU32(encodingRaw);
518
519 // Alpha needs to be pre-multiplied
520 for (int i = 0;i < width*height;i++) {
521 os->writeU8((unsigned)data[0] * data[3] / 255);
522 os->writeU8((unsigned)data[1] * data[3] / 255);
523 os->writeU8((unsigned)data[2] * data[3] / 255);
524 os->writeU8(data[3]);
525 data += 4;
526 }
527}