blob: ce2741e418f62d382e587da67ff8eca55ed55c70 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman615d16b2019-05-03 10:53:06 +02002 * Copyright 2011-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 */
Pierre Ossmandd45b442018-10-31 17:08:59 +010019#include <assert.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000020#include <stdio.h>
21#include <string.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010022
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000023#include <rfb/Exception.h>
Pierre Ossmanc754cce2011-11-14 15:44:11 +000024#include <rfb/fenceTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010025#include <rfb/CMsgReader.h>
26#include <rfb/CMsgWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000027#include <rfb/CSecurity.h>
Pierre Ossman96728352018-06-20 11:35:05 +020028#include <rfb/Decoder.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000029#include <rfb/Security.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010030#include <rfb/SecurityClient.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000031#include <rfb/CConnection.h>
32#include <rfb/util.h>
33
34#include <rfb/LogWriter.h>
35
Pierre Ossman0068a4f2015-11-09 15:48:19 +010036#include <rdr/InStream.h>
37#include <rdr/OutStream.h>
38
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000039using namespace rfb;
40
41static LogWriter vlog("CConnection");
42
43CConnection::CConnection()
Pierre Ossmanb03512c2018-06-20 16:03:23 +020044 : csecurity(0),
45 supportsLocalCursor(false), supportsDesktopResize(false),
46 supportsLEDState(false),
47 is(0), os(0), reader_(0), writer_(0),
Adam Tkac05a0cd62010-07-20 15:07:44 +000048 shared(false),
Pierre Ossmancedce602019-04-01 14:39:39 +020049 state_(RFBSTATE_UNINITIALISED),
Pierre Ossmanef6881b2018-06-20 11:26:18 +020050 pendingPFChange(false), preferredEncoding(encodingTight),
Pierre Ossmanb03512c2018-06-20 16:03:23 +020051 compressLevel(2), qualityLevel(-1),
Pierre Ossmanef6881b2018-06-20 11:26:18 +020052 formatChange(false), encodingChange(false),
53 firstUpdate(true), pendingUpdate(false), continuousUpdates(false),
54 forceNonincremental(true),
Pierre Ossman615d16b2019-05-03 10:53:06 +020055 framebuffer(NULL), decoder(this),
56 serverClipboard(NULL)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057{
58}
59
60CConnection::~CConnection()
61{
Pierre Ossman9f273e92015-11-09 16:34:54 +010062 setFramebuffer(NULL);
Pierre Ossman82d22e62018-09-21 15:26:37 +020063 if (csecurity)
64 delete csecurity;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000065 delete reader_;
66 reader_ = 0;
67 delete writer_;
68 writer_ = 0;
Pierre Ossman615d16b2019-05-03 10:53:06 +020069 strFree(serverClipboard);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000070}
71
72void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
73{
74 is = is_;
75 os = os_;
76}
77
Pierre Ossman9f273e92015-11-09 16:34:54 +010078void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
79{
Pierre Ossman504afa22015-11-12 12:21:58 +010080 decoder.flush();
81
Pierre Ossman6ea58ba2018-06-20 15:47:49 +020082 if (fb) {
83 assert(fb->width() == server.width());
84 assert(fb->height() == server.height());
85 }
86
Pierre Ossman9f273e92015-11-09 16:34:54 +010087 if ((framebuffer != NULL) && (fb != NULL)) {
88 Rect rect;
89
90 const rdr::U8* data;
91 int stride;
92
93 const rdr::U8 black[4] = { 0, 0, 0, 0 };
94
95 // Copy still valid area
96
97 rect.setXYWH(0, 0,
98 __rfbmin(fb->width(), framebuffer->width()),
99 __rfbmin(fb->height(), framebuffer->height()));
100 data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
101 fb->imageRect(rect, data, stride);
102
103 // Black out any new areas
104
105 if (fb->width() > framebuffer->width()) {
106 rect.setXYWH(framebuffer->width(), 0,
Brian P. Hinz5d663052016-09-05 09:15:50 -0400107 fb->width() - framebuffer->width(),
Pierre Ossman9f273e92015-11-09 16:34:54 +0100108 fb->height());
109 fb->fillRect(rect, black);
110 }
111
112 if (fb->height() > framebuffer->height()) {
113 rect.setXYWH(0, framebuffer->height(),
114 fb->width(),
115 fb->height() - framebuffer->height());
116 fb->fillRect(rect, black);
117 }
118 }
119
120 delete framebuffer;
121 framebuffer = fb;
122}
123
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000124void CConnection::initialiseProtocol()
125{
126 state_ = RFBSTATE_PROTOCOL_VERSION;
127}
128
129void CConnection::processMsg()
130{
131 switch (state_) {
132
133 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
134 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
135 case RFBSTATE_SECURITY: processSecurityMsg(); break;
136 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
137 case RFBSTATE_INITIALISATION: processInitMsg(); break;
138 case RFBSTATE_NORMAL: reader_->readMsg(); break;
139 case RFBSTATE_UNINITIALISED:
140 throw Exception("CConnection::processMsg: not initialised yet?");
141 default:
142 throw Exception("CConnection::processMsg: invalid state");
143 }
144}
145
146void CConnection::processVersionMsg()
147{
Pierre Ossman6a85e7a2019-04-01 14:55:40 +0200148 char verStr[27]; // FIXME: gcc has some bug in format-overflow
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200149 int majorVersion;
150 int minorVersion;
151
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000152 vlog.debug("reading protocol version");
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200153
154 if (!is->checkNoWait(12))
155 return;
156
157 is->readBytes(verStr, 12);
158 verStr[12] = '\0';
159
160 if (sscanf(verStr, "RFB %03d.%03d\n",
161 &majorVersion, &minorVersion) != 2) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000162 state_ = RFBSTATE_INVALID;
163 throw Exception("reading version failed: not an RFB server?");
164 }
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200165
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200166 server.setVersion(majorVersion, minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000167
168 vlog.info("Server supports RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200169 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000170
171 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200172 if (server.beforeVersion(3,3)) {
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100173 vlog.error("Server gave unsupported RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200174 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000175 state_ = RFBSTATE_INVALID;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100176 throw Exception("Server gave unsupported RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200177 server.majorVersion, server.minorVersion);
Pierre Ossmancedce602019-04-01 14:39:39 +0200178 } else if (server.beforeVersion(3,7)) {
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200179 server.setVersion(3,3);
180 } else if (server.afterVersion(3,8)) {
181 server.setVersion(3,8);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000182 }
183
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200184 sprintf(verStr, "RFB %03d.%03d\n",
185 server.majorVersion, server.minorVersion);
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200186 os->writeBytes(verStr, 12);
187 os->flush();
188
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000189 state_ = RFBSTATE_SECURITY_TYPES;
190
191 vlog.info("Using RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200192 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000193}
194
195
196void CConnection::processSecurityTypesMsg()
197{
198 vlog.debug("processing security types message");
199
200 int secType = secTypeInvalid;
201
Adam Tkac05a0cd62010-07-20 15:07:44 +0000202 std::list<rdr::U8> secTypes;
Michal Srbdccb5f72017-03-27 13:55:46 +0300203 secTypes = security.GetEnabledSecTypes();
Adam Tkac05a0cd62010-07-20 15:07:44 +0000204
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200205 if (server.isVersion(3,3)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000206
207 // legacy 3.3 server may only offer "vnc authentication" or "none"
208
209 secType = is->readU32();
210 if (secType == secTypeInvalid) {
211 throwConnFailedException();
212
213 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
Adam Tkac05a0cd62010-07-20 15:07:44 +0000214 std::list<rdr::U8>::iterator i;
215 for (i = secTypes.begin(); i != secTypes.end(); i++)
216 if (*i == secType) {
217 secType = *i;
218 break;
219 }
220
221 if (i == secTypes.end())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222 secType = secTypeInvalid;
223 } else {
224 vlog.error("Unknown 3.3 security type %d", secType);
225 throw Exception("Unknown 3.3 security type");
226 }
227
228 } else {
229
230 // >=3.7 server will offer us a list
231
232 int nServerSecTypes = is->readU8();
233 if (nServerSecTypes == 0)
234 throwConnFailedException();
235
Adam Tkac05a0cd62010-07-20 15:07:44 +0000236 std::list<rdr::U8>::iterator j;
Adam Tkac05a0cd62010-07-20 15:07:44 +0000237
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000238 for (int i = 0; i < nServerSecTypes; i++) {
239 rdr::U8 serverSecType = is->readU8();
240 vlog.debug("Server offers security type %s(%d)",
Adam Tkac7cb47d62011-02-21 12:55:24 +0000241 secTypeName(serverSecType), serverSecType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000242
Adam Tkac7cb47d62011-02-21 12:55:24 +0000243 /*
244 * Use the first type sent by server which matches client's type.
245 * It means server's order specifies priority.
246 */
247 if (secType == secTypeInvalid) {
248 for (j = secTypes.begin(); j != secTypes.end(); j++)
249 if (*j == serverSecType) {
250 secType = *j;
251 break;
252 }
253 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000254 }
255
256 // Inform the server of our decision
257 if (secType != secTypeInvalid) {
258 os->writeU8(secType);
259 os->flush();
Pierre Ossman71d66662014-11-11 13:42:51 +0100260 vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000261 }
262 }
263
264 if (secType == secTypeInvalid) {
265 state_ = RFBSTATE_INVALID;
266 vlog.error("No matching security types");
267 throw Exception("No matching security types");
268 }
269
270 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200271 csecurity = security.GetCSecurity(this, secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000272 processSecurityMsg();
273}
274
275void CConnection::processSecurityMsg()
276{
277 vlog.debug("processing security message");
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200278 if (csecurity->processMsg()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000279 state_ = RFBSTATE_SECURITY_RESULT;
280 processSecurityResultMsg();
281 }
282}
283
284void CConnection::processSecurityResultMsg()
285{
286 vlog.debug("processing security result message");
287 int result;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200288 if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000289 result = secResultOK;
290 } else {
291 if (!is->checkNoWait(1)) return;
292 result = is->readU32();
293 }
294 switch (result) {
295 case secResultOK:
296 securityCompleted();
297 return;
298 case secResultFailed:
299 vlog.debug("auth failed");
300 break;
301 case secResultTooMany:
302 vlog.debug("auth failed - too many tries");
303 break;
304 default:
305 throw Exception("Unknown security result from server");
306 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000307 state_ = RFBSTATE_INVALID;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200308 if (server.beforeVersion(3,8))
Pierre Ossman19225502017-10-12 15:05:07 +0200309 throw AuthFailureException();
310 CharArray reason(is->readString());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000311 throw AuthFailureException(reason.buf);
312}
313
314void CConnection::processInitMsg()
315{
316 vlog.debug("reading server initialisation");
317 reader_->readServerInit();
318}
319
320void CConnection::throwConnFailedException()
321{
322 state_ = RFBSTATE_INVALID;
323 CharArray reason;
324 reason.buf = is->readString();
325 throw ConnFailedException(reason.buf);
326}
327
328void CConnection::securityCompleted()
329{
330 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100331 reader_ = new CMsgReader(this, is);
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200332 writer_ = new CMsgWriter(&server, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000333 vlog.debug("Authentication success!");
334 authSuccess();
335 writer_->writeClientInit(shared);
336}
337
Pierre Ossman3da238d2015-11-12 12:20:05 +0100338void CConnection::setDesktopSize(int w, int h)
339{
Pierre Ossman504afa22015-11-12 12:21:58 +0100340 decoder.flush();
341
Pierre Ossman3da238d2015-11-12 12:20:05 +0100342 CMsgHandler::setDesktopSize(w,h);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200343
344 if (continuousUpdates)
345 writer()->writeEnableContinuousUpdates(true, 0, 0,
346 server.width(),
347 server.height());
Pierre Ossman6ea58ba2018-06-20 15:47:49 +0200348
349 resizeFramebuffer();
350 assert(framebuffer != NULL);
351 assert(framebuffer->width() == server.width());
352 assert(framebuffer->height() == server.height());
Pierre Ossman3da238d2015-11-12 12:20:05 +0100353}
354
355void CConnection::setExtendedDesktopSize(unsigned reason,
356 unsigned result,
357 int w, int h,
358 const ScreenSet& layout)
359{
Pierre Ossman504afa22015-11-12 12:21:58 +0100360 decoder.flush();
361
Pierre Ossman3da238d2015-11-12 12:20:05 +0100362 CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200363
364 if (continuousUpdates)
365 writer()->writeEnableContinuousUpdates(true, 0, 0,
366 server.width(),
367 server.height());
Pierre Ossman6ea58ba2018-06-20 15:47:49 +0200368
369 resizeFramebuffer();
370 assert(framebuffer != NULL);
371 assert(framebuffer->width() == server.width());
372 assert(framebuffer->height() == server.height());
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200373}
374
375void CConnection::endOfContinuousUpdates()
376{
377 CMsgHandler::endOfContinuousUpdates();
378
379 // We've gotten the marker for a format change, so make the pending
380 // one active
381 if (pendingPFChange) {
382 server.setPF(pendingPF);
383 pendingPFChange = false;
384
385 // We might have another change pending
386 if (formatChange)
387 requestNewUpdate();
388 }
Pierre Ossman3da238d2015-11-12 12:20:05 +0100389}
390
Pierre Ossmandd45b442018-10-31 17:08:59 +0100391void CConnection::serverInit(int width, int height,
392 const PixelFormat& pf,
393 const char* name)
Pierre Ossman2affd772018-06-20 07:03:10 +0200394{
Pierre Ossmandd45b442018-10-31 17:08:59 +0100395 CMsgHandler::serverInit(width, height, pf, name);
396
Pierre Ossman2affd772018-06-20 07:03:10 +0200397 state_ = RFBSTATE_NORMAL;
398 vlog.debug("initialisation done");
399
400 initDone();
Pierre Ossmandd45b442018-10-31 17:08:59 +0100401 assert(framebuffer != NULL);
402 assert(framebuffer->width() == server.width());
403 assert(framebuffer->height() == server.height());
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200404
405 // We want to make sure we call SetEncodings at least once
406 encodingChange = true;
407
408 requestNewUpdate();
409
410 // This initial update request is a bit of a corner case, so we need
411 // to help out setting the correct format here.
412 if (pendingPFChange) {
413 server.setPF(pendingPF);
414 pendingPFChange = false;
415 }
Pierre Ossman2affd772018-06-20 07:03:10 +0200416}
417
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100418void CConnection::readAndDecodeRect(const Rect& r, int encoding,
419 ModifiablePixelBuffer* pb)
420{
421 decoder.decodeRect(r, encoding, pb);
422 decoder.flush();
423}
424
Pierre Ossman3da238d2015-11-12 12:20:05 +0100425void CConnection::framebufferUpdateStart()
426{
427 CMsgHandler::framebufferUpdateStart();
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200428
429 assert(framebuffer != NULL);
430
431 // Note: This might not be true if continuous updates are supported
432 pendingUpdate = false;
433
434 requestNewUpdate();
Pierre Ossman3da238d2015-11-12 12:20:05 +0100435}
436
437void CConnection::framebufferUpdateEnd()
438{
Pierre Ossman504afa22015-11-12 12:21:58 +0100439 decoder.flush();
440
Pierre Ossman3da238d2015-11-12 12:20:05 +0100441 CMsgHandler::framebufferUpdateEnd();
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200442
443 // A format change has been scheduled and we are now past the update
444 // with the old format. Time to active the new one.
445 if (pendingPFChange && !continuousUpdates) {
446 server.setPF(pendingPF);
447 pendingPFChange = false;
448 }
449
450 if (firstUpdate) {
451 if (server.supportsContinuousUpdates) {
452 vlog.info("Enabling continuous updates");
453 continuousUpdates = true;
454 writer()->writeEnableContinuousUpdates(true, 0, 0,
455 server.width(),
456 server.height());
457 }
458
459 firstUpdate = false;
460 }
Pierre Ossman3da238d2015-11-12 12:20:05 +0100461}
462
Pierre Ossman9f273e92015-11-09 16:34:54 +0100463void CConnection::dataRect(const Rect& r, int encoding)
464{
465 decoder.decodeRect(r, encoding, framebuffer);
466}
467
Pierre Ossman615d16b2019-05-03 10:53:06 +0200468void CConnection::serverCutText(const char* str)
469{
470 strFree(serverClipboard);
471 serverClipboard = NULL;
472
473 serverClipboard = strDup(str);
474
475 handleClipboardAnnounce(true);
476}
477
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000478void CConnection::authSuccess()
479{
480}
481
Pierre Ossman2affd772018-06-20 07:03:10 +0200482void CConnection::initDone()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000483{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000484}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000485
Pierre Ossman6ea58ba2018-06-20 15:47:49 +0200486void CConnection::resizeFramebuffer()
487{
488 assert(false);
489}
490
Pierre Ossman615d16b2019-05-03 10:53:06 +0200491void CConnection::handleClipboardRequest()
492{
493}
494
495void CConnection::handleClipboardAnnounce(bool available)
496{
497}
498
499void CConnection::handleClipboardData(const char* data)
500{
501}
502
503void CConnection::requestClipboard()
504{
505 if (serverClipboard != NULL) {
506 handleClipboardData(serverClipboard);
507 return;
508 }
509}
510
511void CConnection::announceClipboard(bool available)
512{
513 if (available)
514 handleClipboardRequest();
515}
516
517void CConnection::sendClipboardData(const char* data)
518{
519 writer()->writeClientCutText(data);
520}
521
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200522void CConnection::refreshFramebuffer()
523{
524 forceNonincremental = true;
525
526 // Without continuous updates we have to make sure we only have a
527 // single update in flight, so we'll have to wait to do the refresh
528 if (continuousUpdates)
529 requestNewUpdate();
530}
531
532void CConnection::setPreferredEncoding(int encoding)
533{
534 if (preferredEncoding == encoding)
535 return;
536
537 preferredEncoding = encoding;
538 encodingChange = true;
539}
540
541int CConnection::getPreferredEncoding()
542{
543 return preferredEncoding;
544}
545
546void CConnection::setCompressLevel(int level)
547{
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200548 if (compressLevel == level)
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200549 return;
550
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200551 compressLevel = level;
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200552 encodingChange = true;
553}
554
555void CConnection::setQualityLevel(int level)
556{
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200557 if (qualityLevel == level)
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200558 return;
559
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200560 qualityLevel = level;
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200561 encodingChange = true;
562}
563
564void CConnection::setPF(const PixelFormat& pf)
565{
566 if (server.pf().equal(pf) && !formatChange)
567 return;
568
569 nextPF = pf;
570 formatChange = true;
571}
572
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000573void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
574{
575 CMsgHandler::fence(flags, len, data);
576
577 if (!(flags & fenceFlagRequest))
578 return;
579
580 // We cannot guarantee any synchronisation at this level
581 flags = 0;
582
583 writer()->writeFence(flags, len, data);
584}
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200585
586// requestNewUpdate() requests an update from the server, having set the
587// format and encoding appropriately.
588void CConnection::requestNewUpdate()
589{
590 if (formatChange && !pendingPFChange) {
591 /* Catch incorrect requestNewUpdate calls */
592 assert(!pendingUpdate || continuousUpdates);
593
594 // We have to make sure we switch the internal format at a safe
595 // time. For continuous updates we temporarily disable updates and
596 // look for a EndOfContinuousUpdates message to see when to switch.
597 // For classical updates we just got a new update right before this
598 // function was called, so we need to make sure we finish that
599 // update before we can switch.
600
601 pendingPFChange = true;
602 pendingPF = nextPF;
603
604 if (continuousUpdates)
605 writer()->writeEnableContinuousUpdates(false, 0, 0, 0, 0);
606
607 writer()->writeSetPixelFormat(pendingPF);
608
609 if (continuousUpdates)
610 writer()->writeEnableContinuousUpdates(true, 0, 0,
611 server.width(),
612 server.height());
613
614 formatChange = false;
615 }
616
617 if (encodingChange) {
Pierre Ossman96728352018-06-20 11:35:05 +0200618 updateEncodings();
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200619 encodingChange = false;
620 }
621
622 if (forceNonincremental || !continuousUpdates) {
623 pendingUpdate = true;
624 writer()->writeFramebufferUpdateRequest(Rect(0, 0,
625 server.width(),
626 server.height()),
627 !forceNonincremental);
628 }
629
630 forceNonincremental = false;
631}
Pierre Ossman96728352018-06-20 11:35:05 +0200632
633// Ask for encodings based on which decoders are supported. Assumes higher
634// encoding numbers are more desirable.
635
636void CConnection::updateEncodings()
637{
Pierre Ossman1143ee62018-06-20 11:40:37 +0200638 std::list<rdr::U32> encodings;
Pierre Ossman96728352018-06-20 11:35:05 +0200639
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200640 if (supportsLocalCursor) {
Pierre Ossman1143ee62018-06-20 11:40:37 +0200641 encodings.push_back(pseudoEncodingCursorWithAlpha);
Pierre Ossman4a6266f2018-11-05 16:28:18 +0100642 encodings.push_back(pseudoEncodingVMwareCursor);
Pierre Ossman1143ee62018-06-20 11:40:37 +0200643 encodings.push_back(pseudoEncodingCursor);
644 encodings.push_back(pseudoEncodingXCursor);
Pierre Ossman96728352018-06-20 11:35:05 +0200645 }
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200646 if (supportsDesktopResize) {
Pierre Ossman1143ee62018-06-20 11:40:37 +0200647 encodings.push_back(pseudoEncodingDesktopSize);
Pierre Ossman1143ee62018-06-20 11:40:37 +0200648 encodings.push_back(pseudoEncodingExtendedDesktopSize);
Pierre Ossman6ea58ba2018-06-20 15:47:49 +0200649 }
Pierre Ossman62b07862018-11-05 16:28:57 +0100650 if (supportsLEDState) {
Pierre Ossman1143ee62018-06-20 11:40:37 +0200651 encodings.push_back(pseudoEncodingLEDState);
Pierre Ossman62b07862018-11-05 16:28:57 +0100652 encodings.push_back(pseudoEncodingVMwareLEDState);
653 }
Pierre Ossman96728352018-06-20 11:35:05 +0200654
Pierre Ossman5588f4f2018-06-20 11:48:17 +0200655 encodings.push_back(pseudoEncodingDesktopName);
Pierre Ossman1143ee62018-06-20 11:40:37 +0200656 encodings.push_back(pseudoEncodingLastRect);
657 encodings.push_back(pseudoEncodingContinuousUpdates);
658 encodings.push_back(pseudoEncodingFence);
659 encodings.push_back(pseudoEncodingQEMUKeyEvent);
Pierre Ossman96728352018-06-20 11:35:05 +0200660
661 if (Decoder::supported(preferredEncoding)) {
Pierre Ossman1143ee62018-06-20 11:40:37 +0200662 encodings.push_back(preferredEncoding);
Pierre Ossman96728352018-06-20 11:35:05 +0200663 }
664
Pierre Ossman1143ee62018-06-20 11:40:37 +0200665 encodings.push_back(encodingCopyRect);
Pierre Ossman96728352018-06-20 11:35:05 +0200666
Pierre Ossman96728352018-06-20 11:35:05 +0200667 for (int i = encodingMax; i >= 0; i--) {
Pierre Ossman3bbe8d72018-06-20 11:42:36 +0200668 if ((i != preferredEncoding) && Decoder::supported(i))
669 encodings.push_back(i);
Pierre Ossman96728352018-06-20 11:35:05 +0200670 }
671
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200672 if (compressLevel >= 0 && compressLevel <= 9)
673 encodings.push_back(pseudoEncodingCompressLevel0 + compressLevel);
674 if (qualityLevel >= 0 && qualityLevel <= 9)
675 encodings.push_back(pseudoEncodingQualityLevel0 + qualityLevel);
Pierre Ossman96728352018-06-20 11:35:05 +0200676
Pierre Ossman1143ee62018-06-20 11:40:37 +0200677 writer()->writeSetEncodings(encodings);
Pierre Ossman96728352018-06-20 11:35:05 +0200678}