blob: 08f5a2e3ab15b7258620551f2a9fd5675360f6cc [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
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19#include <rdr/OutStream.h>
20#include <rdr/MemOutStream.h>
21#include <rfb/msgTypes.h>
Pierre Ossmanc5e25602009-03-20 12:59:05 +000022#include <rfb/screenTypes.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000023#include <rfb/Exception.h>
24#include <rfb/ConnParams.h>
25#include <rfb/SMsgWriterV3.h>
26
27using namespace rfb;
28
29SMsgWriterV3::SMsgWriterV3(ConnParams* cp, rdr::OutStream* os)
30 : SMsgWriter(cp, os), updateOS(0), realOS(os), nRectsInUpdate(0),
Adam Tkac0c6eff12009-04-02 15:44:23 +000031 nRectsInHeader(0), wsccb(0), needSetDesktopSize(false),
32 needExtendedDesktopSize(false), needSetDesktopName(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000033{
34}
35
36SMsgWriterV3::~SMsgWriterV3()
37{
38 delete updateOS;
39}
40
41void SMsgWriterV3::writeServerInit()
42{
43 os->writeU16(cp->width);
44 os->writeU16(cp->height);
45 cp->pf().write(os);
46 os->writeString(cp->name());
47 endMsg();
48}
49
50void SMsgWriterV3::startMsg(int type)
51{
52 if (os != realOS)
53 throw Exception("startMsg called while writing an update?");
54
55 os->writeU8(type);
56}
57
58void SMsgWriterV3::endMsg()
59{
60 os->flush();
61}
62
63bool SMsgWriterV3::writeSetDesktopSize() {
64 if (!cp->supportsDesktopResize) return false;
65 needSetDesktopSize = true;
66 return true;
67}
68
Pierre Ossman04e62db2009-03-23 16:57:07 +000069bool SMsgWriterV3::writeExtendedDesktopSize() {
Pierre Ossmanc5e25602009-03-20 12:59:05 +000070 if (!cp->supportsExtendedDesktopSize) return false;
Pierre Ossman04e62db2009-03-23 16:57:07 +000071 needExtendedDesktopSize = true;
72 return true;
73}
74
75bool SMsgWriterV3::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
76 int fb_width, int fb_height,
77 const ScreenSet& layout) {
78 ExtendedDesktopSizeMsg msg;
79
80 if (!cp->supportsExtendedDesktopSize) return false;
81
82 msg.reason = reason;
83 msg.result = result;
84 msg.fb_width = fb_width;
85 msg.fb_height = fb_height;
86 msg.layout = layout;
87
88 extendedDesktopSizeMsgs.push_back(msg);
89
Pierre Ossmanc5e25602009-03-20 12:59:05 +000090 return true;
91}
92
Peter Åstrandc39e0782009-01-15 12:21:42 +000093bool SMsgWriterV3::writeSetDesktopName() {
94 if (!cp->supportsDesktopRename) return false;
95 needSetDesktopName = true;
96 return true;
97}
98
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000099void SMsgWriterV3::cursorChange(WriteSetCursorCallback* cb)
100{
101 wsccb = cb;
102}
103
104void SMsgWriterV3::writeSetCursor(int width, int height, const Point& hotspot,
105 void* data, void* mask)
106{
107 if (!wsccb) return;
108 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
109 throw Exception("SMsgWriterV3::writeSetCursor: nRects out of sync");
110 os->writeS16(hotspot.x);
111 os->writeS16(hotspot.y);
112 os->writeU16(width);
113 os->writeU16(height);
114 os->writeU32(pseudoEncodingCursor);
115 os->writeBytes(data, width * height * (cp->pf().bpp/8));
116 os->writeBytes(mask, (width+7)/8 * height);
117}
118
119void SMsgWriterV3::writeSetXCursor(int width, int height, int hotspotX,
120 int hotspotY, void* data, void* mask)
121{
122 if (!wsccb) return;
123 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
124 throw Exception("SMsgWriterV3::writeSetXCursor: nRects out of sync");
125 os->writeS16(hotspotX);
126 os->writeS16(hotspotY);
127 os->writeU16(width);
128 os->writeU16(height);
129 os->writeU32(pseudoEncodingXCursor);
130 // FIXME: We only support black and white cursors, currently. We
131 // could pass the correct color by using the pix0/pix1 values
132 // returned from getBitmap, in writeSetCursorCallback. However, we
133 // would then need to undo the conversion from rgb to Pixel that is
134 // done by FakeAllocColor.
135 if (width * height) {
136 os->writeU8(0);
137 os->writeU8(0);
138 os->writeU8(0);
139 os->writeU8(255);
140 os->writeU8(255);
141 os->writeU8(255);
142 os->writeBytes(data, (width+7)/8 * height);
143 os->writeBytes(mask, (width+7)/8 * height);
144 }
145}
146
Pierre Ossmane9962f72009-04-23 12:31:42 +0000147bool SMsgWriterV3::needFakeUpdate()
148{
149 return wsccb || needSetDesktopName || needNoDataUpdate();
150}
151
152bool SMsgWriterV3::needNoDataUpdate()
153{
154 return needSetDesktopSize || needExtendedDesktopSize ||
155 !extendedDesktopSizeMsgs.empty();
156}
157
158void SMsgWriterV3::writeNoDataUpdate()
159{
160 int nRects;
161
162 nRects = 0;
163
164 if (needSetDesktopSize)
165 nRects++;
166 if (needExtendedDesktopSize)
167 nRects++;
168 if (!extendedDesktopSizeMsgs.empty())
169 nRects += extendedDesktopSizeMsgs.size();
170
171 writeFramebufferUpdateStart(nRects);
172 writeNoDataRects();
173 writeFramebufferUpdateEnd();
174}
175
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000176void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
177{
178 startMsg(msgTypeFramebufferUpdate);
179 os->pad(1);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000180
181 if (wsccb)
182 nRects++;
183 if (needSetDesktopName)
184 nRects++;
185
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000186 os->writeU16(nRects);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000187
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000188 nRectsInUpdate = 0;
189 nRectsInHeader = nRects;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000190
191 writePseudoRects();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000192}
193
194void SMsgWriterV3::writeFramebufferUpdateStart()
195{
196 nRectsInUpdate = nRectsInHeader = 0;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000197
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198 if (!updateOS)
199 updateOS = new rdr::MemOutStream;
200 os = updateOS;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000201
202 writePseudoRects();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000203}
204
205void SMsgWriterV3::writeFramebufferUpdateEnd()
206{
Pierre Ossmane9962f72009-04-23 12:31:42 +0000207 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
208 throw Exception("SMsgWriterV3::writeFramebufferUpdateEnd: "
209 "nRects out of sync");
210
211 if (os == updateOS) {
212 os = realOS;
213 startMsg(msgTypeFramebufferUpdate);
214 os->pad(1);
215 os->writeU16(nRectsInUpdate);
216 os->writeBytes(updateOS->data(), updateOS->length());
217 updateOS->clear();
218 }
219
220 updatesSent++;
221 endMsg();
222}
223
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000224void SMsgWriterV3::startRect(const Rect& r, int encoding)
Pierre Ossmane9962f72009-04-23 12:31:42 +0000225{
226 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
227 throw Exception("SMsgWriterV3::startRect: nRects out of sync");
228
229 currentEncoding = encoding;
230 lenBeforeRect = os->length();
231 if (encoding != encodingCopyRect)
232 rawBytesEquivalent += 12 + r.width() * r.height() * (bpp()/8);
233
234 os->writeS16(r.tl.x);
235 os->writeS16(r.tl.y);
236 os->writeU16(r.width());
237 os->writeU16(r.height());
238 os->writeU32(encoding);
239}
240
241void SMsgWriterV3::endRect()
242{
243 if (currentEncoding <= encodingMax) {
244 bytesSent[currentEncoding] += os->length() - lenBeforeRect;
245 rectsSent[currentEncoding]++;
246 }
247}
248
249void SMsgWriterV3::writePseudoRects()
250{
251 if (wsccb) {
252 wsccb->writeSetCursorCallback();
253 wsccb = 0;
254 }
255
256 if (needSetDesktopName) {
257 if (!cp->supportsDesktopRename)
258 throw Exception("Client does not support desktop rename");
259 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
260 throw Exception("SMsgWriterV3 setDesktopName: nRects out of sync");
261
262 os->writeS16(0);
263 os->writeS16(0);
264 os->writeU16(0);
265 os->writeU16(0);
266 os->writeU32(pseudoEncodingDesktopName);
267 os->writeString(cp->name());
268
269 needSetDesktopName = false;
270 }
271}
272
273void SMsgWriterV3::writeNoDataRects()
274{
275 // Start with specific ExtendedDesktopSize messages
Pierre Ossman04e62db2009-03-23 16:57:07 +0000276 if (!extendedDesktopSizeMsgs.empty()) {
277 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
278 ScreenSet::const_iterator si;
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000279
280 if (!cp->supportsExtendedDesktopSize)
281 throw Exception("Client does not support extended desktop resize");
Pierre Ossman04e62db2009-03-23 16:57:07 +0000282 if ((nRectsInUpdate += extendedDesktopSizeMsgs.size()) > nRectsInHeader && nRectsInHeader)
283 throw Exception("SMsgWriterV3 SetDesktopSize reply: nRects out of sync");
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000284
Pierre Ossman04e62db2009-03-23 16:57:07 +0000285 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
286 os->writeU16(ri->reason);
287 os->writeU16(ri->result);
288 os->writeU16(ri->fb_width);
289 os->writeU16(ri->fb_height);
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000290 os->writeU32(pseudoEncodingExtendedDesktopSize);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000291
292 os->writeU8(ri->layout.num_screens());
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000293 os->pad(3);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000294
295 for (si = ri->layout.begin();si != ri->layout.end();++si) {
296 os->writeU32(si->id);
297 os->writeU16(si->dimensions.tl.x);
298 os->writeU16(si->dimensions.tl.y);
299 os->writeU16(si->dimensions.width());
300 os->writeU16(si->dimensions.height());
301 os->writeU32(si->flags);
302 }
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000303 }
304
Pierre Ossman04e62db2009-03-23 16:57:07 +0000305 extendedDesktopSizeMsgs.clear();
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000306 }
307
Pierre Ossmane9962f72009-04-23 12:31:42 +0000308 // Send this before SetDesktopSize to make life easier on the clients
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000309 if (needExtendedDesktopSize) {
310 if (!cp->supportsExtendedDesktopSize)
311 throw Exception("Client does not support extended desktop resize");
312 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
313 throw Exception("SMsgWriterV3 setExtendedDesktopSize: nRects out of sync");
Pierre Ossmane9962f72009-04-23 12:31:42 +0000314
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000315 os->writeU16(0);
316 os->writeU16(0);
317 os->writeU16(cp->width);
318 os->writeU16(cp->height);
319 os->writeU32(pseudoEncodingExtendedDesktopSize);
Pierre Ossman34e62f32009-03-20 21:46:12 +0000320
321 os->writeU8(cp->screenLayout.num_screens());
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000322 os->pad(3);
Pierre Ossman34e62f32009-03-20 21:46:12 +0000323
324 ScreenSet::const_iterator iter;
325 for (iter = cp->screenLayout.begin();iter != cp->screenLayout.end();++iter) {
326 os->writeU32(iter->id);
327 os->writeU16(iter->dimensions.tl.x);
328 os->writeU16(iter->dimensions.tl.y);
329 os->writeU16(iter->dimensions.width());
330 os->writeU16(iter->dimensions.height());
331 os->writeU32(iter->flags);
332 }
333
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000334 needExtendedDesktopSize = false;
335 }
336
Pierre Ossmane9962f72009-04-23 12:31:42 +0000337 // Some clients assume this is the last rectangle so don't send anything
338 // more after this
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000339 if (needSetDesktopSize) {
340 if (!cp->supportsDesktopResize)
341 throw Exception("Client does not support desktop resize");
342 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
343 throw Exception("SMsgWriterV3 setDesktopSize: nRects out of sync");
Pierre Ossmane9962f72009-04-23 12:31:42 +0000344
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000345 os->writeS16(0);
346 os->writeS16(0);
347 os->writeU16(cp->width);
348 os->writeU16(cp->height);
349 os->writeU32(pseudoEncodingDesktopSize);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000350
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000351 needSetDesktopSize = false;
352 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000353}
354