blob: bcc848fcaff5a7fd8e5d27befd65e85c238e8603 [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 Ossman7638e9c2014-01-16 13:12:40 +010050void SMsgWriter::writeServerInit()
51{
Pierre Ossman0d3ce872018-06-18 15:59:00 +020052 os->writeU16(client->width());
53 os->writeU16(client->height());
54 client->pf().write(os);
55 os->writeString(client->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +010056 endMsg();
57}
58
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010060 const rdr::U16 red[],
61 const rdr::U16 green[],
62 const rdr::U16 blue[])
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063{
64 startMsg(msgTypeSetColourMapEntries);
65 os->pad(1);
66 os->writeU16(firstColour);
67 os->writeU16(nColours);
68 for (int i = firstColour; i < firstColour+nColours; i++) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010069 os->writeU16(red[i]);
70 os->writeU16(green[i]);
71 os->writeU16(blue[i]);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000072 }
73 endMsg();
74}
75
76void SMsgWriter::writeBell()
77{
78 startMsg(msgTypeBell);
79 endMsg();
80}
81
82void SMsgWriter::writeServerCutText(const char* str, int len)
83{
84 startMsg(msgTypeServerCutText);
85 os->pad(3);
86 os->writeU32(len);
87 os->writeBytes(str, len);
88 endMsg();
89}
90
Pierre Ossman7638e9c2014-01-16 13:12:40 +010091void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
92{
Pierre Ossman0d3ce872018-06-18 15:59:00 +020093 if (!client->supportsFence)
Pierre Ossman7638e9c2014-01-16 13:12:40 +010094 throw Exception("Client does not support fences");
95 if (len > 64)
96 throw Exception("Too large fence payload");
97 if ((flags & ~fenceFlagsSupported) != 0)
98 throw Exception("Unknown fence flags");
99
100 startMsg(msgTypeServerFence);
101 os->pad(3);
102
103 os->writeU32(flags);
104
105 os->writeU8(len);
Michal Srbf3afa242017-03-27 19:02:15 +0300106
107 if (len > 0)
108 os->writeBytes(data, len);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100109
110 endMsg();
111}
112
113void SMsgWriter::writeEndOfContinuousUpdates()
114{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200115 if (!client->supportsContinuousUpdates)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100116 throw Exception("Client does not support continuous updates");
117
118 startMsg(msgTypeEndOfContinuousUpdates);
119 endMsg();
120}
121
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100122bool SMsgWriter::writeSetDesktopSize() {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200123 if (!client->supportsDesktopResize)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100124 return false;
125
126 needSetDesktopSize = true;
127
128 return true;
129}
130
131bool SMsgWriter::writeExtendedDesktopSize() {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200132 if (!client->supportsExtendedDesktopSize)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100133 return false;
134
135 needExtendedDesktopSize = true;
136
137 return true;
138}
139
140bool SMsgWriter::writeExtendedDesktopSize(rdr::U16 reason, rdr::U16 result,
141 int fb_width, int fb_height,
142 const ScreenSet& layout) {
143 ExtendedDesktopSizeMsg msg;
144
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200145 if (!client->supportsExtendedDesktopSize)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100146 return false;
147
148 msg.reason = reason;
149 msg.result = result;
150 msg.fb_width = fb_width;
151 msg.fb_height = fb_height;
152 msg.layout = layout;
153
154 extendedDesktopSizeMsgs.push_back(msg);
155
156 return true;
157}
158
159bool SMsgWriter::writeSetDesktopName() {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200160 if (!client->supportsDesktopRename)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100161 return false;
162
163 needSetDesktopName = true;
164
165 return true;
166}
167
Pierre Ossman126e5642014-02-13 14:40:25 +0100168bool SMsgWriter::writeSetCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100169{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200170 if (!client->supportsLocalCursor)
Pierre Ossman126e5642014-02-13 14:40:25 +0100171 return false;
172
173 needSetCursor = true;
174
175 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100176}
177
Pierre Ossman126e5642014-02-13 14:40:25 +0100178bool SMsgWriter::writeSetXCursor()
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100179{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200180 if (!client->supportsLocalXCursor)
Pierre Ossman126e5642014-02-13 14:40:25 +0100181 return false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100182
Pierre Ossman126e5642014-02-13 14:40:25 +0100183 needSetXCursor = true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100184
Pierre Ossman126e5642014-02-13 14:40:25 +0100185 return true;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100186}
187
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100188bool SMsgWriter::writeSetCursorWithAlpha()
189{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200190 if (!client->supportsLocalCursorWithAlpha)
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100191 return false;
192
193 needSetCursorWithAlpha = true;
194
195 return true;
196}
197
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100198bool SMsgWriter::writeLEDState()
199{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200200 if (!client->supportsLEDState)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100201 return false;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200202 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100203 return false;
204
205 needLEDState = true;
206
207 return true;
208}
209
Pierre Ossman5ae28212017-05-16 14:30:38 +0200210bool SMsgWriter::writeQEMUKeyEvent()
211{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200212 if (!client->supportsQEMUKeyEvent)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200213 return false;
214
215 needQEMUKeyEvent = true;
216
217 return true;
218}
219
Pierre Ossmane9962f72009-04-23 12:31:42 +0000220bool SMsgWriter::needFakeUpdate()
221{
Pierre Ossman126e5642014-02-13 14:40:25 +0100222 if (needSetDesktopName)
223 return true;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100224 if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
Pierre Ossman126e5642014-02-13 14:40:25 +0100225 return true;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100226 if (needLEDState)
227 return true;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200228 if (needQEMUKeyEvent)
229 return true;
Pierre Ossman126e5642014-02-13 14:40:25 +0100230 if (needNoDataUpdate())
231 return true;
232
233 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000234}
235
Pierre Ossmane9962f72009-04-23 12:31:42 +0000236bool SMsgWriter::needNoDataUpdate()
237{
Pierre Ossman126e5642014-02-13 14:40:25 +0100238 if (needSetDesktopSize)
239 return true;
240 if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty())
241 return true;
242
243 return false;
Pierre Ossmane9962f72009-04-23 12:31:42 +0000244}
245
246void SMsgWriter::writeNoDataUpdate()
247{
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100248 int nRects;
249
250 nRects = 0;
251
252 if (needSetDesktopSize)
253 nRects++;
254 if (needExtendedDesktopSize)
255 nRects++;
256 if (!extendedDesktopSizeMsgs.empty())
257 nRects += extendedDesktopSizeMsgs.size();
258
259 writeFramebufferUpdateStart(nRects);
260 writeNoDataRects();
261 writeFramebufferUpdateEnd();
Pierre Ossmane9962f72009-04-23 12:31:42 +0000262}
263
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100264void SMsgWriter::writeFramebufferUpdateStart(int nRects)
265{
266 startMsg(msgTypeFramebufferUpdate);
267 os->pad(1);
268
269 if (nRects != 0xFFFF) {
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100270 if (needSetDesktopName)
271 nRects++;
Pierre Ossman126e5642014-02-13 14:40:25 +0100272 if (needSetCursor)
273 nRects++;
274 if (needSetXCursor)
275 nRects++;
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100276 if (needSetCursorWithAlpha)
277 nRects++;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100278 if (needLEDState)
279 nRects++;
Pierre Ossman5ae28212017-05-16 14:30:38 +0200280 if (needQEMUKeyEvent)
281 nRects++;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100282 }
283
284 os->writeU16(nRects);
285
286 nRectsInUpdate = 0;
287 if (nRects == 0xFFFF)
288 nRectsInHeader = 0;
289 else
290 nRectsInHeader = nRects;
291
292 writePseudoRects();
293}
294
295void SMsgWriter::writeFramebufferUpdateEnd()
296{
297 if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
298 throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
299 "nRects out of sync");
300
301 if (nRectsInHeader == 0) {
302 // Send last rect. marker
303 os->writeS16(0);
304 os->writeS16(0);
305 os->writeU16(0);
306 os->writeU16(0);
307 os->writeU32(pseudoEncodingLastRect);
308 }
309
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100310 endMsg();
311}
312
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000313void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
314{
315 startRect(r,encodingCopyRect);
316 os->writeU16(srcX);
317 os->writeU16(srcY);
318 endRect();
319}
320
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100321void SMsgWriter::startRect(const Rect& r, int encoding)
322{
323 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
324 throw Exception("SMsgWriter::startRect: nRects out of sync");
325
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100326 os->writeS16(r.tl.x);
327 os->writeS16(r.tl.y);
328 os->writeU16(r.width());
329 os->writeU16(r.height());
330 os->writeU32(encoding);
331}
332
333void SMsgWriter::endRect()
334{
Pierre Ossman35294682016-04-29 14:27:08 +0200335 os->flush();
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100336}
337
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100338void SMsgWriter::startMsg(int type)
339{
340 os->writeU8(type);
341}
342
343void SMsgWriter::endMsg()
344{
345 os->flush();
346}
347
348void SMsgWriter::writePseudoRects()
349{
Pierre Ossman126e5642014-02-13 14:40:25 +0100350 if (needSetCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200351 const Cursor& cursor = client->cursor();
Pierre Ossman126e5642014-02-13 14:40:25 +0100352
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200353 rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100354 rdr::U8Array mask(cursor.getMask());
355
356 const rdr::U8* in;
357 rdr::U8* out;
358
359 in = cursor.getBuffer();
360 out = data.buf;
361 for (int i = 0;i < cursor.width()*cursor.height();i++) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200362 client->pf().bufferFromRGB(out, in, 1);
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100363 in += 4;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200364 out += client->pf().bpp/8;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100365 }
Pierre Ossman126e5642014-02-13 14:40:25 +0100366
367 writeSetCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100368 cursor.hotspot().x, cursor.hotspot().y,
369 data.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100370 needSetCursor = false;
Pierre Ossman126e5642014-02-13 14:40:25 +0100371 }
372
373 if (needSetXCursor) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200374 const Cursor& cursor = client->cursor();
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100375 rdr::U8Array bitmap(cursor.getBitmap());
376 rdr::U8Array mask(cursor.getMask());
Pierre Ossman126e5642014-02-13 14:40:25 +0100377
378 writeSetXCursorRect(cursor.width(), cursor.height(),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100379 cursor.hotspot().x, cursor.hotspot().y,
380 bitmap.buf, mask.buf);
Pierre Ossman126e5642014-02-13 14:40:25 +0100381 needSetXCursor = false;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100382 }
383
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100384 if (needSetCursorWithAlpha) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200385 const Cursor& cursor = client->cursor();
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100386
387 writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
388 cursor.hotspot().x, cursor.hotspot().y,
389 cursor.getBuffer());
390 needSetCursorWithAlpha = false;
391 }
392
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100393 if (needSetDesktopName) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200394 writeSetDesktopNameRect(client->name());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100395 needSetDesktopName = false;
396 }
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100397
398 if (needLEDState) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200399 writeLEDStateRect(client->ledState());
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100400 needLEDState = false;
401 }
Pierre Ossman5ae28212017-05-16 14:30:38 +0200402
403 if (needQEMUKeyEvent) {
404 writeQEMUKeyEventRect();
405 needQEMUKeyEvent = false;
406 }
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100407}
408
409void SMsgWriter::writeNoDataRects()
410{
411 // Start with specific ExtendedDesktopSize messages
412 if (!extendedDesktopSizeMsgs.empty()) {
413 std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100414
415 for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100416 writeExtendedDesktopSizeRect(ri->reason, ri->result,
417 ri->fb_width, ri->fb_height, ri->layout);
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100418 }
419
420 extendedDesktopSizeMsgs.clear();
421 }
422
423 // Send this before SetDesktopSize to make life easier on the clients
424 if (needExtendedDesktopSize) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200425 writeExtendedDesktopSizeRect(0, 0, client->width(), client->height(),
426 client->screenLayout());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100427 needExtendedDesktopSize = false;
428 }
429
430 // Some clients assume this is the last rectangle so don't send anything
431 // more after this
432 if (needSetDesktopSize) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200433 writeSetDesktopSizeRect(client->width(), client->height());
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100434 needSetDesktopSize = false;
435 }
436}
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100437
438void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
439{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200440 if (!client->supportsDesktopResize)
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100441 throw Exception("Client does not support desktop resize");
442 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
443 throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
444
445 os->writeS16(0);
446 os->writeS16(0);
447 os->writeU16(width);
448 os->writeU16(height);
449 os->writeU32(pseudoEncodingDesktopSize);
450}
451
452void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
453 rdr::U16 result,
454 int fb_width,
455 int fb_height,
456 const ScreenSet& layout)
457{
458 ScreenSet::const_iterator si;
459
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200460 if (!client->supportsExtendedDesktopSize)
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100461 throw Exception("Client does not support extended desktop resize");
462 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
463 throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
464
465 os->writeU16(reason);
466 os->writeU16(result);
467 os->writeU16(fb_width);
468 os->writeU16(fb_height);
469 os->writeU32(pseudoEncodingExtendedDesktopSize);
470
471 os->writeU8(layout.num_screens());
472 os->pad(3);
473
474 for (si = layout.begin();si != layout.end();++si) {
475 os->writeU32(si->id);
476 os->writeU16(si->dimensions.tl.x);
477 os->writeU16(si->dimensions.tl.y);
478 os->writeU16(si->dimensions.width());
479 os->writeU16(si->dimensions.height());
480 os->writeU32(si->flags);
481 }
482}
483
484void SMsgWriter::writeSetDesktopNameRect(const char *name)
485{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200486 if (!client->supportsDesktopRename)
Pierre Ossmanc0b1aa02014-01-16 13:39:05 +0100487 throw Exception("Client does not support desktop rename");
488 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
489 throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
490
491 os->writeS16(0);
492 os->writeS16(0);
493 os->writeU16(0);
494 os->writeU16(0);
495 os->writeU32(pseudoEncodingDesktopName);
496 os->writeString(name);
497}
Pierre Ossman126e5642014-02-13 14:40:25 +0100498
499void SMsgWriter::writeSetCursorRect(int width, int height,
500 int hotspotX, int hotspotY,
501 const void* data, const void* mask)
502{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200503 if (!client->supportsLocalCursor)
Pierre Ossman126e5642014-02-13 14:40:25 +0100504 throw Exception("Client does not support local cursors");
505 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
506 throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
507
508 os->writeS16(hotspotX);
509 os->writeS16(hotspotY);
510 os->writeU16(width);
511 os->writeU16(height);
512 os->writeU32(pseudoEncodingCursor);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200513 os->writeBytes(data, width * height * (client->pf().bpp/8));
Pierre Ossman126e5642014-02-13 14:40:25 +0100514 os->writeBytes(mask, (width+7)/8 * height);
515}
516
517void SMsgWriter::writeSetXCursorRect(int width, int height,
518 int hotspotX, int hotspotY,
Pierre Ossman126e5642014-02-13 14:40:25 +0100519 const void* data, const void* mask)
520{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200521 if (!client->supportsLocalXCursor)
Pierre Ossman126e5642014-02-13 14:40:25 +0100522 throw Exception("Client does not support local cursors");
523 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
524 throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
525
526 os->writeS16(hotspotX);
527 os->writeS16(hotspotY);
528 os->writeU16(width);
529 os->writeU16(height);
530 os->writeU32(pseudoEncodingXCursor);
Brian P. Hinz33d78322017-11-16 17:46:15 -0500531 if (width * height > 0) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100532 os->writeU8(255);
533 os->writeU8(255);
534 os->writeU8(255);
535 os->writeU8(0);
536 os->writeU8(0);
537 os->writeU8(0);
Pierre Ossman126e5642014-02-13 14:40:25 +0100538 os->writeBytes(data, (width+7)/8 * height);
539 os->writeBytes(mask, (width+7)/8 * height);
540 }
541}
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100542
543void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
544 int hotspotX, int hotspotY,
545 const rdr::U8* data)
546{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200547 if (!client->supportsLocalCursorWithAlpha)
Pierre Ossman8053c8e2017-02-21 12:59:04 +0100548 throw Exception("Client does not support local cursors");
549 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
550 throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
551
552 os->writeS16(hotspotX);
553 os->writeS16(hotspotY);
554 os->writeU16(width);
555 os->writeU16(height);
556 os->writeU32(pseudoEncodingCursorWithAlpha);
557
558 // FIXME: Use an encoder with compression?
559 os->writeU32(encodingRaw);
560
561 // Alpha needs to be pre-multiplied
562 for (int i = 0;i < width*height;i++) {
563 os->writeU8((unsigned)data[0] * data[3] / 255);
564 os->writeU8((unsigned)data[1] * data[3] / 255);
565 os->writeU8((unsigned)data[2] * data[3] / 255);
566 os->writeU8(data[3]);
567 data += 4;
568 }
569}
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100570
571void SMsgWriter::writeLEDStateRect(rdr::U8 state)
572{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200573 if (!client->supportsLEDState)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100574 throw Exception("Client does not support LED state updates");
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200575 if (client->ledState() == ledUnknown)
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100576 throw Exception("Server does not support LED state updates");
577 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
578 throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
579
580 os->writeS16(0);
581 os->writeS16(0);
582 os->writeU16(0);
583 os->writeU16(0);
584 os->writeU32(pseudoEncodingLEDState);
585 os->writeU8(state);
586}
Pierre Ossman5ae28212017-05-16 14:30:38 +0200587
588void SMsgWriter::writeQEMUKeyEventRect()
589{
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200590 if (!client->supportsQEMUKeyEvent)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200591 throw Exception("Client does not support QEMU extended key events");
592 if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
593 throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");
594
595 os->writeS16(0);
596 os->writeS16(0);
597 os->writeU16(0);
598 os->writeU16(0);
599 os->writeU32(pseudoEncodingQEMUKeyEvent);
600}