blob: 3180391b2e36442f613e54dd01fe09cc4ec19210 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman0ff26552016-02-05 10:26:56 +01002 * Copyright 2009-2019 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>
Pierre Ossman0ff26552016-02-05 10:26:56 +010020
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000021#include <rdr/OutStream.h>
Pierre Ossman0ff26552016-02-05 10:26:56 +010022#include <rdr/MemOutStream.h>
23#include <rdr/ZlibOutStream.h>
24
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000025#include <rfb/msgTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010026#include <rfb/fenceTypes.h>
Pierre Ossman5ae28212017-05-16 14:30:38 +020027#include <rfb/qemuTypes.h>
Pierre Ossman0ff26552016-02-05 10:26:56 +010028#include <rfb/clipboardTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010029#include <rfb/Exception.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000030#include <rfb/PixelFormat.h>
31#include <rfb/Rect.h>
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020032#include <rfb/ServerParams.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000033#include <rfb/CMsgWriter.h>
34
35using namespace rfb;
36
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020037CMsgWriter::CMsgWriter(ServerParams* server_, rdr::OutStream* os_)
38 : server(server_), os(os_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000039{
40}
41
42CMsgWriter::~CMsgWriter()
43{
44}
45
Pierre Ossman7638e9c2014-01-16 13:12:40 +010046void CMsgWriter::writeClientInit(bool shared)
47{
48 os->writeU8(shared);
49 endMsg();
50}
51
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000052void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf)
53{
54 startMsg(msgTypeSetPixelFormat);
55 os->pad(3);
56 pf.write(os);
57 endMsg();
58}
59
Pierre Ossman1143ee62018-06-20 11:40:37 +020060void CMsgWriter::writeSetEncodings(const std::list<rdr::U32> encodings)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000061{
Pierre Ossman1143ee62018-06-20 11:40:37 +020062 std::list<rdr::U32>::const_iterator iter;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063 startMsg(msgTypeSetEncodings);
64 os->skip(1);
Pierre Ossman1143ee62018-06-20 11:40:37 +020065 os->writeU16(encodings.size());
66 for (iter = encodings.begin(); iter != encodings.end(); ++iter)
67 os->writeU32(*iter);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000068 endMsg();
69}
70
Pierre Ossman7638e9c2014-01-16 13:12:40 +010071void CMsgWriter::writeSetDesktopSize(int width, int height,
72 const ScreenSet& layout)
73{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020074 if (!server->supportsSetDesktopSize)
Pierre Ossman7638e9c2014-01-16 13:12:40 +010075 throw Exception("Server does not support SetDesktopSize");
76
77 startMsg(msgTypeSetDesktopSize);
78 os->pad(1);
79
80 os->writeU16(width);
81 os->writeU16(height);
82
83 os->writeU8(layout.num_screens());
84 os->pad(1);
85
86 ScreenSet::const_iterator iter;
87 for (iter = layout.begin();iter != layout.end();++iter) {
88 os->writeU32(iter->id);
89 os->writeU16(iter->dimensions.tl.x);
90 os->writeU16(iter->dimensions.tl.y);
91 os->writeU16(iter->dimensions.width());
92 os->writeU16(iter->dimensions.height());
93 os->writeU32(iter->flags);
94 }
95
96 endMsg();
97}
98
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000099void CMsgWriter::writeFramebufferUpdateRequest(const Rect& r, bool incremental)
100{
101 startMsg(msgTypeFramebufferUpdateRequest);
102 os->writeU8(incremental);
103 os->writeU16(r.tl.x);
104 os->writeU16(r.tl.y);
105 os->writeU16(r.width());
106 os->writeU16(r.height());
107 endMsg();
108}
109
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100110void CMsgWriter::writeEnableContinuousUpdates(bool enable,
111 int x, int y, int w, int h)
112{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200113 if (!server->supportsContinuousUpdates)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100114 throw Exception("Server does not support continuous updates");
115
116 startMsg(msgTypeEnableContinuousUpdates);
117
118 os->writeU8(!!enable);
119
120 os->writeU16(x);
121 os->writeU16(y);
122 os->writeU16(w);
123 os->writeU16(h);
124
125 endMsg();
126}
127
128void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
129{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200130 if (!server->supportsFence)
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100131 throw Exception("Server does not support fences");
132 if (len > 64)
133 throw Exception("Too large fence payload");
134 if ((flags & ~fenceFlagsSupported) != 0)
135 throw Exception("Unknown fence flags");
136
137 startMsg(msgTypeClientFence);
138 os->pad(3);
139
140 os->writeU32(flags);
141
142 os->writeU8(len);
143 os->writeBytes(data, len);
144
145 endMsg();
146}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000147
Pierre Ossman59da99f2016-02-05 10:43:12 +0100148void CMsgWriter::writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000149{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200150 if (!server->supportsQEMUKeyEvent || !keycode) {
Pierre Ossman5ae28212017-05-16 14:30:38 +0200151 /* This event isn't meaningful without a valid keysym */
152 if (!keysym)
153 return;
154
155 startMsg(msgTypeKeyEvent);
156 os->writeU8(down);
157 os->pad(2);
158 os->writeU32(keysym);
159 endMsg();
160 } else {
161 startMsg(msgTypeQEMUClientMessage);
162 os->writeU8(qemuExtendedKeyEvent);
163 os->writeU16(down);
164 os->writeU32(keysym);
165 os->writeU32(keycode);
166 endMsg();
167 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000168}
169
170
Pierre Ossman59da99f2016-02-05 10:43:12 +0100171void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000172{
173 Point p(pos);
174 if (p.x < 0) p.x = 0;
175 if (p.y < 0) p.y = 0;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200176 if (p.x >= server->width()) p.x = server->width() - 1;
177 if (p.y >= server->height()) p.y = server->height() - 1;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000178
179 startMsg(msgTypePointerEvent);
180 os->writeU8(buttonMask);
181 os->writeU16(p.x);
182 os->writeU16(p.y);
183 endMsg();
184}
185
186
Pierre Ossman66f1db52019-05-02 12:32:03 +0200187void CMsgWriter::writeClientCutText(const char* str)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000188{
Pierre Ossman66f1db52019-05-02 12:32:03 +0200189 size_t len;
190
191 if (strchr(str, '\r') != NULL)
Pierre Ossman546b2ad2019-05-02 12:32:03 +0200192 throw Exception("Invalid carriage return in clipboard data");
193
Pierre Ossman66f1db52019-05-02 12:32:03 +0200194 len = strlen(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000195 startMsg(msgTypeClientCutText);
196 os->pad(3);
197 os->writeU32(len);
198 os->writeBytes(str, len);
199 endMsg();
200}
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100201
Pierre Ossman0ff26552016-02-05 10:26:56 +0100202void CMsgWriter::writeClipboardCaps(rdr::U32 caps,
203 const rdr::U32* lengths)
204{
205 size_t i, count;
206
207 if (!(server->clipboardFlags() & clipboardCaps))
208 throw Exception("Server does not support clipboard \"caps\" action");
209
210 count = 0;
211 for (i = 0;i < 16;i++) {
212 if (caps & (1 << i))
213 count++;
214 }
215
216 startMsg(msgTypeClientCutText);
217 os->pad(3);
218 os->writeS32(-(4 + 4 * count));
219
220 os->writeU32(caps | clipboardCaps);
221
222 count = 0;
223 for (i = 0;i < 16;i++) {
224 if (caps & (1 << i))
225 os->writeU32(lengths[count++]);
226 }
227
228 endMsg();
229}
230
231void CMsgWriter::writeClipboardRequest(rdr::U32 flags)
232{
233 if (!(server->clipboardFlags() & clipboardRequest))
234 throw Exception("Server does not support clipboard \"request\" action");
235
236 startMsg(msgTypeClientCutText);
237 os->pad(3);
238 os->writeS32(-4);
239 os->writeU32(flags | clipboardRequest);
240 endMsg();
241}
242
243void CMsgWriter::writeClipboardPeek(rdr::U32 flags)
244{
245 if (!(server->clipboardFlags() & clipboardPeek))
246 throw Exception("Server does not support clipboard \"peek\" action");
247
248 startMsg(msgTypeClientCutText);
249 os->pad(3);
250 os->writeS32(-4);
251 os->writeU32(flags | clipboardPeek);
252 endMsg();
253}
254
255void CMsgWriter::writeClipboardNotify(rdr::U32 flags)
256{
257 if (!(server->clipboardFlags() & clipboardNotify))
258 throw Exception("Server does not support clipboard \"notify\" action");
259
260 startMsg(msgTypeClientCutText);
261 os->pad(3);
262 os->writeS32(-4);
263 os->writeU32(flags | clipboardNotify);
264 endMsg();
265}
266
267void CMsgWriter::writeClipboardProvide(rdr::U32 flags,
268 const size_t* lengths,
269 const rdr::U8* const* data)
270{
271 rdr::MemOutStream mos;
272 rdr::ZlibOutStream zos;
273
274 int i, count;
275
276 if (!(server->clipboardFlags() & clipboardProvide))
277 throw Exception("Server does not support clipboard \"provide\" action");
278
279 zos.setUnderlying(&mos);
280
281 count = 0;
282 for (i = 0;i < 16;i++) {
283 if (!(flags & (1 << i)))
284 continue;
285 zos.writeU32(lengths[count]);
286 zos.writeBytes(data[count], lengths[count]);
287 count++;
288 }
289
290 zos.flush();
291
292 startMsg(msgTypeClientCutText);
293 os->pad(3);
294 os->writeS32(-(4 + mos.length()));
295 os->writeU32(flags | clipboardProvide);
296 os->writeBytes(mos.data(), mos.length());
297 endMsg();
298}
299
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100300void CMsgWriter::startMsg(int type)
301{
302 os->writeU8(type);
303}
304
305void CMsgWriter::endMsg()
306{
307 os->flush();
308}