blob: 0ca198123a8883e83ee974ffc062bde5f26832d4 [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
Pierre Ossmandfc28f12018-12-10 20:16:12 +0100141bool SMsgWriter::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100142 ExtendedDesktopSizeMsg msg;
143
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200144 if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100145 return false;
146
147 msg.reason = reason;
148 msg.result = result;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100149
150 extendedDesktopSizeMsgs.push_back(msg);
151
152 return true;
153}
154
155bool SMsgWriter::writeSetDesktopName() {
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200156 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100157 return false;
158
159 needSetDesktopName = true;
160
161 return true;
162}
163
Pierre Ossman126e5642014-02-13 14:40:25 +0100164bool SMsgWriter::writeSetCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100165{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200166 if (!client->supportsEncoding(pseudoEncodingCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100167 return false;
168
169 needSetCursor = true;
170
171 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100172}
173
Pierre Ossman126e5642014-02-13 14:40:25 +0100174bool SMsgWriter::writeSetXCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100175{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200176 if (!client->supportsEncoding(pseudoEncodingXCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100177 return false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100178
Pierre Ossman126e5642014-02-13 14:40:25 +0100179 needSetXCursor = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100180
Pierre Ossman126e5642014-02-13 14:40:25 +0100181 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100182}
183
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100184bool SMsgWriter::writeSetCursorWithAlpha()
185{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200186 if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100187 return false;
188
189 needSetCursorWithAlpha = true;
190
191 return true;
192}
193
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100194bool SMsgWriter::writeLEDState()
195{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200196 if (!client->supportsEncoding(pseudoEncodingLEDState))
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100197 return false;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200198 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100199 return false;
200
201 needLEDState = true;
202
203 return true;
204}
205
Pierre Ossman5ae28212017-05-16 14:30:38 +0200206bool SMsgWriter::writeQEMUKeyEvent()
207{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200208 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200209 return false;
210
211 needQEMUKeyEvent = true;
212
213 return true;
214}
215
Pierre Ossmane9962f72009-04-23 12:31:42 +0000216bool SMsgWriter::needFakeUpdate()
217{
Pierre Ossman126e5642014-02-13 14:40:25 +0100218 if (needSetDesktopName)
219 return true;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100220 if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
Pierre Ossman126e5642014-02-13 14:40:25 +0100221 return true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100222 if (needLEDState)
223 return true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200224 if (needQEMUKeyEvent)
225 return true;
Pierre Ossman126e5642014-02-13 14:40:25 +0100226 if (needNoDataUpdate())
227 return true;
228
229 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000230}
231
Pierre Ossmane9962f72009-04-23 12:31:42 +0000232bool SMsgWriter::needNoDataUpdate()
233{
Pierre Ossman126e5642014-02-13 14:40:25 +0100234 if (needSetDesktopSize)
235 return true;
236 if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty())
237 return true;
238
239 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000240}
241
242void SMsgWriter::writeNoDataUpdate()
243{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100244 int nRects;
245
246 nRects = 0;
247
248 if (needSetDesktopSize)
249 nRects++;
250 if (needExtendedDesktopSize)
251 nRects++;
252 if (!extendedDesktopSizeMsgs.empty())
253 nRects += extendedDesktopSizeMsgs.size();
254
255 writeFramebufferUpdateStart(nRects);
256 writeNoDataRects();
257 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000258}
259
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100260void SMsgWriter::writeFramebufferUpdateStart(int nRects)
261{
262 startMsg(msgTypeFramebufferUpdate);
263 os->pad(1);
264
265 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100266 if (needSetDesktopName)
267 nRects++;
Pierre Ossman126e5642014-02-13 14:40:25 +0100268 if (needSetCursor)
269 nRects++;
270 if (needSetXCursor)
271 nRects++;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100272 if (needSetCursorWithAlpha)
273 nRects++;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100274 if (needLEDState)
275 nRects++;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200276 if (needQEMUKeyEvent)
277 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100278 }
279
280 os->writeU16(nRects);
281
282 nRectsInUpdate = 0;
283 if (nRects == 0xFFFF)
284 nRectsInHeader = 0;
285 else
286 nRectsInHeader = nRects;
287
288 writePseudoRects();
289}
290
291void SMsgWriter::writeFramebufferUpdateEnd()
292{
293 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
294 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
295 "nRects out of sync");
296
297 if (nRectsInHeader == 0) {
298 // Send last rect. marker
299 os->writeS16(0);
300 os->writeS16(0);
301 os->writeU16(0);
302 os->writeU16(0);
303 os->writeU32(pseudoEncodingLastRect);
304 }
305
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100306 endMsg();
307}
308
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000309void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
310{
311 startRect(r,encodingCopyRect);
312 os->writeU16(srcX);
313 os->writeU16(srcY);
314 endRect();
315}
316
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100317void SMsgWriter::startRect(const Rect& r, int encoding)
318{
319 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
320 throw Exception("SMsgWriter::startRect: nRects out of sync");
321
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100322 os->writeS16(r.tl.x);
323 os->writeS16(r.tl.y);
324 os->writeU16(r.width());
325 os->writeU16(r.height());
326 os->writeU32(encoding);
327}
328
329void SMsgWriter::endRect()
330{
Pierre Ossman35294682016-04-29 14:27:08 +0200331 os->flush();
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100332}
333
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100334void SMsgWriter::startMsg(int type)
335{
336 os->writeU8(type);
337}
338
339void SMsgWriter::endMsg()
340{
341 os->flush();
342}
343
344void SMsgWriter::writePseudoRects()
345{
Pierre Ossman126e5642014-02-13 14:40:25 +0100346 if (needSetCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200347 const Cursor& cursor = client->cursor();
Pierre Ossman126e5642014-02-13 14:40:25 +0100348
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200349 rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100350 rdr::U8Array mask(cursor.getMask());
351
352 const rdr::U8* in;
353 rdr::U8* out;
354
355 in = cursor.getBuffer();
356 out = data.buf;
357 for (int i = 0;i < cursor.width()*cursor.height();i++) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200358 client->pf().bufferFromRGB(out, in, 1);
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100359 in += 4;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200360 out += client->pf().bpp/8;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100361 }
Pierre Ossman126e5642014-02-13 14:40:25 +0100362
363 writeSetCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100364 cursor.hotspot().x, cursor.hotspot().y,
365 data.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100366 needSetCursor = false;
Pierre Ossman126e5642014-02-13 14:40:25 +0100367 }
368
369 if (needSetXCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200370 const Cursor& cursor = client->cursor();
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100371 rdr::U8Array bitmap(cursor.getBitmap());
372 rdr::U8Array mask(cursor.getMask());
Pierre Ossman126e5642014-02-13 14:40:25 +0100373
374 writeSetXCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100375 cursor.hotspot().x, cursor.hotspot().y,
376 bitmap.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100377 needSetXCursor = false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100378 }
379
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100380 if (needSetCursorWithAlpha) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200381 const Cursor& cursor = client->cursor();
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100382
383 writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
384 cursor.hotspot().x, cursor.hotspot().y,
385 cursor.getBuffer());
386 needSetCursorWithAlpha = false;
387 }
388
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100389 if (needSetDesktopName) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200390 writeSetDesktopNameRect(client->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100391 needSetDesktopName = false;
392 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100393
394 if (needLEDState) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200395 writeLEDStateRect(client->ledState());
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100396 needLEDState = false;
397 }
Pierre Ossman5ae28212017-05-16 14:30:38 +0200398
399 if (needQEMUKeyEvent) {
400 writeQEMUKeyEventRect();
401 needQEMUKeyEvent = false;
402 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100403}
404
405void SMsgWriter::writeNoDataRects()
406{
407 // Start with specific ExtendedDesktopSize messages
408 if (!extendedDesktopSizeMsgs.empty()) {
409 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100410
411 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100412 writeExtendedDesktopSizeRect(ri->reason, ri->result,
Pierre Ossmandfc28f12018-12-10 20:16:12 +0100413 client->width(), client->height(),
414 client->screenLayout());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100415 }
416
417 extendedDesktopSizeMsgs.clear();
418 }
419
420 // Send this before SetDesktopSize to make life easier on the clients
421 if (needExtendedDesktopSize) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200422 writeExtendedDesktopSizeRect(0, 0, client->width(), client->height(),
423 client->screenLayout());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100424 needExtendedDesktopSize = false;
425 }
426
427 // Some clients assume this is the last rectangle so don't send anything
428 // more after this
429 if (needSetDesktopSize) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200430 writeSetDesktopSizeRect(client->width(), client->height());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100431 needSetDesktopSize = false;
432 }
433}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100434
435void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
436{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200437 if (!client->supportsEncoding(pseudoEncodingDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100438 throw Exception("Client does not support desktop resize");
439 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
440 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
441
442 os->writeS16(0);
443 os->writeS16(0);
444 os->writeU16(width);
445 os->writeU16(height);
446 os->writeU32(pseudoEncodingDesktopSize);
447}
448
449void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
450 rdr::U16 result,
451 int fb_width,
452 int fb_height,
453 const ScreenSet& layout)
454{
455 ScreenSet::const_iterator si;
456
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200457 if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100458 throw Exception("Client does not support extended desktop resize");
459 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
460 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
461
462 os->writeU16(reason);
463 os->writeU16(result);
464 os->writeU16(fb_width);
465 os->writeU16(fb_height);
466 os->writeU32(pseudoEncodingExtendedDesktopSize);
467
468 os->writeU8(layout.num_screens());
469 os->pad(3);
470
471 for (si = layout.begin();si != layout.end();++si) {
472 os->writeU32(si->id);
473 os->writeU16(si->dimensions.tl.x);
474 os->writeU16(si->dimensions.tl.y);
475 os->writeU16(si->dimensions.width());
476 os->writeU16(si->dimensions.height());
477 os->writeU32(si->flags);
478 }
479}
480
481void SMsgWriter::writeSetDesktopNameRect(const char *name)
482{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200483 if (!client->supportsEncoding(pseudoEncodingDesktopName))
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100484 throw Exception("Client does not support desktop rename");
485 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
486 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
487
488 os->writeS16(0);
489 os->writeS16(0);
490 os->writeU16(0);
491 os->writeU16(0);
492 os->writeU32(pseudoEncodingDesktopName);
493 os->writeString(name);
494}
Pierre Ossman126e5642014-02-13 14:40:25 +0100495
496void SMsgWriter::writeSetCursorRect(int width, int height,
497 int hotspotX, int hotspotY,
498 const void* data, const void* mask)
499{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200500 if (!client->supportsEncoding(pseudoEncodingCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100501 throw Exception("Client does not support local cursors");
502 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
503 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
504
505 os->writeS16(hotspotX);
506 os->writeS16(hotspotY);
507 os->writeU16(width);
508 os->writeU16(height);
509 os->writeU32(pseudoEncodingCursor);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200510 os->writeBytes(data, width * height * (client->pf().bpp/8));
Pierre Ossman126e5642014-02-13 14:40:25 +0100511 os->writeBytes(mask, (width+7)/8 * height);
512}
513
514void SMsgWriter::writeSetXCursorRect(int width, int height,
515 int hotspotX, int hotspotY,
Pierre Ossman126e5642014-02-13 14:40:25 +0100516 const void* data, const void* mask)
517{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200518 if (!client->supportsEncoding(pseudoEncodingXCursor))
Pierre Ossman126e5642014-02-13 14:40:25 +0100519 throw Exception("Client does not support local cursors");
520 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
521 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
522
523 os->writeS16(hotspotX);
524 os->writeS16(hotspotY);
525 os->writeU16(width);
526 os->writeU16(height);
527 os->writeU32(pseudoEncodingXCursor);
Brian P. Hinz33d78322017-11-16 17:46:15 -0500528 if (width * height > 0) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100529 os->writeU8(255);
530 os->writeU8(255);
531 os->writeU8(255);
532 os->writeU8(0);
533 os->writeU8(0);
534 os->writeU8(0);
Pierre Ossman126e5642014-02-13 14:40:25 +0100535 os->writeBytes(data, (width+7)/8 * height);
536 os->writeBytes(mask, (width+7)/8 * height);
537 }
538}
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100539
540void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
541 int hotspotX, int hotspotY,
542 const rdr::U8* data)
543{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200544 if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100545 throw Exception("Client does not support local cursors");
546 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
547 throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
548
549 os->writeS16(hotspotX);
550 os->writeS16(hotspotY);
551 os->writeU16(width);
552 os->writeU16(height);
553 os->writeU32(pseudoEncodingCursorWithAlpha);
554
555 // FIXME: Use an encoder with compression?
556 os->writeU32(encodingRaw);
557
558 // Alpha needs to be pre-multiplied
559 for (int i = 0;i < width*height;i++) {
560 os->writeU8((unsigned)data[0] * data[3] / 255);
561 os->writeU8((unsigned)data[1] * data[3] / 255);
562 os->writeU8((unsigned)data[2] * data[3] / 255);
563 os->writeU8(data[3]);
564 data += 4;
565 }
566}
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100567
568void SMsgWriter::writeLEDStateRect(rdr::U8 state)
569{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200570 if (!client->supportsEncoding(pseudoEncodingLEDState))
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100571 throw Exception("Client does not support LED state updates");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200572 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100573 throw Exception("Server does not support LED state updates");
574 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
575 throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
576
577 os->writeS16(0);
578 os->writeS16(0);
579 os->writeU16(0);
580 os->writeU16(0);
581 os->writeU32(pseudoEncodingLEDState);
582 os->writeU8(state);
583}
Pierre Ossman5ae28212017-05-16 14:30:38 +0200584
585void SMsgWriter::writeQEMUKeyEventRect()
586{
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200587 if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200588 throw Exception("Client does not support QEMU extended key events");
589 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
590 throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");
591
592 os->writeS16(0);
593 os->writeS16(0);
594 os->writeU16(0);
595 os->writeU16(0);
596 os->writeU32(pseudoEncodingQEMUKeyEvent);
597}