blob: 40e6d7f692f47c5a0ceccadf7161767a5232e25c [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>
21#include <assert.h>
22#include <rdr/OutStream.h>
23#include <rfb/msgTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010024#include <rfb/fenceTypes.h>
25#include <rfb/Exception.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/ConnParams.h>
27#include <rfb/UpdateTracker.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010028#include <rfb/Encoder.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000029#include <rfb/SMsgWriter.h>
30#include <rfb/LogWriter.h>
31
32using namespace rfb;
33
34static LogWriter vlog("SMsgWriter");
35
36SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
Pierre Ossman7638e9c2014-01-16 13:12:40 +010037 : imageBufIdealSize(0), cp(cp_), os(os_), currentEncoding(0),
38 nRectsInUpdate(0), nRectsInHeader(0),
39 wsccb(0), needSetDesktopSize(false),
40 needExtendedDesktopSize(false), needSetDesktopName(false),
41 lenBeforeRect(0), updatesSent(0), rawBytesEquivalent(0),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000042 imageBuf(0), imageBufSize(0)
43{
Peter Åstrand98fe98c2010-02-10 07:43:02 +000044 for (int i = 0; i <= encodingMax; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000045 bytesSent[i] = 0;
46 rectsSent[i] = 0;
47 }
48}
49
50SMsgWriter::~SMsgWriter()
51{
52 vlog.info("framebuffer updates %d",updatesSent);
53 int bytes = 0;
Peter Åstrand98fe98c2010-02-10 07:43:02 +000054 for (int i = 0; i <= encodingMax; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000055 if (i != encodingCopyRect)
56 bytes += bytesSent[i];
57 if (rectsSent[i])
58 vlog.info(" %s rects %d, bytes %d",
59 encodingName(i), rectsSent[i], bytesSent[i]);
60 }
DRC887c5fd2011-08-19 03:13:47 +000061 vlog.info(" raw bytes equivalent %llu, compression ratio %f",
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000062 rawBytesEquivalent, (double)rawBytesEquivalent / bytes);
63 delete [] imageBuf;
64}
65
Pierre Ossman7638e9c2014-01-16 13:12:40 +010066void SMsgWriter::writeServerInit()
67{
68 os->writeU16(cp->width);
69 os->writeU16(cp->height);
70 cp->pf().write(os);
71 os->writeString(cp->name());
72 endMsg();
73}
74
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000075void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010076 const rdr::U16 red[],
77 const rdr::U16 green[],
78 const rdr::U16 blue[])
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000079{
80 startMsg(msgTypeSetColourMapEntries);
81 os->pad(1);
82 os->writeU16(firstColour);
83 os->writeU16(nColours);
84 for (int i = firstColour; i < firstColour+nColours; i++) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010085 os->writeU16(red[i]);
86 os->writeU16(green[i]);
87 os->writeU16(blue[i]);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000088 }
89 endMsg();
90}
91
92void SMsgWriter::writeBell()
93{
94 startMsg(msgTypeBell);
95 endMsg();
96}
97
98void SMsgWriter::writeServerCutText(const char* str, int len)
99{
100 startMsg(msgTypeServerCutText);
101 os->pad(3);
102 os->writeU32(len);
103 os->writeBytes(str, len);
104 endMsg();
105}
106
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100107void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
108{
109 if (!cp->supportsFence)
110 throw Exception("Client does not support fences");
111 if (len > 64)
112 throw Exception("Too large fence payload");
113 if ((flags & ~fenceFlagsSupported) != 0)
114 throw Exception("Unknown fence flags");
115
116 startMsg(msgTypeServerFence);
117 os->pad(3);
118
119 os->writeU32(flags);
120
121 os->writeU8(len);
122 os->writeBytes(data, len);
123
124 endMsg();
125}
126
127void SMsgWriter::writeEndOfContinuousUpdates()
128{
129 if (!cp->supportsContinuousUpdates)
130 throw Exception("Client does not support continuous updates");
131
132 startMsg(msgTypeEndOfContinuousUpdates);
133 endMsg();
134}
135
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100136bool SMsgWriter::writeSetDesktopSize() {
137 if (!cp->supportsDesktopResize)
138 return false;
139
140 needSetDesktopSize = true;
141
142 return true;
143}
144
145bool SMsgWriter::writeExtendedDesktopSize() {
146 if (!cp->supportsExtendedDesktopSize)
147 return false;
148
149 needExtendedDesktopSize = true;
150
151 return true;
152}
153
154bool SMsgWriter::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
155 int fb_width, int fb_height,
156 const ScreenSet& layout) {
157 ExtendedDesktopSizeMsg msg;
158
159 if (!cp->supportsExtendedDesktopSize)
160 return false;
161
162 msg.reason = reason;
163 msg.result = result;
164 msg.fb_width = fb_width;
165 msg.fb_height = fb_height;
166 msg.layout = layout;
167
168 extendedDesktopSizeMsgs.push_back(msg);
169
170 return true;
171}
172
173bool SMsgWriter::writeSetDesktopName() {
174 if (!cp->supportsDesktopRename)
175 return false;
176
177 needSetDesktopName = true;
178
179 return true;
180}
181
182void SMsgWriter::cursorChange(WriteSetCursorCallback* cb)
183{
184 wsccb = cb;
185}
186
187void SMsgWriter::writeSetCursor(int width, int height, const Point& hotspot,
188 void* data, void* mask)
189{
190 if (!wsccb)
191 return;
192
193 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
194 throw Exception("SMsgWriter::writeSetCursor: nRects out of sync");
195
196 os->writeS16(hotspot.x);
197 os->writeS16(hotspot.y);
198 os->writeU16(width);
199 os->writeU16(height);
200 os->writeU32(pseudoEncodingCursor);
201 os->writeBytes(data, width * height * (cp->pf().bpp/8));
202 os->writeBytes(mask, (width+7)/8 * height);
203}
204
205void SMsgWriter::writeSetXCursor(int width, int height, int hotspotX,
206 int hotspotY, void* data, void* mask)
207{
208 if (!wsccb)
209 return;
210
211 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
212 throw Exception("SMsgWriter::writeSetXCursor: nRects out of sync");
213
214 os->writeS16(hotspotX);
215 os->writeS16(hotspotY);
216 os->writeU16(width);
217 os->writeU16(height);
218 os->writeU32(pseudoEncodingXCursor);
219 // FIXME: We only support black and white cursors, currently. We
220 // could pass the correct color by using the pix0/pix1 values
221 // returned from getBitmap, in writeSetCursorCallback. However, we
222 // would then need to undo the conversion from rgb to Pixel that is
223 // done by FakeAllocColor.
224 if (width * height) {
225 os->writeU8(0);
226 os->writeU8(0);
227 os->writeU8(0);
228 os->writeU8(255);
229 os->writeU8(255);
230 os->writeU8(255);
231 os->writeBytes(data, (width+7)/8 * height);
232 os->writeBytes(mask, (width+7)/8 * height);
233 }
234}
235
Pierre Ossmane9962f72009-04-23 12:31:42 +0000236bool SMsgWriter::needFakeUpdate()
237{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100238 return wsccb || needSetDesktopName || needNoDataUpdate();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000239}
240
Pierre Ossmane9962f72009-04-23 12:31:42 +0000241bool SMsgWriter::needNoDataUpdate()
242{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100243 return needSetDesktopSize || needExtendedDesktopSize ||
244 !extendedDesktopSizeMsgs.empty();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000245}
246
247void SMsgWriter::writeNoDataUpdate()
248{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100249 int nRects;
250
251 nRects = 0;
252
253 if (needSetDesktopSize)
254 nRects++;
255 if (needExtendedDesktopSize)
256 nRects++;
257 if (!extendedDesktopSizeMsgs.empty())
258 nRects += extendedDesktopSizeMsgs.size();
259
260 writeFramebufferUpdateStart(nRects);
261 writeNoDataRects();
262 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000263}
264
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100265void SMsgWriter::writeFramebufferUpdateStart(int nRects)
266{
267 startMsg(msgTypeFramebufferUpdate);
268 os->pad(1);
269
270 if (nRects != 0xFFFF) {
271 if (wsccb)
272 nRects++;
273 if (needSetDesktopName)
274 nRects++;
275 }
276
277 os->writeU16(nRects);
278
279 nRectsInUpdate = 0;
280 if (nRects == 0xFFFF)
281 nRectsInHeader = 0;
282 else
283 nRectsInHeader = nRects;
284
285 writePseudoRects();
286}
287
288void SMsgWriter::writeFramebufferUpdateEnd()
289{
290 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
291 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
292 "nRects out of sync");
293
294 if (nRectsInHeader == 0) {
295 // Send last rect. marker
296 os->writeS16(0);
297 os->writeS16(0);
298 os->writeU16(0);
299 os->writeU16(0);
300 os->writeU32(pseudoEncodingLastRect);
301 }
302
303 updatesSent++;
304 endMsg();
305}
306
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000307void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
308{
309 startRect(r,encodingCopyRect);
310 os->writeU16(srcX);
311 os->writeU16(srcY);
312 endRect();
313}
314
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100315void SMsgWriter::startRect(const Rect& r, int encoding)
316{
317 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
318 throw Exception("SMsgWriter::startRect: nRects out of sync");
319
320 currentEncoding = encoding;
321 lenBeforeRect = os->length();
322 if (encoding != encodingCopyRect)
Pierre Ossman668468b2014-01-31 12:37:32 +0100323 rawBytesEquivalent += 12 + r.width() * r.height() * (cp->pf().bpp/8);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100324
325 os->writeS16(r.tl.x);
326 os->writeS16(r.tl.y);
327 os->writeU16(r.width());
328 os->writeU16(r.height());
329 os->writeU32(encoding);
330}
331
332void SMsgWriter::endRect()
333{
334 if (currentEncoding <= encodingMax) {
335 bytesSent[currentEncoding] += os->length() - lenBeforeRect;
336 rectsSent[currentEncoding]++;
337 }
338}
339
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000340rdr::U8* SMsgWriter::getImageBuf(int required, int requested, int* nPixels)
341{
342 int requiredBytes = required * (cp->pf().bpp / 8);
343 int requestedBytes = requested * (cp->pf().bpp / 8);
344 int size = requestedBytes;
345 if (size > imageBufIdealSize) size = imageBufIdealSize;
346
347 if (size < requiredBytes)
348 size = requiredBytes;
349
350 if (imageBufSize < size) {
351 imageBufSize = size;
352 delete [] imageBuf;
353 imageBuf = new rdr::U8[imageBufSize];
354 }
355 if (nPixels)
356 *nPixels = imageBufSize / (cp->pf().bpp / 8);
357 return imageBuf;
358}
359
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100360void SMsgWriter::startMsg(int type)
361{
362 os->writeU8(type);
363}
364
365void SMsgWriter::endMsg()
366{
367 os->flush();
368}
369
370void SMsgWriter::writePseudoRects()
371{
372 if (wsccb) {
373 wsccb->writeSetCursorCallback();
374 wsccb = 0;
375 }
376
377 if (needSetDesktopName) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100378 writeSetDesktopNameRect(cp->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100379 needSetDesktopName = false;
380 }
381}
382
383void SMsgWriter::writeNoDataRects()
384{
385 // Start with specific ExtendedDesktopSize messages
386 if (!extendedDesktopSizeMsgs.empty()) {
387 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100388
389 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100390 writeExtendedDesktopSizeRect(ri->reason, ri->result,
391 ri->fb_width, ri->fb_height, ri->layout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100392 }
393
394 extendedDesktopSizeMsgs.clear();
395 }
396
397 // Send this before SetDesktopSize to make life easier on the clients
398 if (needExtendedDesktopSize) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100399 writeExtendedDesktopSizeRect(0, 0, cp->width, cp->height,
400 cp->screenLayout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100401 needExtendedDesktopSize = false;
402 }
403
404 // Some clients assume this is the last rectangle so don't send anything
405 // more after this
406 if (needSetDesktopSize) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100407 writeSetDesktopSizeRect(cp->width, cp->height);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100408 needSetDesktopSize = false;
409 }
410}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100411
412void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
413{
414 if (!cp->supportsDesktopResize)
415 throw Exception("Client does not support desktop resize");
416 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
417 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
418
419 os->writeS16(0);
420 os->writeS16(0);
421 os->writeU16(width);
422 os->writeU16(height);
423 os->writeU32(pseudoEncodingDesktopSize);
424}
425
426void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
427 rdr::U16 result,
428 int fb_width,
429 int fb_height,
430 const ScreenSet& layout)
431{
432 ScreenSet::const_iterator si;
433
434 if (!cp->supportsExtendedDesktopSize)
435 throw Exception("Client does not support extended desktop resize");
436 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
437 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
438
439 os->writeU16(reason);
440 os->writeU16(result);
441 os->writeU16(fb_width);
442 os->writeU16(fb_height);
443 os->writeU32(pseudoEncodingExtendedDesktopSize);
444
445 os->writeU8(layout.num_screens());
446 os->pad(3);
447
448 for (si = layout.begin();si != layout.end();++si) {
449 os->writeU32(si->id);
450 os->writeU16(si->dimensions.tl.x);
451 os->writeU16(si->dimensions.tl.y);
452 os->writeU16(si->dimensions.width());
453 os->writeU16(si->dimensions.height());
454 os->writeU32(si->flags);
455 }
456}
457
458void SMsgWriter::writeSetDesktopNameRect(const char *name)
459{
460 if (!cp->supportsDesktopRename)
461 throw Exception("Client does not support desktop rename");
462 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
463 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
464
465 os->writeS16(0);
466 os->writeS16(0);
467 os->writeU16(0);
468 os->writeU16(0);
469 os->writeU32(pseudoEncodingDesktopName);
470 os->writeString(name);
471}