blob: becf6e708fbaa450919cc7229736cdc0dde35480 [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 Ossman0ff26552016-02-05 10:26:56 +01003 * Copyright 2009-2019 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>
Pierre Ossman0ff26552016-02-05 10:26:56 +010021
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000022#include <rdr/OutStream.h>
Pierre Ossman0ff26552016-02-05 10:26:56 +010023#include <rdr/MemOutStream.h>
24#include <rdr/ZlibOutStream.h>
25
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/msgTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010027#include <rfb/fenceTypes.h>
Pierre Ossman0ff26552016-02-05 10:26:56 +010028#include <rfb/clipboardTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010029#include <rfb/Exception.h>
Pierre Ossman0d3ce872018-06-18 15:59:00 +020030#include <rfb/ClientParams.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000031#include <rfb/UpdateTracker.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010032#include <rfb/Encoder.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000033#include <rfb/SMsgWriter.h>
34#include <rfb/LogWriter.h>
Pierre Ossmanb45a84f2016-12-12 16:59:15 +010035#include <rfb/ledStates.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036
37using namespace rfb;
38
39static LogWriter vlog("SMsgWriter");
40
Pierre Ossman0d3ce872018-06-18 15:59:00 +020041SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_)
42 : client(client_), os(os_),
Pierre Ossman7638e9c2014-01-16 13:12:40 +010043 nRectsInUpdate(0), nRectsInHeader(0),
Pierre Ossman2daba9b2018-10-29 10:03:37 +010044 needSetDesktopName(false), needCursor(false),
Pierre Ossman5ae28212017-05-16 14:30:38 +020045 needLEDState(false), needQEMUKeyEvent(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047}
48
49SMsgWriter::~SMsgWriter()
50{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051}
52
Pierre Ossman96728352018-06-20 11:35:05 +020053void SMsgWriter::writeServerInit(rdr::U16 width, rdr::U16 height,
54 const PixelFormat& pf, const char* name)
Pierre Ossman7638e9c2014-01-16 13:12:40 +010055{
Pierre Ossman96728352018-06-20 11:35:05 +020056 os->writeU16(width);
57 os->writeU16(height);
58 pf.write(os);
59 os->writeString(name);
Pierre Ossman7638e9c2014-01-16 13:12:40 +010060 endMsg();
61}
62
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010064 const rdr::U16 red[],
65 const rdr::U16 green[],
66 const rdr::U16 blue[])
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000067{
68 startMsg(msgTypeSetColourMapEntries);
69 os->pad(1);
70 os->writeU16(firstColour);
71 os->writeU16(nColours);
72 for (int i = firstColour; i < firstColour+nColours; i++) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010073 os->writeU16(red[i]);
74 os->writeU16(green[i]);
75 os->writeU16(blue[i]);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000076 }
77 endMsg();
78}
79
80void SMsgWriter::writeBell()
81{
82 startMsg(msgTypeBell);
83 endMsg();
84}
85
Pierre Ossman66f1db52019-05-02 12:32:03 +020086void SMsgWriter::writeServerCutText(const char* str)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000087{
Pierre Ossman66f1db52019-05-02 12:32:03 +020088 size_t len;
89
90 if (strchr(str, '\r') != NULL)
Pierre Ossman546b2ad2019-05-02 12:32:03 +020091 throw Exception("Invalid carriage return in clipboard data");
92
Pierre Ossman66f1db52019-05-02 12:32:03 +020093 len = strlen(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000094 startMsg(msgTypeServerCutText);
95 os->pad(3);
96 os->writeU32(len);
97 os->writeBytes(str, len);
98 endMsg();
99}
100
Pierre Ossman0ff26552016-02-05 10:26:56 +0100101void SMsgWriter::writeClipboardCaps(rdr::U32 caps,
102 const rdr::U32* lengths)
103{
104 size_t i, count;
105
106 if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
107 throw Exception("Client does not support extended clipboard");
108
109 count = 0;
110 for (i = 0;i < 16;i++) {
111 if (caps & (1 << i))
112 count++;
113 }
114
115 startMsg(msgTypeServerCutText);
116 os->pad(3);
117 os->writeS32(-(4 + 4 * count));
118
119 os->writeU32(caps | clipboardCaps);
120
121 count = 0;
122 for (i = 0;i < 16;i++) {
123 if (caps & (1 << i))
124 os->writeU32(lengths[count++]);
125 }
126
127 endMsg();
128}
129
130void SMsgWriter::writeClipboardRequest(rdr::U32 flags)
131{
132 if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
133 throw Exception("Client does not support extended clipboard");
134 if (!(client->clipboardFlags() & clipboardRequest))
135 throw Exception("Client does not support clipboard \"request\" action");
136
137 startMsg(msgTypeServerCutText);
138 os->pad(3);
139 os->writeS32(-4);
140 os->writeU32(flags | clipboardRequest);
141 endMsg();
142}
143
144void SMsgWriter::writeClipboardPeek(rdr::U32 flags)
145{
146 if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
147 throw Exception("Client does not support extended clipboard");
148 if (!(client->clipboardFlags() & clipboardPeek))
149 throw Exception("Client does not support clipboard \"peek\" action");
150
151 startMsg(msgTypeServerCutText);
152 os->pad(3);
153 os->writeS32(-4);
154 os->writeU32(flags | clipboardPeek);
155 endMsg();
156}
157
158void SMsgWriter::writeClipboardNotify(rdr::U32 flags)
159{
160 if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
161 throw Exception("Client does not support extended clipboard");
162 if (!(client->clipboardFlags() & clipboardNotify))
163 throw Exception("Client does not support clipboard \"notify\" action");
164
165 startMsg(msgTypeServerCutText);
166 os->pad(3);
167 os->writeS32(-4);
168 os->writeU32(flags | clipboardNotify);
169 endMsg();
170}
171
172void SMsgWriter::writeClipboardProvide(rdr::U32 flags,
173 const size_t* lengths,
174 const rdr::U8* const* data)
175{
176 rdr::MemOutStream mos;
177 rdr::ZlibOutStream zos;
178
179 int i, count;
180
181 if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
182 throw Exception("Client does not support extended clipboard");
183 if (!(client->clipboardFlags() & clipboardProvide))
184 throw Exception("Client does not support clipboard \"provide\" action");
185
186 zos.setUnderlying(&mos);
187
188 count = 0;
189 for (i = 0;i < 16;i++) {
190 if (!(flags & (1 << i)))
191 continue;
192 zos.writeU32(lengths[count]);
193 zos.writeBytes(data[count], lengths[count]);
194 count++;
195 }
196
197 zos.flush();
198
199 startMsg(msgTypeServerCutText);
200 os->pad(3);
201 os->writeS32(-(4 + mos.length()));
202 os->writeU32(flags | clipboardProvide);
203 os->writeBytes(mos.data(), mos.length());
204 endMsg();
205}
206
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100207void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
208{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200209 if (!client->supportsEncoding(pseudoEncodingFence))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100210 throw Exception("Client does not support fences");
211 if (len > 64)
212 throw Exception("Too large fence payload");
213 if ((flags & ~fenceFlagsSupported) != 0)
214 throw Exception("Unknown fence flags");
215
216 startMsg(msgTypeServerFence);
217 os->pad(3);
218
219 os->writeU32(flags);
220
221 os->writeU8(len);
Michal Srbf3afa242017-03-27 19:02:15 +0300222
223 if (len > 0)
224 os->writeBytes(data, len);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100225
226 endMsg();
227}
228
229void SMsgWriter::writeEndOfContinuousUpdates()
230{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200231 if (!client->supportsEncoding(pseudoEncodingContinuousUpdates))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100232 throw Exception("Client does not support continuous updates");
233
234 startMsg(msgTypeEndOfContinuousUpdates);
235 endMsg();
236}
237
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100238void SMsgWriter::writeDesktopSize(rdr::U16 reason, rdr::U16 result)
239{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100240 ExtendedDesktopSizeMsg msg;
241
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100242 if (!client->supportsEncoding(pseudoEncodingDesktopSize) &&
243 !client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
244 throw Exception("Client does not support desktop size changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100245
246 msg.reason = reason;
247 msg.result = result;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100248
249 extendedDesktopSizeMsgs.push_back(msg);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100250}
251
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100252void SMsgWriter::writeSetDesktopName()
253{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200254 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100255 throw Exception("Client does not support desktop name changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100256
257 needSetDesktopName = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100258}
259
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100260void SMsgWriter::writeCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100261{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100262 if (!client->supportsEncoding(pseudoEncodingCursor) &&
263 !client->supportsEncoding(pseudoEncodingXCursor) &&
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100264 !client->supportsEncoding(pseudoEncodingCursorWithAlpha) &&
265 !client->supportsEncoding(pseudoEncodingVMwareCursor))
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100266 throw Exception("Client does not support local cursor");
Pierre Ossman126e5642014-02-13 14:40:25 +0100267
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100268 needCursor = true;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100269}
270
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100271void SMsgWriter::writeLEDState()
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100272{
Pierre Ossman62b07862018-11-05 16:28:57 +0100273 if (!client->supportsEncoding(pseudoEncodingLEDState) &&
274 !client->supportsEncoding(pseudoEncodingVMwareLEDState))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100275 throw Exception("Client does not support LED state");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200276 if (client->ledState() == ledUnknown)
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100277 throw Exception("Server has not specified LED state");
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100278
279 needLEDState = true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100280}
281
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100282void SMsgWriter::writeQEMUKeyEvent()
Pierre Ossman5ae28212017-05-16 14:30:38 +0200283{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200284 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100285 throw Exception("Client does not support QEMU key events");
Pierre Ossman5ae28212017-05-16 14:30:38 +0200286
287 needQEMUKeyEvent = true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200288}
289
Pierre Ossmane9962f72009-04-23 12:31:42 +0000290bool SMsgWriter::needFakeUpdate()
291{
Pierre Ossman126e5642014-02-13 14:40:25 +0100292 if (needSetDesktopName)
293 return true;
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100294 if (needCursor)
Pierre Ossman126e5642014-02-13 14:40:25 +0100295 return true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100296 if (needLEDState)
297 return true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200298 if (needQEMUKeyEvent)
299 return true;
Pierre Ossman126e5642014-02-13 14:40:25 +0100300 if (needNoDataUpdate())
301 return true;
302
303 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000304}
305
Pierre Ossmane9962f72009-04-23 12:31:42 +0000306bool SMsgWriter::needNoDataUpdate()
307{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100308 if (!extendedDesktopSizeMsgs.empty())
Pierre Ossman126e5642014-02-13 14:40:25 +0100309 return true;
310
311 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000312}
313
314void SMsgWriter::writeNoDataUpdate()
315{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100316 int nRects;
317
318 nRects = 0;
319
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100320 if (!extendedDesktopSizeMsgs.empty()) {
321 if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
322 nRects += extendedDesktopSizeMsgs.size();
323 else
324 nRects++;
325 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100326
327 writeFramebufferUpdateStart(nRects);
328 writeNoDataRects();
329 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000330}
331
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100332void SMsgWriter::writeFramebufferUpdateStart(int nRects)
333{
334 startMsg(msgTypeFramebufferUpdate);
335 os->pad(1);
336
337 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100338 if (needSetDesktopName)
339 nRects++;
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100340 if (needCursor)
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100341 nRects++;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100342 if (needLEDState)
343 nRects++;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200344 if (needQEMUKeyEvent)
345 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100346 }
347
348 os->writeU16(nRects);
349
350 nRectsInUpdate = 0;
351 if (nRects == 0xFFFF)
352 nRectsInHeader = 0;
353 else
354 nRectsInHeader = nRects;
355
356 writePseudoRects();
357}
358
359void SMsgWriter::writeFramebufferUpdateEnd()
360{
361 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
362 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
363 "nRects out of sync");
364
365 if (nRectsInHeader == 0) {
366 // Send last rect. marker
367 os->writeS16(0);
368 os->writeS16(0);
369 os->writeU16(0);
370 os->writeU16(0);
371 os->writeU32(pseudoEncodingLastRect);
372 }
373
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100374 endMsg();
375}
376
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000377void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
378{
379 startRect(r,encodingCopyRect);
380 os->writeU16(srcX);
381 os->writeU16(srcY);
382 endRect();
383}
384
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100385void SMsgWriter::startRect(const Rect& r, int encoding)
386{
387 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
388 throw Exception("SMsgWriter::startRect: nRects out of sync");
389
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100390 os->writeS16(r.tl.x);
391 os->writeS16(r.tl.y);
392 os->writeU16(r.width());
393 os->writeU16(r.height());
394 os->writeU32(encoding);
395}
396
397void SMsgWriter::endRect()
398{
Pierre Ossman35294682016-04-29 14:27:08 +0200399 os->flush();
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100400}
401
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100402void SMsgWriter::startMsg(int type)
403{
404 os->writeU8(type);
405}
406
407void SMsgWriter::endMsg()
408{
409 os->flush();
410}
411
412void SMsgWriter::writePseudoRects()
413{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100414 if (needCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200415 const Cursor& cursor = client->cursor();
Pierre Ossman126e5642014-02-13 14:40:25 +0100416
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100417 if (client->supportsEncoding(pseudoEncodingCursorWithAlpha)) {
418 writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
419 cursor.hotspot().x, cursor.hotspot().y,
420 cursor.getBuffer());
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100421 } else if (client->supportsEncoding(pseudoEncodingVMwareCursor)) {
422 writeSetVMwareCursorRect(cursor.width(), cursor.height(),
423 cursor.hotspot().x, cursor.hotspot().y,
424 cursor.getBuffer());
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100425 } else if (client->supportsEncoding(pseudoEncodingCursor)) {
426 rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
427 rdr::U8Array mask(cursor.getMask());
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100428
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100429 const rdr::U8* in;
430 rdr::U8* out;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100431
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100432 in = cursor.getBuffer();
433 out = data.buf;
434 for (int i = 0;i < cursor.width()*cursor.height();i++) {
435 client->pf().bufferFromRGB(out, in, 1);
436 in += 4;
437 out += client->pf().bpp/8;
438 }
439
440 writeSetCursorRect(cursor.width(), cursor.height(),
441 cursor.hotspot().x, cursor.hotspot().y,
442 data.buf, mask.buf);
443 } else if (client->supportsEncoding(pseudoEncodingXCursor)) {
444 rdr::U8Array bitmap(cursor.getBitmap());
445 rdr::U8Array mask(cursor.getMask());
446
447 writeSetXCursorRect(cursor.width(), cursor.height(),
448 cursor.hotspot().x, cursor.hotspot().y,
449 bitmap.buf, mask.buf);
450 } else {
451 throw Exception("Client does not support local cursor");
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100452 }
Pierre Ossman126e5642014-02-13 14:40:25 +0100453
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100454 needCursor = false;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100455 }
456
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100457 if (needSetDesktopName) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200458 writeSetDesktopNameRect(client->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100459 needSetDesktopName = false;
460 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100461
462 if (needLEDState) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200463 writeLEDStateRect(client->ledState());
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100464 needLEDState = false;
465 }
Pierre Ossman5ae28212017-05-16 14:30:38 +0200466
467 if (needQEMUKeyEvent) {
468 writeQEMUKeyEventRect();
469 needQEMUKeyEvent = false;
470 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100471}
472
473void SMsgWriter::writeNoDataRects()
474{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100475 if (!extendedDesktopSizeMsgs.empty()) {
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100476 if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize)) {
477 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
478 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
479 // FIXME: We can probably skip multiple reasonServer entries
480 writeExtendedDesktopSizeRect(ri->reason, ri->result,
481 client->width(), client->height(),
482 client->screenLayout());
483 }
484 } else if (client->supportsEncoding(pseudoEncodingDesktopSize)) {
485 // Some clients assume this is the last rectangle so don't send anything
486 // more after this
487 writeSetDesktopSizeRect(client->width(), client->height());
488 } else {
489 throw Exception("Client does not support desktop size changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100490 }
491
492 extendedDesktopSizeMsgs.clear();
493 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100494}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100495
496void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
497{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200498 if (!client->supportsEncoding(pseudoEncodingDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100499 throw Exception("Client does not support desktop resize");
500 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
501 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
502
503 os->writeS16(0);
504 os->writeS16(0);
505 os->writeU16(width);
506 os->writeU16(height);
507 os->writeU32(pseudoEncodingDesktopSize);
508}
509
510void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
511 rdr::U16 result,
512 int fb_width,
513 int fb_height,
514 const ScreenSet& layout)
515{
516 ScreenSet::const_iterator si;
517
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200518 if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100519 throw Exception("Client does not support extended desktop resize");
520 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
521 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
522
523 os->writeU16(reason);
524 os->writeU16(result);
525 os->writeU16(fb_width);
526 os->writeU16(fb_height);
527 os->writeU32(pseudoEncodingExtendedDesktopSize);
528
529 os->writeU8(layout.num_screens());
530 os->pad(3);
531
532 for (si = layout.begin();si != layout.end();++si) {
533 os->writeU32(si->id);
534 os->writeU16(si->dimensions.tl.x);
535 os->writeU16(si->dimensions.tl.y);
536 os->writeU16(si->dimensions.width());
537 os->writeU16(si->dimensions.height());
538 os->writeU32(si->flags);
539 }
540}
541
542void SMsgWriter::writeSetDesktopNameRect(const char *name)
543{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200544 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100545 throw Exception("Client does not support desktop rename");
546 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
547 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
548
549 os->writeS16(0);
550 os->writeS16(0);
551 os->writeU16(0);
552 os->writeU16(0);
553 os->writeU32(pseudoEncodingDesktopName);
554 os->writeString(name);
555}
Pierre Ossman126e5642014-02-13 14:40:25 +0100556
557void SMsgWriter::writeSetCursorRect(int width, int height,
558 int hotspotX, int hotspotY,
559 const void* data, const void* mask)
560{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200561 if (!client->supportsEncoding(pseudoEncodingCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100562 throw Exception("Client does not support local cursors");
563 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
564 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
565
566 os->writeS16(hotspotX);
567 os->writeS16(hotspotY);
568 os->writeU16(width);
569 os->writeU16(height);
570 os->writeU32(pseudoEncodingCursor);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200571 os->writeBytes(data, width * height * (client->pf().bpp/8));
Pierre Ossman126e5642014-02-13 14:40:25 +0100572 os->writeBytes(mask, (width+7)/8 * height);
573}
574
575void SMsgWriter::writeSetXCursorRect(int width, int height,
576 int hotspotX, int hotspotY,
Pierre Ossman126e5642014-02-13 14:40:25 +0100577 const void* data, const void* mask)
578{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200579 if (!client->supportsEncoding(pseudoEncodingXCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100580 throw Exception("Client does not support local cursors");
581 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
582 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
583
584 os->writeS16(hotspotX);
585 os->writeS16(hotspotY);
586 os->writeU16(width);
587 os->writeU16(height);
588 os->writeU32(pseudoEncodingXCursor);
Brian P. Hinz33d78322017-11-16 17:46:15 -0500589 if (width * height > 0) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100590 os->writeU8(255);
591 os->writeU8(255);
592 os->writeU8(255);
593 os->writeU8(0);
594 os->writeU8(0);
595 os->writeU8(0);
Pierre Ossman126e5642014-02-13 14:40:25 +0100596 os->writeBytes(data, (width+7)/8 * height);
597 os->writeBytes(mask, (width+7)/8 * height);
598 }
599}
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100600
601void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
602 int hotspotX, int hotspotY,
603 const rdr::U8* data)
604{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200605 if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100606 throw Exception("Client does not support local cursors");
607 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
608 throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
609
610 os->writeS16(hotspotX);
611 os->writeS16(hotspotY);
612 os->writeU16(width);
613 os->writeU16(height);
614 os->writeU32(pseudoEncodingCursorWithAlpha);
615
616 // FIXME: Use an encoder with compression?
617 os->writeU32(encodingRaw);
618
619 // Alpha needs to be pre-multiplied
620 for (int i = 0;i < width*height;i++) {
621 os->writeU8((unsigned)data[0] * data[3] / 255);
622 os->writeU8((unsigned)data[1] * data[3] / 255);
623 os->writeU8((unsigned)data[2] * data[3] / 255);
624 os->writeU8(data[3]);
625 data += 4;
626 }
627}
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100628
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100629void SMsgWriter::writeSetVMwareCursorRect(int width, int height,
630 int hotspotX, int hotspotY,
631 const rdr::U8* data)
632{
633 if (!client->supportsEncoding(pseudoEncodingVMwareCursor))
634 throw Exception("Client does not support local cursors");
635 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
636 throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
637
638 os->writeS16(hotspotX);
639 os->writeS16(hotspotY);
640 os->writeU16(width);
641 os->writeU16(height);
642 os->writeU32(pseudoEncodingVMwareCursor);
643
644 os->writeU8(1); // Alpha cursor
645 os->pad(1);
646
647 // FIXME: Should alpha be premultiplied?
648 os->writeBytes(data, width*height*4);
649}
650
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100651void SMsgWriter::writeLEDStateRect(rdr::U8 state)
652{
Pierre Ossman62b07862018-11-05 16:28:57 +0100653 if (!client->supportsEncoding(pseudoEncodingLEDState) &&
654 !client->supportsEncoding(pseudoEncodingVMwareLEDState))
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100655 throw Exception("Client does not support LED state updates");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200656 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100657 throw Exception("Server does not support LED state updates");
658 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
659 throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
660
661 os->writeS16(0);
662 os->writeS16(0);
663 os->writeU16(0);
664 os->writeU16(0);
Pierre Ossman62b07862018-11-05 16:28:57 +0100665 if (client->supportsEncoding(pseudoEncodingLEDState)) {
666 os->writeU32(pseudoEncodingLEDState);
667 os->writeU8(state);
668 } else {
669 os->writeU32(pseudoEncodingVMwareLEDState);
670 os->writeU32(state);
671 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100672}
Pierre Ossman5ae28212017-05-16 14:30:38 +0200673
674void SMsgWriter::writeQEMUKeyEventRect()
675{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200676 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200677 throw Exception("Client does not support QEMU extended key events");
678 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
679 throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");
680
681 os->writeS16(0);
682 os->writeS16(0);
683 os->writeU16(0);
684 os->writeU16(0);
685 os->writeU32(pseudoEncodingQEMUKeyEvent);
686}