blob: f0748ff233025c4c0e64a8483efbd25eb7f80d57 [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 Ossman6a1a0d02017-02-19 15:48:17 +01003 * Copyright 2009-2017 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>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000021#include <rdr/OutStream.h>
22#include <rfb/msgTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010023#include <rfb/fenceTypes.h>
24#include <rfb/Exception.h>
Pierre Ossman0d3ce872018-06-18 15:59:00 +020025#include <rfb/ClientParams.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/UpdateTracker.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010027#include <rfb/Encoder.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028#include <rfb/SMsgWriter.h>
29#include <rfb/LogWriter.h>
Pierre Ossmanb45a84f2016-12-12 16:59:15 +010030#include <rfb/ledStates.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000031
32using namespace rfb;
33
34static LogWriter vlog("SMsgWriter");
35
Pierre Ossman0d3ce872018-06-18 15:59:00 +020036SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_)
37 : client(client_), os(os_),
Pierre Ossman7638e9c2014-01-16 13:12:40 +010038 nRectsInUpdate(0), nRectsInHeader(0),
Pierre Ossman2daba9b2018-10-29 10:03:37 +010039 needSetDesktopName(false), needCursor(false),
Pierre Ossman5ae28212017-05-16 14:30:38 +020040 needLEDState(false), needQEMUKeyEvent(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000041{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000042}
43
44SMsgWriter::~SMsgWriter()
45{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046}
47
Pierre Ossman96728352018-06-20 11:35:05 +020048void SMsgWriter::writeServerInit(rdr::U16 width, rdr::U16 height,
49 const PixelFormat& pf, const char* name)
Pierre Ossman7638e9c2014-01-16 13:12:40 +010050{
Pierre Ossman96728352018-06-20 11:35:05 +020051 os->writeU16(width);
52 os->writeU16(height);
53 pf.write(os);
54 os->writeString(name);
Pierre Ossman7638e9c2014-01-16 13:12:40 +010055 endMsg();
56}
57
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010059 const rdr::U16 red[],
60 const rdr::U16 green[],
61 const rdr::U16 blue[])
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000062{
63 startMsg(msgTypeSetColourMapEntries);
64 os->pad(1);
65 os->writeU16(firstColour);
66 os->writeU16(nColours);
67 for (int i = firstColour; i < firstColour+nColours; i++) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010068 os->writeU16(red[i]);
69 os->writeU16(green[i]);
70 os->writeU16(blue[i]);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071 }
72 endMsg();
73}
74
75void SMsgWriter::writeBell()
76{
77 startMsg(msgTypeBell);
78 endMsg();
79}
80
81void SMsgWriter::writeServerCutText(const char* str, int len)
82{
Pierre Ossman546b2ad2019-05-02 12:32:03 +020083 if (memchr(str, '\r', len) != NULL)
84 throw Exception("Invalid carriage return in clipboard data");
85
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000086 startMsg(msgTypeServerCutText);
87 os->pad(3);
88 os->writeU32(len);
89 os->writeBytes(str, len);
90 endMsg();
91}
92
Pierre Ossman7638e9c2014-01-16 13:12:40 +010093void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
94{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +020095 if (!client->supportsEncoding(pseudoEncodingFence))
Pierre Ossman7638e9c2014-01-16 13:12:40 +010096 throw Exception("Client does not support fences");
97 if (len > 64)
98 throw Exception("Too large fence payload");
99 if ((flags & ~fenceFlagsSupported) != 0)
100 throw Exception("Unknown fence flags");
101
102 startMsg(msgTypeServerFence);
103 os->pad(3);
104
105 os->writeU32(flags);
106
107 os->writeU8(len);
Michal Srbf3afa242017-03-27 19:02:15 +0300108
109 if (len > 0)
110 os->writeBytes(data, len);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100111
112 endMsg();
113}
114
115void SMsgWriter::writeEndOfContinuousUpdates()
116{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200117 if (!client->supportsEncoding(pseudoEncodingContinuousUpdates))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100118 throw Exception("Client does not support continuous updates");
119
120 startMsg(msgTypeEndOfContinuousUpdates);
121 endMsg();
122}
123
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100124void SMsgWriter::writeDesktopSize(rdr::U16 reason, rdr::U16 result)
125{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100126 ExtendedDesktopSizeMsg msg;
127
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100128 if (!client->supportsEncoding(pseudoEncodingDesktopSize) &&
129 !client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
130 throw Exception("Client does not support desktop size changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100131
132 msg.reason = reason;
133 msg.result = result;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100134
135 extendedDesktopSizeMsgs.push_back(msg);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100136}
137
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100138void SMsgWriter::writeSetDesktopName()
139{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200140 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100141 throw Exception("Client does not support desktop name changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100142
143 needSetDesktopName = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100144}
145
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100146void SMsgWriter::writeCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100147{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100148 if (!client->supportsEncoding(pseudoEncodingCursor) &&
149 !client->supportsEncoding(pseudoEncodingXCursor) &&
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100150 !client->supportsEncoding(pseudoEncodingCursorWithAlpha) &&
151 !client->supportsEncoding(pseudoEncodingVMwareCursor))
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100152 throw Exception("Client does not support local cursor");
Pierre Ossman126e5642014-02-13 14:40:25 +0100153
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100154 needCursor = true;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100155}
156
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100157void SMsgWriter::writeLEDState()
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100158{
Pierre Ossman62b07862018-11-05 16:28:57 +0100159 if (!client->supportsEncoding(pseudoEncodingLEDState) &&
160 !client->supportsEncoding(pseudoEncodingVMwareLEDState))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100161 throw Exception("Client does not support LED state");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200162 if (client->ledState() == ledUnknown)
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100163 throw Exception("Server has not specified LED state");
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100164
165 needLEDState = true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100166}
167
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100168void SMsgWriter::writeQEMUKeyEvent()
Pierre Ossman5ae28212017-05-16 14:30:38 +0200169{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200170 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100171 throw Exception("Client does not support QEMU key events");
Pierre Ossman5ae28212017-05-16 14:30:38 +0200172
173 needQEMUKeyEvent = true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200174}
175
Pierre Ossmane9962f72009-04-23 12:31:42 +0000176bool SMsgWriter::needFakeUpdate()
177{
Pierre Ossman126e5642014-02-13 14:40:25 +0100178 if (needSetDesktopName)
179 return true;
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100180 if (needCursor)
Pierre Ossman126e5642014-02-13 14:40:25 +0100181 return true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100182 if (needLEDState)
183 return true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200184 if (needQEMUKeyEvent)
185 return true;
Pierre Ossman126e5642014-02-13 14:40:25 +0100186 if (needNoDataUpdate())
187 return true;
188
189 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000190}
191
Pierre Ossmane9962f72009-04-23 12:31:42 +0000192bool SMsgWriter::needNoDataUpdate()
193{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100194 if (!extendedDesktopSizeMsgs.empty())
Pierre Ossman126e5642014-02-13 14:40:25 +0100195 return true;
196
197 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000198}
199
200void SMsgWriter::writeNoDataUpdate()
201{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100202 int nRects;
203
204 nRects = 0;
205
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100206 if (!extendedDesktopSizeMsgs.empty()) {
207 if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
208 nRects += extendedDesktopSizeMsgs.size();
209 else
210 nRects++;
211 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100212
213 writeFramebufferUpdateStart(nRects);
214 writeNoDataRects();
215 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000216}
217
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100218void SMsgWriter::writeFramebufferUpdateStart(int nRects)
219{
220 startMsg(msgTypeFramebufferUpdate);
221 os->pad(1);
222
223 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100224 if (needSetDesktopName)
225 nRects++;
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100226 if (needCursor)
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100227 nRects++;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100228 if (needLEDState)
229 nRects++;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200230 if (needQEMUKeyEvent)
231 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100232 }
233
234 os->writeU16(nRects);
235
236 nRectsInUpdate = 0;
237 if (nRects == 0xFFFF)
238 nRectsInHeader = 0;
239 else
240 nRectsInHeader = nRects;
241
242 writePseudoRects();
243}
244
245void SMsgWriter::writeFramebufferUpdateEnd()
246{
247 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
248 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
249 "nRects out of sync");
250
251 if (nRectsInHeader == 0) {
252 // Send last rect. marker
253 os->writeS16(0);
254 os->writeS16(0);
255 os->writeU16(0);
256 os->writeU16(0);
257 os->writeU32(pseudoEncodingLastRect);
258 }
259
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100260 endMsg();
261}
262
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000263void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
264{
265 startRect(r,encodingCopyRect);
266 os->writeU16(srcX);
267 os->writeU16(srcY);
268 endRect();
269}
270
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100271void SMsgWriter::startRect(const Rect& r, int encoding)
272{
273 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
274 throw Exception("SMsgWriter::startRect: nRects out of sync");
275
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100276 os->writeS16(r.tl.x);
277 os->writeS16(r.tl.y);
278 os->writeU16(r.width());
279 os->writeU16(r.height());
280 os->writeU32(encoding);
281}
282
283void SMsgWriter::endRect()
284{
Pierre Ossman35294682016-04-29 14:27:08 +0200285 os->flush();
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100286}
287
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100288void SMsgWriter::startMsg(int type)
289{
290 os->writeU8(type);
291}
292
293void SMsgWriter::endMsg()
294{
295 os->flush();
296}
297
298void SMsgWriter::writePseudoRects()
299{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100300 if (needCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200301 const Cursor& cursor = client->cursor();
Pierre Ossman126e5642014-02-13 14:40:25 +0100302
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100303 if (client->supportsEncoding(pseudoEncodingCursorWithAlpha)) {
304 writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
305 cursor.hotspot().x, cursor.hotspot().y,
306 cursor.getBuffer());
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100307 } else if (client->supportsEncoding(pseudoEncodingVMwareCursor)) {
308 writeSetVMwareCursorRect(cursor.width(), cursor.height(),
309 cursor.hotspot().x, cursor.hotspot().y,
310 cursor.getBuffer());
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100311 } else if (client->supportsEncoding(pseudoEncodingCursor)) {
312 rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
313 rdr::U8Array mask(cursor.getMask());
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100314
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100315 const rdr::U8* in;
316 rdr::U8* out;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100317
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100318 in = cursor.getBuffer();
319 out = data.buf;
320 for (int i = 0;i < cursor.width()*cursor.height();i++) {
321 client->pf().bufferFromRGB(out, in, 1);
322 in += 4;
323 out += client->pf().bpp/8;
324 }
325
326 writeSetCursorRect(cursor.width(), cursor.height(),
327 cursor.hotspot().x, cursor.hotspot().y,
328 data.buf, mask.buf);
329 } else if (client->supportsEncoding(pseudoEncodingXCursor)) {
330 rdr::U8Array bitmap(cursor.getBitmap());
331 rdr::U8Array mask(cursor.getMask());
332
333 writeSetXCursorRect(cursor.width(), cursor.height(),
334 cursor.hotspot().x, cursor.hotspot().y,
335 bitmap.buf, mask.buf);
336 } else {
337 throw Exception("Client does not support local cursor");
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100338 }
Pierre Ossman126e5642014-02-13 14:40:25 +0100339
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100340 needCursor = false;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100341 }
342
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100343 if (needSetDesktopName) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200344 writeSetDesktopNameRect(client->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100345 needSetDesktopName = false;
346 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100347
348 if (needLEDState) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200349 writeLEDStateRect(client->ledState());
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100350 needLEDState = false;
351 }
Pierre Ossman5ae28212017-05-16 14:30:38 +0200352
353 if (needQEMUKeyEvent) {
354 writeQEMUKeyEventRect();
355 needQEMUKeyEvent = false;
356 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100357}
358
359void SMsgWriter::writeNoDataRects()
360{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100361 if (!extendedDesktopSizeMsgs.empty()) {
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100362 if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize)) {
363 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
364 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
365 // FIXME: We can probably skip multiple reasonServer entries
366 writeExtendedDesktopSizeRect(ri->reason, ri->result,
367 client->width(), client->height(),
368 client->screenLayout());
369 }
370 } else if (client->supportsEncoding(pseudoEncodingDesktopSize)) {
371 // Some clients assume this is the last rectangle so don't send anything
372 // more after this
373 writeSetDesktopSizeRect(client->width(), client->height());
374 } else {
375 throw Exception("Client does not support desktop size changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100376 }
377
378 extendedDesktopSizeMsgs.clear();
379 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100380}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100381
382void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
383{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200384 if (!client->supportsEncoding(pseudoEncodingDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100385 throw Exception("Client does not support desktop resize");
386 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
387 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
388
389 os->writeS16(0);
390 os->writeS16(0);
391 os->writeU16(width);
392 os->writeU16(height);
393 os->writeU32(pseudoEncodingDesktopSize);
394}
395
396void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
397 rdr::U16 result,
398 int fb_width,
399 int fb_height,
400 const ScreenSet& layout)
401{
402 ScreenSet::const_iterator si;
403
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200404 if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100405 throw Exception("Client does not support extended desktop resize");
406 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
407 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
408
409 os->writeU16(reason);
410 os->writeU16(result);
411 os->writeU16(fb_width);
412 os->writeU16(fb_height);
413 os->writeU32(pseudoEncodingExtendedDesktopSize);
414
415 os->writeU8(layout.num_screens());
416 os->pad(3);
417
418 for (si = layout.begin();si != layout.end();++si) {
419 os->writeU32(si->id);
420 os->writeU16(si->dimensions.tl.x);
421 os->writeU16(si->dimensions.tl.y);
422 os->writeU16(si->dimensions.width());
423 os->writeU16(si->dimensions.height());
424 os->writeU32(si->flags);
425 }
426}
427
428void SMsgWriter::writeSetDesktopNameRect(const char *name)
429{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200430 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100431 throw Exception("Client does not support desktop rename");
432 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
433 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
434
435 os->writeS16(0);
436 os->writeS16(0);
437 os->writeU16(0);
438 os->writeU16(0);
439 os->writeU32(pseudoEncodingDesktopName);
440 os->writeString(name);
441}
Pierre Ossman126e5642014-02-13 14:40:25 +0100442
443void SMsgWriter::writeSetCursorRect(int width, int height,
444 int hotspotX, int hotspotY,
445 const void* data, const void* mask)
446{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200447 if (!client->supportsEncoding(pseudoEncodingCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100448 throw Exception("Client does not support local cursors");
449 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
450 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
451
452 os->writeS16(hotspotX);
453 os->writeS16(hotspotY);
454 os->writeU16(width);
455 os->writeU16(height);
456 os->writeU32(pseudoEncodingCursor);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200457 os->writeBytes(data, width * height * (client->pf().bpp/8));
Pierre Ossman126e5642014-02-13 14:40:25 +0100458 os->writeBytes(mask, (width+7)/8 * height);
459}
460
461void SMsgWriter::writeSetXCursorRect(int width, int height,
462 int hotspotX, int hotspotY,
Pierre Ossman126e5642014-02-13 14:40:25 +0100463 const void* data, const void* mask)
464{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200465 if (!client->supportsEncoding(pseudoEncodingXCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100466 throw Exception("Client does not support local cursors");
467 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
468 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
469
470 os->writeS16(hotspotX);
471 os->writeS16(hotspotY);
472 os->writeU16(width);
473 os->writeU16(height);
474 os->writeU32(pseudoEncodingXCursor);
Brian P. Hinz33d78322017-11-16 17:46:15 -0500475 if (width * height > 0) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100476 os->writeU8(255);
477 os->writeU8(255);
478 os->writeU8(255);
479 os->writeU8(0);
480 os->writeU8(0);
481 os->writeU8(0);
Pierre Ossman126e5642014-02-13 14:40:25 +0100482 os->writeBytes(data, (width+7)/8 * height);
483 os->writeBytes(mask, (width+7)/8 * height);
484 }
485}
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100486
487void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
488 int hotspotX, int hotspotY,
489 const rdr::U8* data)
490{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200491 if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100492 throw Exception("Client does not support local cursors");
493 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
494 throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
495
496 os->writeS16(hotspotX);
497 os->writeS16(hotspotY);
498 os->writeU16(width);
499 os->writeU16(height);
500 os->writeU32(pseudoEncodingCursorWithAlpha);
501
502 // FIXME: Use an encoder with compression?
503 os->writeU32(encodingRaw);
504
505 // Alpha needs to be pre-multiplied
506 for (int i = 0;i < width*height;i++) {
507 os->writeU8((unsigned)data[0] * data[3] / 255);
508 os->writeU8((unsigned)data[1] * data[3] / 255);
509 os->writeU8((unsigned)data[2] * data[3] / 255);
510 os->writeU8(data[3]);
511 data += 4;
512 }
513}
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100514
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100515void SMsgWriter::writeSetVMwareCursorRect(int width, int height,
516 int hotspotX, int hotspotY,
517 const rdr::U8* data)
518{
519 if (!client->supportsEncoding(pseudoEncodingVMwareCursor))
520 throw Exception("Client does not support local cursors");
521 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
522 throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
523
524 os->writeS16(hotspotX);
525 os->writeS16(hotspotY);
526 os->writeU16(width);
527 os->writeU16(height);
528 os->writeU32(pseudoEncodingVMwareCursor);
529
530 os->writeU8(1); // Alpha cursor
531 os->pad(1);
532
533 // FIXME: Should alpha be premultiplied?
534 os->writeBytes(data, width*height*4);
535}
536
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100537void SMsgWriter::writeLEDStateRect(rdr::U8 state)
538{
Pierre Ossman62b07862018-11-05 16:28:57 +0100539 if (!client->supportsEncoding(pseudoEncodingLEDState) &&
540 !client->supportsEncoding(pseudoEncodingVMwareLEDState))
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100541 throw Exception("Client does not support LED state updates");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200542 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100543 throw Exception("Server does not support LED state updates");
544 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
545 throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
546
547 os->writeS16(0);
548 os->writeS16(0);
549 os->writeU16(0);
550 os->writeU16(0);
Pierre Ossman62b07862018-11-05 16:28:57 +0100551 if (client->supportsEncoding(pseudoEncodingLEDState)) {
552 os->writeU32(pseudoEncodingLEDState);
553 os->writeU8(state);
554 } else {
555 os->writeU32(pseudoEncodingVMwareLEDState);
556 os->writeU32(state);
557 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100558}
Pierre Ossman5ae28212017-05-16 14:30:38 +0200559
560void SMsgWriter::writeQEMUKeyEventRect()
561{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200562 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200563 throw Exception("Client does not support QEMU extended key events");
564 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
565 throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");
566
567 os->writeS16(0);
568 os->writeS16(0);
569 os->writeU16(0);
570 os->writeU16(0);
571 os->writeU32(pseudoEncodingQEMUKeyEvent);
572}