blob: 86f35078d12f55b6e286b397eb09dcaf22e08117 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossmanc754cce2011-11-14 15:44:11 +00002 * Copyright 2009-2011 Pierre Ossman for Cendio AB
DRCcd2c5d42011-08-11 11:18:34 +00003 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
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 <rdr/OutStream.h>
21#include <rdr/MemOutStream.h>
22#include <rfb/msgTypes.h>
Pierre Ossmanc5e25602009-03-20 12:59:05 +000023#include <rfb/screenTypes.h>
Pierre Ossmanc754cce2011-11-14 15:44:11 +000024#include <rfb/fenceTypes.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000025#include <rfb/Exception.h>
26#include <rfb/ConnParams.h>
27#include <rfb/SMsgWriterV3.h>
28
29using namespace rfb;
30
31SMsgWriterV3::SMsgWriterV3(ConnParams* cp, rdr::OutStream* os)
32 : SMsgWriter(cp, os), updateOS(0), realOS(os), nRectsInUpdate(0),
Adam Tkac0c6eff12009-04-02 15:44:23 +000033 nRectsInHeader(0), wsccb(0), needSetDesktopSize(false),
34 needExtendedDesktopSize(false), needSetDesktopName(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000035{
36}
37
38SMsgWriterV3::~SMsgWriterV3()
39{
40 delete updateOS;
41}
42
43void SMsgWriterV3::writeServerInit()
44{
45 os->writeU16(cp->width);
46 os->writeU16(cp->height);
47 cp->pf().write(os);
48 os->writeString(cp->name());
49 endMsg();
50}
51
52void SMsgWriterV3::startMsg(int type)
53{
54 if (os != realOS)
55 throw Exception("startMsg called while writing an update?");
56
57 os->writeU8(type);
58}
59
60void SMsgWriterV3::endMsg()
61{
62 os->flush();
63}
64
Pierre Ossmanc754cce2011-11-14 15:44:11 +000065void SMsgWriterV3::writeFence(rdr::U32 flags, unsigned len, const char data[])
66{
67 if (!cp->supportsFence)
68 throw Exception("Client does not support fences");
69 if (len > 64)
70 throw Exception("Too large fence payload");
71 if ((flags & ~fenceFlagsSupported) != 0)
72 throw Exception("Unknown fence flags");
73
74 startMsg(msgTypeServerFence);
75 os->pad(3);
76
77 os->writeU32(flags);
78
79 os->writeU8(len);
80 os->writeBytes(data, len);
81
82 endMsg();
83}
84
Pierre Ossmanc898d9a2011-11-14 16:22:23 +000085void SMsgWriterV3::writeEndOfContinuousUpdates()
86{
87 if (!cp->supportsContinuousUpdates)
88 throw Exception("Client does not support continuous updates");
89
90 startMsg(msgTypeEndOfContinuousUpdates);
91 endMsg();
92}
93
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000094bool SMsgWriterV3::writeSetDesktopSize() {
95 if (!cp->supportsDesktopResize) return false;
96 needSetDesktopSize = true;
97 return true;
98}
99
Pierre Ossman04e62db2009-03-23 16:57:07 +0000100bool SMsgWriterV3::writeExtendedDesktopSize() {
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000101 if (!cp->supportsExtendedDesktopSize) return false;
Pierre Ossman04e62db2009-03-23 16:57:07 +0000102 needExtendedDesktopSize = true;
103 return true;
104}
105
106bool SMsgWriterV3::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
107 int fb_width, int fb_height,
108 const ScreenSet& layout) {
109 ExtendedDesktopSizeMsg msg;
110
111 if (!cp->supportsExtendedDesktopSize) return false;
112
113 msg.reason = reason;
114 msg.result = result;
115 msg.fb_width = fb_width;
116 msg.fb_height = fb_height;
117 msg.layout = layout;
118
119 extendedDesktopSizeMsgs.push_back(msg);
120
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000121 return true;
122}
123
Peter Åstrandc39e0782009-01-15 12:21:42 +0000124bool SMsgWriterV3::writeSetDesktopName() {
125 if (!cp->supportsDesktopRename) return false;
126 needSetDesktopName = true;
127 return true;
128}
129
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000130void SMsgWriterV3::cursorChange(WriteSetCursorCallback* cb)
131{
132 wsccb = cb;
133}
134
135void SMsgWriterV3::writeSetCursor(int width, int height, const Point& hotspot,
136 void* data, void* mask)
137{
138 if (!wsccb) return;
139 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
140 throw Exception("SMsgWriterV3::writeSetCursor: nRects out of sync");
141 os->writeS16(hotspot.x);
142 os->writeS16(hotspot.y);
143 os->writeU16(width);
144 os->writeU16(height);
145 os->writeU32(pseudoEncodingCursor);
146 os->writeBytes(data, width * height * (cp->pf().bpp/8));
147 os->writeBytes(mask, (width+7)/8 * height);
148}
149
150void SMsgWriterV3::writeSetXCursor(int width, int height, int hotspotX,
151 int hotspotY, void* data, void* mask)
152{
153 if (!wsccb) return;
154 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
155 throw Exception("SMsgWriterV3::writeSetXCursor: nRects out of sync");
156 os->writeS16(hotspotX);
157 os->writeS16(hotspotY);
158 os->writeU16(width);
159 os->writeU16(height);
160 os->writeU32(pseudoEncodingXCursor);
161 // FIXME: We only support black and white cursors, currently. We
162 // could pass the correct color by using the pix0/pix1 values
163 // returned from getBitmap, in writeSetCursorCallback. However, we
164 // would then need to undo the conversion from rgb to Pixel that is
165 // done by FakeAllocColor.
166 if (width * height) {
167 os->writeU8(0);
168 os->writeU8(0);
169 os->writeU8(0);
170 os->writeU8(255);
171 os->writeU8(255);
172 os->writeU8(255);
173 os->writeBytes(data, (width+7)/8 * height);
174 os->writeBytes(mask, (width+7)/8 * height);
175 }
176}
177
Pierre Ossmane9962f72009-04-23 12:31:42 +0000178bool SMsgWriterV3::needFakeUpdate()
179{
180 return wsccb || needSetDesktopName || needNoDataUpdate();
181}
182
183bool SMsgWriterV3::needNoDataUpdate()
184{
185 return needSetDesktopSize || needExtendedDesktopSize ||
186 !extendedDesktopSizeMsgs.empty();
187}
188
189void SMsgWriterV3::writeNoDataUpdate()
190{
191 int nRects;
192
193 nRects = 0;
194
195 if (needSetDesktopSize)
196 nRects++;
197 if (needExtendedDesktopSize)
198 nRects++;
199 if (!extendedDesktopSizeMsgs.empty())
200 nRects += extendedDesktopSizeMsgs.size();
201
202 writeFramebufferUpdateStart(nRects);
203 writeNoDataRects();
204 writeFramebufferUpdateEnd();
205}
206
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
208{
209 startMsg(msgTypeFramebufferUpdate);
210 os->pad(1);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000211
DRCcd2c5d42011-08-11 11:18:34 +0000212 if (nRects != 0xFFFF) {
213 if (wsccb)
214 nRects++;
215 if (needSetDesktopName)
216 nRects++;
217 }
Pierre Ossmane9962f72009-04-23 12:31:42 +0000218
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000219 os->writeU16(nRects);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000220
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000221 nRectsInUpdate = 0;
DRCcd2c5d42011-08-11 11:18:34 +0000222 if (nRects == 0xFFFF)
223 nRectsInHeader = 0;
224 else
225 nRectsInHeader = nRects;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000226
227 writePseudoRects();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000228}
229
230void SMsgWriterV3::writeFramebufferUpdateStart()
231{
232 nRectsInUpdate = nRectsInHeader = 0;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000233
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000234 if (!updateOS)
235 updateOS = new rdr::MemOutStream;
236 os = updateOS;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000237
238 writePseudoRects();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000239}
240
241void SMsgWriterV3::writeFramebufferUpdateEnd()
242{
Pierre Ossmane9962f72009-04-23 12:31:42 +0000243 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
244 throw Exception("SMsgWriterV3::writeFramebufferUpdateEnd: "
245 "nRects out of sync");
246
DRCcd2c5d42011-08-11 11:18:34 +0000247 if (nRectsInHeader == 0) {
248 // Send last rect. marker
249 os->writeS16(0);
250 os->writeS16(0);
251 os->writeU16(0);
252 os->writeU16(0);
253 os->writeU32(pseudoEncodingLastRect);
254 }
255
Pierre Ossmane9962f72009-04-23 12:31:42 +0000256 if (os == updateOS) {
257 os = realOS;
258 startMsg(msgTypeFramebufferUpdate);
259 os->pad(1);
260 os->writeU16(nRectsInUpdate);
261 os->writeBytes(updateOS->data(), updateOS->length());
262 updateOS->clear();
263 }
264
265 updatesSent++;
266 endMsg();
267}
268
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000269void SMsgWriterV3::startRect(const Rect& r, int encoding)
Pierre Ossmane9962f72009-04-23 12:31:42 +0000270{
271 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
272 throw Exception("SMsgWriterV3::startRect: nRects out of sync");
273
274 currentEncoding = encoding;
275 lenBeforeRect = os->length();
276 if (encoding != encodingCopyRect)
277 rawBytesEquivalent += 12 + r.width() * r.height() * (bpp()/8);
278
279 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 SMsgWriterV3::endRect()
287{
288 if (currentEncoding <= encodingMax) {
289 bytesSent[currentEncoding] += os->length() - lenBeforeRect;
290 rectsSent[currentEncoding]++;
291 }
292}
293
294void SMsgWriterV3::writePseudoRects()
295{
296 if (wsccb) {
297 wsccb->writeSetCursorCallback();
298 wsccb = 0;
299 }
300
301 if (needSetDesktopName) {
302 if (!cp->supportsDesktopRename)
303 throw Exception("Client does not support desktop rename");
304 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
305 throw Exception("SMsgWriterV3 setDesktopName: nRects out of sync");
306
307 os->writeS16(0);
308 os->writeS16(0);
309 os->writeU16(0);
310 os->writeU16(0);
311 os->writeU32(pseudoEncodingDesktopName);
312 os->writeString(cp->name());
313
314 needSetDesktopName = false;
315 }
316}
317
318void SMsgWriterV3::writeNoDataRects()
319{
320 // Start with specific ExtendedDesktopSize messages
Pierre Ossman04e62db2009-03-23 16:57:07 +0000321 if (!extendedDesktopSizeMsgs.empty()) {
322 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
323 ScreenSet::const_iterator si;
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000324
325 if (!cp->supportsExtendedDesktopSize)
326 throw Exception("Client does not support extended desktop resize");
Pierre Ossman04e62db2009-03-23 16:57:07 +0000327 if ((nRectsInUpdate += extendedDesktopSizeMsgs.size()) > nRectsInHeader && nRectsInHeader)
328 throw Exception("SMsgWriterV3 SetDesktopSize reply: nRects out of sync");
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000329
Pierre Ossman04e62db2009-03-23 16:57:07 +0000330 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
331 os->writeU16(ri->reason);
332 os->writeU16(ri->result);
333 os->writeU16(ri->fb_width);
334 os->writeU16(ri->fb_height);
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000335 os->writeU32(pseudoEncodingExtendedDesktopSize);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000336
337 os->writeU8(ri->layout.num_screens());
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000338 os->pad(3);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000339
340 for (si = ri->layout.begin();si != ri->layout.end();++si) {
341 os->writeU32(si->id);
342 os->writeU16(si->dimensions.tl.x);
343 os->writeU16(si->dimensions.tl.y);
344 os->writeU16(si->dimensions.width());
345 os->writeU16(si->dimensions.height());
346 os->writeU32(si->flags);
347 }
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000348 }
349
Pierre Ossman04e62db2009-03-23 16:57:07 +0000350 extendedDesktopSizeMsgs.clear();
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000351 }
352
Pierre Ossmane9962f72009-04-23 12:31:42 +0000353 // Send this before SetDesktopSize to make life easier on the clients
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000354 if (needExtendedDesktopSize) {
355 if (!cp->supportsExtendedDesktopSize)
356 throw Exception("Client does not support extended desktop resize");
357 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
358 throw Exception("SMsgWriterV3 setExtendedDesktopSize: nRects out of sync");
Pierre Ossmane9962f72009-04-23 12:31:42 +0000359
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000360 os->writeU16(0);
361 os->writeU16(0);
362 os->writeU16(cp->width);
363 os->writeU16(cp->height);
364 os->writeU32(pseudoEncodingExtendedDesktopSize);
Pierre Ossman34e62f32009-03-20 21:46:12 +0000365
366 os->writeU8(cp->screenLayout.num_screens());
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000367 os->pad(3);
Pierre Ossman34e62f32009-03-20 21:46:12 +0000368
369 ScreenSet::const_iterator iter;
370 for (iter = cp->screenLayout.begin();iter != cp->screenLayout.end();++iter) {
371 os->writeU32(iter->id);
372 os->writeU16(iter->dimensions.tl.x);
373 os->writeU16(iter->dimensions.tl.y);
374 os->writeU16(iter->dimensions.width());
375 os->writeU16(iter->dimensions.height());
376 os->writeU32(iter->flags);
377 }
378
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000379 needExtendedDesktopSize = false;
380 }
381
Pierre Ossmane9962f72009-04-23 12:31:42 +0000382 // Some clients assume this is the last rectangle so don't send anything
383 // more after this
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000384 if (needSetDesktopSize) {
385 if (!cp->supportsDesktopResize)
386 throw Exception("Client does not support desktop resize");
387 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
388 throw Exception("SMsgWriterV3 setDesktopSize: nRects out of sync");
Pierre Ossmane9962f72009-04-23 12:31:42 +0000389
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000390 os->writeS16(0);
391 os->writeS16(0);
392 os->writeU16(cp->width);
393 os->writeU16(cp->height);
394 os->writeU32(pseudoEncodingDesktopSize);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000395
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000396 needSetDesktopSize = false;
397 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000398}
399