blob: cf3264e8f0ddcbdecb5d9063a83935c69face29a [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);
104 os->writeBytes(data, len);
105
106 endMsg();
107}
108
109void SMsgWriter::writeEndOfContinuousUpdates()
110{
111 if (!cp->supportsContinuousUpdates)
112 throw Exception("Client does not support continuous updates");
113
114 startMsg(msgTypeEndOfContinuousUpdates);
115 endMsg();
116}
117
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100118bool SMsgWriter::writeSetDesktopSize() {
119 if (!cp->supportsDesktopResize)
120 return false;
121
122 needSetDesktopSize = true;
123
124 return true;
125}
126
127bool SMsgWriter::writeExtendedDesktopSize() {
128 if (!cp->supportsExtendedDesktopSize)
129 return false;
130
131 needExtendedDesktopSize = true;
132
133 return true;
134}
135
136bool SMsgWriter::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
137 int fb_width, int fb_height,
138 const ScreenSet& layout) {
139 ExtendedDesktopSizeMsg msg;
140
141 if (!cp->supportsExtendedDesktopSize)
142 return false;
143
144 msg.reason = reason;
145 msg.result = result;
146 msg.fb_width = fb_width;
147 msg.fb_height = fb_height;
148 msg.layout = layout;
149
150 extendedDesktopSizeMsgs.push_back(msg);
151
152 return true;
153}
154
155bool SMsgWriter::writeSetDesktopName() {
156 if (!cp->supportsDesktopRename)
157 return false;
158
159 needSetDesktopName = true;
160
161 return true;
162}
163
Pierre Ossman126e5642014-02-13 14:40:25 +0100164bool SMsgWriter::writeSetCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100165{
Pierre Ossman126e5642014-02-13 14:40:25 +0100166 if (!cp->supportsLocalCursor)
167 return false;
168
169 needSetCursor = true;
170
171 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100172}
173
Pierre Ossman126e5642014-02-13 14:40:25 +0100174bool SMsgWriter::writeSetXCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100175{
Pierre Ossman126e5642014-02-13 14:40:25 +0100176 if (!cp->supportsLocalXCursor)
177 return false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100178
Pierre Ossman126e5642014-02-13 14:40:25 +0100179 needSetXCursor = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100180
Pierre Ossman126e5642014-02-13 14:40:25 +0100181 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100182}
183
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100184bool SMsgWriter::writeSetCursorWithAlpha()
185{
186 if (!cp->supportsLocalCursorWithAlpha)
187 return false;
188
189 needSetCursorWithAlpha = true;
190
191 return true;
192}
193
Pierre Ossmane9962f72009-04-23 12:31:42 +0000194bool SMsgWriter::needFakeUpdate()
195{
Pierre Ossman126e5642014-02-13 14:40:25 +0100196 if (needSetDesktopName)
197 return true;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100198 if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
Pierre Ossman126e5642014-02-13 14:40:25 +0100199 return true;
200 if (needNoDataUpdate())
201 return true;
202
203 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000204}
205
Pierre Ossmane9962f72009-04-23 12:31:42 +0000206bool SMsgWriter::needNoDataUpdate()
207{
Pierre Ossman126e5642014-02-13 14:40:25 +0100208 if (needSetDesktopSize)
209 return true;
210 if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty())
211 return true;
212
213 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000214}
215
216void SMsgWriter::writeNoDataUpdate()
217{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100218 int nRects;
219
220 nRects = 0;
221
222 if (needSetDesktopSize)
223 nRects++;
224 if (needExtendedDesktopSize)
225 nRects++;
226 if (!extendedDesktopSizeMsgs.empty())
227 nRects += extendedDesktopSizeMsgs.size();
228
229 writeFramebufferUpdateStart(nRects);
230 writeNoDataRects();
231 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000232}
233
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100234void SMsgWriter::writeFramebufferUpdateStart(int nRects)
235{
236 startMsg(msgTypeFramebufferUpdate);
237 os->pad(1);
238
239 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100240 if (needSetDesktopName)
241 nRects++;
Pierre Ossman126e5642014-02-13 14:40:25 +0100242 if (needSetCursor)
243 nRects++;
244 if (needSetXCursor)
245 nRects++;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100246 if (needSetCursorWithAlpha)
247 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100248 }
249
250 os->writeU16(nRects);
251
252 nRectsInUpdate = 0;
253 if (nRects == 0xFFFF)
254 nRectsInHeader = 0;
255 else
256 nRectsInHeader = nRects;
257
258 writePseudoRects();
259}
260
261void SMsgWriter::writeFramebufferUpdateEnd()
262{
263 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
264 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
265 "nRects out of sync");
266
267 if (nRectsInHeader == 0) {
268 // Send last rect. marker
269 os->writeS16(0);
270 os->writeS16(0);
271 os->writeU16(0);
272 os->writeU16(0);
273 os->writeU32(pseudoEncodingLastRect);
274 }
275
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100276 endMsg();
277}
278
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000279void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
280{
281 startRect(r,encodingCopyRect);
282 os->writeU16(srcX);
283 os->writeU16(srcY);
284 endRect();
285}
286
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100287void SMsgWriter::startRect(const Rect& r, int encoding)
288{
289 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
290 throw Exception("SMsgWriter::startRect: nRects out of sync");
291
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100292 os->writeS16(r.tl.x);
293 os->writeS16(r.tl.y);
294 os->writeU16(r.width());
295 os->writeU16(r.height());
296 os->writeU32(encoding);
297}
298
299void SMsgWriter::endRect()
300{
Pierre Ossman35294682016-04-29 14:27:08 +0200301 os->flush();
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100302}
303
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100304void SMsgWriter::startMsg(int type)
305{
306 os->writeU8(type);
307}
308
309void SMsgWriter::endMsg()
310{
311 os->flush();
312}
313
314void SMsgWriter::writePseudoRects()
315{
Pierre Ossman126e5642014-02-13 14:40:25 +0100316 if (needSetCursor) {
Pierre Ossman126e5642014-02-13 14:40:25 +0100317 const Cursor& cursor = cp->cursor();
318
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100319 rdr::U8Array data(cursor.width()*cursor.height() * cp->pf().bpp/8);
320 rdr::U8Array mask(cursor.getMask());
321
322 const rdr::U8* in;
323 rdr::U8* out;
324
325 in = cursor.getBuffer();
326 out = data.buf;
327 for (int i = 0;i < cursor.width()*cursor.height();i++) {
328 cp->pf().bufferFromRGB(out, in, 1);
329 in += 4;
330 out += cp->pf().bpp/8;
331 }
Pierre Ossman126e5642014-02-13 14:40:25 +0100332
333 writeSetCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100334 cursor.hotspot().x, cursor.hotspot().y,
335 data.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100336 needSetCursor = false;
Pierre Ossman126e5642014-02-13 14:40:25 +0100337 }
338
339 if (needSetXCursor) {
340 const Cursor& cursor = cp->cursor();
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100341 rdr::U8Array bitmap(cursor.getBitmap());
342 rdr::U8Array mask(cursor.getMask());
Pierre Ossman126e5642014-02-13 14:40:25 +0100343
344 writeSetXCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100345 cursor.hotspot().x, cursor.hotspot().y,
346 bitmap.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100347 needSetXCursor = false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100348 }
349
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100350 if (needSetCursorWithAlpha) {
351 const Cursor& cursor = cp->cursor();
352
353 writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
354 cursor.hotspot().x, cursor.hotspot().y,
355 cursor.getBuffer());
356 needSetCursorWithAlpha = false;
357 }
358
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100359 if (needSetDesktopName) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100360 writeSetDesktopNameRect(cp->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100361 needSetDesktopName = false;
362 }
363}
364
365void SMsgWriter::writeNoDataRects()
366{
367 // Start with specific ExtendedDesktopSize messages
368 if (!extendedDesktopSizeMsgs.empty()) {
369 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100370
371 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100372 writeExtendedDesktopSizeRect(ri->reason, ri->result,
373 ri->fb_width, ri->fb_height, ri->layout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100374 }
375
376 extendedDesktopSizeMsgs.clear();
377 }
378
379 // Send this before SetDesktopSize to make life easier on the clients
380 if (needExtendedDesktopSize) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100381 writeExtendedDesktopSizeRect(0, 0, cp->width, cp->height,
382 cp->screenLayout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100383 needExtendedDesktopSize = false;
384 }
385
386 // Some clients assume this is the last rectangle so don't send anything
387 // more after this
388 if (needSetDesktopSize) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100389 writeSetDesktopSizeRect(cp->width, cp->height);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100390 needSetDesktopSize = false;
391 }
392}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100393
394void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
395{
396 if (!cp->supportsDesktopResize)
397 throw Exception("Client does not support desktop resize");
398 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
399 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
400
401 os->writeS16(0);
402 os->writeS16(0);
403 os->writeU16(width);
404 os->writeU16(height);
405 os->writeU32(pseudoEncodingDesktopSize);
406}
407
408void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
409 rdr::U16 result,
410 int fb_width,
411 int fb_height,
412 const ScreenSet& layout)
413{
414 ScreenSet::const_iterator si;
415
416 if (!cp->supportsExtendedDesktopSize)
417 throw Exception("Client does not support extended desktop resize");
418 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
419 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
420
421 os->writeU16(reason);
422 os->writeU16(result);
423 os->writeU16(fb_width);
424 os->writeU16(fb_height);
425 os->writeU32(pseudoEncodingExtendedDesktopSize);
426
427 os->writeU8(layout.num_screens());
428 os->pad(3);
429
430 for (si = layout.begin();si != layout.end();++si) {
431 os->writeU32(si->id);
432 os->writeU16(si->dimensions.tl.x);
433 os->writeU16(si->dimensions.tl.y);
434 os->writeU16(si->dimensions.width());
435 os->writeU16(si->dimensions.height());
436 os->writeU32(si->flags);
437 }
438}
439
440void SMsgWriter::writeSetDesktopNameRect(const char *name)
441{
442 if (!cp->supportsDesktopRename)
443 throw Exception("Client does not support desktop rename");
444 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
445 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
446
447 os->writeS16(0);
448 os->writeS16(0);
449 os->writeU16(0);
450 os->writeU16(0);
451 os->writeU32(pseudoEncodingDesktopName);
452 os->writeString(name);
453}
Pierre Ossman126e5642014-02-13 14:40:25 +0100454
455void SMsgWriter::writeSetCursorRect(int width, int height,
456 int hotspotX, int hotspotY,
457 const void* data, const void* mask)
458{
459 if (!cp->supportsLocalCursor)
460 throw Exception("Client does not support local cursors");
461 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
462 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
463
464 os->writeS16(hotspotX);
465 os->writeS16(hotspotY);
466 os->writeU16(width);
467 os->writeU16(height);
468 os->writeU32(pseudoEncodingCursor);
469 os->writeBytes(data, width * height * (cp->pf().bpp/8));
470 os->writeBytes(mask, (width+7)/8 * height);
471}
472
473void SMsgWriter::writeSetXCursorRect(int width, int height,
474 int hotspotX, int hotspotY,
Pierre Ossman126e5642014-02-13 14:40:25 +0100475 const void* data, const void* mask)
476{
477 if (!cp->supportsLocalXCursor)
478 throw Exception("Client does not support local cursors");
479 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
480 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
481
482 os->writeS16(hotspotX);
483 os->writeS16(hotspotY);
484 os->writeU16(width);
485 os->writeU16(height);
486 os->writeU32(pseudoEncodingXCursor);
487 if (width * height) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100488 os->writeU8(255);
489 os->writeU8(255);
490 os->writeU8(255);
491 os->writeU8(0);
492 os->writeU8(0);
493 os->writeU8(0);
Pierre Ossman126e5642014-02-13 14:40:25 +0100494 os->writeBytes(data, (width+7)/8 * height);
495 os->writeBytes(mask, (width+7)/8 * height);
496 }
497}
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100498
499void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
500 int hotspotX, int hotspotY,
501 const rdr::U8* data)
502{
503 if (!cp->supportsLocalCursorWithAlpha)
504 throw Exception("Client does not support local cursors");
505 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
506 throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
507
508 os->writeS16(hotspotX);
509 os->writeS16(hotspotY);
510 os->writeU16(width);
511 os->writeU16(height);
512 os->writeU32(pseudoEncodingCursorWithAlpha);
513
514 // FIXME: Use an encoder with compression?
515 os->writeU32(encodingRaw);
516
517 // Alpha needs to be pre-multiplied
518 for (int i = 0;i < width*height;i++) {
519 os->writeU8((unsigned)data[0] * data[3] / 255);
520 os->writeU8((unsigned)data[1] * data[3] / 255);
521 os->writeU8((unsigned)data[2] * data[3] / 255);
522 os->writeU8(data[3]);
523 data += 4;
524 }
525}