blob: d2e85440da0d5078d767b7197a9d8144b2d31f18 [file] [log] [blame]
Pierre Ossman5156d5e2011-03-09 09:42:34 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
DRC33c15e32011-11-03 18:49:21 +00002 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +01003 * Copyright 2009-2014 Pierre Ossman for Cendio AB
Pierre Ossman5156d5e2011-03-09 09:42:34 +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
Peter Åstrandc359f362011-08-23 12:04:46 +000021#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
Pierre Ossman5156d5e2011-03-09 09:42:34 +000025#include <assert.h>
DRCb65bb932011-06-24 03:17:00 +000026#ifndef _WIN32
Pierre Ossman5156d5e2011-03-09 09:42:34 +000027#include <unistd.h>
DRCb65bb932011-06-24 03:17:00 +000028#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +000029
30#include <rfb/CMsgWriter.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010031#include <rfb/CSecurity.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000032#include <rfb/Hostname.h>
33#include <rfb/LogWriter.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010034#include <rfb/Security.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000035#include <rfb/util.h>
36#include <rfb/screenTypes.h>
Pierre Ossmane28bdb22011-11-14 16:02:06 +000037#include <rfb/fenceTypes.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000038#include <rfb/Timer.h>
39#include <network/TcpSocket.h>
Pierre Ossman5d055462018-05-03 14:04:38 +020040#ifndef WIN32
41#include <network/UnixSocket.h>
42#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +000043
44#include <FL/Fl.H>
45#include <FL/fl_ask.H>
46
47#include "CConn.h"
Pierre Ossmanf4f30942011-05-17 09:39:07 +000048#include "OptionsDialog.h"
Pierre Ossman947b48d2014-01-27 16:52:35 +010049#include "DesktopWindow.h"
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +010050#include "PlatformPixelBuffer.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000051#include "i18n.h"
52#include "parameters.h"
Pierre Ossman39ceb502011-07-12 15:54:25 +000053#include "vncviewer.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000054
DRCb65bb932011-06-24 03:17:00 +000055#ifdef WIN32
56#include "win32.h"
57#endif
58
Pierre Ossman5156d5e2011-03-09 09:42:34 +000059using namespace rdr;
60using namespace rfb;
61using namespace std;
62
Pierre Ossman5156d5e2011-03-09 09:42:34 +000063static rfb::LogWriter vlog("CConn");
64
Pierre Ossmancf836f22011-07-14 14:34:09 +000065// 8 colours (1 bit per component)
66static const PixelFormat verylowColourPF(8, 3,false, true,
67 1, 1, 1, 2, 1, 0);
68// 64 colours (2 bits per component)
69static const PixelFormat lowColourPF(8, 6, false, true,
70 3, 3, 3, 4, 2, 0);
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010071// 256 colours (2-3 bits per component)
72static const PixelFormat mediumColourPF(8, 8, false, true,
73 7, 7, 3, 5, 2, 0);
Pierre Ossmanf4f30942011-05-17 09:39:07 +000074
Pierre Ossman2a7a8d62013-02-15 08:33:39 +000075CConn::CConn(const char* vncServerName, network::Socket* socket=NULL)
76 : serverHost(0), serverPort(0), desktop(NULL),
Pierre Ossmanef6881b2018-06-20 11:26:18 +020077 updateCount(0), pixelCount(0),
78 lastServerEncoding((unsigned int)-1)
Pierre Ossman5156d5e2011-03-09 09:42:34 +000079{
80 setShared(::shared);
Pierre Ossman2a7a8d62013-02-15 08:33:39 +000081 sock = socket;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000082
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020083 server.supportsLocalCursor = true;
Pierre Ossmanda389562011-06-09 08:24:37 +000084
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020085 server.supportsDesktopResize = true;
86 server.supportsExtendedDesktopSize = true;
87 server.supportsDesktopRename = true;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000088
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +020089 server.supportsLEDState = true;
Pierre Ossman2fa63f82016-12-05 15:26:21 +010090
Pierre Ossmana22459d2014-03-17 14:42:10 +010091 if (customCompressLevel)
Pierre Ossmanef6881b2018-06-20 11:26:18 +020092 setCompressLevel(compressLevel);
Pierre Ossman5156d5e2011-03-09 09:42:34 +000093
Pierre Ossmana22459d2014-03-17 14:42:10 +010094 if (!noJpeg)
Pierre Ossmanef6881b2018-06-20 11:26:18 +020095 setQualityLevel(qualityLevel);
Pierre Ossman5156d5e2011-03-09 09:42:34 +000096
Pierre Ossman2a7a8d62013-02-15 08:33:39 +000097 if(sock == NULL) {
98 try {
Pierre Ossman5d055462018-05-03 14:04:38 +020099#ifndef WIN32
100 if (strchr(vncServerName, '/') != NULL) {
101 sock = new network::UnixSocket(vncServerName);
102 serverHost = sock->getPeerAddress();
Pierre Ossman4af93a92018-10-25 10:32:38 +0200103 vlog.info(_("Connected to socket %s"), serverHost);
Pierre Ossman5d055462018-05-03 14:04:38 +0200104 } else
105#endif
106 {
107 getHostAndPort(vncServerName, &serverHost, &serverPort);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000108
Pierre Ossman5d055462018-05-03 14:04:38 +0200109 sock = new network::TcpSocket(serverHost, serverPort);
Pierre Ossman4af93a92018-10-25 10:32:38 +0200110 vlog.info(_("Connected to host %s port %d"), serverHost, serverPort);
Pierre Ossman5d055462018-05-03 14:04:38 +0200111 }
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000112 } catch (rdr::Exception& e) {
113 vlog.error("%s", e.str());
Dr. David Alan Gilbertf4d1d892017-07-11 12:11:50 +0100114 if (alertOnFatalError)
115 fl_alert("%s", e.str());
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000116 exit_vncviewer();
117 return;
118 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000119 }
120
121 Fl::add_fd(sock->getFd(), FL_READ | FL_EXCEPT, socketEvent, this);
122
123 // See callback below
124 sock->inStream().setBlockCallback(this);
125
126 setServerName(serverHost);
127 setStreams(&sock->inStream(), &sock->outStream());
128
129 initialiseProtocol();
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000130
131 OptionsDialog::addCallback(handleOptions, this);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000132}
133
134CConn::~CConn()
135{
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000136 OptionsDialog::removeCallback(handleOptions);
Pierre Ossmand9b90032015-09-23 12:18:52 +0200137 Fl::remove_timeout(handleUpdateTimeout, this);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000138
Pierre Ossman6a9e2e62011-05-19 14:47:43 +0000139 if (desktop)
140 delete desktop;
141
Pierre Ossman5e04c262011-11-09 11:31:12 +0000142 delete [] serverHost;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000143 if (sock)
144 Fl::remove_fd(sock->getFd());
145 delete sock;
146}
147
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000148const char *CConn::connectionInfo()
149{
150 static char infoText[1024] = "";
151
Pierre Ossmanb2046432014-09-22 11:17:34 +0200152 char scratch[100];
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000153 char pfStr[100];
Pierre Ossmanb2046432014-09-22 11:17:34 +0200154
155 // Crude way of avoiding constant overflow checks
156 assert((sizeof(scratch) + 1) * 10 < sizeof(infoText));
157
158 infoText[0] = '\0';
159
160 snprintf(scratch, sizeof(scratch),
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200161 _("Desktop name: %.80s"), server.name());
Pierre Ossmanb2046432014-09-22 11:17:34 +0200162 strcat(infoText, scratch);
163 strcat(infoText, "\n");
164
165 snprintf(scratch, sizeof(scratch),
166 _("Host: %.80s port: %d"), serverHost, serverPort);
167 strcat(infoText, scratch);
168 strcat(infoText, "\n");
169
170 snprintf(scratch, sizeof(scratch),
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200171 _("Size: %d x %d"), server.width(), server.height());
Pierre Ossmanb2046432014-09-22 11:17:34 +0200172 strcat(infoText, scratch);
173 strcat(infoText, "\n");
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000174
Pierre Ossman744e55c2014-12-03 14:00:54 +0100175 // TRANSLATORS: Will be filled in with a string describing the
176 // protocol pixel format in a fairly language neutral way
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200177 server.pf().print(pfStr, 100);
Pierre Ossmanb2046432014-09-22 11:17:34 +0200178 snprintf(scratch, sizeof(scratch),
179 _("Pixel format: %s"), pfStr);
180 strcat(infoText, scratch);
181 strcat(infoText, "\n");
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000182
Pierre Ossman744e55c2014-12-03 14:00:54 +0100183 // TRANSLATORS: Similar to the earlier "Pixel format" string
Pierre Ossmanb2046432014-09-22 11:17:34 +0200184 serverPF.print(pfStr, 100);
185 snprintf(scratch, sizeof(scratch),
186 _("(server default %s)"), pfStr);
187 strcat(infoText, scratch);
188 strcat(infoText, "\n");
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000189
Pierre Ossmanb2046432014-09-22 11:17:34 +0200190 snprintf(scratch, sizeof(scratch),
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200191 _("Requested encoding: %s"), encodingName(getPreferredEncoding()));
Pierre Ossmanb2046432014-09-22 11:17:34 +0200192 strcat(infoText, scratch);
193 strcat(infoText, "\n");
194
195 snprintf(scratch, sizeof(scratch),
196 _("Last used encoding: %s"), encodingName(lastServerEncoding));
197 strcat(infoText, scratch);
198 strcat(infoText, "\n");
199
200 snprintf(scratch, sizeof(scratch),
201 _("Line speed estimate: %d kbit/s"), sock->inStream().kbitsPerSecond());
202 strcat(infoText, scratch);
203 strcat(infoText, "\n");
204
205 snprintf(scratch, sizeof(scratch),
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200206 _("Protocol version: %d.%d"), server.majorVersion, server.minorVersion);
Pierre Ossmanb2046432014-09-22 11:17:34 +0200207 strcat(infoText, scratch);
208 strcat(infoText, "\n");
209
210 snprintf(scratch, sizeof(scratch),
211 _("Security method: %s"), secTypeName(csecurity->getType()));
212 strcat(infoText, scratch);
213 strcat(infoText, "\n");
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000214
215 return infoText;
216}
217
Pierre Ossmanfecf0a42018-03-26 12:22:47 +0200218unsigned CConn::getUpdateCount()
Pierre Ossman921f6c82017-02-24 12:33:09 +0100219{
Pierre Ossmanfecf0a42018-03-26 12:22:47 +0200220 return updateCount;
Pierre Ossman921f6c82017-02-24 12:33:09 +0100221}
222
223unsigned CConn::getPixelCount()
224{
225 return pixelCount;
226}
227
228unsigned CConn::getPosition()
229{
230 return sock->inStream().pos();
231}
232
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000233// The RFB core is not properly asynchronous, so it calls this callback
234// whenever it needs to block to wait for more data. Since FLTK is
235// monitoring the socket, we just make sure FLTK gets to run.
236
237void CConn::blockCallback()
238{
Pierre Ossman5102fa92015-11-18 16:23:21 +0100239 run_mainloop();
Pierre Ossman1db73242015-09-23 12:20:32 +0200240
241 if (should_exit())
242 throw rdr::Exception("Termination requested");
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000243}
244
DRC3e7ed812013-02-26 10:34:22 +0000245void CConn::socketEvent(FL_SOCKET fd, void *data)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000246{
247 CConn *cc;
248 static bool recursing = false;
249
250 assert(data);
251 cc = (CConn*)data;
252
253 // I don't think processMsg() is recursion safe, so add this check
254 if (recursing)
255 return;
256
257 recursing = true;
258
259 try {
260 // processMsg() only processes one message, so we need to loop
261 // until the buffers are empty or things will stall.
262 do {
263 cc->processMsg();
Pierre Ossman9a73adf2016-12-29 16:52:56 +0100264
265 // Make sure that the FLTK handling and the timers gets some CPU
266 // time in case of back to back messages
267 Fl::check();
268 Timer::checkTimeouts();
269
270 // Also check if we need to stop reading and terminate
271 if (should_exit())
272 break;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000273 } while (cc->sock->inStream().checkNoWait(1));
274 } catch (rdr::EndOfStream& e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000275 vlog.info("%s", e.str());
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000276 exit_vncviewer();
277 } catch (rdr::Exception& e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000278 vlog.error("%s", e.str());
Pierre Ossman1db73242015-09-23 12:20:32 +0200279 // Somebody might already have requested us to terminate, and
280 // might have already provided an error message.
281 if (!should_exit())
282 exit_vncviewer(e.str());
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000283 }
284
285 recursing = false;
286}
287
288////////////////////// CConnection callback methods //////////////////////
289
Pierre Ossman2affd772018-06-20 07:03:10 +0200290// initDone() is called when the serverInit message has been received. At
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000291// this point we create the desktop window and display it. We also tell the
292// server the pixel format and encodings to use and request the first update.
Pierre Ossman2affd772018-06-20 07:03:10 +0200293void CConn::initDone()
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000294{
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000295 // If using AutoSelect with old servers, start in FullColor
296 // mode. See comment in autoSelectFormatAndEncoding.
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200297 if (server.beforeVersion(3, 8) && autoSelect)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000298 fullColour.setParam(true);
299
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200300 serverPF = server.pf();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000301
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200302 desktop = new DesktopWindow(server.width(), server.height(),
303 server.name(), serverPF, this);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000304 fullColourPF = desktop->getPreferredPF();
305
Pierre Ossman5d512c32011-11-04 11:42:16 +0000306 // Force a switch to the format and encoding we'd like
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200307 updatePixelFormat();
308 int encNum = encodingNum(::preferredEncoding);
309 if (encNum != -1)
310 setPreferredEncoding(encNum);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000311}
312
313// setDesktopSize() is called when the desktop size changes (including when
314// it is set initially).
315void CConn::setDesktopSize(int w, int h)
316{
317 CConnection::setDesktopSize(w,h);
318 resizeFramebuffer();
319}
320
321// setExtendedDesktopSize() is a more advanced version of setDesktopSize()
Pierre Ossman28c1d542015-03-03 16:27:44 +0100322void CConn::setExtendedDesktopSize(unsigned reason, unsigned result,
323 int w, int h, const rfb::ScreenSet& layout)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000324{
325 CConnection::setExtendedDesktopSize(reason, result, w, h, layout);
326
327 if ((reason == reasonClient) && (result != resultSuccess)) {
328 vlog.error(_("SetDesktopSize failed: %d"), result);
329 return;
330 }
331
332 resizeFramebuffer();
333}
334
335// setName() is called when the desktop name changes
336void CConn::setName(const char* name)
337{
338 CConnection::setName(name);
Pierre Ossmandd45b442018-10-31 17:08:59 +0100339 desktop->setName(name);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000340}
341
342// framebufferUpdateStart() is called at the beginning of an update.
343// Here we try to send out a new framebuffer update request so that the
344// next update can be sent out in parallel with us decoding the current
Pierre Ossman5d512c32011-11-04 11:42:16 +0000345// one.
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000346void CConn::framebufferUpdateStart()
347{
Pierre Ossman3da238d2015-11-12 12:20:05 +0100348 CConnection::framebufferUpdateStart();
349
Pierre Ossmand9b90032015-09-23 12:18:52 +0200350 // Update the screen prematurely for very slow updates
351 Fl::add_timeout(1.0, handleUpdateTimeout, this);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000352}
353
354// framebufferUpdateEnd() is called at the end of an update.
355// For each rectangle, the FdInStream will have timed the speed
356// of the connection, allowing us to select format and encoding
357// appropriately, and then request another incremental update.
358void CConn::framebufferUpdateEnd()
359{
Pierre Ossman3da238d2015-11-12 12:20:05 +0100360 CConnection::framebufferUpdateEnd();
361
Pierre Ossmanfecf0a42018-03-26 12:22:47 +0200362 updateCount++;
Pierre Ossman921f6c82017-02-24 12:33:09 +0100363
Pierre Ossmand9b90032015-09-23 12:18:52 +0200364 Fl::remove_timeout(handleUpdateTimeout, this);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000365 desktop->updateWindow();
366
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000367 // Compute new settings based on updated bandwidth values
368 if (autoSelect)
369 autoSelectFormatAndEncoding();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000370}
371
372// The rest of the callbacks are fairly self-explanatory...
373
374void CConn::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
375{
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200376 vlog.error(_("Invalid SetColourMapEntries from server!"));
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000377}
378
379void CConn::bell()
380{
381 fl_beep();
382}
383
384void CConn::serverCutText(const char* str, rdr::U32 len)
385{
Pierre Ossman4c204232018-03-26 13:32:49 +0200386 desktop->serverCutText(str, len);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000387}
388
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100389void CConn::dataRect(const Rect& r, int encoding)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000390{
391 sock->inStream().startTiming();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000392
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100393 if (encoding != encodingCopyRect)
394 lastServerEncoding = encoding;
395
Pierre Ossman9f273e92015-11-09 16:34:54 +0100396 CConnection::dataRect(r, encoding);
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100397
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000398 sock->inStream().stopTiming();
Pierre Ossman921f6c82017-02-24 12:33:09 +0100399
400 pixelCount += r.area();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000401}
402
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000403void CConn::setCursor(int width, int height, const Point& hotspot,
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100404 const rdr::U8* data)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000405{
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100406 desktop->setCursor(width, height, hotspot, data);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000407}
408
Pierre Ossmane28bdb22011-11-14 16:02:06 +0000409void CConn::fence(rdr::U32 flags, unsigned len, const char data[])
410{
411 CMsgHandler::fence(flags, len, data);
412
413 if (flags & fenceFlagRequest) {
414 // We handle everything synchronously so we trivially honor these modes
415 flags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter);
416
417 writer()->writeFence(flags, len, data);
418 return;
419 }
Pierre Ossman6eecbca2018-06-19 10:42:24 +0200420}
Pierre Ossmane28bdb22011-11-14 16:02:06 +0000421
Pierre Ossman2fa63f82016-12-05 15:26:21 +0100422void CConn::setLEDState(unsigned int state)
423{
424 CConnection::setLEDState(state);
425
426 desktop->setLEDState(state);
427}
428
DRC33c15e32011-11-03 18:49:21 +0000429
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000430////////////////////// Internal methods //////////////////////
431
432void CConn::resizeFramebuffer()
433{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200434 desktop->resizeFramebuffer(server.width(), server.height());
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000435}
436
437// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
438// to the connection speed:
439//
440// First we wait for at least one second of bandwidth measurement.
441//
442// Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,
443// which should be perceptually lossless.
444//
445// If the bandwidth is below that, we choose a more lossy JPEG quality.
446//
447// If the bandwidth drops below 256 Kbps, we switch to palette mode.
448//
449// Note: The system here is fairly arbitrary and should be replaced
450// with something more intelligent at the server end.
451//
452void CConn::autoSelectFormatAndEncoding()
453{
454 int kbitsPerSecond = sock->inStream().kbitsPerSecond();
455 unsigned int timeWaited = sock->inStream().timeWaited();
456 bool newFullColour = fullColour;
457 int newQualityLevel = qualityLevel;
458
459 // Always use Tight
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200460 setPreferredEncoding(encodingTight);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000461
462 // Check that we have a decent bandwidth measurement
463 if ((kbitsPerSecond == 0) || (timeWaited < 10000))
464 return;
465
466 // Select appropriate quality level
467 if (!noJpeg) {
468 if (kbitsPerSecond > 16000)
469 newQualityLevel = 8;
470 else
471 newQualityLevel = 6;
472
473 if (newQualityLevel != qualityLevel) {
474 vlog.info(_("Throughput %d kbit/s - changing to quality %d"),
475 kbitsPerSecond, newQualityLevel);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000476 qualityLevel.setParam(newQualityLevel);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200477 setQualityLevel(newQualityLevel);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000478 }
479 }
480
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200481 if (server.beforeVersion(3, 8)) {
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000482 // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
483 // cursors "asynchronously". If this happens in the middle of a
484 // pixel format change, the server will encode the cursor with
485 // the old format, but the client will try to decode it
486 // according to the new format. This will lead to a
487 // crash. Therefore, we do not allow automatic format change for
488 // old servers.
489 return;
490 }
491
492 // Select best color level
493 newFullColour = (kbitsPerSecond > 256);
494 if (newFullColour != fullColour) {
Pierre Ossman4af93a92018-10-25 10:32:38 +0200495 if (newFullColour)
496 vlog.info(_("Throughput %d kbit/s - full color is now enabled"),
497 kbitsPerSecond);
498 else
499 vlog.info(_("Throughput %d kbit/s - full color is now disabled"),
500 kbitsPerSecond);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000501 fullColour.setParam(newFullColour);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200502 updatePixelFormat();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000503 }
504}
505
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000506// requestNewUpdate() requests an update from the server, having set the
507// format and encoding appropriately.
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200508void CConn::updatePixelFormat()
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000509{
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200510 PixelFormat pf;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000511
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200512 if (fullColour) {
513 pf = fullColourPF;
514 } else {
515 if (lowColourLevel == 0)
516 pf = verylowColourPF;
517 else if (lowColourLevel == 1)
518 pf = lowColourPF;
519 else
520 pf = mediumColourPF;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000521 }
Pierre Ossmand4c61ce2011-04-29 11:18:12 +0000522
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200523 char str[256];
524 pf.print(str, 256);
525 vlog.info(_("Using pixel format %s"),str);
526 setPF(pf);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000527}
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000528
529void CConn::handleOptions(void *data)
530{
531 CConn *self = (CConn*)data;
532
533 // Checking all the details of the current set of encodings is just
534 // a pain. Assume something has changed, as resending the encoding
535 // list is cheap. Avoid overriding what the auto logic has selected
536 // though.
537 if (!autoSelect) {
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200538 int encNum = encodingNum(::preferredEncoding);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000539
540 if (encNum != -1)
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200541 self->setPreferredEncoding(encNum);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000542 }
543
Pierre Ossmana22459d2014-03-17 14:42:10 +0100544 if (customCompressLevel)
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200545 self->setCompressLevel(compressLevel);
Pierre Ossmana22459d2014-03-17 14:42:10 +0100546 else
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200547 self->setCompressLevel(-1);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000548
Pierre Ossmana22459d2014-03-17 14:42:10 +0100549 if (!noJpeg && !autoSelect)
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200550 self->setQualityLevel(qualityLevel);
Pierre Ossmana22459d2014-03-17 14:42:10 +0100551 else
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200552 self->setQualityLevel(-1);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000553
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200554 self->updatePixelFormat();
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000555}
Pierre Ossmand9b90032015-09-23 12:18:52 +0200556
557void CConn::handleUpdateTimeout(void *data)
558{
559 CConn *self = (CConn *)data;
560
561 assert(self);
562
563 self->desktop->updateWindow();
564
565 Fl::repeat_timeout(1.0, handleUpdateTimeout, data);
566}