blob: 3d5a64caf6ff7e40b10a6a17867191af3a279a9a [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
Pierre Ossman66f1db52019-05-02 12:32:03 +020081void SMsgWriter::writeServerCutText(const char* str)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000082{
Pierre Ossman66f1db52019-05-02 12:32:03 +020083 size_t len;
84
85 if (strchr(str, '\r') != NULL)
Pierre Ossman546b2ad2019-05-02 12:32:03 +020086 throw Exception("Invalid carriage return in clipboard data");
87
Pierre Ossman66f1db52019-05-02 12:32:03 +020088 len = strlen(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000089 startMsg(msgTypeServerCutText);
90 os->pad(3);
91 os->writeU32(len);
92 os->writeBytes(str, len);
93 endMsg();
94}
95
Pierre Ossman7638e9c2014-01-16 13:12:40 +010096void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
97{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +020098 if (!client->supportsEncoding(pseudoEncodingFence))
Pierre Ossman7638e9c2014-01-16 13:12:40 +010099 throw Exception("Client does not support fences");
100 if (len > 64)
101 throw Exception("Too large fence payload");
102 if ((flags & ~fenceFlagsSupported) != 0)
103 throw Exception("Unknown fence flags");
104
105 startMsg(msgTypeServerFence);
106 os->pad(3);
107
108 os->writeU32(flags);
109
110 os->writeU8(len);
Michal Srbf3afa242017-03-27 19:02:15 +0300111
112 if (len > 0)
113 os->writeBytes(data, len);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100114
115 endMsg();
116}
117
118void SMsgWriter::writeEndOfContinuousUpdates()
119{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200120 if (!client->supportsEncoding(pseudoEncodingContinuousUpdates))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100121 throw Exception("Client does not support continuous updates");
122
123 startMsg(msgTypeEndOfContinuousUpdates);
124 endMsg();
125}
126
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100127void SMsgWriter::writeDesktopSize(rdr::U16 reason, rdr::U16 result)
128{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100129 ExtendedDesktopSizeMsg msg;
130
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100131 if (!client->supportsEncoding(pseudoEncodingDesktopSize) &&
132 !client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
133 throw Exception("Client does not support desktop size changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100134
135 msg.reason = reason;
136 msg.result = result;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100137
138 extendedDesktopSizeMsgs.push_back(msg);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100139}
140
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100141void SMsgWriter::writeSetDesktopName()
142{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200143 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100144 throw Exception("Client does not support desktop name changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100145
146 needSetDesktopName = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100147}
148
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100149void SMsgWriter::writeCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100150{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100151 if (!client->supportsEncoding(pseudoEncodingCursor) &&
152 !client->supportsEncoding(pseudoEncodingXCursor) &&
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100153 !client->supportsEncoding(pseudoEncodingCursorWithAlpha) &&
154 !client->supportsEncoding(pseudoEncodingVMwareCursor))
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100155 throw Exception("Client does not support local cursor");
Pierre Ossman126e5642014-02-13 14:40:25 +0100156
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100157 needCursor = true;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100158}
159
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100160void SMsgWriter::writeLEDState()
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100161{
Pierre Ossman62b07862018-11-05 16:28:57 +0100162 if (!client->supportsEncoding(pseudoEncodingLEDState) &&
163 !client->supportsEncoding(pseudoEncodingVMwareLEDState))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100164 throw Exception("Client does not support LED state");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200165 if (client->ledState() == ledUnknown)
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100166 throw Exception("Server has not specified LED state");
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100167
168 needLEDState = true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100169}
170
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100171void SMsgWriter::writeQEMUKeyEvent()
Pierre Ossman5ae28212017-05-16 14:30:38 +0200172{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200173 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100174 throw Exception("Client does not support QEMU key events");
Pierre Ossman5ae28212017-05-16 14:30:38 +0200175
176 needQEMUKeyEvent = true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200177}
178
Pierre Ossmane9962f72009-04-23 12:31:42 +0000179bool SMsgWriter::needFakeUpdate()
180{
Pierre Ossman126e5642014-02-13 14:40:25 +0100181 if (needSetDesktopName)
182 return true;
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100183 if (needCursor)
Pierre Ossman126e5642014-02-13 14:40:25 +0100184 return true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100185 if (needLEDState)
186 return true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200187 if (needQEMUKeyEvent)
188 return true;
Pierre Ossman126e5642014-02-13 14:40:25 +0100189 if (needNoDataUpdate())
190 return true;
191
192 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000193}
194
Pierre Ossmane9962f72009-04-23 12:31:42 +0000195bool SMsgWriter::needNoDataUpdate()
196{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100197 if (!extendedDesktopSizeMsgs.empty())
Pierre Ossman126e5642014-02-13 14:40:25 +0100198 return true;
199
200 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000201}
202
203void SMsgWriter::writeNoDataUpdate()
204{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100205 int nRects;
206
207 nRects = 0;
208
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100209 if (!extendedDesktopSizeMsgs.empty()) {
210 if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
211 nRects += extendedDesktopSizeMsgs.size();
212 else
213 nRects++;
214 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100215
216 writeFramebufferUpdateStart(nRects);
217 writeNoDataRects();
218 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000219}
220
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100221void SMsgWriter::writeFramebufferUpdateStart(int nRects)
222{
223 startMsg(msgTypeFramebufferUpdate);
224 os->pad(1);
225
226 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100227 if (needSetDesktopName)
228 nRects++;
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100229 if (needCursor)
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100230 nRects++;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100231 if (needLEDState)
232 nRects++;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200233 if (needQEMUKeyEvent)
234 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100235 }
236
237 os->writeU16(nRects);
238
239 nRectsInUpdate = 0;
240 if (nRects == 0xFFFF)
241 nRectsInHeader = 0;
242 else
243 nRectsInHeader = nRects;
244
245 writePseudoRects();
246}
247
248void SMsgWriter::writeFramebufferUpdateEnd()
249{
250 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
251 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
252 "nRects out of sync");
253
254 if (nRectsInHeader == 0) {
255 // Send last rect. marker
256 os->writeS16(0);
257 os->writeS16(0);
258 os->writeU16(0);
259 os->writeU16(0);
260 os->writeU32(pseudoEncodingLastRect);
261 }
262
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100263 endMsg();
264}
265
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000266void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
267{
268 startRect(r,encodingCopyRect);
269 os->writeU16(srcX);
270 os->writeU16(srcY);
271 endRect();
272}
273
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100274void SMsgWriter::startRect(const Rect& r, int encoding)
275{
276 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
277 throw Exception("SMsgWriter::startRect: nRects out of sync");
278
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100279 os->writeS16(r.tl.x);
280 os->writeS16(r.tl.y);
281 os->writeU16(r.width());
282 os->writeU16(r.height());
283 os->writeU32(encoding);
284}
285
286void SMsgWriter::endRect()
287{
Pierre Ossman35294682016-04-29 14:27:08 +0200288 os->flush();
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100289}
290
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100291void SMsgWriter::startMsg(int type)
292{
293 os->writeU8(type);
294}
295
296void SMsgWriter::endMsg()
297{
298 os->flush();
299}
300
301void SMsgWriter::writePseudoRects()
302{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100303 if (needCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200304 const Cursor& cursor = client->cursor();
Pierre Ossman126e5642014-02-13 14:40:25 +0100305
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100306 if (client->supportsEncoding(pseudoEncodingCursorWithAlpha)) {
307 writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
308 cursor.hotspot().x, cursor.hotspot().y,
309 cursor.getBuffer());
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100310 } else if (client->supportsEncoding(pseudoEncodingVMwareCursor)) {
311 writeSetVMwareCursorRect(cursor.width(), cursor.height(),
312 cursor.hotspot().x, cursor.hotspot().y,
313 cursor.getBuffer());
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100314 } else if (client->supportsEncoding(pseudoEncodingCursor)) {
315 rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
316 rdr::U8Array mask(cursor.getMask());
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100317
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100318 const rdr::U8* in;
319 rdr::U8* out;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100320
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100321 in = cursor.getBuffer();
322 out = data.buf;
323 for (int i = 0;i < cursor.width()*cursor.height();i++) {
324 client->pf().bufferFromRGB(out, in, 1);
325 in += 4;
326 out += client->pf().bpp/8;
327 }
328
329 writeSetCursorRect(cursor.width(), cursor.height(),
330 cursor.hotspot().x, cursor.hotspot().y,
331 data.buf, mask.buf);
332 } else if (client->supportsEncoding(pseudoEncodingXCursor)) {
333 rdr::U8Array bitmap(cursor.getBitmap());
334 rdr::U8Array mask(cursor.getMask());
335
336 writeSetXCursorRect(cursor.width(), cursor.height(),
337 cursor.hotspot().x, cursor.hotspot().y,
338 bitmap.buf, mask.buf);
339 } else {
340 throw Exception("Client does not support local cursor");
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100341 }
Pierre Ossman126e5642014-02-13 14:40:25 +0100342
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100343 needCursor = false;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100344 }
345
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100346 if (needSetDesktopName) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200347 writeSetDesktopNameRect(client->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100348 needSetDesktopName = false;
349 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100350
351 if (needLEDState) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200352 writeLEDStateRect(client->ledState());
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100353 needLEDState = false;
354 }
Pierre Ossman5ae28212017-05-16 14:30:38 +0200355
356 if (needQEMUKeyEvent) {
357 writeQEMUKeyEventRect();
358 needQEMUKeyEvent = false;
359 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100360}
361
362void SMsgWriter::writeNoDataRects()
363{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100364 if (!extendedDesktopSizeMsgs.empty()) {
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100365 if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize)) {
366 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
367 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
368 // FIXME: We can probably skip multiple reasonServer entries
369 writeExtendedDesktopSizeRect(ri->reason, ri->result,
370 client->width(), client->height(),
371 client->screenLayout());
372 }
373 } else if (client->supportsEncoding(pseudoEncodingDesktopSize)) {
374 // Some clients assume this is the last rectangle so don't send anything
375 // more after this
376 writeSetDesktopSizeRect(client->width(), client->height());
377 } else {
378 throw Exception("Client does not support desktop size changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100379 }
380
381 extendedDesktopSizeMsgs.clear();
382 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100383}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100384
385void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
386{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200387 if (!client->supportsEncoding(pseudoEncodingDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100388 throw Exception("Client does not support desktop resize");
389 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
390 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
391
392 os->writeS16(0);
393 os->writeS16(0);
394 os->writeU16(width);
395 os->writeU16(height);
396 os->writeU32(pseudoEncodingDesktopSize);
397}
398
399void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
400 rdr::U16 result,
401 int fb_width,
402 int fb_height,
403 const ScreenSet& layout)
404{
405 ScreenSet::const_iterator si;
406
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200407 if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100408 throw Exception("Client does not support extended desktop resize");
409 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
410 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
411
412 os->writeU16(reason);
413 os->writeU16(result);
414 os->writeU16(fb_width);
415 os->writeU16(fb_height);
416 os->writeU32(pseudoEncodingExtendedDesktopSize);
417
418 os->writeU8(layout.num_screens());
419 os->pad(3);
420
421 for (si = layout.begin();si != layout.end();++si) {
422 os->writeU32(si->id);
423 os->writeU16(si->dimensions.tl.x);
424 os->writeU16(si->dimensions.tl.y);
425 os->writeU16(si->dimensions.width());
426 os->writeU16(si->dimensions.height());
427 os->writeU32(si->flags);
428 }
429}
430
431void SMsgWriter::writeSetDesktopNameRect(const char *name)
432{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200433 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100434 throw Exception("Client does not support desktop rename");
435 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
436 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
437
438 os->writeS16(0);
439 os->writeS16(0);
440 os->writeU16(0);
441 os->writeU16(0);
442 os->writeU32(pseudoEncodingDesktopName);
443 os->writeString(name);
444}
Pierre Ossman126e5642014-02-13 14:40:25 +0100445
446void SMsgWriter::writeSetCursorRect(int width, int height,
447 int hotspotX, int hotspotY,
448 const void* data, const void* mask)
449{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200450 if (!client->supportsEncoding(pseudoEncodingCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100451 throw Exception("Client does not support local cursors");
452 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
453 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
454
455 os->writeS16(hotspotX);
456 os->writeS16(hotspotY);
457 os->writeU16(width);
458 os->writeU16(height);
459 os->writeU32(pseudoEncodingCursor);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200460 os->writeBytes(data, width * height * (client->pf().bpp/8));
Pierre Ossman126e5642014-02-13 14:40:25 +0100461 os->writeBytes(mask, (width+7)/8 * height);
462}
463
464void SMsgWriter::writeSetXCursorRect(int width, int height,
465 int hotspotX, int hotspotY,
Pierre Ossman126e5642014-02-13 14:40:25 +0100466 const void* data, const void* mask)
467{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200468 if (!client->supportsEncoding(pseudoEncodingXCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100469 throw Exception("Client does not support local cursors");
470 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
471 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
472
473 os->writeS16(hotspotX);
474 os->writeS16(hotspotY);
475 os->writeU16(width);
476 os->writeU16(height);
477 os->writeU32(pseudoEncodingXCursor);
Brian P. Hinz33d78322017-11-16 17:46:15 -0500478 if (width * height > 0) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100479 os->writeU8(255);
480 os->writeU8(255);
481 os->writeU8(255);
482 os->writeU8(0);
483 os->writeU8(0);
484 os->writeU8(0);
Pierre Ossman126e5642014-02-13 14:40:25 +0100485 os->writeBytes(data, (width+7)/8 * height);
486 os->writeBytes(mask, (width+7)/8 * height);
487 }
488}
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100489
490void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
491 int hotspotX, int hotspotY,
492 const rdr::U8* data)
493{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200494 if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100495 throw Exception("Client does not support local cursors");
496 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
497 throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
498
499 os->writeS16(hotspotX);
500 os->writeS16(hotspotY);
501 os->writeU16(width);
502 os->writeU16(height);
503 os->writeU32(pseudoEncodingCursorWithAlpha);
504
505 // FIXME: Use an encoder with compression?
506 os->writeU32(encodingRaw);
507
508 // Alpha needs to be pre-multiplied
509 for (int i = 0;i < width*height;i++) {
510 os->writeU8((unsigned)data[0] * data[3] / 255);
511 os->writeU8((unsigned)data[1] * data[3] / 255);
512 os->writeU8((unsigned)data[2] * data[3] / 255);
513 os->writeU8(data[3]);
514 data += 4;
515 }
516}
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100517
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100518void SMsgWriter::writeSetVMwareCursorRect(int width, int height,
519 int hotspotX, int hotspotY,
520 const rdr::U8* data)
521{
522 if (!client->supportsEncoding(pseudoEncodingVMwareCursor))
523 throw Exception("Client does not support local cursors");
524 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
525 throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
526
527 os->writeS16(hotspotX);
528 os->writeS16(hotspotY);
529 os->writeU16(width);
530 os->writeU16(height);
531 os->writeU32(pseudoEncodingVMwareCursor);
532
533 os->writeU8(1); // Alpha cursor
534 os->pad(1);
535
536 // FIXME: Should alpha be premultiplied?
537 os->writeBytes(data, width*height*4);
538}
539
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100540void SMsgWriter::writeLEDStateRect(rdr::U8 state)
541{
Pierre Ossman62b07862018-11-05 16:28:57 +0100542 if (!client->supportsEncoding(pseudoEncodingLEDState) &&
543 !client->supportsEncoding(pseudoEncodingVMwareLEDState))
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100544 throw Exception("Client does not support LED state updates");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200545 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100546 throw Exception("Server does not support LED state updates");
547 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
548 throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
549
550 os->writeS16(0);
551 os->writeS16(0);
552 os->writeU16(0);
553 os->writeU16(0);
Pierre Ossman62b07862018-11-05 16:28:57 +0100554 if (client->supportsEncoding(pseudoEncodingLEDState)) {
555 os->writeU32(pseudoEncodingLEDState);
556 os->writeU8(state);
557 } else {
558 os->writeU32(pseudoEncodingVMwareLEDState);
559 os->writeU32(state);
560 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100561}
Pierre Ossman5ae28212017-05-16 14:30:38 +0200562
563void SMsgWriter::writeQEMUKeyEventRect()
564{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200565 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200566 throw Exception("Client does not support QEMU extended key events");
567 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
568 throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");
569
570 os->writeS16(0);
571 os->writeS16(0);
572 os->writeU16(0);
573 os->writeU16(0);
574 os->writeU32(pseudoEncodingQEMUKeyEvent);
575}