blob: 6a2c2ba0bcf23cf70f921b7a395d7fb20cd856d6 [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{
83 startMsg(msgTypeServerCutText);
84 os->pad(3);
85 os->writeU32(len);
86 os->writeBytes(str, len);
87 endMsg();
88}
89
Pierre Ossman7638e9c2014-01-16 13:12:40 +010090void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
91{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +020092 if (!client->supportsEncoding(pseudoEncodingFence))
Pierre Ossman7638e9c2014-01-16 13:12:40 +010093 throw Exception("Client does not support fences");
94 if (len > 64)
95 throw Exception("Too large fence payload");
96 if ((flags & ~fenceFlagsSupported) != 0)
97 throw Exception("Unknown fence flags");
98
99 startMsg(msgTypeServerFence);
100 os->pad(3);
101
102 os->writeU32(flags);
103
104 os->writeU8(len);
Michal Srbf3afa242017-03-27 19:02:15 +0300105
106 if (len > 0)
107 os->writeBytes(data, len);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100108
109 endMsg();
110}
111
112void SMsgWriter::writeEndOfContinuousUpdates()
113{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200114 if (!client->supportsEncoding(pseudoEncodingContinuousUpdates))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100115 throw Exception("Client does not support continuous updates");
116
117 startMsg(msgTypeEndOfContinuousUpdates);
118 endMsg();
119}
120
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100121void SMsgWriter::writeDesktopSize(rdr::U16 reason, rdr::U16 result)
122{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100123 ExtendedDesktopSizeMsg msg;
124
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100125 if (!client->supportsEncoding(pseudoEncodingDesktopSize) &&
126 !client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
127 throw Exception("Client does not support desktop size changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100128
129 msg.reason = reason;
130 msg.result = result;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100131
132 extendedDesktopSizeMsgs.push_back(msg);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100133}
134
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100135void SMsgWriter::writeSetDesktopName()
136{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200137 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100138 throw Exception("Client does not support desktop name changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100139
140 needSetDesktopName = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100141}
142
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100143void SMsgWriter::writeCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100144{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100145 if (!client->supportsEncoding(pseudoEncodingCursor) &&
146 !client->supportsEncoding(pseudoEncodingXCursor) &&
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100147 !client->supportsEncoding(pseudoEncodingCursorWithAlpha) &&
148 !client->supportsEncoding(pseudoEncodingVMwareCursor))
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100149 throw Exception("Client does not support local cursor");
Pierre Ossman126e5642014-02-13 14:40:25 +0100150
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100151 needCursor = true;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100152}
153
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100154void SMsgWriter::writeLEDState()
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100155{
Pierre Ossman62b07862018-11-05 16:28:57 +0100156 if (!client->supportsEncoding(pseudoEncodingLEDState) &&
157 !client->supportsEncoding(pseudoEncodingVMwareLEDState))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100158 throw Exception("Client does not support LED state");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200159 if (client->ledState() == ledUnknown)
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100160 throw Exception("Server has not specified LED state");
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100161
162 needLEDState = true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100163}
164
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100165void SMsgWriter::writeQEMUKeyEvent()
Pierre Ossman5ae28212017-05-16 14:30:38 +0200166{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200167 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100168 throw Exception("Client does not support QEMU key events");
Pierre Ossman5ae28212017-05-16 14:30:38 +0200169
170 needQEMUKeyEvent = true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200171}
172
Pierre Ossmane9962f72009-04-23 12:31:42 +0000173bool SMsgWriter::needFakeUpdate()
174{
Pierre Ossman126e5642014-02-13 14:40:25 +0100175 if (needSetDesktopName)
176 return true;
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100177 if (needCursor)
Pierre Ossman126e5642014-02-13 14:40:25 +0100178 return true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100179 if (needLEDState)
180 return true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200181 if (needQEMUKeyEvent)
182 return true;
Pierre Ossman126e5642014-02-13 14:40:25 +0100183 if (needNoDataUpdate())
184 return true;
185
186 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000187}
188
Pierre Ossmane9962f72009-04-23 12:31:42 +0000189bool SMsgWriter::needNoDataUpdate()
190{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100191 if (!extendedDesktopSizeMsgs.empty())
Pierre Ossman126e5642014-02-13 14:40:25 +0100192 return true;
193
194 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000195}
196
197void SMsgWriter::writeNoDataUpdate()
198{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100199 int nRects;
200
201 nRects = 0;
202
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100203 if (!extendedDesktopSizeMsgs.empty()) {
204 if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
205 nRects += extendedDesktopSizeMsgs.size();
206 else
207 nRects++;
208 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100209
210 writeFramebufferUpdateStart(nRects);
211 writeNoDataRects();
212 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000213}
214
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100215void SMsgWriter::writeFramebufferUpdateStart(int nRects)
216{
217 startMsg(msgTypeFramebufferUpdate);
218 os->pad(1);
219
220 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100221 if (needSetDesktopName)
222 nRects++;
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100223 if (needCursor)
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100224 nRects++;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100225 if (needLEDState)
226 nRects++;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200227 if (needQEMUKeyEvent)
228 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100229 }
230
231 os->writeU16(nRects);
232
233 nRectsInUpdate = 0;
234 if (nRects == 0xFFFF)
235 nRectsInHeader = 0;
236 else
237 nRectsInHeader = nRects;
238
239 writePseudoRects();
240}
241
242void SMsgWriter::writeFramebufferUpdateEnd()
243{
244 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
245 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
246 "nRects out of sync");
247
248 if (nRectsInHeader == 0) {
249 // Send last rect. marker
250 os->writeS16(0);
251 os->writeS16(0);
252 os->writeU16(0);
253 os->writeU16(0);
254 os->writeU32(pseudoEncodingLastRect);
255 }
256
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100257 endMsg();
258}
259
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000260void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
261{
262 startRect(r,encodingCopyRect);
263 os->writeU16(srcX);
264 os->writeU16(srcY);
265 endRect();
266}
267
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100268void SMsgWriter::startRect(const Rect& r, int encoding)
269{
270 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
271 throw Exception("SMsgWriter::startRect: nRects out of sync");
272
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100273 os->writeS16(r.tl.x);
274 os->writeS16(r.tl.y);
275 os->writeU16(r.width());
276 os->writeU16(r.height());
277 os->writeU32(encoding);
278}
279
280void SMsgWriter::endRect()
281{
Pierre Ossman35294682016-04-29 14:27:08 +0200282 os->flush();
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100283}
284
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100285void SMsgWriter::startMsg(int type)
286{
287 os->writeU8(type);
288}
289
290void SMsgWriter::endMsg()
291{
292 os->flush();
293}
294
295void SMsgWriter::writePseudoRects()
296{
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100297 if (needCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200298 const Cursor& cursor = client->cursor();
Pierre Ossman126e5642014-02-13 14:40:25 +0100299
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100300 if (client->supportsEncoding(pseudoEncodingCursorWithAlpha)) {
301 writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
302 cursor.hotspot().x, cursor.hotspot().y,
303 cursor.getBuffer());
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100304 } else if (client->supportsEncoding(pseudoEncodingVMwareCursor)) {
305 writeSetVMwareCursorRect(cursor.width(), cursor.height(),
306 cursor.hotspot().x, cursor.hotspot().y,
307 cursor.getBuffer());
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100308 } else if (client->supportsEncoding(pseudoEncodingCursor)) {
309 rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
310 rdr::U8Array mask(cursor.getMask());
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100311
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100312 const rdr::U8* in;
313 rdr::U8* out;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100314
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100315 in = cursor.getBuffer();
316 out = data.buf;
317 for (int i = 0;i < cursor.width()*cursor.height();i++) {
318 client->pf().bufferFromRGB(out, in, 1);
319 in += 4;
320 out += client->pf().bpp/8;
321 }
322
323 writeSetCursorRect(cursor.width(), cursor.height(),
324 cursor.hotspot().x, cursor.hotspot().y,
325 data.buf, mask.buf);
326 } else if (client->supportsEncoding(pseudoEncodingXCursor)) {
327 rdr::U8Array bitmap(cursor.getBitmap());
328 rdr::U8Array mask(cursor.getMask());
329
330 writeSetXCursorRect(cursor.width(), cursor.height(),
331 cursor.hotspot().x, cursor.hotspot().y,
332 bitmap.buf, mask.buf);
333 } else {
334 throw Exception("Client does not support local cursor");
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100335 }
Pierre Ossman126e5642014-02-13 14:40:25 +0100336
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100337 needCursor = false;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100338 }
339
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100340 if (needSetDesktopName) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200341 writeSetDesktopNameRect(client->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100342 needSetDesktopName = false;
343 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100344
345 if (needLEDState) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200346 writeLEDStateRect(client->ledState());
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100347 needLEDState = false;
348 }
Pierre Ossman5ae28212017-05-16 14:30:38 +0200349
350 if (needQEMUKeyEvent) {
351 writeQEMUKeyEventRect();
352 needQEMUKeyEvent = false;
353 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100354}
355
356void SMsgWriter::writeNoDataRects()
357{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100358 if (!extendedDesktopSizeMsgs.empty()) {
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100359 if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize)) {
360 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
361 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
362 // FIXME: We can probably skip multiple reasonServer entries
363 writeExtendedDesktopSizeRect(ri->reason, ri->result,
364 client->width(), client->height(),
365 client->screenLayout());
366 }
367 } else if (client->supportsEncoding(pseudoEncodingDesktopSize)) {
368 // Some clients assume this is the last rectangle so don't send anything
369 // more after this
370 writeSetDesktopSizeRect(client->width(), client->height());
371 } else {
372 throw Exception("Client does not support desktop size changes");
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100373 }
374
375 extendedDesktopSizeMsgs.clear();
376 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100377}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100378
379void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
380{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200381 if (!client->supportsEncoding(pseudoEncodingDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100382 throw Exception("Client does not support desktop resize");
383 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
384 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
385
386 os->writeS16(0);
387 os->writeS16(0);
388 os->writeU16(width);
389 os->writeU16(height);
390 os->writeU32(pseudoEncodingDesktopSize);
391}
392
393void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
394 rdr::U16 result,
395 int fb_width,
396 int fb_height,
397 const ScreenSet& layout)
398{
399 ScreenSet::const_iterator si;
400
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200401 if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100402 throw Exception("Client does not support extended desktop resize");
403 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
404 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
405
406 os->writeU16(reason);
407 os->writeU16(result);
408 os->writeU16(fb_width);
409 os->writeU16(fb_height);
410 os->writeU32(pseudoEncodingExtendedDesktopSize);
411
412 os->writeU8(layout.num_screens());
413 os->pad(3);
414
415 for (si = layout.begin();si != layout.end();++si) {
416 os->writeU32(si->id);
417 os->writeU16(si->dimensions.tl.x);
418 os->writeU16(si->dimensions.tl.y);
419 os->writeU16(si->dimensions.width());
420 os->writeU16(si->dimensions.height());
421 os->writeU32(si->flags);
422 }
423}
424
425void SMsgWriter::writeSetDesktopNameRect(const char *name)
426{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200427 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100428 throw Exception("Client does not support desktop rename");
429 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
430 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
431
432 os->writeS16(0);
433 os->writeS16(0);
434 os->writeU16(0);
435 os->writeU16(0);
436 os->writeU32(pseudoEncodingDesktopName);
437 os->writeString(name);
438}
Pierre Ossman126e5642014-02-13 14:40:25 +0100439
440void SMsgWriter::writeSetCursorRect(int width, int height,
441 int hotspotX, int hotspotY,
442 const void* data, const void* mask)
443{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200444 if (!client->supportsEncoding(pseudoEncodingCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100445 throw Exception("Client does not support local cursors");
446 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
447 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
448
449 os->writeS16(hotspotX);
450 os->writeS16(hotspotY);
451 os->writeU16(width);
452 os->writeU16(height);
453 os->writeU32(pseudoEncodingCursor);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200454 os->writeBytes(data, width * height * (client->pf().bpp/8));
Pierre Ossman126e5642014-02-13 14:40:25 +0100455 os->writeBytes(mask, (width+7)/8 * height);
456}
457
458void SMsgWriter::writeSetXCursorRect(int width, int height,
459 int hotspotX, int hotspotY,
Pierre Ossman126e5642014-02-13 14:40:25 +0100460 const void* data, const void* mask)
461{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200462 if (!client->supportsEncoding(pseudoEncodingXCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100463 throw Exception("Client does not support local cursors");
464 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
465 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
466
467 os->writeS16(hotspotX);
468 os->writeS16(hotspotY);
469 os->writeU16(width);
470 os->writeU16(height);
471 os->writeU32(pseudoEncodingXCursor);
Brian P. Hinz33d78322017-11-16 17:46:15 -0500472 if (width * height > 0) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100473 os->writeU8(255);
474 os->writeU8(255);
475 os->writeU8(255);
476 os->writeU8(0);
477 os->writeU8(0);
478 os->writeU8(0);
Pierre Ossman126e5642014-02-13 14:40:25 +0100479 os->writeBytes(data, (width+7)/8 * height);
480 os->writeBytes(mask, (width+7)/8 * height);
481 }
482}
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100483
484void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
485 int hotspotX, int hotspotY,
486 const rdr::U8* data)
487{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200488 if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100489 throw Exception("Client does not support local cursors");
490 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
491 throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
492
493 os->writeS16(hotspotX);
494 os->writeS16(hotspotY);
495 os->writeU16(width);
496 os->writeU16(height);
497 os->writeU32(pseudoEncodingCursorWithAlpha);
498
499 // FIXME: Use an encoder with compression?
500 os->writeU32(encodingRaw);
501
502 // Alpha needs to be pre-multiplied
503 for (int i = 0;i < width*height;i++) {
504 os->writeU8((unsigned)data[0] * data[3] / 255);
505 os->writeU8((unsigned)data[1] * data[3] / 255);
506 os->writeU8((unsigned)data[2] * data[3] / 255);
507 os->writeU8(data[3]);
508 data += 4;
509 }
510}
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100511
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100512void SMsgWriter::writeSetVMwareCursorRect(int width, int height,
513 int hotspotX, int hotspotY,
514 const rdr::U8* data)
515{
516 if (!client->supportsEncoding(pseudoEncodingVMwareCursor))
517 throw Exception("Client does not support local cursors");
518 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
519 throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
520
521 os->writeS16(hotspotX);
522 os->writeS16(hotspotY);
523 os->writeU16(width);
524 os->writeU16(height);
525 os->writeU32(pseudoEncodingVMwareCursor);
526
527 os->writeU8(1); // Alpha cursor
528 os->pad(1);
529
530 // FIXME: Should alpha be premultiplied?
531 os->writeBytes(data, width*height*4);
532}
533
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100534void SMsgWriter::writeLEDStateRect(rdr::U8 state)
535{
Pierre Ossman62b07862018-11-05 16:28:57 +0100536 if (!client->supportsEncoding(pseudoEncodingLEDState) &&
537 !client->supportsEncoding(pseudoEncodingVMwareLEDState))
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100538 throw Exception("Client does not support LED state updates");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200539 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100540 throw Exception("Server does not support LED state updates");
541 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
542 throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
543
544 os->writeS16(0);
545 os->writeS16(0);
546 os->writeU16(0);
547 os->writeU16(0);
Pierre Ossman62b07862018-11-05 16:28:57 +0100548 if (client->supportsEncoding(pseudoEncodingLEDState)) {
549 os->writeU32(pseudoEncodingLEDState);
550 os->writeU8(state);
551 } else {
552 os->writeU32(pseudoEncodingVMwareLEDState);
553 os->writeU32(state);
554 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100555}
Pierre Ossman5ae28212017-05-16 14:30:38 +0200556
557void SMsgWriter::writeQEMUKeyEventRect()
558{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200559 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200560 throw Exception("Client does not support QEMU extended key events");
561 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
562 throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");
563
564 os->writeS16(0);
565 os->writeS16(0);
566 os->writeU16(0);
567 os->writeU16(0);
568 os->writeU32(pseudoEncodingQEMUKeyEvent);
569}