blob: 5585652df7aed08b9024f9d181b7037211373a80 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman7638e9c2014-01-16 13:12:40 +01002 * Copyright 2009-2014 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19#include <stdio.h>
20#include <rdr/OutStream.h>
21#include <rfb/msgTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010022#include <rfb/fenceTypes.h>
Pierre Ossman316a3242014-01-15 12:40:20 +010023#include <rfb/encodings.h>
Pierre Ossman5ae28212017-05-16 14:30:38 +020024#include <rfb/qemuTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010025#include <rfb/Exception.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/PixelFormat.h>
27#include <rfb/Rect.h>
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020028#include <rfb/ServerParams.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000029#include <rfb/Decoder.h>
30#include <rfb/CMsgWriter.h>
31
32using namespace rfb;
33
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020034CMsgWriter::CMsgWriter(ServerParams* server_, rdr::OutStream* os_)
35 : server(server_), os(os_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036{
37}
38
39CMsgWriter::~CMsgWriter()
40{
41}
42
Pierre Ossman7638e9c2014-01-16 13:12:40 +010043void CMsgWriter::writeClientInit(bool shared)
44{
45 os->writeU8(shared);
46 endMsg();
47}
48
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf)
50{
51 startMsg(msgTypeSetPixelFormat);
52 os->pad(3);
53 pf.write(os);
54 endMsg();
55}
56
57void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings)
58{
59 startMsg(msgTypeSetEncodings);
60 os->skip(1);
61 os->writeU16(nEncodings);
62 for (int i = 0; i < nEncodings; i++)
63 os->writeU32(encodings[i]);
64 endMsg();
65}
66
67// Ask for encodings based on which decoders are supported. Assumes higher
68// encoding numbers are more desirable.
69
70void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect)
71{
72 int nEncodings = 0;
73 rdr::U32 encodings[encodingMax+3];
Pierre Ossmanc754cce2011-11-14 15:44:11 +000074
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020075 if (server->supportsLocalCursor) {
Pierre Ossmana4c0aac2017-02-19 15:50:29 +010076 encodings[nEncodings++] = pseudoEncodingCursorWithAlpha;
Pierre Ossmana8ecb792017-08-16 16:25:24 +020077 encodings[nEncodings++] = pseudoEncodingCursor;
78 encodings[nEncodings++] = pseudoEncodingXCursor;
Pierre Ossman6b68f972017-02-19 15:51:19 +010079 }
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020080 if (server->supportsDesktopResize)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000081 encodings[nEncodings++] = pseudoEncodingDesktopSize;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020082 if (server->supportsExtendedDesktopSize)
Pierre Ossman49f88222009-03-20 13:02:50 +000083 encodings[nEncodings++] = pseudoEncodingExtendedDesktopSize;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020084 if (server->supportsDesktopRename)
Peter Åstrandc39e0782009-01-15 12:21:42 +000085 encodings[nEncodings++] = pseudoEncodingDesktopName;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020086 if (server->supportsLEDState)
Pierre Ossman2fa63f82016-12-05 15:26:21 +010087 encodings[nEncodings++] = pseudoEncodingLEDState;
Pierre Ossmanc754cce2011-11-14 15:44:11 +000088
89 encodings[nEncodings++] = pseudoEncodingLastRect;
Pierre Ossmanc898d9a2011-11-14 16:22:23 +000090 encodings[nEncodings++] = pseudoEncodingContinuousUpdates;
Pierre Ossmanc754cce2011-11-14 15:44:11 +000091 encodings[nEncodings++] = pseudoEncodingFence;
Pierre Ossman5ae28212017-05-16 14:30:38 +020092 encodings[nEncodings++] = pseudoEncodingQEMUKeyEvent;
Pierre Ossmanc754cce2011-11-14 15:44:11 +000093
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000094 if (Decoder::supported(preferredEncoding)) {
95 encodings[nEncodings++] = preferredEncoding;
96 }
Pierre Ossmanc754cce2011-11-14 15:44:11 +000097
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098 if (useCopyRect) {
99 encodings[nEncodings++] = encodingCopyRect;
100 }
Pierre Ossman090e7d62009-03-12 10:16:07 +0000101
102 /*
103 * Prefer encodings in this order:
104 *
105 * Tight, ZRLE, Hextile, *
106 */
107
108 if ((preferredEncoding != encodingTight) &&
109 Decoder::supported(encodingTight))
110 encodings[nEncodings++] = encodingTight;
111
112 if ((preferredEncoding != encodingZRLE) &&
113 Decoder::supported(encodingZRLE))
114 encodings[nEncodings++] = encodingZRLE;
115
116 if ((preferredEncoding != encodingHextile) &&
117 Decoder::supported(encodingHextile))
118 encodings[nEncodings++] = encodingHextile;
119
120 // Remaining encodings
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000121 for (int i = encodingMax; i >= 0; i--) {
Pierre Ossman090e7d62009-03-12 10:16:07 +0000122 switch (i) {
Pierre Ossmand704e4a2014-01-31 11:21:51 +0100123 case encodingCopyRect:
Pierre Ossman090e7d62009-03-12 10:16:07 +0000124 case encodingTight:
125 case encodingZRLE:
126 case encodingHextile:
Pierre Ossmand704e4a2014-01-31 11:21:51 +0100127 /* These have already been sent earlier */
Pierre Ossman090e7d62009-03-12 10:16:07 +0000128 break;
129 default:
130 if ((i != preferredEncoding) && Decoder::supported(i))
131 encodings[nEncodings++] = i;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000132 }
133 }
Pierre Ossman090e7d62009-03-12 10:16:07 +0000134
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200135 if (server->compressLevel >= 0 && server->compressLevel <= 9)
136 encodings[nEncodings++] = pseudoEncodingCompressLevel0 + server->compressLevel;
137 if (server->qualityLevel >= 0 && server->qualityLevel <= 9)
138 encodings[nEncodings++] = pseudoEncodingQualityLevel0 + server->qualityLevel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000139
140 writeSetEncodings(nEncodings, encodings);
141}
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100142
143void CMsgWriter::writeSetDesktopSize(int width, int height,
144 const ScreenSet& layout)
145{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200146 if (!server->supportsSetDesktopSize)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100147 throw Exception("Server does not support SetDesktopSize");
148
149 startMsg(msgTypeSetDesktopSize);
150 os->pad(1);
151
152 os->writeU16(width);
153 os->writeU16(height);
154
155 os->writeU8(layout.num_screens());
156 os->pad(1);
157
158 ScreenSet::const_iterator iter;
159 for (iter = layout.begin();iter != layout.end();++iter) {
160 os->writeU32(iter->id);
161 os->writeU16(iter->dimensions.tl.x);
162 os->writeU16(iter->dimensions.tl.y);
163 os->writeU16(iter->dimensions.width());
164 os->writeU16(iter->dimensions.height());
165 os->writeU32(iter->flags);
166 }
167
168 endMsg();
169}
170
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000171void CMsgWriter::writeFramebufferUpdateRequest(const Rect& r, bool incremental)
172{
173 startMsg(msgTypeFramebufferUpdateRequest);
174 os->writeU8(incremental);
175 os->writeU16(r.tl.x);
176 os->writeU16(r.tl.y);
177 os->writeU16(r.width());
178 os->writeU16(r.height());
179 endMsg();
180}
181
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100182void CMsgWriter::writeEnableContinuousUpdates(bool enable,
183 int x, int y, int w, int h)
184{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200185 if (!server->supportsContinuousUpdates)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100186 throw Exception("Server does not support continuous updates");
187
188 startMsg(msgTypeEnableContinuousUpdates);
189
190 os->writeU8(!!enable);
191
192 os->writeU16(x);
193 os->writeU16(y);
194 os->writeU16(w);
195 os->writeU16(h);
196
197 endMsg();
198}
199
200void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
201{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200202 if (!server->supportsFence)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100203 throw Exception("Server does not support fences");
204 if (len > 64)
205 throw Exception("Too large fence payload");
206 if ((flags & ~fenceFlagsSupported) != 0)
207 throw Exception("Unknown fence flags");
208
209 startMsg(msgTypeClientFence);
210 os->pad(3);
211
212 os->writeU32(flags);
213
214 os->writeU8(len);
215 os->writeBytes(data, len);
216
217 endMsg();
218}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000219
Pierre Ossman59da99f2016-02-05 10:43:12 +0100220void CMsgWriter::writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000221{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200222 if (!server->supportsQEMUKeyEvent || !keycode) {
Pierre Ossman5ae28212017-05-16 14:30:38 +0200223 /* This event isn't meaningful without a valid keysym */
224 if (!keysym)
225 return;
226
227 startMsg(msgTypeKeyEvent);
228 os->writeU8(down);
229 os->pad(2);
230 os->writeU32(keysym);
231 endMsg();
232 } else {
233 startMsg(msgTypeQEMUClientMessage);
234 os->writeU8(qemuExtendedKeyEvent);
235 os->writeU16(down);
236 os->writeU32(keysym);
237 os->writeU32(keycode);
238 endMsg();
239 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000240}
241
242
Pierre Ossman59da99f2016-02-05 10:43:12 +0100243void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000244{
245 Point p(pos);
246 if (p.x < 0) p.x = 0;
247 if (p.y < 0) p.y = 0;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200248 if (p.x >= server->width()) p.x = server->width() - 1;
249 if (p.y >= server->height()) p.y = server->height() - 1;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000250
251 startMsg(msgTypePointerEvent);
252 os->writeU8(buttonMask);
253 os->writeU16(p.x);
254 os->writeU16(p.y);
255 endMsg();
256}
257
258
Pierre Ossman59da99f2016-02-05 10:43:12 +0100259void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000260{
261 startMsg(msgTypeClientCutText);
262 os->pad(3);
263 os->writeU32(len);
264 os->writeBytes(str, len);
265 endMsg();
266}
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100267
268void CMsgWriter::startMsg(int type)
269{
270 os->writeU8(type);
271}
272
273void CMsgWriter::endMsg()
274{
275 os->flush();
276}