blob: e42150864586ffc1beb88e179ca3ed35bbe75f94 [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 Ossman7638e9c2014-01-16 13:12:40 +01003 * Copyright 2009-2014 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 Ossman20dd2a92015-02-11 17:43:15 +010039 needSetDesktopName(false), needSetCursor(false), needSetXCursor(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000040{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000041}
42
43SMsgWriter::~SMsgWriter()
44{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000045}
46
Pierre Ossman7638e9c2014-01-16 13:12:40 +010047void SMsgWriter::writeServerInit()
48{
49 os->writeU16(cp->width);
50 os->writeU16(cp->height);
51 cp->pf().write(os);
52 os->writeString(cp->name());
53 endMsg();
54}
55
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000056void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010057 const rdr::U16 red[],
58 const rdr::U16 green[],
59 const rdr::U16 blue[])
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000060{
61 startMsg(msgTypeSetColourMapEntries);
62 os->pad(1);
63 os->writeU16(firstColour);
64 os->writeU16(nColours);
65 for (int i = firstColour; i < firstColour+nColours; i++) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010066 os->writeU16(red[i]);
67 os->writeU16(green[i]);
68 os->writeU16(blue[i]);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000069 }
70 endMsg();
71}
72
73void SMsgWriter::writeBell()
74{
75 startMsg(msgTypeBell);
76 endMsg();
77}
78
79void SMsgWriter::writeServerCutText(const char* str, int len)
80{
81 startMsg(msgTypeServerCutText);
82 os->pad(3);
83 os->writeU32(len);
84 os->writeBytes(str, len);
85 endMsg();
86}
87
Pierre Ossman7638e9c2014-01-16 13:12:40 +010088void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
89{
90 if (!cp->supportsFence)
91 throw Exception("Client does not support fences");
92 if (len > 64)
93 throw Exception("Too large fence payload");
94 if ((flags & ~fenceFlagsSupported) != 0)
95 throw Exception("Unknown fence flags");
96
97 startMsg(msgTypeServerFence);
98 os->pad(3);
99
100 os->writeU32(flags);
101
102 os->writeU8(len);
103 os->writeBytes(data, len);
104
105 endMsg();
106}
107
108void SMsgWriter::writeEndOfContinuousUpdates()
109{
110 if (!cp->supportsContinuousUpdates)
111 throw Exception("Client does not support continuous updates");
112
113 startMsg(msgTypeEndOfContinuousUpdates);
114 endMsg();
115}
116
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100117bool SMsgWriter::writeSetDesktopSize() {
118 if (!cp->supportsDesktopResize)
119 return false;
120
121 needSetDesktopSize = true;
122
123 return true;
124}
125
126bool SMsgWriter::writeExtendedDesktopSize() {
127 if (!cp->supportsExtendedDesktopSize)
128 return false;
129
130 needExtendedDesktopSize = true;
131
132 return true;
133}
134
135bool SMsgWriter::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
136 int fb_width, int fb_height,
137 const ScreenSet& layout) {
138 ExtendedDesktopSizeMsg msg;
139
140 if (!cp->supportsExtendedDesktopSize)
141 return false;
142
143 msg.reason = reason;
144 msg.result = result;
145 msg.fb_width = fb_width;
146 msg.fb_height = fb_height;
147 msg.layout = layout;
148
149 extendedDesktopSizeMsgs.push_back(msg);
150
151 return true;
152}
153
154bool SMsgWriter::writeSetDesktopName() {
155 if (!cp->supportsDesktopRename)
156 return false;
157
158 needSetDesktopName = true;
159
160 return true;
161}
162
Pierre Ossman126e5642014-02-13 14:40:25 +0100163bool SMsgWriter::writeSetCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100164{
Pierre Ossman126e5642014-02-13 14:40:25 +0100165 if (!cp->supportsLocalCursor)
166 return false;
167
168 needSetCursor = true;
169
170 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100171}
172
Pierre Ossman126e5642014-02-13 14:40:25 +0100173bool SMsgWriter::writeSetXCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100174{
Pierre Ossman126e5642014-02-13 14:40:25 +0100175 if (!cp->supportsLocalXCursor)
176 return false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100177
Pierre Ossman126e5642014-02-13 14:40:25 +0100178 needSetXCursor = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100179
Pierre Ossman126e5642014-02-13 14:40:25 +0100180 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100181}
182
Pierre Ossmane9962f72009-04-23 12:31:42 +0000183bool SMsgWriter::needFakeUpdate()
184{
Pierre Ossman126e5642014-02-13 14:40:25 +0100185 if (needSetDesktopName)
186 return true;
187 if (needSetCursor || needSetXCursor)
188 return true;
189 if (needNoDataUpdate())
190 return true;
191
192 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000193}
194
Pierre Ossmane9962f72009-04-23 12:31:42 +0000195bool SMsgWriter::needNoDataUpdate()
196{
Pierre Ossman126e5642014-02-13 14:40:25 +0100197 if (needSetDesktopSize)
198 return true;
199 if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty())
200 return true;
201
202 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000203}
204
205void SMsgWriter::writeNoDataUpdate()
206{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100207 int nRects;
208
209 nRects = 0;
210
211 if (needSetDesktopSize)
212 nRects++;
213 if (needExtendedDesktopSize)
214 nRects++;
215 if (!extendedDesktopSizeMsgs.empty())
216 nRects += extendedDesktopSizeMsgs.size();
217
218 writeFramebufferUpdateStart(nRects);
219 writeNoDataRects();
220 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000221}
222
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100223void SMsgWriter::writeFramebufferUpdateStart(int nRects)
224{
225 startMsg(msgTypeFramebufferUpdate);
226 os->pad(1);
227
228 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100229 if (needSetDesktopName)
230 nRects++;
Pierre Ossman126e5642014-02-13 14:40:25 +0100231 if (needSetCursor)
232 nRects++;
233 if (needSetXCursor)
234 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100235 }
236
237 os->writeU16(nRects);
238
239 nRectsInUpdate = 0;
240 if (nRects == 0xFFFF)
241 nRectsInHeader = 0;
242 else
243 nRectsInHeader = nRects;
244
245 writePseudoRects();
246}
247
248void SMsgWriter::writeFramebufferUpdateEnd()
249{
250 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
251 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
252 "nRects out of sync");
253
254 if (nRectsInHeader == 0) {
255 // Send last rect. marker
256 os->writeS16(0);
257 os->writeS16(0);
258 os->writeU16(0);
259 os->writeU16(0);
260 os->writeU32(pseudoEncodingLastRect);
261 }
262
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100263 endMsg();
264}
265
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000266void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
267{
268 startRect(r,encodingCopyRect);
269 os->writeU16(srcX);
270 os->writeU16(srcY);
271 endRect();
272}
273
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100274void SMsgWriter::startRect(const Rect& r, int encoding)
275{
276 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
277 throw Exception("SMsgWriter::startRect: nRects out of sync");
278
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100279 os->writeS16(r.tl.x);
280 os->writeS16(r.tl.y);
281 os->writeU16(r.width());
282 os->writeU16(r.height());
283 os->writeU32(encoding);
284}
285
286void SMsgWriter::endRect()
287{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100288}
289
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100290void SMsgWriter::startMsg(int type)
291{
292 os->writeU8(type);
293}
294
295void SMsgWriter::endMsg()
296{
297 os->flush();
298}
299
300void SMsgWriter::writePseudoRects()
301{
Pierre Ossman126e5642014-02-13 14:40:25 +0100302 if (needSetCursor) {
303 rdr::U8* data;
Pierre Ossman126e5642014-02-13 14:40:25 +0100304
305 const Cursor& cursor = cp->cursor();
306
307 data = new rdr::U8[cursor.area() * cp->pf().bpp/8];
308 cursor.getImage(cp->pf(), data, cursor.getRect());
309
310 writeSetCursorRect(cursor.width(), cursor.height(),
311 cursor.hotspot.x, cursor.hotspot.y,
312 data, cursor.mask.buf);
313 needSetCursor = false;
314
315 delete [] data;
316 }
317
318 if (needSetXCursor) {
319 const Cursor& cursor = cp->cursor();
320 Pixel pix0, pix1;
321 rdr::U8 rgb0[3], rgb1[3];
322 rdr::U8Array bitmap(cursor.getBitmap(&pix0, &pix1));
323
324 if (!bitmap.buf) {
325 // FIXME: We could reduce to two colors.
326 throw Exception("SMsgWriter::writePseudoRects: Unable to send multicolor cursor: RichCursor not supported by client");
327 }
328
329 cp->pf().rgbFromPixel(pix0, &rgb0[0], &rgb0[1], &rgb0[2]);
330 cp->pf().rgbFromPixel(pix1, &rgb1[0], &rgb1[1], &rgb1[2]);
331
332 writeSetXCursorRect(cursor.width(), cursor.height(),
333 cursor.hotspot.x, cursor.hotspot.y,
334 rgb0, rgb1, bitmap.buf, cursor.mask.buf);
335 needSetXCursor = false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100336 }
337
338 if (needSetDesktopName) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100339 writeSetDesktopNameRect(cp->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100340 needSetDesktopName = false;
341 }
342}
343
344void SMsgWriter::writeNoDataRects()
345{
346 // Start with specific ExtendedDesktopSize messages
347 if (!extendedDesktopSizeMsgs.empty()) {
348 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100349
350 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100351 writeExtendedDesktopSizeRect(ri->reason, ri->result,
352 ri->fb_width, ri->fb_height, ri->layout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100353 }
354
355 extendedDesktopSizeMsgs.clear();
356 }
357
358 // Send this before SetDesktopSize to make life easier on the clients
359 if (needExtendedDesktopSize) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100360 writeExtendedDesktopSizeRect(0, 0, cp->width, cp->height,
361 cp->screenLayout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100362 needExtendedDesktopSize = false;
363 }
364
365 // Some clients assume this is the last rectangle so don't send anything
366 // more after this
367 if (needSetDesktopSize) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100368 writeSetDesktopSizeRect(cp->width, cp->height);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100369 needSetDesktopSize = false;
370 }
371}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100372
373void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
374{
375 if (!cp->supportsDesktopResize)
376 throw Exception("Client does not support desktop resize");
377 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
378 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
379
380 os->writeS16(0);
381 os->writeS16(0);
382 os->writeU16(width);
383 os->writeU16(height);
384 os->writeU32(pseudoEncodingDesktopSize);
385}
386
387void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
388 rdr::U16 result,
389 int fb_width,
390 int fb_height,
391 const ScreenSet& layout)
392{
393 ScreenSet::const_iterator si;
394
395 if (!cp->supportsExtendedDesktopSize)
396 throw Exception("Client does not support extended desktop resize");
397 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
398 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
399
400 os->writeU16(reason);
401 os->writeU16(result);
402 os->writeU16(fb_width);
403 os->writeU16(fb_height);
404 os->writeU32(pseudoEncodingExtendedDesktopSize);
405
406 os->writeU8(layout.num_screens());
407 os->pad(3);
408
409 for (si = layout.begin();si != layout.end();++si) {
410 os->writeU32(si->id);
411 os->writeU16(si->dimensions.tl.x);
412 os->writeU16(si->dimensions.tl.y);
413 os->writeU16(si->dimensions.width());
414 os->writeU16(si->dimensions.height());
415 os->writeU32(si->flags);
416 }
417}
418
419void SMsgWriter::writeSetDesktopNameRect(const char *name)
420{
421 if (!cp->supportsDesktopRename)
422 throw Exception("Client does not support desktop rename");
423 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
424 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
425
426 os->writeS16(0);
427 os->writeS16(0);
428 os->writeU16(0);
429 os->writeU16(0);
430 os->writeU32(pseudoEncodingDesktopName);
431 os->writeString(name);
432}
Pierre Ossman126e5642014-02-13 14:40:25 +0100433
434void SMsgWriter::writeSetCursorRect(int width, int height,
435 int hotspotX, int hotspotY,
436 const void* data, const void* mask)
437{
438 if (!cp->supportsLocalCursor)
439 throw Exception("Client does not support local cursors");
440 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
441 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
442
443 os->writeS16(hotspotX);
444 os->writeS16(hotspotY);
445 os->writeU16(width);
446 os->writeU16(height);
447 os->writeU32(pseudoEncodingCursor);
448 os->writeBytes(data, width * height * (cp->pf().bpp/8));
449 os->writeBytes(mask, (width+7)/8 * height);
450}
451
452void SMsgWriter::writeSetXCursorRect(int width, int height,
453 int hotspotX, int hotspotY,
454 const rdr::U8 pix0[],
455 const rdr::U8 pix1[],
456 const void* data, const void* mask)
457{
458 if (!cp->supportsLocalXCursor)
459 throw Exception("Client does not support local cursors");
460 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
461 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
462
463 os->writeS16(hotspotX);
464 os->writeS16(hotspotY);
465 os->writeU16(width);
466 os->writeU16(height);
467 os->writeU32(pseudoEncodingXCursor);
468 if (width * height) {
469 os->writeU8(pix0[0]);
470 os->writeU8(pix0[1]);
471 os->writeU8(pix0[2]);
472 os->writeU8(pix1[0]);
473 os->writeU8(pix1[1]);
474 os->writeU8(pix1[2]);
475 os->writeBytes(data, (width+7)/8 * height);
476 os->writeBytes(mask, (width+7)/8 * height);
477 }
478}