blob: 766b0cb307e03a3db317e7067532bef56480cf90 [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 Ossman126e5642014-02-13 14:40:25 +010039 needSetDesktopSize(false), needExtendedDesktopSize(false),
Pierre Ossman8053c8e2017-02-21 12:59:04 +010040 needSetDesktopName(false), needSetCursor(false),
Pierre Ossmanb45a84f2016-12-12 16:59:15 +010041 needSetXCursor(false), needSetCursorWithAlpha(false),
Pierre Ossman5ae28212017-05-16 14:30:38 +020042 needLEDState(false), needQEMUKeyEvent(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000043{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044}
45
46SMsgWriter::~SMsgWriter()
47{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000048}
49
Pierre Ossman96728352018-06-20 11:35:05 +020050void SMsgWriter::writeServerInit(rdr::U16 width, rdr::U16 height,
51 const PixelFormat& pf, const char* name)
Pierre Ossman7638e9c2014-01-16 13:12:40 +010052{
Pierre Ossman96728352018-06-20 11:35:05 +020053 os->writeU16(width);
54 os->writeU16(height);
55 pf.write(os);
56 os->writeString(name);
Pierre Ossman7638e9c2014-01-16 13:12:40 +010057 endMsg();
58}
59
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000060void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010061 const rdr::U16 red[],
62 const rdr::U16 green[],
63 const rdr::U16 blue[])
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000064{
65 startMsg(msgTypeSetColourMapEntries);
66 os->pad(1);
67 os->writeU16(firstColour);
68 os->writeU16(nColours);
69 for (int i = firstColour; i < firstColour+nColours; i++) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010070 os->writeU16(red[i]);
71 os->writeU16(green[i]);
72 os->writeU16(blue[i]);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000073 }
74 endMsg();
75}
76
77void SMsgWriter::writeBell()
78{
79 startMsg(msgTypeBell);
80 endMsg();
81}
82
83void SMsgWriter::writeServerCutText(const char* str, int len)
84{
85 startMsg(msgTypeServerCutText);
86 os->pad(3);
87 os->writeU32(len);
88 os->writeBytes(str, len);
89 endMsg();
90}
91
Pierre Ossman7638e9c2014-01-16 13:12:40 +010092void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
93{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +020094 if (!client->supportsEncoding(pseudoEncodingFence))
Pierre Ossman7638e9c2014-01-16 13:12:40 +010095 throw Exception("Client does not support fences");
96 if (len > 64)
97 throw Exception("Too large fence payload");
98 if ((flags & ~fenceFlagsSupported) != 0)
99 throw Exception("Unknown fence flags");
100
101 startMsg(msgTypeServerFence);
102 os->pad(3);
103
104 os->writeU32(flags);
105
106 os->writeU8(len);
Michal Srbf3afa242017-03-27 19:02:15 +0300107
108 if (len > 0)
109 os->writeBytes(data, len);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100110
111 endMsg();
112}
113
114void SMsgWriter::writeEndOfContinuousUpdates()
115{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200116 if (!client->supportsEncoding(pseudoEncodingContinuousUpdates))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100117 throw Exception("Client does not support continuous updates");
118
119 startMsg(msgTypeEndOfContinuousUpdates);
120 endMsg();
121}
122
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100123bool SMsgWriter::writeSetDesktopSize() {
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200124 if (!client->supportsEncoding(pseudoEncodingDesktopSize))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100125 return false;
126
127 needSetDesktopSize = true;
128
129 return true;
130}
131
132bool SMsgWriter::writeExtendedDesktopSize() {
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200133 if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100134 return false;
135
136 needExtendedDesktopSize = true;
137
138 return true;
139}
140
141bool SMsgWriter::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
142 int fb_width, int fb_height,
143 const ScreenSet& layout) {
144 ExtendedDesktopSizeMsg msg;
145
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200146 if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100147 return false;
148
149 msg.reason = reason;
150 msg.result = result;
151 msg.fb_width = fb_width;
152 msg.fb_height = fb_height;
153 msg.layout = layout;
154
155 extendedDesktopSizeMsgs.push_back(msg);
156
157 return true;
158}
159
160bool SMsgWriter::writeSetDesktopName() {
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200161 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100162 return false;
163
164 needSetDesktopName = true;
165
166 return true;
167}
168
Pierre Ossman126e5642014-02-13 14:40:25 +0100169bool SMsgWriter::writeSetCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100170{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200171 if (!client->supportsEncoding(pseudoEncodingCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100172 return false;
173
174 needSetCursor = true;
175
176 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100177}
178
Pierre Ossman126e5642014-02-13 14:40:25 +0100179bool SMsgWriter::writeSetXCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100180{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200181 if (!client->supportsEncoding(pseudoEncodingXCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100182 return false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100183
Pierre Ossman126e5642014-02-13 14:40:25 +0100184 needSetXCursor = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100185
Pierre Ossman126e5642014-02-13 14:40:25 +0100186 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100187}
188
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100189bool SMsgWriter::writeSetCursorWithAlpha()
190{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200191 if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100192 return false;
193
194 needSetCursorWithAlpha = true;
195
196 return true;
197}
198
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100199bool SMsgWriter::writeLEDState()
200{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200201 if (!client->supportsEncoding(pseudoEncodingLEDState))
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100202 return false;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200203 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100204 return false;
205
206 needLEDState = true;
207
208 return true;
209}
210
Pierre Ossman5ae28212017-05-16 14:30:38 +0200211bool SMsgWriter::writeQEMUKeyEvent()
212{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200213 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200214 return false;
215
216 needQEMUKeyEvent = true;
217
218 return true;
219}
220
Pierre Ossmane9962f72009-04-23 12:31:42 +0000221bool SMsgWriter::needFakeUpdate()
222{
Pierre Ossman126e5642014-02-13 14:40:25 +0100223 if (needSetDesktopName)
224 return true;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100225 if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
Pierre Ossman126e5642014-02-13 14:40:25 +0100226 return true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100227 if (needLEDState)
228 return true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200229 if (needQEMUKeyEvent)
230 return true;
Pierre Ossman126e5642014-02-13 14:40:25 +0100231 if (needNoDataUpdate())
232 return true;
233
234 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000235}
236
Pierre Ossmane9962f72009-04-23 12:31:42 +0000237bool SMsgWriter::needNoDataUpdate()
238{
Pierre Ossman126e5642014-02-13 14:40:25 +0100239 if (needSetDesktopSize)
240 return true;
241 if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty())
242 return true;
243
244 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000245}
246
247void SMsgWriter::writeNoDataUpdate()
248{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100249 int nRects;
250
251 nRects = 0;
252
253 if (needSetDesktopSize)
254 nRects++;
255 if (needExtendedDesktopSize)
256 nRects++;
257 if (!extendedDesktopSizeMsgs.empty())
258 nRects += extendedDesktopSizeMsgs.size();
259
260 writeFramebufferUpdateStart(nRects);
261 writeNoDataRects();
262 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000263}
264
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100265void SMsgWriter::writeFramebufferUpdateStart(int nRects)
266{
267 startMsg(msgTypeFramebufferUpdate);
268 os->pad(1);
269
270 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100271 if (needSetDesktopName)
272 nRects++;
Pierre Ossman126e5642014-02-13 14:40:25 +0100273 if (needSetCursor)
274 nRects++;
275 if (needSetXCursor)
276 nRects++;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100277 if (needSetCursorWithAlpha)
278 nRects++;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100279 if (needLEDState)
280 nRects++;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200281 if (needQEMUKeyEvent)
282 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100283 }
284
285 os->writeU16(nRects);
286
287 nRectsInUpdate = 0;
288 if (nRects == 0xFFFF)
289 nRectsInHeader = 0;
290 else
291 nRectsInHeader = nRects;
292
293 writePseudoRects();
294}
295
296void SMsgWriter::writeFramebufferUpdateEnd()
297{
298 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
299 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
300 "nRects out of sync");
301
302 if (nRectsInHeader == 0) {
303 // Send last rect. marker
304 os->writeS16(0);
305 os->writeS16(0);
306 os->writeU16(0);
307 os->writeU16(0);
308 os->writeU32(pseudoEncodingLastRect);
309 }
310
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100311 endMsg();
312}
313
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000314void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
315{
316 startRect(r,encodingCopyRect);
317 os->writeU16(srcX);
318 os->writeU16(srcY);
319 endRect();
320}
321
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100322void SMsgWriter::startRect(const Rect& r, int encoding)
323{
324 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
325 throw Exception("SMsgWriter::startRect: nRects out of sync");
326
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100327 os->writeS16(r.tl.x);
328 os->writeS16(r.tl.y);
329 os->writeU16(r.width());
330 os->writeU16(r.height());
331 os->writeU32(encoding);
332}
333
334void SMsgWriter::endRect()
335{
Pierre Ossman35294682016-04-29 14:27:08 +0200336 os->flush();
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100337}
338
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100339void SMsgWriter::startMsg(int type)
340{
341 os->writeU8(type);
342}
343
344void SMsgWriter::endMsg()
345{
346 os->flush();
347}
348
349void SMsgWriter::writePseudoRects()
350{
Pierre Ossman126e5642014-02-13 14:40:25 +0100351 if (needSetCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200352 const Cursor& cursor = client->cursor();
Pierre Ossman126e5642014-02-13 14:40:25 +0100353
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200354 rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100355 rdr::U8Array mask(cursor.getMask());
356
357 const rdr::U8* in;
358 rdr::U8* out;
359
360 in = cursor.getBuffer();
361 out = data.buf;
362 for (int i = 0;i < cursor.width()*cursor.height();i++) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200363 client->pf().bufferFromRGB(out, in, 1);
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100364 in += 4;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200365 out += client->pf().bpp/8;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100366 }
Pierre Ossman126e5642014-02-13 14:40:25 +0100367
368 writeSetCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100369 cursor.hotspot().x, cursor.hotspot().y,
370 data.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100371 needSetCursor = false;
Pierre Ossman126e5642014-02-13 14:40:25 +0100372 }
373
374 if (needSetXCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200375 const Cursor& cursor = client->cursor();
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100376 rdr::U8Array bitmap(cursor.getBitmap());
377 rdr::U8Array mask(cursor.getMask());
Pierre Ossman126e5642014-02-13 14:40:25 +0100378
379 writeSetXCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100380 cursor.hotspot().x, cursor.hotspot().y,
381 bitmap.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100382 needSetXCursor = false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100383 }
384
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100385 if (needSetCursorWithAlpha) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200386 const Cursor& cursor = client->cursor();
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100387
388 writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
389 cursor.hotspot().x, cursor.hotspot().y,
390 cursor.getBuffer());
391 needSetCursorWithAlpha = false;
392 }
393
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100394 if (needSetDesktopName) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200395 writeSetDesktopNameRect(client->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100396 needSetDesktopName = false;
397 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100398
399 if (needLEDState) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200400 writeLEDStateRect(client->ledState());
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100401 needLEDState = false;
402 }
Pierre Ossman5ae28212017-05-16 14:30:38 +0200403
404 if (needQEMUKeyEvent) {
405 writeQEMUKeyEventRect();
406 needQEMUKeyEvent = false;
407 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100408}
409
410void SMsgWriter::writeNoDataRects()
411{
412 // Start with specific ExtendedDesktopSize messages
413 if (!extendedDesktopSizeMsgs.empty()) {
414 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100415
416 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100417 writeExtendedDesktopSizeRect(ri->reason, ri->result,
418 ri->fb_width, ri->fb_height, ri->layout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100419 }
420
421 extendedDesktopSizeMsgs.clear();
422 }
423
424 // Send this before SetDesktopSize to make life easier on the clients
425 if (needExtendedDesktopSize) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200426 writeExtendedDesktopSizeRect(0, 0, client->width(), client->height(),
427 client->screenLayout());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100428 needExtendedDesktopSize = false;
429 }
430
431 // Some clients assume this is the last rectangle so don't send anything
432 // more after this
433 if (needSetDesktopSize) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200434 writeSetDesktopSizeRect(client->width(), client->height());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100435 needSetDesktopSize = false;
436 }
437}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100438
439void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
440{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200441 if (!client->supportsEncoding(pseudoEncodingDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100442 throw Exception("Client does not support desktop resize");
443 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
444 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
445
446 os->writeS16(0);
447 os->writeS16(0);
448 os->writeU16(width);
449 os->writeU16(height);
450 os->writeU32(pseudoEncodingDesktopSize);
451}
452
453void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
454 rdr::U16 result,
455 int fb_width,
456 int fb_height,
457 const ScreenSet& layout)
458{
459 ScreenSet::const_iterator si;
460
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200461 if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100462 throw Exception("Client does not support extended desktop resize");
463 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
464 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
465
466 os->writeU16(reason);
467 os->writeU16(result);
468 os->writeU16(fb_width);
469 os->writeU16(fb_height);
470 os->writeU32(pseudoEncodingExtendedDesktopSize);
471
472 os->writeU8(layout.num_screens());
473 os->pad(3);
474
475 for (si = layout.begin();si != layout.end();++si) {
476 os->writeU32(si->id);
477 os->writeU16(si->dimensions.tl.x);
478 os->writeU16(si->dimensions.tl.y);
479 os->writeU16(si->dimensions.width());
480 os->writeU16(si->dimensions.height());
481 os->writeU32(si->flags);
482 }
483}
484
485void SMsgWriter::writeSetDesktopNameRect(const char *name)
486{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200487 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100488 throw Exception("Client does not support desktop rename");
489 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
490 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
491
492 os->writeS16(0);
493 os->writeS16(0);
494 os->writeU16(0);
495 os->writeU16(0);
496 os->writeU32(pseudoEncodingDesktopName);
497 os->writeString(name);
498}
Pierre Ossman126e5642014-02-13 14:40:25 +0100499
500void SMsgWriter::writeSetCursorRect(int width, int height,
501 int hotspotX, int hotspotY,
502 const void* data, const void* mask)
503{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200504 if (!client->supportsEncoding(pseudoEncodingCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100505 throw Exception("Client does not support local cursors");
506 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
507 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
508
509 os->writeS16(hotspotX);
510 os->writeS16(hotspotY);
511 os->writeU16(width);
512 os->writeU16(height);
513 os->writeU32(pseudoEncodingCursor);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200514 os->writeBytes(data, width * height * (client->pf().bpp/8));
Pierre Ossman126e5642014-02-13 14:40:25 +0100515 os->writeBytes(mask, (width+7)/8 * height);
516}
517
518void SMsgWriter::writeSetXCursorRect(int width, int height,
519 int hotspotX, int hotspotY,
Pierre Ossman126e5642014-02-13 14:40:25 +0100520 const void* data, const void* mask)
521{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200522 if (!client->supportsEncoding(pseudoEncodingXCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100523 throw Exception("Client does not support local cursors");
524 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
525 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
526
527 os->writeS16(hotspotX);
528 os->writeS16(hotspotY);
529 os->writeU16(width);
530 os->writeU16(height);
531 os->writeU32(pseudoEncodingXCursor);
Brian P. Hinz33d78322017-11-16 17:46:15 -0500532 if (width * height > 0) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100533 os->writeU8(255);
534 os->writeU8(255);
535 os->writeU8(255);
536 os->writeU8(0);
537 os->writeU8(0);
538 os->writeU8(0);
Pierre Ossman126e5642014-02-13 14:40:25 +0100539 os->writeBytes(data, (width+7)/8 * height);
540 os->writeBytes(mask, (width+7)/8 * height);
541 }
542}
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100543
544void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
545 int hotspotX, int hotspotY,
546 const rdr::U8* data)
547{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200548 if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100549 throw Exception("Client does not support local cursors");
550 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
551 throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
552
553 os->writeS16(hotspotX);
554 os->writeS16(hotspotY);
555 os->writeU16(width);
556 os->writeU16(height);
557 os->writeU32(pseudoEncodingCursorWithAlpha);
558
559 // FIXME: Use an encoder with compression?
560 os->writeU32(encodingRaw);
561
562 // Alpha needs to be pre-multiplied
563 for (int i = 0;i < width*height;i++) {
564 os->writeU8((unsigned)data[0] * data[3] / 255);
565 os->writeU8((unsigned)data[1] * data[3] / 255);
566 os->writeU8((unsigned)data[2] * data[3] / 255);
567 os->writeU8(data[3]);
568 data += 4;
569 }
570}
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100571
572void SMsgWriter::writeLEDStateRect(rdr::U8 state)
573{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200574 if (!client->supportsEncoding(pseudoEncodingLEDState))
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100575 throw Exception("Client does not support LED state updates");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200576 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100577 throw Exception("Server does not support LED state updates");
578 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
579 throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
580
581 os->writeS16(0);
582 os->writeS16(0);
583 os->writeU16(0);
584 os->writeU16(0);
585 os->writeU32(pseudoEncodingLEDState);
586 os->writeU8(state);
587}
Pierre Ossman5ae28212017-05-16 14:30:38 +0200588
589void SMsgWriter::writeQEMUKeyEventRect()
590{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200591 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200592 throw Exception("Client does not support QEMU extended key events");
593 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
594 throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");
595
596 os->writeS16(0);
597 os->writeS16(0);
598 os->writeU16(0);
599 os->writeU16(0);
600 os->writeU32(pseudoEncodingQEMUKeyEvent);
601}