blob: 0867b107b37ec9bcdc3606e1bf5f2a6bdce5c823 [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
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000085bool SMsgWriterV3::writeSetDesktopSize() {
86 if (!cp->supportsDesktopResize) return false;
87 needSetDesktopSize = true;
88 return true;
89}
90
Pierre Ossman04e62db2009-03-23 16:57:07 +000091bool SMsgWriterV3::writeExtendedDesktopSize() {
Pierre Ossmanc5e25602009-03-20 12:59:05 +000092 if (!cp->supportsExtendedDesktopSize) return false;
Pierre Ossman04e62db2009-03-23 16:57:07 +000093 needExtendedDesktopSize = true;
94 return true;
95}
96
97bool SMsgWriterV3::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
98 int fb_width, int fb_height,
99 const ScreenSet& layout) {
100 ExtendedDesktopSizeMsg msg;
101
102 if (!cp->supportsExtendedDesktopSize) return false;
103
104 msg.reason = reason;
105 msg.result = result;
106 msg.fb_width = fb_width;
107 msg.fb_height = fb_height;
108 msg.layout = layout;
109
110 extendedDesktopSizeMsgs.push_back(msg);
111
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000112 return true;
113}
114
Peter Åstrandc39e0782009-01-15 12:21:42 +0000115bool SMsgWriterV3::writeSetDesktopName() {
116 if (!cp->supportsDesktopRename) return false;
117 needSetDesktopName = true;
118 return true;
119}
120
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000121void SMsgWriterV3::cursorChange(WriteSetCursorCallback* cb)
122{
123 wsccb = cb;
124}
125
126void SMsgWriterV3::writeSetCursor(int width, int height, const Point& hotspot,
127 void* data, void* mask)
128{
129 if (!wsccb) return;
130 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
131 throw Exception("SMsgWriterV3::writeSetCursor: nRects out of sync");
132 os->writeS16(hotspot.x);
133 os->writeS16(hotspot.y);
134 os->writeU16(width);
135 os->writeU16(height);
136 os->writeU32(pseudoEncodingCursor);
137 os->writeBytes(data, width * height * (cp->pf().bpp/8));
138 os->writeBytes(mask, (width+7)/8 * height);
139}
140
141void SMsgWriterV3::writeSetXCursor(int width, int height, int hotspotX,
142 int hotspotY, void* data, void* mask)
143{
144 if (!wsccb) return;
145 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
146 throw Exception("SMsgWriterV3::writeSetXCursor: nRects out of sync");
147 os->writeS16(hotspotX);
148 os->writeS16(hotspotY);
149 os->writeU16(width);
150 os->writeU16(height);
151 os->writeU32(pseudoEncodingXCursor);
152 // FIXME: We only support black and white cursors, currently. We
153 // could pass the correct color by using the pix0/pix1 values
154 // returned from getBitmap, in writeSetCursorCallback. However, we
155 // would then need to undo the conversion from rgb to Pixel that is
156 // done by FakeAllocColor.
157 if (width * height) {
158 os->writeU8(0);
159 os->writeU8(0);
160 os->writeU8(0);
161 os->writeU8(255);
162 os->writeU8(255);
163 os->writeU8(255);
164 os->writeBytes(data, (width+7)/8 * height);
165 os->writeBytes(mask, (width+7)/8 * height);
166 }
167}
168
Pierre Ossmane9962f72009-04-23 12:31:42 +0000169bool SMsgWriterV3::needFakeUpdate()
170{
171 return wsccb || needSetDesktopName || needNoDataUpdate();
172}
173
174bool SMsgWriterV3::needNoDataUpdate()
175{
176 return needSetDesktopSize || needExtendedDesktopSize ||
177 !extendedDesktopSizeMsgs.empty();
178}
179
180void SMsgWriterV3::writeNoDataUpdate()
181{
182 int nRects;
183
184 nRects = 0;
185
186 if (needSetDesktopSize)
187 nRects++;
188 if (needExtendedDesktopSize)
189 nRects++;
190 if (!extendedDesktopSizeMsgs.empty())
191 nRects += extendedDesktopSizeMsgs.size();
192
193 writeFramebufferUpdateStart(nRects);
194 writeNoDataRects();
195 writeFramebufferUpdateEnd();
196}
197
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
199{
200 startMsg(msgTypeFramebufferUpdate);
201 os->pad(1);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000202
DRCcd2c5d42011-08-11 11:18:34 +0000203 if (nRects != 0xFFFF) {
204 if (wsccb)
205 nRects++;
206 if (needSetDesktopName)
207 nRects++;
208 }
Pierre Ossmane9962f72009-04-23 12:31:42 +0000209
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000210 os->writeU16(nRects);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000211
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000212 nRectsInUpdate = 0;
DRCcd2c5d42011-08-11 11:18:34 +0000213 if (nRects == 0xFFFF)
214 nRectsInHeader = 0;
215 else
216 nRectsInHeader = nRects;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000217
218 writePseudoRects();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000219}
220
221void SMsgWriterV3::writeFramebufferUpdateStart()
222{
223 nRectsInUpdate = nRectsInHeader = 0;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000224
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225 if (!updateOS)
226 updateOS = new rdr::MemOutStream;
227 os = updateOS;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000228
229 writePseudoRects();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000230}
231
232void SMsgWriterV3::writeFramebufferUpdateEnd()
233{
Pierre Ossmane9962f72009-04-23 12:31:42 +0000234 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
235 throw Exception("SMsgWriterV3::writeFramebufferUpdateEnd: "
236 "nRects out of sync");
237
DRCcd2c5d42011-08-11 11:18:34 +0000238 if (nRectsInHeader == 0) {
239 // Send last rect. marker
240 os->writeS16(0);
241 os->writeS16(0);
242 os->writeU16(0);
243 os->writeU16(0);
244 os->writeU32(pseudoEncodingLastRect);
245 }
246
Pierre Ossmane9962f72009-04-23 12:31:42 +0000247 if (os == updateOS) {
248 os = realOS;
249 startMsg(msgTypeFramebufferUpdate);
250 os->pad(1);
251 os->writeU16(nRectsInUpdate);
252 os->writeBytes(updateOS->data(), updateOS->length());
253 updateOS->clear();
254 }
255
256 updatesSent++;
257 endMsg();
258}
259
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000260void SMsgWriterV3::startRect(const Rect& r, int encoding)
Pierre Ossmane9962f72009-04-23 12:31:42 +0000261{
262 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
263 throw Exception("SMsgWriterV3::startRect: nRects out of sync");
264
265 currentEncoding = encoding;
266 lenBeforeRect = os->length();
267 if (encoding != encodingCopyRect)
268 rawBytesEquivalent += 12 + r.width() * r.height() * (bpp()/8);
269
270 os->writeS16(r.tl.x);
271 os->writeS16(r.tl.y);
272 os->writeU16(r.width());
273 os->writeU16(r.height());
274 os->writeU32(encoding);
275}
276
277void SMsgWriterV3::endRect()
278{
279 if (currentEncoding <= encodingMax) {
280 bytesSent[currentEncoding] += os->length() - lenBeforeRect;
281 rectsSent[currentEncoding]++;
282 }
283}
284
285void SMsgWriterV3::writePseudoRects()
286{
287 if (wsccb) {
288 wsccb->writeSetCursorCallback();
289 wsccb = 0;
290 }
291
292 if (needSetDesktopName) {
293 if (!cp->supportsDesktopRename)
294 throw Exception("Client does not support desktop rename");
295 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
296 throw Exception("SMsgWriterV3 setDesktopName: nRects out of sync");
297
298 os->writeS16(0);
299 os->writeS16(0);
300 os->writeU16(0);
301 os->writeU16(0);
302 os->writeU32(pseudoEncodingDesktopName);
303 os->writeString(cp->name());
304
305 needSetDesktopName = false;
306 }
307}
308
309void SMsgWriterV3::writeNoDataRects()
310{
311 // Start with specific ExtendedDesktopSize messages
Pierre Ossman04e62db2009-03-23 16:57:07 +0000312 if (!extendedDesktopSizeMsgs.empty()) {
313 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
314 ScreenSet::const_iterator si;
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000315
316 if (!cp->supportsExtendedDesktopSize)
317 throw Exception("Client does not support extended desktop resize");
Pierre Ossman04e62db2009-03-23 16:57:07 +0000318 if ((nRectsInUpdate += extendedDesktopSizeMsgs.size()) > nRectsInHeader && nRectsInHeader)
319 throw Exception("SMsgWriterV3 SetDesktopSize reply: nRects out of sync");
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000320
Pierre Ossman04e62db2009-03-23 16:57:07 +0000321 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
322 os->writeU16(ri->reason);
323 os->writeU16(ri->result);
324 os->writeU16(ri->fb_width);
325 os->writeU16(ri->fb_height);
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000326 os->writeU32(pseudoEncodingExtendedDesktopSize);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000327
328 os->writeU8(ri->layout.num_screens());
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000329 os->pad(3);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000330
331 for (si = ri->layout.begin();si != ri->layout.end();++si) {
332 os->writeU32(si->id);
333 os->writeU16(si->dimensions.tl.x);
334 os->writeU16(si->dimensions.tl.y);
335 os->writeU16(si->dimensions.width());
336 os->writeU16(si->dimensions.height());
337 os->writeU32(si->flags);
338 }
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000339 }
340
Pierre Ossman04e62db2009-03-23 16:57:07 +0000341 extendedDesktopSizeMsgs.clear();
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000342 }
343
Pierre Ossmane9962f72009-04-23 12:31:42 +0000344 // Send this before SetDesktopSize to make life easier on the clients
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000345 if (needExtendedDesktopSize) {
346 if (!cp->supportsExtendedDesktopSize)
347 throw Exception("Client does not support extended desktop resize");
348 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
349 throw Exception("SMsgWriterV3 setExtendedDesktopSize: nRects out of sync");
Pierre Ossmane9962f72009-04-23 12:31:42 +0000350
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000351 os->writeU16(0);
352 os->writeU16(0);
353 os->writeU16(cp->width);
354 os->writeU16(cp->height);
355 os->writeU32(pseudoEncodingExtendedDesktopSize);
Pierre Ossman34e62f32009-03-20 21:46:12 +0000356
357 os->writeU8(cp->screenLayout.num_screens());
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000358 os->pad(3);
Pierre Ossman34e62f32009-03-20 21:46:12 +0000359
360 ScreenSet::const_iterator iter;
361 for (iter = cp->screenLayout.begin();iter != cp->screenLayout.end();++iter) {
362 os->writeU32(iter->id);
363 os->writeU16(iter->dimensions.tl.x);
364 os->writeU16(iter->dimensions.tl.y);
365 os->writeU16(iter->dimensions.width());
366 os->writeU16(iter->dimensions.height());
367 os->writeU32(iter->flags);
368 }
369
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000370 needExtendedDesktopSize = false;
371 }
372
Pierre Ossmane9962f72009-04-23 12:31:42 +0000373 // Some clients assume this is the last rectangle so don't send anything
374 // more after this
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000375 if (needSetDesktopSize) {
376 if (!cp->supportsDesktopResize)
377 throw Exception("Client does not support desktop resize");
378 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
379 throw Exception("SMsgWriterV3 setDesktopSize: nRects out of sync");
Pierre Ossmane9962f72009-04-23 12:31:42 +0000380
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000381 os->writeS16(0);
382 os->writeS16(0);
383 os->writeU16(cp->width);
384 os->writeU16(cp->height);
385 os->writeU32(pseudoEncodingDesktopSize);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000386
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000387 needSetDesktopSize = false;
388 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000389}
390