blob: af1187ebfe82b6d66e8cce743df54bb7086fe83b [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossmanc5e25602009-03-20 12:59:05 +00002 * Copyright 2009 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>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024#include <rfb/Exception.h>
25#include <rfb/ConnParams.h>
26#include <rfb/SMsgWriterV3.h>
27
28using namespace rfb;
29
30SMsgWriterV3::SMsgWriterV3(ConnParams* cp, rdr::OutStream* os)
31 : SMsgWriter(cp, os), updateOS(0), realOS(os), nRectsInUpdate(0),
Adam Tkac0c6eff12009-04-02 15:44:23 +000032 nRectsInHeader(0), wsccb(0), needSetDesktopSize(false),
33 needExtendedDesktopSize(false), needSetDesktopName(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000034{
35}
36
37SMsgWriterV3::~SMsgWriterV3()
38{
39 delete updateOS;
40}
41
42void SMsgWriterV3::writeServerInit()
43{
44 os->writeU16(cp->width);
45 os->writeU16(cp->height);
46 cp->pf().write(os);
47 os->writeString(cp->name());
48 endMsg();
49}
50
51void SMsgWriterV3::startMsg(int type)
52{
53 if (os != realOS)
54 throw Exception("startMsg called while writing an update?");
55
56 os->writeU8(type);
57}
58
59void SMsgWriterV3::endMsg()
60{
61 os->flush();
62}
63
64bool SMsgWriterV3::writeSetDesktopSize() {
65 if (!cp->supportsDesktopResize) return false;
66 needSetDesktopSize = true;
67 return true;
68}
69
Pierre Ossman04e62db2009-03-23 16:57:07 +000070bool SMsgWriterV3::writeExtendedDesktopSize() {
Pierre Ossmanc5e25602009-03-20 12:59:05 +000071 if (!cp->supportsExtendedDesktopSize) return false;
Pierre Ossman04e62db2009-03-23 16:57:07 +000072 needExtendedDesktopSize = true;
73 return true;
74}
75
76bool SMsgWriterV3::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
77 int fb_width, int fb_height,
78 const ScreenSet& layout) {
79 ExtendedDesktopSizeMsg msg;
80
81 if (!cp->supportsExtendedDesktopSize) return false;
82
83 msg.reason = reason;
84 msg.result = result;
85 msg.fb_width = fb_width;
86 msg.fb_height = fb_height;
87 msg.layout = layout;
88
89 extendedDesktopSizeMsgs.push_back(msg);
90
Pierre Ossmanc5e25602009-03-20 12:59:05 +000091 return true;
92}
93
Peter Åstrandc39e0782009-01-15 12:21:42 +000094bool SMsgWriterV3::writeSetDesktopName() {
95 if (!cp->supportsDesktopRename) return false;
96 needSetDesktopName = true;
97 return true;
98}
99
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000100void SMsgWriterV3::cursorChange(WriteSetCursorCallback* cb)
101{
102 wsccb = cb;
103}
104
105void SMsgWriterV3::writeSetCursor(int width, int height, const Point& hotspot,
106 void* data, void* mask)
107{
108 if (!wsccb) return;
109 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
110 throw Exception("SMsgWriterV3::writeSetCursor: nRects out of sync");
111 os->writeS16(hotspot.x);
112 os->writeS16(hotspot.y);
113 os->writeU16(width);
114 os->writeU16(height);
115 os->writeU32(pseudoEncodingCursor);
116 os->writeBytes(data, width * height * (cp->pf().bpp/8));
117 os->writeBytes(mask, (width+7)/8 * height);
118}
119
120void SMsgWriterV3::writeSetXCursor(int width, int height, int hotspotX,
121 int hotspotY, void* data, void* mask)
122{
123 if (!wsccb) return;
124 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
125 throw Exception("SMsgWriterV3::writeSetXCursor: nRects out of sync");
126 os->writeS16(hotspotX);
127 os->writeS16(hotspotY);
128 os->writeU16(width);
129 os->writeU16(height);
130 os->writeU32(pseudoEncodingXCursor);
131 // FIXME: We only support black and white cursors, currently. We
132 // could pass the correct color by using the pix0/pix1 values
133 // returned from getBitmap, in writeSetCursorCallback. However, we
134 // would then need to undo the conversion from rgb to Pixel that is
135 // done by FakeAllocColor.
136 if (width * height) {
137 os->writeU8(0);
138 os->writeU8(0);
139 os->writeU8(0);
140 os->writeU8(255);
141 os->writeU8(255);
142 os->writeU8(255);
143 os->writeBytes(data, (width+7)/8 * height);
144 os->writeBytes(mask, (width+7)/8 * height);
145 }
146}
147
Pierre Ossmane9962f72009-04-23 12:31:42 +0000148bool SMsgWriterV3::needFakeUpdate()
149{
150 return wsccb || needSetDesktopName || needNoDataUpdate();
151}
152
153bool SMsgWriterV3::needNoDataUpdate()
154{
155 return needSetDesktopSize || needExtendedDesktopSize ||
156 !extendedDesktopSizeMsgs.empty();
157}
158
159void SMsgWriterV3::writeNoDataUpdate()
160{
161 int nRects;
162
163 nRects = 0;
164
165 if (needSetDesktopSize)
166 nRects++;
167 if (needExtendedDesktopSize)
168 nRects++;
169 if (!extendedDesktopSizeMsgs.empty())
170 nRects += extendedDesktopSizeMsgs.size();
171
172 writeFramebufferUpdateStart(nRects);
173 writeNoDataRects();
174 writeFramebufferUpdateEnd();
175}
176
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000177void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
178{
179 startMsg(msgTypeFramebufferUpdate);
180 os->pad(1);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000181
DRCcd2c5d42011-08-11 11:18:34 +0000182 if (nRects != 0xFFFF) {
183 if (wsccb)
184 nRects++;
185 if (needSetDesktopName)
186 nRects++;
187 }
Pierre Ossmane9962f72009-04-23 12:31:42 +0000188
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000189 os->writeU16(nRects);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000190
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000191 nRectsInUpdate = 0;
DRCcd2c5d42011-08-11 11:18:34 +0000192 if (nRects == 0xFFFF)
193 nRectsInHeader = 0;
194 else
195 nRectsInHeader = nRects;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000196
197 writePseudoRects();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198}
199
200void SMsgWriterV3::writeFramebufferUpdateStart()
201{
202 nRectsInUpdate = nRectsInHeader = 0;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000203
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000204 if (!updateOS)
205 updateOS = new rdr::MemOutStream;
206 os = updateOS;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000207
208 writePseudoRects();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000209}
210
211void SMsgWriterV3::writeFramebufferUpdateEnd()
212{
Pierre Ossmane9962f72009-04-23 12:31:42 +0000213 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
214 throw Exception("SMsgWriterV3::writeFramebufferUpdateEnd: "
215 "nRects out of sync");
216
DRCcd2c5d42011-08-11 11:18:34 +0000217 if (nRectsInHeader == 0) {
218 // Send last rect. marker
219 os->writeS16(0);
220 os->writeS16(0);
221 os->writeU16(0);
222 os->writeU16(0);
223 os->writeU32(pseudoEncodingLastRect);
224 }
225
Pierre Ossmane9962f72009-04-23 12:31:42 +0000226 if (os == updateOS) {
227 os = realOS;
228 startMsg(msgTypeFramebufferUpdate);
229 os->pad(1);
230 os->writeU16(nRectsInUpdate);
231 os->writeBytes(updateOS->data(), updateOS->length());
232 updateOS->clear();
233 }
234
235 updatesSent++;
236 endMsg();
237}
238
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000239void SMsgWriterV3::startRect(const Rect& r, int encoding)
Pierre Ossmane9962f72009-04-23 12:31:42 +0000240{
241 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
242 throw Exception("SMsgWriterV3::startRect: nRects out of sync");
243
244 currentEncoding = encoding;
245 lenBeforeRect = os->length();
246 if (encoding != encodingCopyRect)
247 rawBytesEquivalent += 12 + r.width() * r.height() * (bpp()/8);
248
249 os->writeS16(r.tl.x);
250 os->writeS16(r.tl.y);
251 os->writeU16(r.width());
252 os->writeU16(r.height());
253 os->writeU32(encoding);
254}
255
256void SMsgWriterV3::endRect()
257{
258 if (currentEncoding <= encodingMax) {
259 bytesSent[currentEncoding] += os->length() - lenBeforeRect;
260 rectsSent[currentEncoding]++;
261 }
262}
263
264void SMsgWriterV3::writePseudoRects()
265{
266 if (wsccb) {
267 wsccb->writeSetCursorCallback();
268 wsccb = 0;
269 }
270
271 if (needSetDesktopName) {
272 if (!cp->supportsDesktopRename)
273 throw Exception("Client does not support desktop rename");
274 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
275 throw Exception("SMsgWriterV3 setDesktopName: nRects out of sync");
276
277 os->writeS16(0);
278 os->writeS16(0);
279 os->writeU16(0);
280 os->writeU16(0);
281 os->writeU32(pseudoEncodingDesktopName);
282 os->writeString(cp->name());
283
284 needSetDesktopName = false;
285 }
286}
287
288void SMsgWriterV3::writeNoDataRects()
289{
290 // Start with specific ExtendedDesktopSize messages
Pierre Ossman04e62db2009-03-23 16:57:07 +0000291 if (!extendedDesktopSizeMsgs.empty()) {
292 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
293 ScreenSet::const_iterator si;
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000294
295 if (!cp->supportsExtendedDesktopSize)
296 throw Exception("Client does not support extended desktop resize");
Pierre Ossman04e62db2009-03-23 16:57:07 +0000297 if ((nRectsInUpdate += extendedDesktopSizeMsgs.size()) > nRectsInHeader && nRectsInHeader)
298 throw Exception("SMsgWriterV3 SetDesktopSize reply: nRects out of sync");
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000299
Pierre Ossman04e62db2009-03-23 16:57:07 +0000300 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
301 os->writeU16(ri->reason);
302 os->writeU16(ri->result);
303 os->writeU16(ri->fb_width);
304 os->writeU16(ri->fb_height);
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000305 os->writeU32(pseudoEncodingExtendedDesktopSize);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000306
307 os->writeU8(ri->layout.num_screens());
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000308 os->pad(3);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000309
310 for (si = ri->layout.begin();si != ri->layout.end();++si) {
311 os->writeU32(si->id);
312 os->writeU16(si->dimensions.tl.x);
313 os->writeU16(si->dimensions.tl.y);
314 os->writeU16(si->dimensions.width());
315 os->writeU16(si->dimensions.height());
316 os->writeU32(si->flags);
317 }
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000318 }
319
Pierre Ossman04e62db2009-03-23 16:57:07 +0000320 extendedDesktopSizeMsgs.clear();
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000321 }
322
Pierre Ossmane9962f72009-04-23 12:31:42 +0000323 // Send this before SetDesktopSize to make life easier on the clients
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000324 if (needExtendedDesktopSize) {
325 if (!cp->supportsExtendedDesktopSize)
326 throw Exception("Client does not support extended desktop resize");
327 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
328 throw Exception("SMsgWriterV3 setExtendedDesktopSize: nRects out of sync");
Pierre Ossmane9962f72009-04-23 12:31:42 +0000329
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000330 os->writeU16(0);
331 os->writeU16(0);
332 os->writeU16(cp->width);
333 os->writeU16(cp->height);
334 os->writeU32(pseudoEncodingExtendedDesktopSize);
Pierre Ossman34e62f32009-03-20 21:46:12 +0000335
336 os->writeU8(cp->screenLayout.num_screens());
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000337 os->pad(3);
Pierre Ossman34e62f32009-03-20 21:46:12 +0000338
339 ScreenSet::const_iterator iter;
340 for (iter = cp->screenLayout.begin();iter != cp->screenLayout.end();++iter) {
341 os->writeU32(iter->id);
342 os->writeU16(iter->dimensions.tl.x);
343 os->writeU16(iter->dimensions.tl.y);
344 os->writeU16(iter->dimensions.width());
345 os->writeU16(iter->dimensions.height());
346 os->writeU32(iter->flags);
347 }
348
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000349 needExtendedDesktopSize = false;
350 }
351
Pierre Ossmane9962f72009-04-23 12:31:42 +0000352 // Some clients assume this is the last rectangle so don't send anything
353 // more after this
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000354 if (needSetDesktopSize) {
355 if (!cp->supportsDesktopResize)
356 throw Exception("Client does not support desktop resize");
357 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
358 throw Exception("SMsgWriterV3 setDesktopSize: nRects out of sync");
Pierre Ossmane9962f72009-04-23 12:31:42 +0000359
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000360 os->writeS16(0);
361 os->writeS16(0);
362 os->writeU16(cp->width);
363 os->writeU16(cp->height);
364 os->writeU32(pseudoEncodingDesktopSize);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000365
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000366 needSetDesktopSize = false;
367 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000368}
369